From: Jaromír Mikeš Date: Thu, 3 Sep 2015 01:47:59 +0000 (+0200) Subject: Imported Upstream version 0.10.1~dfsg1 X-Git-Tag: archive/raspbian/0.15.4+ds1-1+rpi1^2~12^2~20 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=3e72421251cef1f9a4239ed179d557f7e0ae62c5;p=giada.git Imported Upstream version 0.10.1~dfsg1 --- diff --git a/ChangeLog b/ChangeLog index b662cd6..b38ea26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,14 @@ +0.10.1 --- 2015 . ? . ? +- Remove support for patches created with Giada < 0.6.x +- Massive source folders refactoring +- Improved usability of "play" buttons for channels +- Fix check for configured soundsystem (would break compilation on g++5) +- Small fixes and cleanup in Makefile.am + + 0.10.0 --- 2015 . 07 . 05 - MIDI lightning output - Other minor fixes diff --git a/Makefile.am b/Makefile.am index e1c45f3..16a0685 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1,194 @@ -SUBDIRS=src \ No newline at end of file +AUTOMAKE_OPTIONS = foreign + +AM_CXXFLAGS = -Wall -pedantic -Werror + +bin_PROGRAMS = giada + +giada_SOURCES = \ +src/main.cpp \ +src/core/const.h \ +src/core/channel.h \ +src/core/channel.cpp \ +src/core/sampleChannel.h \ +src/core/sampleChannel.cpp \ +src/core/midiChannel.h \ +src/core/midiChannel.cpp \ +src/core/midiMapConf.h \ +src/core/midiMapConf.cpp \ +src/core/conf.h \ +src/core/conf.cpp \ +src/core/kernelAudio.h \ +src/core/kernelAudio.cpp \ +src/core/pluginHost.h \ +src/core/pluginHost.cpp \ +src/core/mixerHandler.h \ +src/core/mixerHandler.cpp \ +src/core/init.h \ +src/core/init.cpp \ +src/core/plugin.h \ +src/core/plugin.cpp \ +src/core/wave.h \ +src/core/wave.cpp \ +src/core/waveFx.h \ +src/core/waveFx.cpp \ +src/core/kernelMidi.h \ +src/core/kernelMidi.cpp \ +src/core/graphics.h \ +src/core/graphics.cpp \ +src/core/patch.h \ +src/core/patch.cpp \ +src/core/recorder.h \ +src/core/recorder.cpp \ +src/core/mixer.h \ +src/core/mixer.cpp \ +src/core/dataStorage.h \ +src/core/dataStorage.cpp \ +src/glue/glue.h \ +src/glue/glue.cpp \ +src/gui/dialogs/gd_keyGrabber.h \ +src/gui/dialogs/gd_keyGrabber.cpp \ +src/gui/dialogs/gd_about.h \ +src/gui/dialogs/gd_about.cpp \ +src/gui/dialogs/gd_mainWindow.h \ +src/gui/dialogs/gd_mainWindow.cpp \ +src/gui/dialogs/gd_beatsInput.h \ +src/gui/dialogs/gd_beatsInput.cpp \ +src/gui/dialogs/gd_warnings.h \ +src/gui/dialogs/gd_warnings.cpp \ +src/gui/dialogs/gd_bpmInput.h \ +src/gui/dialogs/gd_bpmInput.cpp \ +src/gui/dialogs/gd_browser.h \ +src/gui/dialogs/gd_browser.cpp \ +src/gui/dialogs/gd_config.h \ +src/gui/dialogs/gd_config.cpp \ +src/gui/dialogs/gd_devInfo.h \ +src/gui/dialogs/gd_devInfo.cpp \ +src/gui/dialogs/gd_pluginList.h \ +src/gui/dialogs/gd_pluginList.cpp \ +src/gui/dialogs/gd_pluginWindow.h \ +src/gui/dialogs/gd_pluginWindow.cpp \ +src/gui/dialogs/gd_editor.h \ +src/gui/dialogs/gd_editor.cpp \ +src/gui/dialogs/gd_pluginWindowGUI.h \ +src/gui/dialogs/gd_pluginWindowGUI.cpp \ +src/gui/dialogs/gd_midiOutput.h \ +src/gui/dialogs/gd_midiOutput.cpp \ +src/gui/dialogs/gd_midiInput.h \ +src/gui/dialogs/gd_midiInput.cpp \ +src/gui/dialogs/gd_actionEditor.h \ +src/gui/dialogs/gd_actionEditor.cpp \ +src/gui/elems/ge_column.h \ +src/gui/elems/ge_column.cpp \ +src/gui/elems/ge_sampleChannel.h \ +src/gui/elems/ge_sampleChannel.cpp \ +src/gui/elems/ge_midiChannel.h \ +src/gui/elems/ge_midiChannel.cpp \ +src/gui/elems/ge_midiIoTools.h \ +src/gui/elems/ge_midiIoTools.cpp \ +src/gui/elems/ge_mixed.h \ +src/gui/elems/ge_mixed.cpp \ +src/gui/elems/ge_waveform.h \ +src/gui/elems/ge_waveform.cpp \ +src/gui/elems/ge_browser.h \ +src/gui/elems/ge_browser.cpp \ +src/gui/elems/ge_actionWidget.h \ +src/gui/elems/ge_actionWidget.cpp \ +src/gui/elems/ge_envelopeChannel.h \ +src/gui/elems/ge_envelopeChannel.cpp \ +src/gui/elems/ge_pianoRoll.h \ +src/gui/elems/ge_pianoRoll.cpp \ +src/gui/elems/ge_channel.h \ +src/gui/elems/ge_channel.cpp \ +src/gui/elems/ge_muteChannel.h \ +src/gui/elems/ge_muteChannel.cpp \ +src/gui/elems/ge_actionChannel.h \ +src/gui/elems/ge_actionChannel.cpp \ +src/gui/elems/ge_window.h \ +src/gui/elems/ge_window.cpp \ +src/gui/elems/ge_status.h \ +src/gui/elems/ge_status.cpp \ +src/gui/elems/ge_keyboard.h \ +src/gui/elems/ge_keyboard.cpp \ +src/gui/elems/ge_waveTools.h \ +src/gui/elems/ge_waveTools.cpp \ +src/gui/elems/ge_modeBox.h \ +src/gui/elems/ge_modeBox.cpp \ +src/gui/elems/ge_controller.h \ +src/gui/elems/ge_controller.cpp \ +src/gui/elems/ge_channelButton.h \ +src/gui/elems/ge_channelButton.cpp \ +src/utils/log.h \ +src/utils/log.cpp \ +src/utils/gui_utils.h \ +src/utils/gui_utils.cpp \ +src/utils/utils.h \ +src/utils/utils.cpp + +# Check for environment: these vars are defined via AM_CONDITIONAL +# inside configure.ac + +if LINUX +giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm \ + src/deps/rtaudio-mod/librtaudio.a -ljack -lasound -lpthread -ldl \ + -lpulse-simple -lpulse -lsamplerate -lrtmidi +#giada_LDFLAGS = -DWITH_VST +endif +if WINDOWS +giada_LDADD = -lrtaudio -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \ + -lshell32 -lvfw32 -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 \ + -lsndfile -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser \ + -lpthreadGC2 +giada_LDFLAGS = -mwindows -static +giada_SOURCES += resource.rc +endif +if OSX +giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \ + -lsamplerate +giada_CXXFLAGS = -m32 +giada_LDFLAGS = -m32 -framework CoreAudio -framework Cocoa -framework Carbon \ + -framework CoreMIDI -framework CoreFoundation +endif + + +# used only under MinGW to compile the resource.rc file (program icon) + +resource.o: + windres src/ext/resource.rc -o resource.o + + +#compile libraries + +#libs: +#if LINUX +# @cd rtaudio-mod; echo "Building RtAudio for Linux..."; \ +# ./configure --with-jack --with-alsa --with-pulse; \ +# make; +#endif +#if WINDOWS +# echo "Building RtAudio for Windows: nothing to do" +#endif +#if OSX +# @cd rtaudio-mod; echo "Building RtAudio for OS X..."; \ +# ./configure --with-core; \ +# make; +#endif + +src/deps/rtaudio-mod/librtaudio.a: + @cd src/deps/rtaudio-mod; echo "Building RtAudio for Linux..."; \ + ./configure --with-jack --with-alsa --with-pulse; \ + make; + +# rename the binaries + +if LINUX +rename: + mv giada giada_lin +endif +if WINDOWS +rename: + mv giada giada_win.exe +endif +if OSX +rename: + mv giada giada_osx +endif diff --git a/configure.ac b/configure.ac index 5bb3b59..a501966 100644 --- a/configure.ac +++ b/configure.ac @@ -6,9 +6,9 @@ AC_PREREQ(2.60) AC_INIT([giada], [0.10], [giadaloopmachine@gmail.com]) AC_CONFIG_SRCDIR([src/main.cpp]) -AM_INIT_AUTOMAKE +AM_INIT_AUTOMAKE([subdir-objects]) -# ---------------------------------------------------------------------- +# ------------------------------------------------------------------------------ # test the build environment. These vars are used in Makefile.am during # the linking of the libraries. @@ -36,7 +36,7 @@ AM_CONDITIONAL(LINUX, test "x$os" = "xlinux") AM_CONDITIONAL(WINDOWS, test "x$os" = "xwindows") AM_CONDITIONAL(OSX, test "x$os" = "xosx") -# ---------------------------------------------------------------------- +# ------------------------------------------------------------------------------ # --enable-vst. VST compilation is disabled by default # @@ -54,16 +54,21 @@ AC_ARG_ENABLE( [AC_DEFINE(WITH_VST)] ) -# ---------------------------------------------------------------------- +# ------------------------------------------------------------------------------ -# Check for programs. +# Check for C++ compiler AC_PROG_CXX + +# Check for C compiler (TODO - is that really needed?) + AC_PROG_CC -AC_PROG_INSTALL + +# Check for make + AC_PROG_MAKE_SET -# ---------------------------------------------------------------------- +# ------------------------------------------------------------------------------ # Check for libraries. @@ -74,7 +79,7 @@ AC_CHECK_LIB( [AC_MSG_ERROR([error: library 'pthread' not found!])] ) -# ---------------------------------------------------------------------- +# ------------------------------------------------------------------------------ # Check for generic headers (fltk, rtaudio and libsndfile are static, # we ask if headers are available) @@ -138,30 +143,12 @@ else fi -# ---------------------------------------------------------------------- +# ------------------------------------------------------------------------------ # Check for linux header files. if test "x$os" = "xlinux"; then -# TODO - -# AC_LANG_PUSH([C++]) -# AC_CHECK_HEADER( -# [X11/extensions/Xext.h], -# [], -# [AC_MSG_ERROR([missing Xext.h, maybe you need to install the libxext-dev package?])] -# ) -# AC_LANG_POP - -# AC_LANG_PUSH([C++]) -# AC_CHECK_HEADER( -# [X11/Xft.h], -# [], -# [AC_MSG_ERROR([missing Xft.h, maybe you need to install the libxft-dev package?])] -# ) -# AC_LANG_POP - AC_LANG_PUSH([C++]) AC_CHECK_HEADER( [X11/xpm.h], @@ -171,9 +158,9 @@ if test "x$os" = "xlinux"; then AC_LANG_POP fi -# ---------------------------------------------------------------------- +# ------------------------------------------------------------------------------ # finalizing -AC_CONFIG_FILES([Makefile src/Makefile]) +AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index a717df4..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,116 +0,0 @@ -AUTOMAKE_OPTIONS = foreign - - - -# aeffect.h (header from VST SDK) uses 'long long' which is not supported -# in ISO C++ 1998 and -Werror flag breaks the compilation. -# This is a workaround, fixes needed. - -if WINDOWS -AM_CXXFLAGS = -Wall -pedantic -else -AM_CXXFLAGS = -Wall -pedantic -Werror -endif - -bin_PROGRAMS = giada - -giada_SOURCES = \ -const.h gd_keyGrabber.h glue.h mixerHandler.cpp \ -gd_about.cpp gd_mainWindow.cpp graphics.cpp mixerHandler.h \ -gd_about.h gd_mainWindow.h graphics.h patch.cpp \ -gd_beatsInput.cpp gd_warnings.cpp ge_mixed.cpp patch.h \ -gd_beatsInput.h gd_warnings.h ge_mixed.h recorder.cpp \ -gd_bpmInput.cpp ge_waveform.cpp gui_utils.cpp recorder.h \ -gd_bpmInput.h ge_waveform.h gui_utils.h utils.cpp \ -gd_browser.cpp init.cpp channel.h utils.h \ -gd_browser.h init.h gd_config.cpp channel.cpp \ -gg_keyboard.cpp kernelAudio.cpp wave.cpp gd_config.h \ -gg_keyboard.h kernelAudio.h waveFx.cpp gd_editor.cpp \ -gg_waveTools.cpp main.cpp waveFx.h gd_editor.h \ -gg_waveTools.h mixer.cpp wave.h gd_keyGrabber.cpp \ -glue.cpp mixer.h ge_browser.h ge_browser.cpp \ -gd_devInfo.cpp gd_devInfo.h plugin.h plugin.cpp \ -pluginHost.h pluginHost.cpp gd_pluginList.h gd_pluginList.cpp \ -gd_pluginWindow.h gd_pluginWindow.cpp ge_window.h ge_window.cpp \ -dataStorage.h dataStorage.cpp conf.h conf.cpp \ -gd_actionEditor.h gd_actionEditor.cpp ge_muteChannel.h ge_muteChannel.cpp \ -ge_actionChannel.h ge_actionChannel.cpp gd_pluginWindowGUI.h gd_pluginWindowGUI.cpp \ -ge_actionWidget.h ge_actionWidget.cpp ge_envelopeChannel.h ge_envelopeChannel.cpp \ -ge_pianoRoll.h ge_pianoRoll.cpp kernelMidi.h kernelMidi.cpp \ -gd_midiOutput.h gd_midiOutput.cpp gd_midiInput.h gd_midiInput.cpp \ -sampleChannel.h sampleChannel.cpp midiChannel.cpp midiChannel.h \ -midiMapConf.cpp midiMapConf.h ge_channel.h ge_channel.cpp \ -log.h log.cpp ge_column.h ge_column.cpp \ -ge_sampleChannel.h ge_sampleChannel.cpp ge_midiChannel.h ge_midiChannel.cpp \ -ge_midiIoTools.h ge_midiIoTools.cpp - - - -# Check for environment: these vars are defined via AM_CONDITIONAL -# inside configure.ac - -if LINUX -giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm \ - rtaudio-mod/librtaudio.a -ljack -lasound -lpthread -ldl \ - -lpulse-simple -lpulse -lsamplerate -lrtmidi -endif -if WINDOWS -giada_LDADD = -lrtaudio -ldsound -lwsock32 -lm -lpthread \ - -lfltk -lwininet -lgdi32 -lshell32 -lvfw32 -lrpcrt4 \ - -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile \ - -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -giada_LDFLAGS = -mwindows -mno-cygwin -static -giada_SOURCES += resource.rc -endif -if OSX -giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \ - -lsamplerate -giada_LDFLAGS = -framework CoreAudio -framework Cocoa -framework Carbon \ - -framework CoreMIDI -framework CoreFoundation -endif - - -# used only under MinGW to compile the resource.rc file (program icon) - -.rc.o: - windres $^ -o $@ -%.o : %.rc - windres $^ -o $@ - - - -#compile libraries - -libs: -if LINUX - @cd rtaudio-mod; echo "Building RtAudio for Linux..."; \ - ./configure --with-jack --with-alsa --with-pulse; \ - make; -endif -if WINDOWS - @cd rtaudio-mod; echo "Building RtAudio for Windows..."; \ - ./configure --with-asio --with-ds; \ - make; -endif -if OSX - @cd rtaudio-mod; echo "Building RtAudio for OS X..."; \ - ./configure --with-core; \ - make; -endif - - - -# rename the binaries - -if LINUX -rename: - mv giada giada_lin -endif -if WINDOWS -rename: - mv giada giada_win.exe -endif -if OSX -rename: - mv giada giada_osx -endif diff --git a/src/channel.cpp b/src/channel.cpp deleted file mode 100644 index 6c4c48e..0000000 --- a/src/channel.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * channel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "channel.h" -#include "pluginHost.h" -#include "kernelMidi.h" -#include "patch.h" -#include "wave.h" -#include "mixer.h" -#include "mixerHandler.h" -#include "conf.h" -#include "waveFx.h" -#include "log.h" -#include "midiMapConf.h" -#include "ge_channel.h" - - -extern Patch G_Patch; -extern Mixer G_Mixer; -extern Conf G_Conf; -extern MidiMapConf G_MidiMap; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -Channel::Channel(int type, int status, int bufferSize) - : bufferSize(bufferSize), - type (type), - status (status), - key (0), - volume (DEFAULT_VOL), - volume_i (1.0f), - volume_d (0.0f), - panLeft (1.0f), - panRight (1.0f), - mute_i (false), - mute_s (false), - mute (false), - solo (false), - hasActions(false), - recStatus (REC_STOPPED), - vChan (NULL), - guiChannel(NULL), - midiIn (true), - midiInKeyPress (0x0), - midiInKeyRel (0x0), - midiInKill (0x0), - midiInVolume (0x0), - midiInMute (0x0), - midiInSolo (0x0), - midiOutL (false), - midiOutLplaying(0x0), - midiOutLmute (0x0), - midiOutLsolo (0x0) -{ - vChan = (float *) malloc(bufferSize * sizeof(float)); - if (!vChan) - gLog("[Channel] unable to alloc memory for vChan\n"); - memset(vChan, 0, bufferSize * sizeof(float)); -} - - -/* -------------------------------------------------------------------------- */ - - -Channel::~Channel() -{ - status = STATUS_OFF; - if (vChan) - free(vChan); -} - - -/* -------------------------------------------------------------------------- */ - - -void Channel::sendMidiLmessage(uint32_t learn, int chan, uint32_t msg, int offset) -{ - gLog("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n", - learn, chan, msg, offset); - - uint32_t out; - - /* isolate 'channel' from learnt message and offset it as requested by 'nn' - * in the midimap configuration file. */ - - out = ((learn & 0x00FF0000) >> 16) << offset; - - /* merge the previously prepared channel into final message, and finally - * send it. */ - - out |= msg | (chan << 24); - kernelMidi::send(out); -} - - -/* -------------------------------------------------------------------------- */ - - -void Channel::readPatchMidiIn(int i) -{ - midiIn = G_Patch.getMidiValue(i, "In"); - midiInKeyPress = G_Patch.getMidiValue(i, "InKeyPress"); - midiInKeyRel = G_Patch.getMidiValue(i, "InKeyRel"); - midiInKill = G_Patch.getMidiValue(i, "InKill"); - midiInVolume = G_Patch.getMidiValue(i, "InVolume"); - midiInMute = G_Patch.getMidiValue(i, "InMute"); - midiInSolo = G_Patch.getMidiValue(i, "InSolo"); -} - -void Channel::readPatchMidiOut(int i) -{ - midiOutL = G_Patch.getMidiValue(i, "OutL"); - midiOutLplaying = G_Patch.getMidiValue(i, "OutLplaying"); - midiOutLmute = G_Patch.getMidiValue(i, "OutLmute"); - midiOutLsolo = G_Patch.getMidiValue(i, "OutLsolo"); -} - - -/* -------------------------------------------------------------------------- */ - - -bool Channel::isPlaying() -{ - return status & (STATUS_PLAY | STATUS_ENDING); -} - - -/* -------------------------------------------------------------------------- */ - - -void Channel::writePatch(FILE *fp, int i, bool isProject) -{ - fprintf(fp, "chanType%d=%d\n", i, type); - fprintf(fp, "chanIndex%d=%d\n", i, index); - fprintf(fp, "chanColumn%d=%d\n", i, guiChannel->getColumnIndex()); - fprintf(fp, "chanMute%d=%d\n", i, mute); - fprintf(fp, "chanMute_s%d=%d\n", i, mute_s); - fprintf(fp, "chanSolo%d=%d\n", i, solo); - fprintf(fp, "chanvol%d=%f\n", i, volume); - fprintf(fp, "chanPanLeft%d=%f\n", i, panLeft); - fprintf(fp, "chanPanRight%d=%f\n", i, panRight); - - fprintf(fp, "chanMidiIn%d=%u\n", i, midiIn); - fprintf(fp, "chanMidiInKeyPress%d=%u\n", i, midiInKeyPress); - fprintf(fp, "chanMidiInKeyRel%d=%u\n", i, midiInKeyRel); - fprintf(fp, "chanMidiInKill%d=%u\n", i, midiInKill); - fprintf(fp, "chanMidiInVolume%d=%u\n", i, midiInVolume); - fprintf(fp, "chanMidiInMute%d=%u\n", i, midiInMute); - fprintf(fp, "chanMidiInSolo%d=%u\n", i, midiInSolo); - - fprintf(fp, "chanMidiOutL%d=%u\n", i, midiOutL); - fprintf(fp, "chanMidiOutLplaying%d=%u\n", i, midiOutLplaying); - fprintf(fp, "chanMidiOutLmute%d=%u\n", i, midiOutLmute); - fprintf(fp, "chanMidiOutLsolo%d=%u\n", i, midiOutLsolo); -} - - -/* -------------------------------------------------------------------------- */ - - -void Channel::sendMidiLmute() -{ - if (!midiOutL || midiOutLmute == 0x0) - return; - if (mute) - sendMidiLmessage(midiOutLsolo, G_MidiMap.muteOnChan, G_MidiMap.muteOnMsg, G_MidiMap.muteOnOffset); - else - sendMidiLmessage(midiOutLsolo, G_MidiMap.muteOffChan, G_MidiMap.muteOffMsg, G_MidiMap.muteOffOffset); -} - - -/* -------------------------------------------------------------------------- */ - - -void Channel::sendMidiLsolo() -{ - if (!midiOutL || midiOutLsolo == 0x0) - return; - if (solo) - sendMidiLmessage(midiOutLsolo, G_MidiMap.soloOnChan, G_MidiMap.soloOnMsg, G_MidiMap.soloOnOffset); - else - sendMidiLmessage(midiOutLsolo, G_MidiMap.soloOffChan, G_MidiMap.soloOffMsg, G_MidiMap.soloOffOffset); -} - - -/* -------------------------------------------------------------------------- */ - - -void Channel::sendMidiLplay() -{ - if (!midiOutL || midiOutLplaying == 0x0) - return; - switch (status) { - case STATUS_OFF: - sendMidiLmessage(midiOutLplaying, G_MidiMap.stoppedChan, G_MidiMap.stoppedMsg, G_MidiMap.stoppedOffset); - break; - case STATUS_PLAY: - sendMidiLmessage(midiOutLplaying, G_MidiMap.playingChan, G_MidiMap.playingMsg, G_MidiMap.playingOffset); - break; - case STATUS_WAIT: - sendMidiLmessage(midiOutLplaying, G_MidiMap.waitingChan, G_MidiMap.waitingMsg, G_MidiMap.waitingOffset); - break; - case STATUS_ENDING: - sendMidiLmessage(midiOutLplaying, G_MidiMap.stoppingChan, G_MidiMap.stoppingMsg, G_MidiMap.stoppingOffset); - } -} diff --git a/src/channel.h b/src/channel.h deleted file mode 100644 index fa34eac..0000000 --- a/src/channel.h +++ /dev/null @@ -1,221 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * channel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef CHANNEL_H -#define CHANNEL_H - - -#include "utils.h" -#include "const.h" -#include "recorder.h" - - -#ifdef WITH_VST - -/* before including aeffetx(x).h we must define __cdecl, otherwise VST - * headers can't be compiled correctly. In windows __cdecl is already - * defined. */ - - #ifdef __GNUC__ - #ifndef _WIN32 - #define __cdecl - #endif - #endif - #include "vst/aeffectx.h" - -#endif - - -class Channel { - -protected: - - /* bufferSize - * size of every buffer in this channel (vChan, pChan) */ - - int bufferSize; - - /* sendMidiLMessage - * compose a MIDI message by merging bytes from MidiMap conf class, and send - * it to KernelMidi. */ - - void sendMidiLmessage(uint32_t learn, int chan, uint32_t msg, int offset); - -public: - - Channel(int type, int status, int bufferSize); - virtual ~Channel(); - - /* writePatch - * store values in patch, writing to *fp. */ - - virtual void writePatch(FILE *fp, int i, bool isProject); - - /* loadByPatch - * load a sample inside a patch. */ - - virtual int loadByPatch(const char *file, int i) = 0; - - /* process - * merge vChannels into buffer, plus plugin processing (if any). */ - - virtual void process(float *buffer) = 0; - - /* start - * action to do when channel starts. doQuantize = false (don't - * quantize) when Mixer is reading actions from Recorder::. */ - - virtual void start(int frame, bool doQuantize) = 0; - - /* stop - * action to do when channel is stopped normally (via key or MIDI). */ - - virtual void stop() = 0; - - /* kill - * action to do when channel stops abruptly. */ - - virtual void kill(int frame) = 0; - - /* mute - * action to do when channel is muted. If internal == true, set - * internal mute without altering main mute. */ - - virtual void setMute (bool internal) = 0; - virtual void unsetMute(bool internal) = 0; - - /* empty - * free any associated resources (e.g. waveform for SAMPLE). */ - - virtual void empty() = 0; - - /* stopBySeq - * action to do when channel is stopped by sequencer. */ - - virtual void stopBySeq() = 0; - - /* quantize - * start channel according to quantizer. Index = array index of - * mixer::channels, used by recorder. LocalFrame = frame within buffer. - * GloalFrame = actual frame from mixer. */ - - virtual void quantize(int index, int localFrame, int globalFrame) = 0; - - /* onZero - * action to do when frame goes to zero, i.e. sequencer restart. */ - - virtual void onZero(int frame) = 0; - - /* onBar - * action to do when a bar has passed. */ - - virtual void onBar(int frame) = 0; - - /* parseAction - * do something on a recorded action. Parameters: - * action *a - action to parse - * localFrame - frame number of the processed buffer - * globalFrame - actual frame in Mixer */ - - virtual void parseAction(recorder::action *a, int localFrame, int globalFrame) = 0; - - /* rewind - * rewind channel when rewind button is pressed. */ - - virtual void rewind() = 0; - - /* ------------------------------------------------------------------------ */ - - int index; // unique id - int type; // midi or sample - int status; // status: see const.h - int key; // keyboard button - float volume; // global volume - float volume_i; // internal volume - float volume_d; // delta volume (for envelope) - float panLeft; - float panRight; - bool mute_i; // internal mute - bool mute_s; // previous mute status after being solo'd - bool mute; // global mute - bool solo; - bool hasActions; // has something recorded - int recStatus; // status of recordings (waiting, ending, ...) - float *vChan; // virtual channel - class gChannel *guiChannel; // pointer to a gChannel object, part of the GUI - - // TODO - midi structs, please - - bool midiIn; // enable midi input - uint32_t midiInKeyPress; - uint32_t midiInKeyRel; - uint32_t midiInKill; - uint32_t midiInVolume; - uint32_t midiInMute; - uint32_t midiInSolo; - - /* midiOutL* - * Enable 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. */ - - bool midiOutL; - uint32_t midiOutLplaying; - uint32_t midiOutLmute; - uint32_t midiOutLsolo; - -#ifdef WITH_VST - gVector plugins; -#endif - - - /* ------------------------------------------------------------------------ */ - - /* isPlaying - * tell wether the channel is playing or is stopped. */ - - bool isPlaying(); - - /* readPatchMidiIn - * read from patch all midi-related parameters such as keypress, mute - * and so on. */ - - void readPatchMidiIn(int i); - void readPatchMidiOut(int i); - - /* sendMidiL* - * send MIDI lightning events to a physical device. */ - - void sendMidiLmute(); - void sendMidiLsolo(); - void sendMidiLplay(); -}; - - -#endif diff --git a/src/conf.cpp b/src/conf.cpp deleted file mode 100644 index fd6720e..0000000 --- a/src/conf.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * conf - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include "const.h" -#include "conf.h" -#include "utils.h" -#include "log.h" - - -int Conf::openFileForReading() -{ - std::string path = gGetHomePath() + "/" + CONF_FILENAME; - fp = fopen(path.c_str(), "r"); - if (fp == NULL) { - gLog("[Conf::openFile] unable to open conf file for reading\n"); - return 0; - } - return 1; -} - - -/* -------------------------------------------------------------------------- */ - - -int Conf::createConfigFolder(const char *path) -{ - if (gDirExists(path)) - return 1; - - gLog("[Conf] .giada folder not present. Updating...\n"); - - if (gMkdir(path)) { - gLog("[Conf] status: ok\n"); - return 1; - } - else { - gLog("[Conf] status: error!\n"); - return 0; - } -} - - -/* -------------------------------------------------------------------------- */ - - -int Conf::openFileForWriting() -{ - /* writing config file. In windows is in the same dir of the .exe, - * in Linux and OS X in home */ - -#if defined(__linux__) - - char giadaPath[PATH_MAX]; - sprintf(giadaPath, "%s/.giada", getenv("HOME")); - - if (!createConfigFolder(giadaPath)) - return 0; - - char path[PATH_MAX]; - sprintf(path, "%s/%s", giadaPath, CONF_FILENAME); - -#elif defined(_WIN32) - - const char *path = CONF_FILENAME; - -#elif defined(__APPLE__) - - struct passwd *p = getpwuid(getuid()); - const char *home = p->pw_dir; - char giadaPath[PATH_MAX]; - snprintf(giadaPath, PATH_MAX, "%s/Library/Application Support/Giada", home); - - if (!createConfigFolder(giadaPath)) - return 0; - - char path[PATH_MAX]; - sprintf(path, "%s/%s", giadaPath, CONF_FILENAME); - -#endif - - fp = fopen(path, "w"); - if (fp == NULL) - return 0; - return 1; - -} - - -/* -------------------------------------------------------------------------- */ - - -void Conf::setDefault() -{ - logMode = LOG_MODE_MUTE; - - soundSystem = DEFAULT_SOUNDSYS; - soundDeviceOut = DEFAULT_SOUNDDEV_OUT; - soundDeviceIn = DEFAULT_SOUNDDEV_IN; - samplerate = DEFAULT_SAMPLERATE; - buffersize = DEFAULT_BUFSIZE; - delayComp = DEFAULT_DELAYCOMP; - limitOutput = false; - rsmpQuality = 0; - - midiPortIn = DEFAULT_MIDI_PORT_IN; - noNoteOff = false; - midiMapPath[0] = '\0'; - midiPortOut = DEFAULT_MIDI_PORT_OUT; - midiSync = MIDI_SYNC_NONE; - midiTCfps = 25.0f; - - midiInRewind = 0x0; - midiInStartStop = 0x0; - midiInActionRec = 0x0; - midiInInputRec = 0x0; - midiInVolumeIn = 0x0; - midiInVolumeOut = 0x0; - midiInBeatDouble = 0x0; - midiInBeatHalf = 0x0; - midiInMetronome = 0x0; - - pluginPath[0] = '\0'; - patchPath [0] = '\0'; - samplePath[0] = '\0'; - - recsStopOnChanHalt = false; - chansStopOnSeqHalt = false; - treatRecsAsLoops = false; - - resizeRecordings = true; - - actionEditorZoom = 100; - actionEditorGridOn = false; - actionEditorGridVal = 1; - - mainWindowX = 0; - mainWindowY = 0; - mainWindowW = GUI_WIDTH; - mainWindowH = GUI_HEIGHT; - - pianoRollY = -1; - pianoRollH = 422; -} - - - -/* -------------------------------------------------------------------------- */ - - -int Conf::read() -{ - setDefault(); - - if (!openFileForReading()) { - gLog("[Conf] unreadable .conf file, using default parameters\n"); - return 0; - } - - if (getValue("header") != "GIADACFG") { - gLog("[Conf] corrupted .conf file, using default parameters\n"); - return -1; - } - - logMode = atoi(getValue("logMode").c_str()); - - soundSystem = atoi(getValue("soundSystem").c_str()); - if (!soundSystem & (SYS_API_ANY)) soundSystem = DEFAULT_SOUNDSYS; - - soundDeviceOut = atoi(getValue("soundDeviceOut").c_str()); - if (soundDeviceOut < 0) soundDeviceOut = DEFAULT_SOUNDDEV_OUT; - - soundDeviceIn = atoi(getValue("soundDeviceIn").c_str()); - if (soundDeviceIn < -1) soundDeviceIn = DEFAULT_SOUNDDEV_IN; - - channelsOut = atoi(getValue("channelsOut").c_str()); - channelsIn = atoi(getValue("channelsIn").c_str()); - if (channelsOut < 0) channelsOut = 0; - if (channelsIn < 0) channelsIn = 0; - - buffersize = atoi(getValue("buffersize").c_str()); - if (buffersize < 8) buffersize = DEFAULT_BUFSIZE; - - delayComp = atoi(getValue("delayComp").c_str()); - if (delayComp < 0) delayComp = DEFAULT_DELAYCOMP; - - midiSystem = atoi(getValue("midiSystem").c_str()); - if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_SYSTEM; - - midiPortOut = atoi(getValue("midiPortOut").c_str()); - if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_PORT_OUT; - - midiPortIn = atoi(getValue("midiPortIn").c_str()); - if (midiPortIn < -1) midiPortIn = DEFAULT_MIDI_PORT_IN; - - noNoteOff = atoi(getValue("noNoteOff").c_str()); - - std::string tmpMidiMapPath = getValue("midiMapPath"); - strncpy(midiMapPath, tmpMidiMapPath.c_str(), tmpMidiMapPath.size()); - midiMapPath[tmpMidiMapPath.size()] = '\0'; // strncpy doesn't add '\0' - - std::string tmplastFileMap = getValue("lastFileMap"); - strncpy(lastFileMap, tmplastFileMap.c_str(), tmplastFileMap.size()); - lastFileMap[tmplastFileMap.size()] = '\0'; // strncpy doesn't add '\0' - - midiSync = atoi(getValue("midiSync").c_str()); - midiTCfps = atof(getValue("midiTCfps").c_str()); - - midiInRewind = strtoul(getValue("midiInRewind").c_str(), NULL, 10); - midiInStartStop = strtoul(getValue("midiInStartStop").c_str(), NULL, 10); - midiInActionRec = strtoul(getValue("midiInActionRec").c_str(), NULL, 10); - midiInInputRec = strtoul(getValue("midiInInputRec").c_str(), NULL, 10); - midiInMetronome = strtoul(getValue("midiInMetronome").c_str(), NULL, 10); - midiInVolumeIn = strtoul(getValue("midiInVolumeIn").c_str(), NULL, 10); - midiInVolumeOut = strtoul(getValue("midiInVolumeOut").c_str(), NULL, 10); - midiInBeatDouble = strtoul(getValue("midiInBeatDouble").c_str(), NULL, 10); - midiInBeatHalf = strtoul(getValue("midiInBeatHalf").c_str(), NULL, 10); - - mainWindowX = atoi(getValue("mainWindowX").c_str()); - mainWindowY = atoi(getValue("mainWindowY").c_str()); - mainWindowW = atoi(getValue("mainWindowW").c_str()); - mainWindowH = atoi(getValue("mainWindowH").c_str()); - - browserX = atoi(getValue("browserX").c_str()); - browserY = atoi(getValue("browserY").c_str()); - browserW = atoi(getValue("browserW").c_str()); - browserH = atoi(getValue("browserH").c_str()); - if (browserX < 0) browserX = 0; - if (browserY < 0) browserY = 0; - if (browserW < 396) browserW = 396; - if (browserH < 302) browserH = 302; - - actionEditorX = atoi(getValue("actionEditorX").c_str()); - actionEditorY = atoi(getValue("actionEditorY").c_str()); - actionEditorW = atoi(getValue("actionEditorW").c_str()); - actionEditorH = atoi(getValue("actionEditorH").c_str()); - actionEditorZoom = atoi(getValue("actionEditorZoom").c_str()); - actionEditorGridVal = atoi(getValue("actionEditorGridVal").c_str()); - actionEditorGridOn = atoi(getValue("actionEditorGridOn").c_str()); - if (actionEditorX < 0) actionEditorX = 0; - if (actionEditorY < 0) actionEditorY = 0; - if (actionEditorW < 640) actionEditorW = 640; - if (actionEditorH < 176) actionEditorH = 176; - if (actionEditorZoom < 100) actionEditorZoom = 100; - if (actionEditorGridVal < 0) actionEditorGridVal = 0; - if (actionEditorGridOn < 0) actionEditorGridOn = 0; - - pianoRollY = atoi(getValue("pianoRollY").c_str()); - pianoRollH = atoi(getValue("pianoRollH").c_str()); - if (pianoRollH <= 0) - pianoRollH = 422; - - sampleEditorX = atoi(getValue("sampleEditorX").c_str()); - sampleEditorY = atoi(getValue("sampleEditorY").c_str()); - sampleEditorW = atoi(getValue("sampleEditorW").c_str()); - sampleEditorH = atoi(getValue("sampleEditorH").c_str()); - sampleEditorGridVal = atoi(getValue("sampleEditorGridVal").c_str()); - sampleEditorGridOn = atoi(getValue("sampleEditorGridOn").c_str()); - if (sampleEditorX < 0) sampleEditorX = 0; - if (sampleEditorY < 0) sampleEditorY = 0; - if (sampleEditorW < 500) sampleEditorW = 500; - if (sampleEditorH < 292) sampleEditorH = 292; - if (sampleEditorGridVal < 0) sampleEditorGridVal = 0; - if (sampleEditorGridOn < 0) sampleEditorGridOn = 0; - - configX = atoi(getValue("configX").c_str()); - configY = atoi(getValue("configY").c_str()); - if (configX < 0) configX = 0; - if (configY < 0) configY = 0; - - pluginListX = atoi(getValue("pluginListX").c_str()); - pluginListY = atoi(getValue("pluginListY").c_str()); - if (pluginListX < 0) pluginListX = 0; - if (pluginListY < 0) pluginListY = 0; - - bpmX = atoi(getValue("bpmX").c_str()); - bpmY = atoi(getValue("bpmY").c_str()); - if (bpmX < 0) bpmX = 0; - if (bpmY < 0) bpmY = 0; - - beatsX = atoi(getValue("beatsX").c_str()); - beatsY = atoi(getValue("beatsY").c_str()); - if (beatsX < 0) beatsX = 0; - if (beatsY < 0) beatsY = 0; - - aboutX = atoi(getValue("aboutX").c_str()); - aboutY = atoi(getValue("aboutY").c_str()); - if (aboutX < 0) aboutX = 0; - if (aboutY < 0) aboutY = 0; - - samplerate = atoi(getValue("samplerate").c_str()); - if (samplerate < 8000) samplerate = DEFAULT_SAMPLERATE; - - limitOutput = atoi(getValue("limitOutput").c_str()); - rsmpQuality = atoi(getValue("rsmpQuality").c_str()); - - std::string p = getValue("pluginPath"); - strncpy(pluginPath, p.c_str(), p.size()); - pluginPath[p.size()] = '\0'; // strncpy doesn't add '\0' - - p = getValue("patchPath"); - strncpy(patchPath, p.c_str(), p.size()); - patchPath[p.size()] = '\0'; // strncpy doesn't add '\0' - - p = getValue("samplePath"); - strncpy(samplePath, p.c_str(), p.size()); - samplePath[p.size()] = '\0'; // strncpy doesn't add '\0' - - recsStopOnChanHalt = atoi(getValue("recsStopOnChanHalt").c_str()); - chansStopOnSeqHalt = atoi(getValue("chansStopOnSeqHalt").c_str()); - treatRecsAsLoops = atoi(getValue("treatRecsAsLoops").c_str()); - - resizeRecordings = atoi(getValue("resizeRecordings").c_str()); - - close(); - return 1; -} - - -/* -------------------------------------------------------------------------- */ - - -int Conf::write() -{ - if (!openFileForWriting()) - return 0; - - fprintf(fp, "# --- Giada configuration file --- \n"); - fprintf(fp, "header=GIADACFG\n"); - fprintf(fp, "version=%s\n", VERSIONE); - - fprintf(fp, "logMode=%d\n", logMode); - - fprintf(fp, "soundSystem=%d\n", soundSystem); - fprintf(fp, "soundDeviceOut=%d\n", soundDeviceOut); - fprintf(fp, "soundDeviceIn=%d\n", soundDeviceIn); - fprintf(fp, "channelsOut=%d\n", channelsOut); - fprintf(fp, "channelsIn=%d\n", channelsIn); - fprintf(fp, "buffersize=%d\n", buffersize); - fprintf(fp, "delayComp=%d\n", delayComp); - fprintf(fp, "samplerate=%d\n", samplerate); - fprintf(fp, "limitOutput=%d\n", limitOutput); - fprintf(fp, "rsmpQuality=%d\n", rsmpQuality); - - fprintf(fp, "midiSystem=%d\n", midiSystem); - fprintf(fp, "midiPortOut=%d\n", midiPortOut); - fprintf(fp, "midiPortIn=%d\n", midiPortIn); - fprintf(fp, "noNoteOff=%d\n", noNoteOff); - fprintf(fp, "midiMapPath=%s\n", midiMapPath); - fprintf(fp, "lastFileMap=%s\n", lastFileMap); - fprintf(fp, "midiSync=%d\n", midiSync); - fprintf(fp, "midiTCfps=%f\n", midiTCfps); - - fprintf(fp, "midiInRewind=%u\n", midiInRewind); - fprintf(fp, "midiInStartStop=%u\n", midiInStartStop); - fprintf(fp, "midiInActionRec=%u\n", midiInActionRec); - fprintf(fp, "midiInInputRec=%u\n", midiInInputRec); - fprintf(fp, "midiInMetronome=%u\n", midiInMetronome); - fprintf(fp, "midiInVolumeIn=%u\n", midiInVolumeIn); - fprintf(fp, "midiInVolumeOut=%u\n", midiInVolumeOut); - fprintf(fp, "midiInBeatDouble=%u\n", midiInBeatDouble); - fprintf(fp, "midiInBeatHalf=%u\n", midiInBeatHalf); - - fprintf(fp, "pluginPath=%s\n", pluginPath); - fprintf(fp, "patchPath=%s\n", patchPath); - fprintf(fp, "samplePath=%s\n", samplePath); - - fprintf(fp, "mainWindowX=%d\n", mainWindowX); - fprintf(fp, "mainWindowY=%d\n", mainWindowY); - fprintf(fp, "mainWindowW=%d\n", mainWindowW); - fprintf(fp, "mainWindowH=%d\n", mainWindowH); - - fprintf(fp, "browserX=%d\n", browserX); - fprintf(fp, "browserY=%d\n", browserY); - fprintf(fp, "browserW=%d\n", browserW); - fprintf(fp, "browserH=%d\n", browserH); - - fprintf(fp, "actionEditorX=%d\n", actionEditorX); - fprintf(fp, "actionEditorY=%d\n", actionEditorY); - fprintf(fp, "actionEditorW=%d\n", actionEditorW); - fprintf(fp, "actionEditorH=%d\n", actionEditorH); - fprintf(fp, "actionEditorZoom=%d\n", actionEditorZoom); - fprintf(fp, "actionEditorGridOn=%d\n", actionEditorGridOn); - fprintf(fp, "actionEditorGridVal=%d\n", actionEditorGridVal); - - fprintf(fp, "pianoRollY=%d\n", pianoRollY); - fprintf(fp, "pianoRollH=%d\n", pianoRollH); - - fprintf(fp, "sampleEditorX=%d\n", sampleEditorX); - fprintf(fp, "sampleEditorY=%d\n", sampleEditorY); - fprintf(fp, "sampleEditorW=%d\n", sampleEditorW); - fprintf(fp, "sampleEditorH=%d\n", sampleEditorH); - fprintf(fp, "sampleEditorGridOn=%d\n", sampleEditorGridOn); - fprintf(fp, "sampleEditorGridVal=%d\n", sampleEditorGridVal); - - fprintf(fp, "configX=%d\n", configX); - fprintf(fp, "configY=%d\n", configY); - - fprintf(fp, "pluginListX=%d\n", pluginListX); - fprintf(fp, "pluginListY=%d\n", pluginListY); - - fprintf(fp, "bpmX=%d\n", bpmX); - fprintf(fp, "bpmY=%d\n", bpmY); - - fprintf(fp, "beatsX=%d\n", beatsX); - fprintf(fp, "beatsY=%d\n", beatsY); - - fprintf(fp, "aboutX=%d\n", aboutX); - fprintf(fp, "aboutY=%d\n", aboutY); - - fprintf(fp, "recsStopOnChanHalt=%d\n", recsStopOnChanHalt); - fprintf(fp, "chansStopOnSeqHalt=%d\n", chansStopOnSeqHalt); - fprintf(fp, "treatRecsAsLoops=%d\n", treatRecsAsLoops); - - fprintf(fp, "resizeRecordings=%d\n", resizeRecordings); - - close(); - return 1; -} - - - -/* -------------------------------------------------------------------------- */ - - -void Conf::close() -{ - if (fp != NULL) - fclose(fp); -} - - -/* -------------------------------------------------------------------------- */ - - -void Conf::setPath(char *path, const char *p) -{ - path[0] = '\0'; - strncpy(path, p, strlen(p)); - path[strlen(p)] = '\0'; -} diff --git a/src/conf.h b/src/conf.h deleted file mode 100644 index 9120f2e..0000000 --- a/src/conf.h +++ /dev/null @@ -1,124 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * conf - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef __CONF_H__ -#define __CONF_H__ - - -#include -#include -#include "dataStorage.h" - - -#if defined(__APPLE__) - #include -#endif - - -class Conf : public DataStorage -{ -private: - - int openFileForReading(); - int openFileForWriting(); - int createConfigFolder(const char *path); - -public: - - int logMode; - - int soundSystem; - int soundDeviceOut; - int soundDeviceIn; - int channelsOut; - int channelsIn; - int samplerate; - int buffersize; - int delayComp; - bool limitOutput; - int rsmpQuality; - - int midiSystem; - int midiPortOut; - int midiPortIn; - bool noNoteOff; - char midiMapPath[FILENAME_MAX]; - char lastFileMap[FILENAME_MAX]; - int midiSync; // see const.h - float midiTCfps; - - uint32_t midiInRewind; - uint32_t midiInStartStop; - uint32_t midiInActionRec; - uint32_t midiInInputRec; - uint32_t midiInMetronome; - uint32_t midiInVolumeIn; - uint32_t midiInVolumeOut; - uint32_t midiInBeatDouble; - uint32_t midiInBeatHalf; - - bool recsStopOnChanHalt; - bool chansStopOnSeqHalt; - bool treatRecsAsLoops; - - bool resizeRecordings; - - char pluginPath[FILENAME_MAX]; - char patchPath [FILENAME_MAX]; - char samplePath[FILENAME_MAX]; - - int mainWindowX, mainWindowY, mainWindowW, mainWindowH; - int browserX, browserY, browserW, browserH; - int actionEditorX, actionEditorY, actionEditorW, actionEditorH, actionEditorZoom; - int actionEditorGridVal; - int actionEditorGridOn; - int sampleEditorX, sampleEditorY, sampleEditorW, sampleEditorH; - int sampleEditorGridVal; - int sampleEditorGridOn; - int pianoRollY, pianoRollH; - int pluginListX, pluginListY; - int configX, configY; - int bpmX, bpmY; - int beatsX, beatsY; - int aboutX, aboutY; - - int read(); - int write(); - void setDefault(); - - /* setPath - * updates one of the following values: plugin, patch or sample. - * Pass it a pointer to one of these (path) and the string to save (p). */ - - void setPath(char *path, const char *p); - - void close(); -}; - -#endif diff --git a/src/const.h b/src/const.h deleted file mode 100644 index 9ada42e..0000000 --- a/src/const.h +++ /dev/null @@ -1,291 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * const.h - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - -#ifndef CONST_H -#define CONST_H - - -/* -- version --------------------------------------------------------------- */ -#define VERSIONE "0.10.0" -#define VERSIONE_STR "Giada" -#define VERSIONE_FLOAT 1.0f - -#define CONF_FILENAME "giada.conf" - - - -/* -- GUI ------------------------------------------------------------------- */ -#ifdef _WIN32 - #define GUI_SLEEP 1000/24 -#else - #define GUI_SLEEP 1000000/24 // == 1.000.000 / 24 == 1/24 sec == 24 Hz -#endif -#define GUI_WIDTH 810 -#define GUI_HEIGHT 510 - -#define COLOR_BD_0 fl_rgb_color(78, 78, 78) // border off -#define COLOR_BD_1 fl_rgb_color(188, 188, 188) // border on -#define COLOR_BG_0 fl_rgb_color(37, 37, 37) // bg off -#define COLOR_BG_1 fl_rgb_color(78, 78, 78) // bg on (clicked) -#define COLOR_BG_2 fl_rgb_color(177, 142, 142) // bg active (play, for some widgets) -#define COLOR_BG_3 fl_rgb_color(28, 32, 80) // bg input rec -#define COLOR_BG_4 fl_rgb_color(113, 31, 31) // bg action rec -#define COLOR_ALERT fl_rgb_color(239, 75, 53) // peak meter alert -#define COLOR_TEXT_0 fl_rgb_color(200, 200, 200) -#define COLOR_TEXT_1 fl_rgb_color(25, 25, 25) // TODO - duplicate! -#define COLOR_BG_MAIN fl_rgb_color(25, 25, 25) // windows background -#define COLOR_BG_DARK fl_rgb_color(0, 0, 0) // inputs background - - - -/* -- MIN/MAX values -------------------------------------------------------- */ -#define MAX_BEATS 32 -#define MAX_BARS 32 -#define MAX_PATCHNAME_LEN 32 -#define DB_MIN_SCALE 60.0f -#define MAX_VST_EVENTS 32 - - - -/* -- kernel audio ---------------------------------------------------------- */ -#define SYS_API_JACK 0x01 // 0000 0001 -#define SYS_API_ALSA 0x02 // 0000 0010 -#define SYS_API_DS 0x04 // 0000 0100 -#define SYS_API_ASIO 0x08 // 0000 1000 -#define SYS_API_CORE 0x10 // 0001 0000 -#define SYS_API_PULSE 0x20 // 0010 0000 -#define SYS_API_ANY 0x3F // 0011 1111 - -#define KERNEL_OK 0 -#define KERNEL_UNDERRUN -1 -#define KERNEL_CRITICAL -2 - - - -/* -- kernel midi ----------------------------------------------------------- */ -#define MIDI_API_JACK 0x01 // 0000 0001 -#define MIDI_API_ALSA 0x02 // 0000 0010 - - - -/* -- default system -------------------------------------------------------- */ -#if defined(__linux__) - #define DEFAULT_SOUNDSYS SYS_API_ALSA -#elif defined(_WIN32) - #define DEFAULT_SOUNDSYS SYS_API_DS -#elif defined(__APPLE__) - #define DEFAULT_SOUNDSYS SYS_API_CORE -#endif - -#define DEFAULT_SOUNDDEV_OUT 0 /// FIXME - please override with rtAudio::getDefaultDevice (or similar) -#define DEFAULT_SOUNDDEV_IN -1 // no recording by default: input disabled -#define DEFAULT_MIDI_SYSTEM 0 -#define DEFAULT_MIDI_PORT_IN -1 -#define DEFAULT_MIDI_PORT_OUT -1 -#define DEFAULT_SAMPLERATE 44100 -#define DEFAULT_BUFSIZE 1024 -#define DEFAULT_DELAYCOMP 0 -#define DEFAULT_VOL 0.0f -#define DEFAULT_BOOST 0.0f -#define gDEFAULT_PITCH 1.0f // ugly and temporary fix to avoid conflicts with wingdi.h (Windows only). -#define DEFAULT_OUT_VOL 1.0f -#define DEFAULT_IN_VOL 0.0f -#define DEFAULT_CHANMODE SINGLE_BASIC -#define DEFAULT_BPM 120.0f -#define DEFAULT_BEATS 4 -#define DEFAULT_BARS 1 -#define DEFAULT_QUANTIZE 0 // quantizer off -#define DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed - - - -/* -- mixer statuses and modes ---------------------------------------------- */ -#define LOOP_BASIC 0x01 // 0000 0001 chanMode -#define LOOP_ONCE 0x02 // 0000 0010 chanMode -#define SINGLE_BASIC 0x04 // 0000 0100 chanMode -#define SINGLE_PRESS 0x08 // 0000 1000 chanMode -#define SINGLE_RETRIG 0x10 // 0001 0000 chanMode -#define LOOP_REPEAT 0x20 // 0010 0000 chanMode -#define SINGLE_ENDLESS 0x40 // 0100 0000 chanMode -#define LOOP_ONCE_BAR 0x80 // 1000 0000 chanMode - -#define LOOP_ANY 0xA3 // 1010 0011 chanMode - any loop mode -#define SINGLE_ANY 0x5C // 0101 1100 chanMode - any single mode - -#define STATUS_ENDING 0x01 // 0000 0001 chanStatus - ending (loop mode only) -#define STATUS_WAIT 0x02 // 0000 0010 chanStatus - waiting for start (loop mode only) -#define STATUS_PLAY 0x04 // 0000 0100 chanStatus - playing -#define STATUS_OFF 0x08 // 0000 1000 chanStatus - off -#define STATUS_EMPTY 0x10 // 0001 0000 chanStatus - not loaded (empty chan) -#define STATUS_MISSING 0x20 // 0010 0000 chanStatus - not found -#define STATUS_WRONG 0x40 // 0100 0000 chanStatus - something wrong (freq, bitrate, ...) - -#define REC_WAITING 0x01 // 0000 0001 -#define REC_ENDING 0x02 // 0000 0010 -#define REC_READING 0x04 // 0000 0100 -#define REC_STOPPED 0x08 // 0000 1000 - - - -/* -- actions --------------------------------------------------------------- */ -#define ACTION_KEYPRESS 0x01 // 0000 0001 -#define ACTION_KEYREL 0x02 // 0000 0010 -#define ACTION_KILLCHAN 0x04 // 0000 0100 -#define ACTION_MUTEON 0x08 // 0000 1000 -#define ACTION_MUTEOFF 0x10 // 0001 0000 -#define ACTION_VOLUME 0x20 // 0010 0000 -#define ACTION_MIDI 0x40 // 0100 0000 - -#define ACTION_KEYS 0x03 // 0000 0011 any key -#define ACTION_MUTES 0x24 // 0001 1000 any mute - -#define RANGE_CHAR 0x01 // range for MIDI (0-127) -#define RANGE_FLOAT 0x02 // range for volumes and VST params (0.0-1.0) - - - -/* -- mixerHandler signals -------------------------------------------------- */ -#define SAMPLE_LOADED_OK 0x01 -#define SAMPLE_LEFT_EMPTY 0x02 -#define SAMPLE_NOT_VALID 0x04 -#define SAMPLE_MULTICHANNEL 0x08 -#define SAMPLE_WRONG_BIT 0x10 -#define SAMPLE_WRONG_ENDIAN 0x20 -#define SAMPLE_WRONG_FORMAT 0x40 -#define SAMPLE_READ_ERROR 0x80 -#define SAMPLE_PATH_TOO_LONG 0x100 - -/** FIXME - add to SAMPLE_ series those for when exporting */ - - - -/* -- log modes ------------------------------------------------------------- */ -#define LOG_MODE_STDOUT 0x01 -#define LOG_MODE_FILE 0x02 -#define LOG_MODE_MUTE 0x04 - - - -/* -- browser types --------------------------------------------------------- */ -#define BROWSER_LOAD_PATCH 0x00 -#define BROWSER_LOAD_SAMPLE 0x01 -#define BROWSER_SAVE_PATCH 0x02 -#define BROWSER_SAVE_SAMPLE 0x04 -#define BROWSER_SAVE_PROJECT 0x08 -#define BROWSER_LOAD_PLUGIN 0x10 - - - -/* -- channel types --------------------------------------------------------- */ -#define CHANNEL_SAMPLE 0x01 -#define CHANNEL_MIDI 0x02 - - - -/* -- unique IDs of mainWin's subwindows ------------------------------------ */ -/* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */ -#define WID_BEATS -1 -#define WID_BPM -2 -#define WID_ABOUT -3 -#define WID_FILE_BROWSER -4 -#define WID_CONFIG -5 -#define WID_FX_LIST -6 -#define WID_ACTION_EDITOR -7 -#define WID_SAMPLE_EDITOR -8 -#define WID_FX -9 -#define WID_KEY_GRABBER -10 - - -/* -- patch signals --------------------------------------------------------- */ -#define PATCH_UNREADABLE 0 -#define PATCH_INVALID -1 -#define PATCH_OPEN_OK 1 - -/** TODO - addo to PATCH_ serie the signals for saving/loading */ - - - -/* -- MIDI signals ------------------------------------------------------------- - * all signals are set to channel 0 (where channels are considered). - * It's up to the caller to bitmask them with the proper channel number. */ - -/* 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_NOTE_ON 0x90 << 24 -#define MIDI_NOTE_OFF 0x80 << 24 -#define MIDI_ALL_NOTES_OFF (MIDI_CONTROLLER) | (0x7B << 16) -#define MIDI_VOLUME (MIDI_CONTROLLER) | (0x07 << 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 */ - -#define MIDI_CHAN_0 0x00 << 24 -#define MIDI_CHAN_1 0x01 << 24 -#define MIDI_CHAN_2 0x02 << 24 -#define MIDI_CHAN_3 0x03 << 24 -#define MIDI_CHAN_4 0x04 << 24 -#define MIDI_CHAN_5 0x05 << 24 -#define MIDI_CHAN_6 0x06 << 24 -#define MIDI_CHAN_7 0x07 << 24 -#define MIDI_CHAN_8 0x08 << 24 -#define MIDI_CHAN_9 0x09 << 24 -#define MIDI_CHAN_10 0x0A << 24 -#define MIDI_CHAN_11 0x0B << 24 -#define MIDI_CHAN_12 0x0C << 24 -#define MIDI_CHAN_13 0x0D << 24 -#define MIDI_CHAN_14 0x0E << 24 -#define MIDI_CHAN_15 0x0F << 24 - -const int MIDI_CHANS[16] = { - MIDI_CHAN_0, MIDI_CHAN_1, MIDI_CHAN_2, MIDI_CHAN_3, - MIDI_CHAN_4, MIDI_CHAN_5, MIDI_CHAN_6, MIDI_CHAN_7, - MIDI_CHAN_8, MIDI_CHAN_9, MIDI_CHAN_10, MIDI_CHAN_11, - MIDI_CHAN_12, MIDI_CHAN_13, MIDI_CHAN_14, MIDI_CHAN_15 -}; - -/* midi sync constants */ - -#define MIDI_SYNC_NONE 0x00 -#define MIDI_SYNC_CLOCK_M 0x01 -#define MIDI_SYNC_CLOCK_S 0x02 -#define MIDI_SYNC_MTC_M 0x04 -#define MIDI_SYNC_MTC_S 0x08 - -#endif diff --git a/src/core/.dirstamp b/src/core/.dirstamp new file mode 100644 index 0000000..e69de29 diff --git a/src/core/channel.cpp b/src/core/channel.cpp new file mode 100644 index 0000000..85ee975 --- /dev/null +++ b/src/core/channel.cpp @@ -0,0 +1,234 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../utils/log.h" +#include "../gui/elems/ge_channel.h" +#include "channel.h" +#include "pluginHost.h" +#include "kernelMidi.h" +#include "patch.h" +#include "wave.h" +#include "mixer.h" +#include "mixerHandler.h" +#include "conf.h" +#include "waveFx.h" +#include "midiMapConf.h" + + +extern Patch G_Patch; +extern Mixer G_Mixer; +extern Conf G_Conf; +extern MidiMapConf G_MidiMap; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +Channel::Channel(int type, int status, int bufferSize) + : bufferSize(bufferSize), + type (type), + status (status), + key (0), + volume (DEFAULT_VOL), + volume_i (1.0f), + volume_d (0.0f), + panLeft (1.0f), + panRight (1.0f), + mute_i (false), + mute_s (false), + mute (false), + solo (false), + hasActions(false), + recStatus (REC_STOPPED), + vChan (NULL), + guiChannel(NULL), + midiIn (true), + midiInKeyPress (0x0), + midiInKeyRel (0x0), + midiInKill (0x0), + midiInVolume (0x0), + midiInMute (0x0), + midiInSolo (0x0), + midiOutL (false), + midiOutLplaying(0x0), + midiOutLmute (0x0), + midiOutLsolo (0x0) +{ + vChan = (float *) malloc(bufferSize * sizeof(float)); + if (!vChan) + gLog("[Channel] unable to alloc memory for vChan\n"); + memset(vChan, 0, bufferSize * sizeof(float)); +} + + +/* -------------------------------------------------------------------------- */ + + +Channel::~Channel() +{ + status = STATUS_OFF; + if (vChan) + free(vChan); +} + + +/* -------------------------------------------------------------------------- */ + + +void Channel::sendMidiLmessage(uint32_t learn, int chan, uint32_t msg, int offset) +{ + gLog("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n", + learn, chan, msg, offset); + + uint32_t out; + + /* isolate 'channel' from learnt message and offset it as requested by 'nn' + * in the midimap configuration file. */ + + out = ((learn & 0x00FF0000) >> 16) << offset; + + /* merge the previously prepared channel into final message, and finally + * send it. */ + + out |= msg | (chan << 24); + kernelMidi::send(out); +} + + +/* -------------------------------------------------------------------------- */ + + +void Channel::readPatchMidiIn(int i) +{ + midiIn = G_Patch.getMidiValue(i, "In"); + midiInKeyPress = G_Patch.getMidiValue(i, "InKeyPress"); + midiInKeyRel = G_Patch.getMidiValue(i, "InKeyRel"); + midiInKill = G_Patch.getMidiValue(i, "InKill"); + midiInVolume = G_Patch.getMidiValue(i, "InVolume"); + midiInMute = G_Patch.getMidiValue(i, "InMute"); + midiInSolo = G_Patch.getMidiValue(i, "InSolo"); +} + +void Channel::readPatchMidiOut(int i) +{ + midiOutL = G_Patch.getMidiValue(i, "OutL"); + midiOutLplaying = G_Patch.getMidiValue(i, "OutLplaying"); + midiOutLmute = G_Patch.getMidiValue(i, "OutLmute"); + midiOutLsolo = G_Patch.getMidiValue(i, "OutLsolo"); +} + + +/* -------------------------------------------------------------------------- */ + + +bool Channel::isPlaying() +{ + return status & (STATUS_PLAY | STATUS_ENDING); +} + + +/* -------------------------------------------------------------------------- */ + + +void Channel::writePatch(FILE *fp, int i, bool isProject) +{ + fprintf(fp, "chanType%d=%d\n", i, type); + fprintf(fp, "chanIndex%d=%d\n", i, index); + fprintf(fp, "chanColumn%d=%d\n", i, guiChannel->getColumnIndex()); + fprintf(fp, "chanMute%d=%d\n", i, mute); + fprintf(fp, "chanMute_s%d=%d\n", i, mute_s); + fprintf(fp, "chanSolo%d=%d\n", i, solo); + fprintf(fp, "chanvol%d=%f\n", i, volume); + fprintf(fp, "chanPanLeft%d=%f\n", i, panLeft); + fprintf(fp, "chanPanRight%d=%f\n", i, panRight); + + fprintf(fp, "chanMidiIn%d=%u\n", i, midiIn); + fprintf(fp, "chanMidiInKeyPress%d=%u\n", i, midiInKeyPress); + fprintf(fp, "chanMidiInKeyRel%d=%u\n", i, midiInKeyRel); + fprintf(fp, "chanMidiInKill%d=%u\n", i, midiInKill); + fprintf(fp, "chanMidiInVolume%d=%u\n", i, midiInVolume); + fprintf(fp, "chanMidiInMute%d=%u\n", i, midiInMute); + fprintf(fp, "chanMidiInSolo%d=%u\n", i, midiInSolo); + + fprintf(fp, "chanMidiOutL%d=%u\n", i, midiOutL); + fprintf(fp, "chanMidiOutLplaying%d=%u\n", i, midiOutLplaying); + fprintf(fp, "chanMidiOutLmute%d=%u\n", i, midiOutLmute); + fprintf(fp, "chanMidiOutLsolo%d=%u\n", i, midiOutLsolo); +} + + +/* -------------------------------------------------------------------------- */ + + +void Channel::sendMidiLmute() +{ + if (!midiOutL || midiOutLmute == 0x0) + return; + if (mute) + sendMidiLmessage(midiOutLsolo, G_MidiMap.muteOnChan, G_MidiMap.muteOnMsg, G_MidiMap.muteOnOffset); + else + sendMidiLmessage(midiOutLsolo, G_MidiMap.muteOffChan, G_MidiMap.muteOffMsg, G_MidiMap.muteOffOffset); +} + + +/* -------------------------------------------------------------------------- */ + + +void Channel::sendMidiLsolo() +{ + if (!midiOutL || midiOutLsolo == 0x0) + return; + if (solo) + sendMidiLmessage(midiOutLsolo, G_MidiMap.soloOnChan, G_MidiMap.soloOnMsg, G_MidiMap.soloOnOffset); + else + sendMidiLmessage(midiOutLsolo, G_MidiMap.soloOffChan, G_MidiMap.soloOffMsg, G_MidiMap.soloOffOffset); +} + + +/* -------------------------------------------------------------------------- */ + + +void Channel::sendMidiLplay() +{ + if (!midiOutL || midiOutLplaying == 0x0) + return; + switch (status) { + case STATUS_OFF: + sendMidiLmessage(midiOutLplaying, G_MidiMap.stoppedChan, G_MidiMap.stoppedMsg, G_MidiMap.stoppedOffset); + break; + case STATUS_PLAY: + sendMidiLmessage(midiOutLplaying, G_MidiMap.playingChan, G_MidiMap.playingMsg, G_MidiMap.playingOffset); + break; + case STATUS_WAIT: + sendMidiLmessage(midiOutLplaying, G_MidiMap.waitingChan, G_MidiMap.waitingMsg, G_MidiMap.waitingOffset); + break; + case STATUS_ENDING: + sendMidiLmessage(midiOutLplaying, G_MidiMap.stoppingChan, G_MidiMap.stoppingMsg, G_MidiMap.stoppingOffset); + } +} diff --git a/src/core/channel.h b/src/core/channel.h new file mode 100644 index 0000000..e346280 --- /dev/null +++ b/src/core/channel.h @@ -0,0 +1,221 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef CHANNEL_H +#define CHANNEL_H + + +#include "../utils/utils.h" +#include "const.h" +#include "recorder.h" + + +#ifdef WITH_VST + +/* before including aeffetx(x).h we must define __cdecl, otherwise VST + * headers can't be compiled correctly. In windows __cdecl is already + * defined. */ + + #ifdef __GNUC__ + #ifndef _WIN32 + #define __cdecl + #endif + #endif + #include "../deps/vst/aeffectx.h" + +#endif + + +class Channel { + +protected: + + /* bufferSize + * size of every buffer in this channel (vChan, pChan) */ + + int bufferSize; + + /* sendMidiLMessage + * compose a MIDI message by merging bytes from MidiMap conf class, and send + * it to KernelMidi. */ + + void sendMidiLmessage(uint32_t learn, int chan, uint32_t msg, int offset); + +public: + + Channel(int type, int status, int bufferSize); + virtual ~Channel(); + + /* writePatch + * store values in patch, writing to *fp. */ + + virtual void writePatch(FILE *fp, int i, bool isProject); + + /* loadByPatch + * load a sample inside a patch. */ + + virtual int loadByPatch(const char *file, int i) = 0; + + /* process + * merge vChannels into buffer, plus plugin processing (if any). */ + + virtual void process(float *buffer) = 0; + + /* start + * action to do when channel starts. doQuantize = false (don't + * quantize) when Mixer is reading actions from Recorder::. */ + + virtual void start(int frame, bool doQuantize) = 0; + + /* stop + * action to do when channel is stopped normally (via key or MIDI). */ + + virtual void stop() = 0; + + /* kill + * action to do when channel stops abruptly. */ + + virtual void kill(int frame) = 0; + + /* mute + * action to do when channel is muted. If internal == true, set + * internal mute without altering main mute. */ + + virtual void setMute (bool internal) = 0; + virtual void unsetMute(bool internal) = 0; + + /* empty + * free any associated resources (e.g. waveform for SAMPLE). */ + + virtual void empty() = 0; + + /* stopBySeq + * action to do when channel is stopped by sequencer. */ + + virtual void stopBySeq() = 0; + + /* quantize + * start channel according to quantizer. Index = array index of + * mixer::channels, used by recorder. LocalFrame = frame within buffer. + * GloalFrame = actual frame from mixer. */ + + virtual void quantize(int index, int localFrame, int globalFrame) = 0; + + /* onZero + * action to do when frame goes to zero, i.e. sequencer restart. */ + + virtual void onZero(int frame) = 0; + + /* onBar + * action to do when a bar has passed. */ + + virtual void onBar(int frame) = 0; + + /* parseAction + * do something on a recorded action. Parameters: + * action *a - action to parse + * localFrame - frame number of the processed buffer + * globalFrame - actual frame in Mixer */ + + virtual void parseAction(recorder::action *a, int localFrame, int globalFrame) = 0; + + /* rewind + * rewind channel when rewind button is pressed. */ + + virtual void rewind() = 0; + + /* ------------------------------------------------------------------------ */ + + int index; // unique id + int type; // midi or sample + int status; // status: see const.h + int key; // keyboard button + float volume; // global volume + float volume_i; // internal volume + float volume_d; // delta volume (for envelope) + float panLeft; + float panRight; + bool mute_i; // internal mute + bool mute_s; // previous mute status after being solo'd + bool mute; // global mute + bool solo; + bool hasActions; // has something recorded + int recStatus; // status of recordings (waiting, ending, ...) + float *vChan; // virtual channel + class gChannel *guiChannel; // pointer to a gChannel object, part of the GUI + + // TODO - midi structs, please + + bool midiIn; // enable midi input + uint32_t midiInKeyPress; + uint32_t midiInKeyRel; + uint32_t midiInKill; + uint32_t midiInVolume; + uint32_t midiInMute; + uint32_t midiInSolo; + + /* midiOutL* + * Enable 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. */ + + bool midiOutL; + uint32_t midiOutLplaying; + uint32_t midiOutLmute; + uint32_t midiOutLsolo; + +#ifdef WITH_VST + gVector plugins; +#endif + + + /* ------------------------------------------------------------------------ */ + + /* isPlaying + * tell wether the channel is playing or is stopped. */ + + bool isPlaying(); + + /* readPatchMidiIn + * read from patch all midi-related parameters such as keypress, mute + * and so on. */ + + void readPatchMidiIn(int i); + void readPatchMidiOut(int i); + + /* sendMidiL* + * send MIDI lightning events to a physical device. */ + + void sendMidiLmute(); + void sendMidiLsolo(); + void sendMidiLplay(); +}; + + +#endif diff --git a/src/core/conf.cpp b/src/core/conf.cpp new file mode 100644 index 0000000..0dd258e --- /dev/null +++ b/src/core/conf.cpp @@ -0,0 +1,467 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * conf + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include "conf.h" +#include "const.h" +#include "../utils/utils.h" +#include "../utils/log.h" + + +int Conf::openFileForReading() +{ + std::string path = gGetHomePath() + "/" + CONF_FILENAME; + fp = fopen(path.c_str(), "r"); + if (fp == NULL) { + gLog("[Conf::openFile] unable to open conf file for reading\n"); + return 0; + } + return 1; +} + + +/* -------------------------------------------------------------------------- */ + + +int Conf::createConfigFolder(const char *path) +{ + if (gDirExists(path)) + return 1; + + gLog("[Conf] .giada folder not present. Updating...\n"); + + if (gMkdir(path)) { + gLog("[Conf] status: ok\n"); + return 1; + } + else { + gLog("[Conf] status: error!\n"); + return 0; + } +} + + +/* -------------------------------------------------------------------------- */ + + +int Conf::openFileForWriting() +{ + /* writing config file. In windows is in the same dir of the .exe, + * in Linux and OS X in home */ + +#if defined(__linux__) + + char giadaPath[PATH_MAX]; + sprintf(giadaPath, "%s/.giada", getenv("HOME")); + + if (!createConfigFolder(giadaPath)) + return 0; + + char path[PATH_MAX]; + sprintf(path, "%s/%s", giadaPath, CONF_FILENAME); + +#elif defined(_WIN32) + + const char *path = CONF_FILENAME; + +#elif defined(__APPLE__) + + struct passwd *p = getpwuid(getuid()); + const char *home = p->pw_dir; + char giadaPath[PATH_MAX]; + snprintf(giadaPath, PATH_MAX, "%s/Library/Application Support/Giada", home); + + if (!createConfigFolder(giadaPath)) + return 0; + + char path[PATH_MAX]; + sprintf(path, "%s/%s", giadaPath, CONF_FILENAME); + +#endif + + fp = fopen(path, "w"); + if (fp == NULL) + return 0; + return 1; + +} + + +/* -------------------------------------------------------------------------- */ + + +void Conf::setDefault() +{ + logMode = LOG_MODE_MUTE; + + soundSystem = DEFAULT_SOUNDSYS; + soundDeviceOut = DEFAULT_SOUNDDEV_OUT; + soundDeviceIn = DEFAULT_SOUNDDEV_IN; + samplerate = DEFAULT_SAMPLERATE; + buffersize = DEFAULT_BUFSIZE; + delayComp = DEFAULT_DELAYCOMP; + limitOutput = false; + rsmpQuality = 0; + + midiPortIn = DEFAULT_MIDI_PORT_IN; + noNoteOff = false; + midiMapPath[0] = '\0'; + midiPortOut = DEFAULT_MIDI_PORT_OUT; + midiSync = MIDI_SYNC_NONE; + midiTCfps = 25.0f; + + midiInRewind = 0x0; + midiInStartStop = 0x0; + midiInActionRec = 0x0; + midiInInputRec = 0x0; + midiInVolumeIn = 0x0; + midiInVolumeOut = 0x0; + midiInBeatDouble = 0x0; + midiInBeatHalf = 0x0; + midiInMetronome = 0x0; + + pluginPath[0] = '\0'; + patchPath [0] = '\0'; + samplePath[0] = '\0'; + + recsStopOnChanHalt = false; + chansStopOnSeqHalt = false; + treatRecsAsLoops = false; + + resizeRecordings = true; + + actionEditorZoom = 100; + actionEditorGridOn = false; + actionEditorGridVal = 1; + + mainWindowX = 0; + mainWindowY = 0; + mainWindowW = GUI_WIDTH; + mainWindowH = GUI_HEIGHT; + + pianoRollY = -1; + pianoRollH = 422; +} + + + +/* -------------------------------------------------------------------------- */ + + +int Conf::read() +{ + setDefault(); + + if (!openFileForReading()) { + gLog("[Conf] unreadable .conf file, using default parameters\n"); + return 0; + } + + if (getValue("header") != "GIADACFG") { + gLog("[Conf] corrupted .conf file, using default parameters\n"); + return -1; + } + + logMode = atoi(getValue("logMode").c_str()); + + soundSystem = atoi(getValue("soundSystem").c_str()); + if (!(soundSystem & SYS_API_ANY)) soundSystem = DEFAULT_SOUNDSYS; + + soundDeviceOut = atoi(getValue("soundDeviceOut").c_str()); + if (soundDeviceOut < 0) soundDeviceOut = DEFAULT_SOUNDDEV_OUT; + + soundDeviceIn = atoi(getValue("soundDeviceIn").c_str()); + if (soundDeviceIn < -1) soundDeviceIn = DEFAULT_SOUNDDEV_IN; + + channelsOut = atoi(getValue("channelsOut").c_str()); + channelsIn = atoi(getValue("channelsIn").c_str()); + if (channelsOut < 0) channelsOut = 0; + if (channelsIn < 0) channelsIn = 0; + + buffersize = atoi(getValue("buffersize").c_str()); + if (buffersize < 8) buffersize = DEFAULT_BUFSIZE; + + delayComp = atoi(getValue("delayComp").c_str()); + if (delayComp < 0) delayComp = DEFAULT_DELAYCOMP; + + midiSystem = atoi(getValue("midiSystem").c_str()); + if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_SYSTEM; + + midiPortOut = atoi(getValue("midiPortOut").c_str()); + if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_PORT_OUT; + + midiPortIn = atoi(getValue("midiPortIn").c_str()); + if (midiPortIn < -1) midiPortIn = DEFAULT_MIDI_PORT_IN; + + noNoteOff = atoi(getValue("noNoteOff").c_str()); + + std::string tmpMidiMapPath = getValue("midiMapPath"); + strncpy(midiMapPath, tmpMidiMapPath.c_str(), tmpMidiMapPath.size()); + midiMapPath[tmpMidiMapPath.size()] = '\0'; // strncpy doesn't add '\0' + + std::string tmplastFileMap = getValue("lastFileMap"); + strncpy(lastFileMap, tmplastFileMap.c_str(), tmplastFileMap.size()); + lastFileMap[tmplastFileMap.size()] = '\0'; // strncpy doesn't add '\0' + + midiSync = atoi(getValue("midiSync").c_str()); + midiTCfps = atof(getValue("midiTCfps").c_str()); + + midiInRewind = strtoul(getValue("midiInRewind").c_str(), NULL, 10); + midiInStartStop = strtoul(getValue("midiInStartStop").c_str(), NULL, 10); + midiInActionRec = strtoul(getValue("midiInActionRec").c_str(), NULL, 10); + midiInInputRec = strtoul(getValue("midiInInputRec").c_str(), NULL, 10); + midiInMetronome = strtoul(getValue("midiInMetronome").c_str(), NULL, 10); + midiInVolumeIn = strtoul(getValue("midiInVolumeIn").c_str(), NULL, 10); + midiInVolumeOut = strtoul(getValue("midiInVolumeOut").c_str(), NULL, 10); + midiInBeatDouble = strtoul(getValue("midiInBeatDouble").c_str(), NULL, 10); + midiInBeatHalf = strtoul(getValue("midiInBeatHalf").c_str(), NULL, 10); + + mainWindowX = atoi(getValue("mainWindowX").c_str()); + mainWindowY = atoi(getValue("mainWindowY").c_str()); + mainWindowW = atoi(getValue("mainWindowW").c_str()); + mainWindowH = atoi(getValue("mainWindowH").c_str()); + + browserX = atoi(getValue("browserX").c_str()); + browserY = atoi(getValue("browserY").c_str()); + browserW = atoi(getValue("browserW").c_str()); + browserH = atoi(getValue("browserH").c_str()); + if (browserX < 0) browserX = 0; + if (browserY < 0) browserY = 0; + if (browserW < 396) browserW = 396; + if (browserH < 302) browserH = 302; + + actionEditorX = atoi(getValue("actionEditorX").c_str()); + actionEditorY = atoi(getValue("actionEditorY").c_str()); + actionEditorW = atoi(getValue("actionEditorW").c_str()); + actionEditorH = atoi(getValue("actionEditorH").c_str()); + actionEditorZoom = atoi(getValue("actionEditorZoom").c_str()); + actionEditorGridVal = atoi(getValue("actionEditorGridVal").c_str()); + actionEditorGridOn = atoi(getValue("actionEditorGridOn").c_str()); + if (actionEditorX < 0) actionEditorX = 0; + if (actionEditorY < 0) actionEditorY = 0; + if (actionEditorW < 640) actionEditorW = 640; + if (actionEditorH < 176) actionEditorH = 176; + if (actionEditorZoom < 100) actionEditorZoom = 100; + if (actionEditorGridVal < 0) actionEditorGridVal = 0; + if (actionEditorGridOn < 0) actionEditorGridOn = 0; + + pianoRollY = atoi(getValue("pianoRollY").c_str()); + pianoRollH = atoi(getValue("pianoRollH").c_str()); + if (pianoRollH <= 0) + pianoRollH = 422; + + sampleEditorX = atoi(getValue("sampleEditorX").c_str()); + sampleEditorY = atoi(getValue("sampleEditorY").c_str()); + sampleEditorW = atoi(getValue("sampleEditorW").c_str()); + sampleEditorH = atoi(getValue("sampleEditorH").c_str()); + sampleEditorGridVal = atoi(getValue("sampleEditorGridVal").c_str()); + sampleEditorGridOn = atoi(getValue("sampleEditorGridOn").c_str()); + if (sampleEditorX < 0) sampleEditorX = 0; + if (sampleEditorY < 0) sampleEditorY = 0; + if (sampleEditorW < 500) sampleEditorW = 500; + if (sampleEditorH < 292) sampleEditorH = 292; + if (sampleEditorGridVal < 0) sampleEditorGridVal = 0; + if (sampleEditorGridOn < 0) sampleEditorGridOn = 0; + + configX = atoi(getValue("configX").c_str()); + configY = atoi(getValue("configY").c_str()); + if (configX < 0) configX = 0; + if (configY < 0) configY = 0; + + pluginListX = atoi(getValue("pluginListX").c_str()); + pluginListY = atoi(getValue("pluginListY").c_str()); + if (pluginListX < 0) pluginListX = 0; + if (pluginListY < 0) pluginListY = 0; + + bpmX = atoi(getValue("bpmX").c_str()); + bpmY = atoi(getValue("bpmY").c_str()); + if (bpmX < 0) bpmX = 0; + if (bpmY < 0) bpmY = 0; + + beatsX = atoi(getValue("beatsX").c_str()); + beatsY = atoi(getValue("beatsY").c_str()); + if (beatsX < 0) beatsX = 0; + if (beatsY < 0) beatsY = 0; + + aboutX = atoi(getValue("aboutX").c_str()); + aboutY = atoi(getValue("aboutY").c_str()); + if (aboutX < 0) aboutX = 0; + if (aboutY < 0) aboutY = 0; + + samplerate = atoi(getValue("samplerate").c_str()); + if (samplerate < 8000) samplerate = DEFAULT_SAMPLERATE; + + limitOutput = atoi(getValue("limitOutput").c_str()); + rsmpQuality = atoi(getValue("rsmpQuality").c_str()); + + std::string p = getValue("pluginPath"); + strncpy(pluginPath, p.c_str(), p.size()); + pluginPath[p.size()] = '\0'; // strncpy doesn't add '\0' + + p = getValue("patchPath"); + strncpy(patchPath, p.c_str(), p.size()); + patchPath[p.size()] = '\0'; // strncpy doesn't add '\0' + + p = getValue("samplePath"); + strncpy(samplePath, p.c_str(), p.size()); + samplePath[p.size()] = '\0'; // strncpy doesn't add '\0' + + recsStopOnChanHalt = atoi(getValue("recsStopOnChanHalt").c_str()); + chansStopOnSeqHalt = atoi(getValue("chansStopOnSeqHalt").c_str()); + treatRecsAsLoops = atoi(getValue("treatRecsAsLoops").c_str()); + + resizeRecordings = atoi(getValue("resizeRecordings").c_str()); + + close(); + return 1; +} + + +/* -------------------------------------------------------------------------- */ + + +int Conf::write() +{ + if (!openFileForWriting()) + return 0; + + fprintf(fp, "# --- Giada configuration file --- \n"); + fprintf(fp, "header=GIADACFG\n"); + fprintf(fp, "version=%s\n", VERSIONE); + + fprintf(fp, "logMode=%d\n", logMode); + + fprintf(fp, "soundSystem=%d\n", soundSystem); + fprintf(fp, "soundDeviceOut=%d\n", soundDeviceOut); + fprintf(fp, "soundDeviceIn=%d\n", soundDeviceIn); + fprintf(fp, "channelsOut=%d\n", channelsOut); + fprintf(fp, "channelsIn=%d\n", channelsIn); + fprintf(fp, "buffersize=%d\n", buffersize); + fprintf(fp, "delayComp=%d\n", delayComp); + fprintf(fp, "samplerate=%d\n", samplerate); + fprintf(fp, "limitOutput=%d\n", limitOutput); + fprintf(fp, "rsmpQuality=%d\n", rsmpQuality); + + fprintf(fp, "midiSystem=%d\n", midiSystem); + fprintf(fp, "midiPortOut=%d\n", midiPortOut); + fprintf(fp, "midiPortIn=%d\n", midiPortIn); + fprintf(fp, "noNoteOff=%d\n", noNoteOff); + fprintf(fp, "midiMapPath=%s\n", midiMapPath); + fprintf(fp, "lastFileMap=%s\n", lastFileMap); + fprintf(fp, "midiSync=%d\n", midiSync); + fprintf(fp, "midiTCfps=%f\n", midiTCfps); + + fprintf(fp, "midiInRewind=%u\n", midiInRewind); + fprintf(fp, "midiInStartStop=%u\n", midiInStartStop); + fprintf(fp, "midiInActionRec=%u\n", midiInActionRec); + fprintf(fp, "midiInInputRec=%u\n", midiInInputRec); + fprintf(fp, "midiInMetronome=%u\n", midiInMetronome); + fprintf(fp, "midiInVolumeIn=%u\n", midiInVolumeIn); + fprintf(fp, "midiInVolumeOut=%u\n", midiInVolumeOut); + fprintf(fp, "midiInBeatDouble=%u\n", midiInBeatDouble); + fprintf(fp, "midiInBeatHalf=%u\n", midiInBeatHalf); + + fprintf(fp, "pluginPath=%s\n", pluginPath); + fprintf(fp, "patchPath=%s\n", patchPath); + fprintf(fp, "samplePath=%s\n", samplePath); + + fprintf(fp, "mainWindowX=%d\n", mainWindowX); + fprintf(fp, "mainWindowY=%d\n", mainWindowY); + fprintf(fp, "mainWindowW=%d\n", mainWindowW); + fprintf(fp, "mainWindowH=%d\n", mainWindowH); + + fprintf(fp, "browserX=%d\n", browserX); + fprintf(fp, "browserY=%d\n", browserY); + fprintf(fp, "browserW=%d\n", browserW); + fprintf(fp, "browserH=%d\n", browserH); + + fprintf(fp, "actionEditorX=%d\n", actionEditorX); + fprintf(fp, "actionEditorY=%d\n", actionEditorY); + fprintf(fp, "actionEditorW=%d\n", actionEditorW); + fprintf(fp, "actionEditorH=%d\n", actionEditorH); + fprintf(fp, "actionEditorZoom=%d\n", actionEditorZoom); + fprintf(fp, "actionEditorGridOn=%d\n", actionEditorGridOn); + fprintf(fp, "actionEditorGridVal=%d\n", actionEditorGridVal); + + fprintf(fp, "pianoRollY=%d\n", pianoRollY); + fprintf(fp, "pianoRollH=%d\n", pianoRollH); + + fprintf(fp, "sampleEditorX=%d\n", sampleEditorX); + fprintf(fp, "sampleEditorY=%d\n", sampleEditorY); + fprintf(fp, "sampleEditorW=%d\n", sampleEditorW); + fprintf(fp, "sampleEditorH=%d\n", sampleEditorH); + fprintf(fp, "sampleEditorGridOn=%d\n", sampleEditorGridOn); + fprintf(fp, "sampleEditorGridVal=%d\n", sampleEditorGridVal); + + fprintf(fp, "configX=%d\n", configX); + fprintf(fp, "configY=%d\n", configY); + + fprintf(fp, "pluginListX=%d\n", pluginListX); + fprintf(fp, "pluginListY=%d\n", pluginListY); + + fprintf(fp, "bpmX=%d\n", bpmX); + fprintf(fp, "bpmY=%d\n", bpmY); + + fprintf(fp, "beatsX=%d\n", beatsX); + fprintf(fp, "beatsY=%d\n", beatsY); + + fprintf(fp, "aboutX=%d\n", aboutX); + fprintf(fp, "aboutY=%d\n", aboutY); + + fprintf(fp, "recsStopOnChanHalt=%d\n", recsStopOnChanHalt); + fprintf(fp, "chansStopOnSeqHalt=%d\n", chansStopOnSeqHalt); + fprintf(fp, "treatRecsAsLoops=%d\n", treatRecsAsLoops); + + fprintf(fp, "resizeRecordings=%d\n", resizeRecordings); + + close(); + return 1; +} + + + +/* -------------------------------------------------------------------------- */ + + +void Conf::close() +{ + if (fp != NULL) + fclose(fp); +} + + +/* -------------------------------------------------------------------------- */ + + +void Conf::setPath(char *path, const char *p) +{ + path[0] = '\0'; + strncpy(path, p, strlen(p)); + path[strlen(p)] = '\0'; +} diff --git a/src/core/conf.h b/src/core/conf.h new file mode 100644 index 0000000..9120f2e --- /dev/null +++ b/src/core/conf.h @@ -0,0 +1,124 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * conf + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef __CONF_H__ +#define __CONF_H__ + + +#include +#include +#include "dataStorage.h" + + +#if defined(__APPLE__) + #include +#endif + + +class Conf : public DataStorage +{ +private: + + int openFileForReading(); + int openFileForWriting(); + int createConfigFolder(const char *path); + +public: + + int logMode; + + int soundSystem; + int soundDeviceOut; + int soundDeviceIn; + int channelsOut; + int channelsIn; + int samplerate; + int buffersize; + int delayComp; + bool limitOutput; + int rsmpQuality; + + int midiSystem; + int midiPortOut; + int midiPortIn; + bool noNoteOff; + char midiMapPath[FILENAME_MAX]; + char lastFileMap[FILENAME_MAX]; + int midiSync; // see const.h + float midiTCfps; + + uint32_t midiInRewind; + uint32_t midiInStartStop; + uint32_t midiInActionRec; + uint32_t midiInInputRec; + uint32_t midiInMetronome; + uint32_t midiInVolumeIn; + uint32_t midiInVolumeOut; + uint32_t midiInBeatDouble; + uint32_t midiInBeatHalf; + + bool recsStopOnChanHalt; + bool chansStopOnSeqHalt; + bool treatRecsAsLoops; + + bool resizeRecordings; + + char pluginPath[FILENAME_MAX]; + char patchPath [FILENAME_MAX]; + char samplePath[FILENAME_MAX]; + + int mainWindowX, mainWindowY, mainWindowW, mainWindowH; + int browserX, browserY, browserW, browserH; + int actionEditorX, actionEditorY, actionEditorW, actionEditorH, actionEditorZoom; + int actionEditorGridVal; + int actionEditorGridOn; + int sampleEditorX, sampleEditorY, sampleEditorW, sampleEditorH; + int sampleEditorGridVal; + int sampleEditorGridOn; + int pianoRollY, pianoRollH; + int pluginListX, pluginListY; + int configX, configY; + int bpmX, bpmY; + int beatsX, beatsY; + int aboutX, aboutY; + + int read(); + int write(); + void setDefault(); + + /* setPath + * updates one of the following values: plugin, patch or sample. + * Pass it a pointer to one of these (path) and the string to save (p). */ + + void setPath(char *path, const char *p); + + void close(); +}; + +#endif diff --git a/src/core/const.h b/src/core/const.h new file mode 100644 index 0000000..0b34668 --- /dev/null +++ b/src/core/const.h @@ -0,0 +1,291 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * const.h + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + +#ifndef CONST_H +#define CONST_H + + +/* -- version --------------------------------------------------------------- */ +#define VERSIONE "0.10.1" +#define VERSIONE_STR "Giada" +#define VERSIONE_FLOAT 1.01f + +#define CONF_FILENAME "giada.conf" + + + +/* -- GUI ------------------------------------------------------------------- */ +#ifdef _WIN32 + #define GUI_SLEEP 1000/24 +#else + #define GUI_SLEEP 1000000/24 // == 1.000.000 / 24 == 1/24 sec == 24 Hz +#endif +#define GUI_WIDTH 810 +#define GUI_HEIGHT 510 + +#define COLOR_BD_0 fl_rgb_color(78, 78, 78) // border off +#define COLOR_BD_1 fl_rgb_color(188, 188, 188) // border on +#define COLOR_BG_0 fl_rgb_color(37, 37, 37) // bg off +#define COLOR_BG_1 fl_rgb_color(78, 78, 78) // bg on (clicked) +#define COLOR_BG_2 fl_rgb_color(177, 142, 142) // bg active (play, for some widgets) +#define COLOR_BG_3 fl_rgb_color(28, 32, 80) // bg input rec +#define COLOR_BG_4 fl_rgb_color(113, 31, 31) // bg action rec +#define COLOR_ALERT fl_rgb_color(239, 75, 53) // peak meter alert +#define COLOR_TEXT_0 fl_rgb_color(200, 200, 200) +#define COLOR_TEXT_1 fl_rgb_color(25, 25, 25) // TODO - duplicate! +#define COLOR_BG_MAIN fl_rgb_color(25, 25, 25) // windows background +#define COLOR_BG_DARK fl_rgb_color(0, 0, 0) // inputs background + + + +/* -- MIN/MAX values -------------------------------------------------------- */ +#define MAX_BEATS 32 +#define MAX_BARS 32 +#define MAX_PATCHNAME_LEN 32 +#define DB_MIN_SCALE 60.0f +#define MAX_VST_EVENTS 32 + + + +/* -- kernel audio ---------------------------------------------------------- */ +#define SYS_API_JACK 0x01 // 0000 0001 +#define SYS_API_ALSA 0x02 // 0000 0010 +#define SYS_API_DS 0x04 // 0000 0100 +#define SYS_API_ASIO 0x08 // 0000 1000 +#define SYS_API_CORE 0x10 // 0001 0000 +#define SYS_API_PULSE 0x20 // 0010 0000 +#define SYS_API_ANY 0x3F // 0011 1111 + +#define KERNEL_OK 0 +#define KERNEL_UNDERRUN -1 +#define KERNEL_CRITICAL -2 + + + +/* -- kernel midi ----------------------------------------------------------- */ +#define MIDI_API_JACK 0x01 // 0000 0001 +#define MIDI_API_ALSA 0x02 // 0000 0010 + + + +/* -- default system -------------------------------------------------------- */ +#if defined(__linux__) + #define DEFAULT_SOUNDSYS SYS_API_ALSA +#elif defined(_WIN32) + #define DEFAULT_SOUNDSYS SYS_API_DS +#elif defined(__APPLE__) + #define DEFAULT_SOUNDSYS SYS_API_CORE +#endif + +#define DEFAULT_SOUNDDEV_OUT 0 /// FIXME - please override with rtAudio::getDefaultDevice (or similar) +#define DEFAULT_SOUNDDEV_IN -1 // no recording by default: input disabled +#define DEFAULT_MIDI_SYSTEM 0 +#define DEFAULT_MIDI_PORT_IN -1 +#define DEFAULT_MIDI_PORT_OUT -1 +#define DEFAULT_SAMPLERATE 44100 +#define DEFAULT_BUFSIZE 1024 +#define DEFAULT_DELAYCOMP 0 +#define DEFAULT_VOL 0.0f +#define DEFAULT_BOOST 0.0f +#define gDEFAULT_PITCH 1.0f // ugly and temporary fix to avoid conflicts with wingdi.h (Windows only). +#define DEFAULT_OUT_VOL 1.0f +#define DEFAULT_IN_VOL 0.0f +#define DEFAULT_CHANMODE SINGLE_BASIC +#define DEFAULT_BPM 120.0f +#define DEFAULT_BEATS 4 +#define DEFAULT_BARS 1 +#define DEFAULT_QUANTIZE 0 // quantizer off +#define DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed + + + +/* -- mixer statuses and modes ---------------------------------------------- */ +#define LOOP_BASIC 0x01 // 0000 0001 chanMode +#define LOOP_ONCE 0x02 // 0000 0010 chanMode +#define SINGLE_BASIC 0x04 // 0000 0100 chanMode +#define SINGLE_PRESS 0x08 // 0000 1000 chanMode +#define SINGLE_RETRIG 0x10 // 0001 0000 chanMode +#define LOOP_REPEAT 0x20 // 0010 0000 chanMode +#define SINGLE_ENDLESS 0x40 // 0100 0000 chanMode +#define LOOP_ONCE_BAR 0x80 // 1000 0000 chanMode + +#define LOOP_ANY 0xA3 // 1010 0011 chanMode - any loop mode +#define SINGLE_ANY 0x5C // 0101 1100 chanMode - any single mode + +#define STATUS_ENDING 0x01 // 0000 0001 chanStatus - ending (loop mode only) +#define STATUS_WAIT 0x02 // 0000 0010 chanStatus - waiting for start (loop mode only) +#define STATUS_PLAY 0x04 // 0000 0100 chanStatus - playing +#define STATUS_OFF 0x08 // 0000 1000 chanStatus - off +#define STATUS_EMPTY 0x10 // 0001 0000 chanStatus - not loaded (empty chan) +#define STATUS_MISSING 0x20 // 0010 0000 chanStatus - not found +#define STATUS_WRONG 0x40 // 0100 0000 chanStatus - something wrong (freq, bitrate, ...) + +#define REC_WAITING 0x01 // 0000 0001 +#define REC_ENDING 0x02 // 0000 0010 +#define REC_READING 0x04 // 0000 0100 +#define REC_STOPPED 0x08 // 0000 1000 + + + +/* -- actions --------------------------------------------------------------- */ +#define ACTION_KEYPRESS 0x01 // 0000 0001 +#define ACTION_KEYREL 0x02 // 0000 0010 +#define ACTION_KILLCHAN 0x04 // 0000 0100 +#define ACTION_MUTEON 0x08 // 0000 1000 +#define ACTION_MUTEOFF 0x10 // 0001 0000 +#define ACTION_VOLUME 0x20 // 0010 0000 +#define ACTION_MIDI 0x40 // 0100 0000 + +#define ACTION_KEYS 0x03 // 0000 0011 any key +#define ACTION_MUTES 0x24 // 0001 1000 any mute + +#define RANGE_CHAR 0x01 // range for MIDI (0-127) +#define RANGE_FLOAT 0x02 // range for volumes and VST params (0.0-1.0) + + + +/* -- mixerHandler signals -------------------------------------------------- */ +#define SAMPLE_LOADED_OK 0x01 +#define SAMPLE_LEFT_EMPTY 0x02 +#define SAMPLE_NOT_VALID 0x04 +#define SAMPLE_MULTICHANNEL 0x08 +#define SAMPLE_WRONG_BIT 0x10 +#define SAMPLE_WRONG_ENDIAN 0x20 +#define SAMPLE_WRONG_FORMAT 0x40 +#define SAMPLE_READ_ERROR 0x80 +#define SAMPLE_PATH_TOO_LONG 0x100 + +/** FIXME - add to SAMPLE_ series those for when exporting */ + + + +/* -- log modes ------------------------------------------------------------- */ +#define LOG_MODE_STDOUT 0x01 +#define LOG_MODE_FILE 0x02 +#define LOG_MODE_MUTE 0x04 + + + +/* -- browser types --------------------------------------------------------- */ +#define BROWSER_LOAD_PATCH 0x00 +#define BROWSER_LOAD_SAMPLE 0x01 +#define BROWSER_SAVE_PATCH 0x02 +#define BROWSER_SAVE_SAMPLE 0x04 +#define BROWSER_SAVE_PROJECT 0x08 +#define BROWSER_LOAD_PLUGIN 0x10 + + + +/* -- channel types --------------------------------------------------------- */ +#define CHANNEL_SAMPLE 0x01 +#define CHANNEL_MIDI 0x02 + + + +/* -- unique IDs of mainWin's subwindows ------------------------------------ */ +/* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */ +#define WID_BEATS -1 +#define WID_BPM -2 +#define WID_ABOUT -3 +#define WID_FILE_BROWSER -4 +#define WID_CONFIG -5 +#define WID_FX_LIST -6 +#define WID_ACTION_EDITOR -7 +#define WID_SAMPLE_EDITOR -8 +#define WID_FX -9 +#define WID_KEY_GRABBER -10 + + +/* -- patch signals --------------------------------------------------------- */ +#define PATCH_UNREADABLE 0 +#define PATCH_INVALID -1 +#define PATCH_OPEN_OK 1 + +/** TODO - addo to PATCH_ serie the signals for saving/loading */ + + + +/* -- MIDI signals ------------------------------------------------------------- + * all signals are set to channel 0 (where channels are considered). + * It's up to the caller to bitmask them with the proper channel number. */ + +/* 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_NOTE_ON 0x90 << 24 +#define MIDI_NOTE_OFF 0x80 << 24 +#define MIDI_ALL_NOTES_OFF (MIDI_CONTROLLER) | (0x7B << 16) +#define MIDI_VOLUME (MIDI_CONTROLLER) | (0x07 << 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 */ + +#define MIDI_CHAN_0 0x00 << 24 +#define MIDI_CHAN_1 0x01 << 24 +#define MIDI_CHAN_2 0x02 << 24 +#define MIDI_CHAN_3 0x03 << 24 +#define MIDI_CHAN_4 0x04 << 24 +#define MIDI_CHAN_5 0x05 << 24 +#define MIDI_CHAN_6 0x06 << 24 +#define MIDI_CHAN_7 0x07 << 24 +#define MIDI_CHAN_8 0x08 << 24 +#define MIDI_CHAN_9 0x09 << 24 +#define MIDI_CHAN_10 0x0A << 24 +#define MIDI_CHAN_11 0x0B << 24 +#define MIDI_CHAN_12 0x0C << 24 +#define MIDI_CHAN_13 0x0D << 24 +#define MIDI_CHAN_14 0x0E << 24 +#define MIDI_CHAN_15 0x0F << 24 + +const int MIDI_CHANS[16] = { + MIDI_CHAN_0, MIDI_CHAN_1, MIDI_CHAN_2, MIDI_CHAN_3, + MIDI_CHAN_4, MIDI_CHAN_5, MIDI_CHAN_6, MIDI_CHAN_7, + MIDI_CHAN_8, MIDI_CHAN_9, MIDI_CHAN_10, MIDI_CHAN_11, + MIDI_CHAN_12, MIDI_CHAN_13, MIDI_CHAN_14, MIDI_CHAN_15 +}; + +/* midi sync constants */ + +#define MIDI_SYNC_NONE 0x00 +#define MIDI_SYNC_CLOCK_M 0x01 +#define MIDI_SYNC_CLOCK_S 0x02 +#define MIDI_SYNC_MTC_M 0x04 +#define MIDI_SYNC_MTC_S 0x08 + +#endif diff --git a/src/core/dataStorage.cpp b/src/core/dataStorage.cpp new file mode 100644 index 0000000..0cea01f --- /dev/null +++ b/src/core/dataStorage.cpp @@ -0,0 +1,70 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * dataStorage + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#include "../utils/log.h" +#include "dataStorage.h" +#include "const.h" + + +std::string DataStorage::getValue(const char *in) { + + /* on each call reset the pointe to the beginning of the file. Not so + * good but necessary if you want to pick up random values from the + * file. */ + + fseek(fp, 0L, SEEK_SET); + std::string out = ""; + + while (!feof(fp)) { + + char buffer[MAX_LINE_LEN]; + if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) { + gLog("[PATCH] get_value error (key=%s)\n", in); + return ""; + } + + if (buffer[0] == '#') + continue; + + unsigned len = strlen(in); + if (strncmp(buffer, in, len) == 0) { + + for (unsigned i=len+1; i. + * + * ------------------------------------------------------------------ */ + + +#ifndef __DATA_STORAGE_H__ +#define __DATA_STORAGE_H__ + +#include +#include +#include + +#define MAX_LINE_LEN 1024 + + +class DataStorage { + +protected: + + FILE *fp; + std::string getValue(const char *in); +}; + +#endif diff --git a/src/core/graphics.cpp b/src/core/graphics.cpp new file mode 100644 index 0000000..7bed054 --- /dev/null +++ b/src/core/graphics.cpp @@ -0,0 +1,1692 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * graphics + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#include "graphics.h" + +const char *giada_logo_xpm[] = { +"300 82 8 1", +" c #181917", +". c #333432", +"+ c #484A47", +"@ c #5F615E", +"# c #767875", +"$ c #8D8F8C", +"% c #A5A7A4", +"& c #C5C7C4", +" .#%%&$ ", +" ..#%&&&&&# ", +" +@#$%&&&&&&&&&@ ", +" .. +&&&&&&&&&&&&&&. ", +" +$%%#+ +%&&&&&&&&&&&&&. ", +" #&&&&&%+ .+@@$&&&&&&&&&%. ", +" #&&&&&&&$ +&&&&&&&&# ", +" .&&&&&&&&%. #&&&&&&&@ ", +" @&&&&&&&&$ +&&&&&&&+ ", +" $&&&&&&&&# .&&&&&&&. ", +" #&&&&&&&&. +&&&&&&%. ", +" .&&&&&&&@ @&&&&&&$. ", +" @&&&&&@ $&&&&&&# ", +" .##@. %&&&&&&@ ", +" &&&&&&&+ ", +" .&&&&&&%. ", +" @&&&&&&$. ", +" .+@@###@+. ...... ... ++@@###@@. ....... .+@@###@@+. #&&&&&&# .+@@###@@+ ....... ", +" .@$%%&&&&&&&%$+ +%%%%%%. .+@#$%%$. .@$%&&&&&&&&%$@. $%%%%%%+ .@#%%&&&&&&&&%$@ %&&&&&&@ .@$%%&&&&&&&%$#. @%%%%%%@ ", +" #%&&&&&&&&&&&&&&$. #&&&&&& +@#$$%%%&&&&&%. .$%&&&&&&&&&&&&&&%@ .&&&&&&&+ #%&&&&&&&&&&&&&&&$+ &&&&&&&@ #%&&&&&&&&&&&&&&%#. %&&&&&&@ ", +" +%&&&&&&&&&&&&&&&&&%@ .&&&&&&% .%&&&&&&&&&&&&$ #%&&&&&&&&&&&&&&&&&&$ +&&&&&&%. @%&&&&&&&&&&&&&&&&&&&# .&&&&&&%+ @%&&&&&&&&&&&&&&&&&&%. &&&&&&&+ ", +" #&&&&&&&&&#+....@$&&&&@@&&&&&&$ .&&&&&&&&&&&&&# +%&&&&&&&&%#+....+#%&&&$#&&&&&&$. .$&&&&&&&&&$+.....@%&&&&$@&&&&&&%. .$&&&&&&&&&#@.....#%&&&%+&&&&&&%. ", +" $&&&&&&&&@ .%&&&&&&&&&&@ .@$&&&&&&&&&&@ +%&&&&&&&%@ .#&&&&&&&&&&$ .%&&&&&&&&@ .$&&&&&&&&&&$ .%&&&&&&&&#. @&&&&&&&&&&%. ", +" $&&&&&&&%. @&&&&&&&&&+ .%&&&&&&&%+ @&&&&&&&&# +%&&&&&&&&# +%&&&&&&&$. @&&&&&&&&&# .%&&&&&&&$. .%&&&&&&&&$ ", +" %&&&&&&&# @&&&&&&&&. +%&&&&&&%. @&&&&&&&&# .%&&&&&&&@ +%&&&&&&&# @&&&&&&&&@ +%&&&&&&&# %&&&&&&&@ ", +" $&&&&&&&$ #&&&&&&%. %&&&&&&% +&&&&&&&&@ +&&&&&&%+ .%&&&&&&&# @&&&&&&&+ .%&&&&&&&# &&&&&&&+ ", +" @&&&&&&&$. #&&&&&&$ %&&&&&&$ .%&&&&&&&@ @&&&&&&%. .$&&&&&&&$ +&&&&&&%+ $&&&&&&&$ .&&&&&&&+ ", +" +&&&&&&&%+ %&&&&&&# %&&&&&&# $&&&&&&&$ $&&&&&&% @&&&&&&&% #&&&&&&%. #&&&&&&&%. @&&&&&&%. ", +" %&&&&&&&# &&&&&&&+ .%&&&&&&@ @&&&&&&&$ %&&&&&&$ +&&&&&&&&+ $&&&&&&$ +&&&&&&&%. $&&&&&&$. ", +" @&&&&&&&%. .&&&&&&&. +%&&&&&%. .&&&&&&&&. .%&&&&&&# $&&&&&&&# %&&&&&&# .$&&&&&&&@ %&&&&&&# ", +" .%&&&&&&&@ @&&&&&&&. @&&&&&&% @&&&&&&&# @&&&&&&&+ +&&&&&&&&. +&&&&&&&@ +&&&&&&&% .&&&&&&&@ ", +" @&&&&&&&% $&&&&&&%. #&&&&&&% &&&&&&&&. #&&&&&&%. %&&&&&&&# @&&&&&&%+ .$&&&&&&&@ +&&&&&&&+ ", +" .$&&&&&&&# %&&&&&&# $&&&&&&# #&&&&&&&# $&&&&&&% +&&&&&&&&. $&&&&&&%. +&&&&&&&% #&&&&&&%+ ", +" +%&&&&&&&+ .&&&&&&&@ .%&&&&&&@ %&&&&&&&+ .%&&&&&&$ $&&&&&&&$ %&&&&&&% #&&&&&&&# %&&&&&&%. ", +" @&&&&&&&% +&&&&&&&+ +%&&&&&&+ .&&&&&&&%. +&&&&&&&# &&&&&&&&+ +%&&&&&&$ .%&&&&&&&. &&&&&&&$ ", +" $&&&&&&&@ #&&&&&&&. @&&&&&&&. #&&&&&&&# #&&&&&&&@ +&&&&&&&%. @&&&&&&&# .&&&&&&&% +&&&&&&&# ", +" .%&&&&&&&. %&&&&&&%. #&&&&&&% %&&&&&&&+ $&&&&&&&+ #&&&&&&&$. $&&&&&&&+ @&&&&&&&@ #&&&&&&&@ ", +" +&&&&&&&& &&&&&&&$. #&&&&&&$ &&&&&&&%. .%&&&&&&& %&&&&&&&# .%&&&&&&%. $&&&&&&&+ $&&&&&&%+ ", +" +&&&&&&&$ +&&&&&&&# .$&&&&&&# .&&&&&&&$. +&&&&&&&% %&&&&&&&+ +%&&&&&&% %&&&&&&&. .%&&&&&&%. ", +" @&&&&&&&@ $&&&&&&&@ .%&&&&&&+ +&&&&&&&# #&&&&&&&$ &&&&&&&&+ #&&&&&&&% &&&&&&&% @&&&&&&&$ ", +" @&&&&&&&+ &&&&&&&&+ +%&&&&&&. @&&&&&&&@ .%&&&&&&&@ .&&&&&&&%. .%&&&&&&&# &&&&&&&# $&&&&&&&$ ", +" #&&&&&&&. @&&&&&&&%. @&&&&&&& @&&&&&&&@ @&&&&&&&&+ .&&&&&&&%. @&&&&&&&&@ .&&&&&&&# +%&&&&&&&# ", +" #&&&&&&&. %&&&&&&&$. #&&&&&&% #&&&&&&&+ .$&&&&&&&&. .&&&&&&&$. .$&&&&&&&&. .&&&&&&&@ $&&&&&&&&@ ", +" #&&&&&&& #&&&&&&&&$ $&&&&&&# @&&&&&&&+ @&&&&&&&&& .&&&&&&&$. @&&&&&&&&& .&&&&&&&+ +%&&&&&&&%+ ", +" @&&&&&&& .%&&&&&&&&# .%&&&&&&@ @&&&&&&%+ .%&&&&&&&&% &&&&&&&$. .%&&&&&&&&% &&&&&&&+ $&&&&&&&&%. ", +" @&&&&&&&. $&&&&&&&&&@ +%&&&&&&. +&&&&&&&@ @&&&&&&&&&$ &&&&&&&%. #&&&&&&&&&$ &&&&&&&@ +&&&&&&&&&% ", +" +&&&&&&&+ @&&&&&&&&&%+ +&&&&&&& &&&&&&&@ +&&&&&&&&&&# $&&&&&&%+ @&&&&&&&&&&# $&&&&&&# .%&&&&&&&&&% ", +" .%&&&&&&@ +%&&$&&&&&&%. +&&&&&&& %&&&&&&# .&&&&&&&&&&&@ @&&&&&&&+ +&&&$%&&&&&&@ @&&&&&&$ .$&&&&&&&&&&$ ", +" #&&&&&&$ .$&&##&&&&&&$ @&&&&&&& @&&&&&&%. .%&&%@%&&&&&&@ .&&&&&&&# .&&&$+%&&&&&&. .&&&&&&&. .#&&%@%&&&&&&$ ", +" +&&&&&&&. .%&&% $&&&&&&# +&&&&&&&. +.&&&&&&&@ .%&&%+.%&&&&&&$ @+$&&&&&&&+ +&&&% +&&&&&&&+ .+ $&&&&&&$ .$&&&@ %&&&&&&% .# ", +" $&&&&&&$ +%&&&+ %&&&&&&@ +&&&&&&&@ #&$#&&&&&&%+ @&&&&@ .$&&&&&&&+ #&&@&&&&&&&$. .#&&&%. +%&&&&&&# .$&$ +&&&&&&&+ +%&&&# $&&&&&&&# +&&$ ", +" +%&&&&&&#. +#&&&%@ .%&&&&&&+ .%&&&&&&&+ .$&&%.%&&&&&&%+ +#&&&&@ #&&&&&&&%@..+@$&&&+@&&&&&&&%+ .@%&&&%+ .%&&&&&&&+ +%&&$. #&&&&&&%@ +#&&&&# @&&&&&&&&@...+#&&&# ", +" @&&&&&&&$@+..++#%&&&%+ +&&&&&&%. $&&&&&&&%#@@#%&&%. .%&&&&&&%@+...+@$&&&&%+ +%&&&&&&&&%$%&&&&+ $&&&&&&&&$#@+@@#%&&&&$. #&&&&&&&&#@+@$&&&$. .$&&&&&&&#+...++#%&&&&@ .%&&&&&&&&%$%&&&&# ", +" @&&&&&&&&%%%%&&&&&$. @&&&&&&% +%&&&&&&&&&&&&&$. +%&&&&&&&&%$%%&&&&&$. #&&&&&&&&&&&&&%+ .$&&&&&&&&&&&&&&&&&&# .%&&&&&&&&&&&&&&# .$&&&&&&&&%$%%&&&&&%+ @&&&&&&&&&&&&&%@ ", +" @%&&&&&&&&&&&&&%@. #&&&&&&$ #&&&&&&&&&&&%@. .$&&&&&&&&&&&&&&%@. .%&&&&&&&&&&&#. @&&&&&&&&&&&&&&&$+ +&&&&&&&&&&&&%+ .#&&&&&&&&&&&&&&&#. $&&&&&&&&&&&$+ ", +" .#%&&&&&&&&&%+. %&&&&&&# +%&&&&&&&%#. +$&&&&&&&&&&%@. .$&&&&&&&&#+ .#&&&&&&&&&&%#. .$&&&&&&&&%@. +$&&&&&&&&&&%@. .@&&&&&&&&$+. ", +" .+#$%%$#+.. .%&&&&&&+ .@#$%$#+. .@#$%%$#@+ +@$%$#+. .+#$%%$#@+. +@$%$$@. .+#$%%$#@+. .@$%$#@. ", +" +&&&&&&%. ", +" #&&&&&&% ", +" .%&&&&&&@ ", +" @&&&&&&%. ", +" $&&&&&&$ ", +" @&&&&&&&+ ", +" @#$#+ .$&&&&&&$ ", +" $&&&&&# #&&&&&&% ", +"#&&&&&&&@ @&&&&&&&+ ", +"%&&&&&&&%. @&&&&&&%+ ", +"%&&&&&&&&# #&&&&&&%. ", +"@&&&&&&&&&@ .$&&&&&&#. ", +" $&&&&&&&&&$+ +$&&&&&&%+ ", +" +&&&&&&&&&&%#@@#$%&&&&&&&#. +. .+ +@. ++ .+ +++++ +. .+ ++ +++++. .++++. +@. .@+ .++++. .+++++++ +. +@. .+@. .++++. ++. ++. .+. .+@. .+ +. .+ .+. .+ .+++++++ ", +" .$&&&&&&&&&&&&&&&&&&&%@ $%. %@ +&&%&%. .$$ +& .&&&&&&# .%@ +&. %&+ $&&&&&%. @&&&&&%. +&&%&%. .%&%&&+ #&&&&&&@ +&&&&&&%. &# +&&%&%. @&&%&%. +&&&&&&@ $&% +%&+ .$&@ @&&%&$. $% .%$ $% +&%. +%. +&&&&&&$. ", +" +$%%&&&&&&&&&&%$@. +&# #% +&# %% $$ +& .&+ @&@ .%@ +&. +$&$ $$ .%% @% .%&. .&$ .%$. %%. #&+ #& .$&+ +&. &# +&# $%. @&# .%%. +&. $&+ $%&+ #%&+ .&%$ @&# +%$ $% .%# $% +&%$ +%. +&. ", +" .++@###@@+. #&. .&+ $%. .&@ $$ +& .&+ %$ .%@ +&. ##$% $$ @&. @% %$ $%. @&. @&+ %$. #& .%@ +&. &# %% .&# %% .&@ +&. &# $#%$ $#&+ @%@%. %% @%+ $% .%# $% +&+%+ +%. +&. ", +" .%$ $$ .&# %% $$ +& .&+ %$ .%@ +&. .%@+&+ $$ @&. @% #& &# +. %% #&. #& .%@ +&. &# .&@ $$ +&+ .$$ +&. %# $#@& +$@&+ $#.%@ +&@ .+. $% .%# $% +& $$ +%. +&. ", +" @&#&. .&@ $& $$ +& .&#+++#&+ .%%####$&. +%. %$ $%.++@&$ @% +&..&@ &$ @&+ #&++++$%. +&$###@ &# +&+ #%.@&. #% +&+ ..#&+ $#.&@ ##+&+ .&..$$ @&. $&$###$&# $% +& +%@ +%. +&####+ ", +" %&@ +&+ #& $$ +& .&%$$%%@ .%%####$&. #$ #& $&$$%&#. @% +&++&@ &# +&+ #&$$%&%+ +&$$$$# &# @&. #%.@&. #%.+%%%%%%@ $# $% .$+.&+ @% @%. @&. $&$###$&# $% +& #%.+%. +&$$$$@ ", +" #&. .&@ $% $$ +& .&#+.$%. .%@ +&. .$%##$&+ $%.+@&@ @% +&..&@ &$ @&. #&+++$$ +&+ &# +&. #% @&. $$ +&#@@++ $# +&.+$.+&+ $%@#$&+ @&. $% .%# $% +& .%@+%. +&. ", +" @& .%# &$ $$ +% .&+ .%@ .%@ +&. +%$###&$ $$ $% @% $& &$ .$+ $% #%. #& +&+ +&. &# .&@ .%$ +&@ .%# +&. $# .%### +&+ .&####&# .&@ .$+ $% .%# $% +& @%@%. +&. ", +" @& #%. @&. #%. $% .&+ $% .%@ +&. @$. #& $$ +&+ @% +&@ #&. #&. +&+ .%@ #& .%# +&. &# $% +&+ %% @&+ +&. $# #&%+ +&+ @% #%. %% #%. $% .%# $% +& .$%%. +&. ", +" @& .%%+.@&# .%%++#&@ .&+ +&+ .%@ +&. $# .&+ $$ %$ @&+++#&%. $%+.#&# #&@.+%%. #& @%. +&@+++++. &$++++. .%%+.@%# .%%..@&# +&. $# +&$ +&+ %# +&+ .%%+.#&# $% .%# $% +& +&&. +&@++++. %&", +" @& .$&&%@ +%&&&@ .&+ %$ .%@ +&..%+ %$ $$ @&. @&&&&%@ .$%&&# @%&&$. #& .%@ +&&&&&&%+ &&&&&&$. .$&&%# .$&&%@ +&. $# .%# +&+.&. .%# .$&&&@ $% .%# $% +& $&. +&&&&&&%. &&"}; + + +const char *loopRepeat_xpm[] = { +"18 18 8 1", +" c #181917", +". c #242523", +"+ c #323331", +"@ c #4D4F4C", +"# c #646663", +"$ c #787A77", +"% c #919390", +"& c #BFC1BD", +"..................", +"..................", +"..................", +"...&%#......#%&...", +"...&&&%+..+%&&&...", +"...$%&&%..%&&%$...", +".....$&&##&&$.....", +"......%&%%&%......", +"......$&&&&$......", +"......$&&&&$......", +"......%&%%&%......", +".....$&&##&&$.....", +"...$%&&%..%&&%$...", +"...&&&%+..+%&&&...", +"...&%#......#%&...", +"..................", +"..................", +".................."}; + + +const char *loopBasic_xpm[] = { +"18 18 8 1", +" c #181917", +". c #242523", +"+ c #313230", +"@ c #4D4F4C", +"# c #666765", +"$ c #787A77", +"% c #919390", +"& c #BEC0BD", +"..................", +"..................", +"..................", +"......#%&&%#......", +"....+%&&&&&&%+....", +"....%&&&%%&&&%....", +"...#&&%+..+%&&#...", +"...%&&+....+&&%...", +"...&&%......%&&...", +"...&&%......%&&...", +"...%&&+....+&&%...", +"...#&&%+..+%&&#...", +"....%&&&%%&&&%....", +"....+%&&&&&&%+....", +"......#%&&%#......", +"..................", +"..................", +".................."}; + + +const char *loopOnce_xpm[] = { +"18 18 8 1", +" c #181917", +". c #242523", +"+ c #323331", +"@ c #4D4F4C", +"# c #646663", +"$ c #787A77", +"% c #919390", +"& c #BFC1BD", +"..................", +"..................", +"..................", +"......$%&&%#......", +"....+&&&&&&&&+....", +"...+&&&&$$&&&&+...", +"...$&&$....$&&$...", +"...%&&......%&%...", +"..................", +"..................", +"...%&&+.....&&&...", +"...#&&$....$&&#...", +"....%&&&%$&&&%....", +"....+%&&&&&&%+....", +"......#%&&%#......", +"..................", +"..................", +".................."}; + + +const char *loopOnceBar_xpm[] = { +"18 18 8 1", +" c #242523", +". c #393A38", +"+ c #545553", +"@ c #747673", +"# c #A3A5A2", +"$ c #ADAFAC", +"% c #B5B7B4", +"& c #C7C9C6", +" ", +" ", +" ", +" @$&%#@ ", +" .$&&&&&&$. ", +" %&&#@@#&&$ ", +" @&&@ @&&@ ", +" %&# +%$+ #&$ ", +" %&&% ", +" %&&% ", +" $&# +%%+ #&$ ", +" @&&@ @&&@ ", +" $&&#@@#&&$ ", +" .$&&&&&&$. ", +" @#&&#@ ", +" ", +" ", +" "}; + + +const char *oneshotBasic_xpm[] = { +"18 18 8 1", +" c #181917", +". c #242523", +"+ c #313230", +"@ c #4D4F4C", +"# c #666765", +"$ c #787A77", +"% c #919390", +"& c #BEC0BD", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"...$$$$$$$$$$$$...", +"...&&&&&&&&&&&&...", +"...&&&&&&&&&&&&...", +"..................", +"..................", +".................."}; + + +const char *oneshotRetrig_xpm[] = { +"18 18 8 1", +" c #181917", +". c #242523", +"+ c #313230", +"@ c #4D4F4C", +"# c #666765", +"$ c #787A77", +"% c #919390", +"& c #BEC0BD", +"..................", +"..................", +"..................", +"..................", +"..................", +"...$$$$$$$#@......", +"...&&&&&&&&&&@....", +"...&&&&&&&&&&&+...", +"..........+$&&%...", +"............%&&...", +"............%&&...", +"...........+&&%...", +"...$$$$$$$%&&&#...", +"...&&&&&&&&&&%....", +"...&&&&&&&&%#.....", +"..................", +"..................", +".................."}; + + +const char *oneshotPress_xpm[] = { +"18 18 8 1", +" c #181917", +". c #242523", +"+ c #313230", +"@ c #4D4F4C", +"# c #666765", +"$ c #787A77", +"% c #919390", +"& c #BEC0BD", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"...+%&%+..........", +"...%&&&%..........", +"...&&&&&..........", +"...$&&&$..........", +"...+$&$+..........", +"..................", +"...$$$$$$$$$$$$...", +"...&&&&&&&&&&&&...", +"...&&&&&&&&&&&&...", +"..................", +"..................", +".................."}; + + +const char *oneshotEndless_xpm[] = { +"18 18 6 1", +" c #242523", +". c #464745", +"+ c #6D6F6C", +"@ c #888A87", +"# c #ADAFAC", +"$ c #C6C8C5", +" ", +" ", +" ", +" ", +" ", +" .++. ", +" @$$$$#. ", +" @$$$$$$$. ", +" .$$#. +$$@ ", +" +$$. @$# ", +" +$$ @$$ ", +" .$$+ #$# ", +" @@@$$$@@#$$+ ", +" $$$$$$$$$$@ ", +" $$$$$$$$#+ ", +" ", +" ", +" "}; + + +const char *updirOff_xpm[] = { +"18 18 8 1", +" c #242523", +". c #332F2E", +"+ c #54494A", +"@ c #6B5A5C", +"# c #866C6B", +"$ c #967B7A", +"% c #987D7C", +"& c #B18E8F", +" ", +" ", +" ", +" ", +" @@ ", +" #&&# ", +" .#&&&&#. ", +" .$&&&&&&$. ", +" +@%&&&&%@+ ", +" #&&&&# ", +" #&&&&# ", +" #&&&&# ", +" #&&&&# ", +" #&&&&# ", +" .... ", +" ", +" ", +" "}; + + +const char *updirOn_xpm[] = { +"18 18 8 1", +" c #4D4F4C", +". c #555150", +"+ c #706465", +"@ c #7D6B6E", +"# c #877373", +"$ c #957978", +"% c #9F8382", +"& c #B18E8F", +" ", +" ", +" ", +" ", +" ## ", +" #&&# ", +" .$&&&&$. ", +" .%&&&&&&%. ", +" +@%&&&&%@+ ", +" $&&&&$ ", +" $&&&&$ ", +" $&&&&$ ", +" $&&&&$ ", +" $&&&&$ ", +" .... ", +" ", +" ", +" "}; + + +const char *pause_xpm[] = { +"23 23 8 1", +" c #4D4F4C", +". c #514E53", +"+ c #5C4F61", +"@ c #6F507E", +"# c #855098", +"$ c #9551AE", +"% c #A652C5", +"& c #AE52D1", +" ", +" ", +" ", +" ", +" ", +" #+ ", +" &%#. ", +" &&&%@ ", +" &&&&&$+ ", +" &&&&&&&#. ", +" &&&&&&&&%@ ", +" &&&&&&&&&&# ", +" &&&&&&&&%@. ", +" &&&&&&&#. ", +" &&&&&$+ ", +" &&&%@ ", +" &&#. ", +" $+ ", +" ", +" ", +" ", +" ", +" "}; + + +const char *play_xpm[] = { +"23 23 8 1", +" c #242523", +". c #393534", +"+ c #574B4C", +"@ c #6E5B5A", +"# c #7C6663", +"$ c #8C7170", +"% c #A48384", +"& c #B18E8F", +" ", +" ", +" ", +" ", +" ", +" $. ", +" &&@ ", +" &&&%+ ", +" &&&&&$. ", +" &&&&&&&@ ", +" &&&&&&&&%+ ", +" &&&&&&&&&&# ", +" &&&&&&&&%+ ", +" &&&&&&&#. ", +" &&&&&$. ", +" &&&%+ ", +" &&@ ", +" $. ", +" ", +" ", +" ", +" ", +" "}; + + +const char *rewindOff_xpm[] = { +"23 23 8 1", +" c #242523", +". c #393534", +"+ c #574B4C", +"@ c #6E5B5A", +"# c #7C6663", +"$ c #8C7170", +"% c #A48384", +"& c #B18E8F", +" ", +" ", +" ", +" ", +" ", +" .$ ", +" @&& ", +" +%&&& ", +" .$&&&&& ", +" @&&&&&&& ", +" +%&&&&&&&& ", +" #&&&&&&&&&& ", +" +%&&&&&&&& ", +" .#&&&&&&& ", +" .$&&&&& ", +" +%&&& ", +" @&& ", +" .$ ", +" ", +" ", +" ", +" ", +" "}; + + +const char *rewindOn_xpm[] = { +"23 23 8 1", +" c #4D4F4C", +". c #514E53", +"+ c #5C4F61", +"@ c #6F507E", +"# c #855098", +"$ c #9551AE", +"% c #A652C5", +"& c #AE52D1", +" ", +" ", +" ", +" ", +" ", +" +# ", +" .#%& ", +" @%&&& ", +" +$&&&&& ", +" .#&&&&&&& ", +" @%&&&&&&&& ", +" #&&&&&&&&&& ", +" .@%&&&&&&&& ", +" .#&&&&&&& ", +" +$&&&&& ", +" @%&&& ", +" .#&& ", +" +$ ", +" ", +" ", +" ", +" ", +" "}; + + +// 18x18 +/* +const unsigned char giada_icon[] = { + 0x00, 0x00, 0x00, 0xfc, 0xff, 0x00, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01, + 0x3e, 0xf0, 0x01, 0x1e, 0xe0, 0x01, 0x0e, 0xc3, 0x01, 0x8e, 0xff, 0x01, + 0x8e, 0xc1, 0x01, 0x8e, 0xc1, 0x01, 0x8e, 0xc7, 0x01, 0x0e, 0xc7, 0x01, + 0x1e, 0xc0, 0x01, 0x3e, 0xf0, 0x01, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01, + 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00 }; +*/ + +const char *giada_icon[] = { +"65 65 11 1", +" c None", +". c #000000", +"+ c #000100", +"@ c #FFFFFF", +"# c #FDFFFC", +"$ c #CBCDC9", +"% c #292B28", +"& c #626461", +"* c #484A47", +"= c #888A87", +"- c #A7A9A6", +"....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++....", +".@@@#####################$%+++++++++++++%&&*%+++++++&##%+++++....", +".@@#####################&++++++++++++=$#######-*+++++&$+++++++...", +".@@####################&++++++++++%-############$*+++++++++++++..", +"+#####################*++++++++++&################-++++++++++++%+", +"+####################*++++++++++=##################$+++++++++++*+", +"+###################*++++++++++$####################$++++++++++=+", +"+##################=++++++++++-######################-+++++++++-+", +"+#################$++++++++++=#######################=+++++++++$+", +"+#################%+++++++++*########################*+++++++++#+", +"+################*++++++++++#########################%++++++++*#+", +"+###############-++++++++++=########################$+++++++++&#+", +"+###############%+++++++++%#########################-+++++++++-#+", +"+##############-++++++++++-#########################&+++++++++$#+", +"+##############%+++++++++%##########################*+++++++++##+", +"+#############-++++++++++-##########################+++++++++%##+", +"+#############%++++++++++##########################$+++++++++*##+", +"+############$++++++++++&##########################=+++++++++=##+", +"+############=++++++++++$##########################&+++++++++-##+", +"+############*+++++++++%###########################%+++++++++$##+", +"+############++++++++++=###########################++++++++++###+", +"+###########$++++++++++$##########################-+++++++++*###+", +"+###########=++++++++++###########################=+++++++++&###+", +"+###########*+++++++++%###########################*+++++++++-###+", +"+###########%+++++++++&###########################++++++++++$###+", +"+###########%+++++++++-##########################=++++++++++####+", +"+###########++++++++++$##########################%+++++++++%####+", +"+###########++++++++++##########################$++++++++++&####+", +"+##########$++++++++++##########################&++++++++++=####+", +"+##########$+++++++++%#########################$+++++++++++-####+", +"+##########$+++++++++%#########################&+++++++++++$####+", +"+###########+++++++++*########################$++++++++++++#####+", +"+###########+++++++++*########################*+++++++++++*#####+", +"+###########%++++++++%#######################=++++++++++++&#####+", +"+###########&+++++++++######################$+++++++++++++-#####+", +"+###########=+++++++++$#####################%+++%+++++++++$#####+", +"+###########$+++++++++$####################&+++%=+++++++++######+", +"+############%++++++++&###################=++++$&++++++++%######+", +"+############-+++++++++$#################&++++=#*++++++++&######+", +"+#############+++++++++&################*++++*##+++++++++=######+", +"+#############=+++++++++-#############$%++++%###+++++++++-######+", +"+##############*+++++++++=##########$*+++++%###$+++++++++#######+", +"+###############++++++++++&$######$&++++++&####=+++++++++#######+", +"+###############$++++++++++++%&*%++++++++=#####&++++++++*#######+", +"+################$%+++++++++++++++++++++-######%++++++++=#######+", +"+##################&++++++++++++++++++=########+++++++++-#######+", +"+###################$%+++++++++++++%=#########$+++++++++########+", +"+#####################$=%++++++%*=-###########=++++++++%########+", +"+##########################$$$################*++++++++&########+", +"+#############################################+++++++++=########+", +"+############################################$+++++++++$########+", +"+############################################&++++++++%#########+", +"+############################################+++++++++=#########+", +"+###########################################=+++++++++##########+", +"+###########################################%++++++++*##########+", +"+##########################################=+++++++++-##########+", +"+#########-==$############################$+++++++++&###########+", +"+#######=++++++-##########################*+++++++++############+", +"+######$++++++++$########################=+++++++++-############+", +"+######=+++++++++$######################-+++++++++&#############+", +"+######=+++++++++*#####################$+++++++++&##############.", +".@#####=++++++++++-###################-+++++++++=##############@.", +".@@####=++++++++++%##################=+++++++++=#############@@@.", +".@@@###=+++++++++++*###############$*++++++++%$##############@@@.", +"....++++++++++++++++++++++++++++++++++++++++++++++++++++++++....."}; + +const char *recOff_xpm[] = { +"23 23 8 1", +" c #242523", +". c #342F2E", +"+ c #3F3B3A", +"@ c #594F4F", +"# c #7A6663", +"$ c #8C7170", +"% c #A68384", +"& c #B18E8F", +" ", +" ", +" ", +" ", +" ", +" @$%%$@ ", +" .$&&&&&&$. ", +" $&&&&&&&&$ ", +" @&&&#++#&&&@ ", +" $&&# #&&$ ", +" %&&+ +&&% ", +" %&&+ +&&% ", +" $&&# #&&$ ", +" @&&&#++#&&&@ ", +" $&&&&&&&&$ ", +" .$&&&&&&$. ", +" @$%%$@ ", +" ", +" ", +" ", +" ", +" ", +" "}; + +const char *recOn_xpm[] = { +"23 23 8 1", +" c #4D4F4C", +". c #5F4E50", +"+ c #6E4F50", +"@ c #8C5050", +"# c #AE5454", +"$ c #BB5253", +"% c #C55352", +"& c #E85557", +" ", +" ", +" ", +" ", +" ", +" @$&&$@ ", +" .%&&&&&&%. ", +" %&&&&&&&&% ", +" @&&&#++#&&&@ ", +" $&&# #&&$ ", +" &&&+ +&&& ", +" &&&+ +&&& ", +" $&&# #&&$ ", +" @&&&#++#&&&@ ", +" %&&&&&&&&% ", +" .%&&&&&&%. ", +" @$&&$@ ", +" ", +" ", +" ", +" ", +" ", +" "}; + +const char *inputRecOn_xpm[] = { +"23 23 8 1", +" c #524D4C", +". c #4D4F4C", +"+ c #5D4F50", +"@ c #8C5050", +"# c #BB5253", +"$ c #C45251", +"% c #DD5256", +"& c #EA5657", +".......................", +".......................", +".......................", +".......................", +".......................", +"........ @#%%#@ .......", +".......+$&&&&&&$+......", +"...... $&&&&&&&&$ .....", +"......@&&&&&&&&&&@.....", +"......#&&&&&&&&&&#.....", +"......%&&&&&&&&&&%.....", +"......%&&&&&&&&&&%.....", +"......#&&&&&&&&&&#.....", +"......@&&&&&&&&&&@.....", +".......$&&&&&&&&$......", +".......+$&&&&&&$+......", +"........ @#%%#@ .......", +".......................", +".......................", +".......................", +".......................", +".......................", +"......................."}; + +const char *inputRecOff_xpm[] = { +"23 23 8 1", +" c #242523", +". c #252724", +"+ c #332F2E", +"@ c #594E4F", +"# c #896E6D", +"$ c #8D7271", +"% c #A68384", +"& c #B18E8F", +" ", +" ", +" ", +" ", +" ", +" .@#%%#@. ", +" +$&&&&&&$+ ", +" .$&&&&&&&&$. ", +" @&&&&&&&&&&@ ", +" #&&&&&&&&&&# ", +" %&&&&&&&&&&% ", +" %&&&&&&&&&&% ", +" #&&&&&&&&&&# ", +" @&&&&&&&&&&@ ", +" $&&&&&&&&$ ", +" +$&&&&&&$+ ", +" .@#%%#@. ", +" ", +" ", +" ", +" ", +" ", +" "}; + +const char *muteOff_xpm[] = { +"18 18 8 1", +" c #242523", +". c #2E2F2D", +"+ c #3B3C3A", +"@ c #525451", +"# c #6F716E", +"$ c #878986", +"% c #ADAFAC", +"& c #C6C8C5", +" ", +" ", +" ", +" ", +" ++. .++ ", +" +&&$ $&&+ ", +" +&&% %&&+ ", +" +&%&++&%&+ ", +" +&$&##&$&+ ", +" +&#%$$%#&+ ", +" +&#$%%$#&+ ", +" +&#@&&@#&+ ", +" +&#+&&+#&+ ", +" .#@ ## @#. ", +" ", +" ", +" ", +" "}; + +const char *muteOn_xpm[] = { +"18 18 8 1", +" c #4D4F4C", +". c #585A57", +"+ c #616260", +"@ c #7A7C79", +"# c #888A87", +"$ c #989A97", +"% c #B2B4B1", +"& c #C6C8C5", +" ", +" ", +" ", +" ", +" .. .. ", +" +&&$ $&&+ ", +" +&&% %&&+ ", +" +&%&++&%&+ ", +" +&$&@@&$&+ ", +" +&#%$$%#&+ ", +" +&#$&&$#&+ ", +" +&#@&&@#&+ ", +" +&#.&&.#&+ ", +" .#+ ## +#. ", +" ", +" ", +" ", +" "}; + + +const char *readActionOff_xpm[] = { +"18 18 8 1", +" c #242523", +". c #393B38", +"+ c #555754", +"@ c #6B6D6A", +"# c #7F807E", +"$ c #9C9E9B", +"% c #B1B3B0", +"& c #C3C5C2", +" ", +" ", +" ", +" ", +" .... ", +" %&&&&%+ ", +" %&@@@&& ", +" %% $&. ", +" %&@@#&$ ", +" %&&&&@ ", +" %% +&$ ", +" %% #&# ", +" %% %&+ ", +" @@ .#+ ", +" ", +" ", +" ", +" "}; + + +const char *readActionOn_xpm[] = { +"18 18 8 1", +" c #4D4F4C", +". c #696B68", +"+ c #7A7C79", +"@ c #888A87", +"# c #939592", +"$ c #A7A9A6", +"% c #B7B9B6", +"& c #C4C6C3", +" ", +" ", +" ", +" ", +" ", +" %&&&&%. ", +" %&++@&& ", +" %% $& ", +" %&@@#&$ ", +" %&&&&@ ", +" %% +&$ ", +" %% #&# ", +" %% %&. ", +" +@ .@+ ", +" ", +" ", +" ", +" "}; + + +const char *metronomeOff_xpm[] = { +"13 13 8 1", +" c #242523", +". c #2D2928", +"+ c #34302F", +"@ c #443D3C", +"# c #4F4445", +"$ c #685659", +"% c #826A68", +"& c #A18282", +" ", +" ", +" . . ", +" #% %# ", +" .&+ +&. ", +" %$ $% ", +" @& &@ ", +" &@ @& ", +" $% %$ ", +" +&. .&+ ", +" %# #% ", +" . . ", +" "}; + + +const char *metronomeOn_xpm[] = { +"13 13 8 1", +" c #4D4F4C", +". c #565150", +"+ c #645C5C", +"@ c #716465", +"# c #837070", +"$ c #8F7775", +"% c #977C7B", +"& c #A68787", +" ", +" ", +" . . ", +" @% %@ ", +" .&. .&. ", +" $# #$ ", +" +& &+ ", +" &+ +& ", +" #$ $# ", +" .&. .&. ", +" %@ @% ", +" . . ", +" "}; + + +const char *zoomInOff_xpm[] = { +"18 18 8 1", +" c None", +". c #252525", +"+ c #262626", +"@ c #535353", +"# c #ACACAC", +"$ c #AEAEAE", +"% c #B1B1B1", +"& c #C4C4C4", +"++++++++++++++++++", +"+................+", +"+................+", +"+................+", +"+................+", +"+.......@@.......+", +"+.......#$.......+", +"+.......#$.......+", +"+....@%%&&%%@....+", +"+....@%%&&%%@....+", +"+.......#$.......+", +"+.......#$.......+", +"+.......@@.......+", +"+................+", +"+................+", +"+................+", +"+................+", +"++++++++++++++++++"}; + + +const char *zoomInOn_xpm[] = { +"18 18 8 1", +" c None", +". c #4E4E4E", +"+ c #707070", +"@ c #717171", +"# c #B3B3B3", +"$ c #B5B5B5", +"% c #B7B7B7", +"& c #C5C5C5", +"..................", +"..................", +"..................", +"..................", +"..................", +"........++........", +"........#$........", +"........#$........", +".....@%%&&%%@.....", +".....@%%&&%%@.....", +"........#$........", +"........#$........", +"........++........", +"..................", +"..................", +"..................", +"..................", +".................."}; + + +const char *zoomOutOff_xpm[] = { +"18 18 5 1", +" c None", +". c #252525", +"+ c #262626", +"@ c #9C9C9C", +"# c #BBBBBB", +"++++++++++++++++++", +"+................+", +"+................+", +"+................+", +"+................+", +"+................+", +"+................+", +"+................+", +"+......@##@......+", +"+......@##@......+", +"+................+", +"+................+", +"+................+", +"+................+", +"+................+", +"+................+", +"+................+", +"++++++++++++++++++"}; + + +const char *zoomOutOn_xpm[] = { +"18 18 4 1", +" c None", +". c #4E4E4E", +"+ c #A7A7A7", +"@ c #BEBEBE", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +".......+@@+.......", +".......+@@+.......", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +"..................", +".................."}; + + + +const char *scrollRightOff_xpm[] = { +"12 12 8 1", +" c #181917", +". c #242523", +"+ c #2E2F2D", +"@ c #4D4F4C", +"# c #5D5F5C", +"$ c #828481", +"% c #9B9D9A", +"& c #BCBEBB", +"............", +"............", +"...+........", +"...&$@......", +"...$&&%@....", +"....+#%&%...", +"....+#%&%...", +"...$&&%#....", +"...&$@......", +"...+........", +"............", +"............"}; + + +const char *scrollLeftOff_xpm[] = { +"12 12 8 1", +" c #181917", +". c #242523", +"+ c #2E2F2D", +"@ c #4D4F4C", +"# c #5D5F5C", +"$ c #828481", +"% c #9B9D9A", +"& c #BCBEBB", +"............", +"............", +"........+...", +"......@$&...", +"....@%&&$...", +"...%&%#+....", +"...%&%#+....", +"....#%&&$...", +"......@$&...", +"........+...", +"............", +"............"}; + + +const char *scrollLeftOn_xpm[] = { +"12 12 8 1", +" c #4D4F4C", +". c #6B6D6A", +"+ c #7B7D7A", +"@ c #969895", +"# c #A6A8A5", +"$ c #B4B6B3", +"% c #C0C2BF", +"& c #FEFFFC", +" ", +" ", +" ", +" .@$ ", +" +#%%@ ", +" $%#+ ", +" %%#+ ", +" +$%%@ ", +" .#$ ", +" ", +" ", +" "}; + + +const char *scrollRightOn_xpm[] = { +"12 12 8 1", +" c #4D4F4C", +". c #6B6D6A", +"+ c #7B7D7A", +"@ c #969895", +"# c #A6A8A5", +"$ c #B4B6B3", +"% c #C0C2BF", +"& c #FEFFFC", +" ", +" ", +" ", +" %@. ", +" @%%#. ", +" +#%# ", +" +#%# ", +" @%%#+ ", +" %@. ", +" ", +" ", +" "}; + + +const char *soloOn_xpm[] = { +"18 18 8 1", +" c #4D4F4C", +". c #616360", +"+ c #737572", +"@ c #838582", +"# c #929491", +"$ c #A5A7A4", +"% c #B1B3B0", +"& c #C6C8C5", +" ", +" ", +" ", +" ", +" .@+. ", +" #&&&&# ", +" .&$ %&. ", +" &%+ .. ", +" #&&&$. ", +" .@$&&. ", +" .#. @&@ ", +" .&$. #&+ ", +" #&&&&$ ", +" .+@+ ", +" ", +" ", +" ", +" "}; + + +const char *soloOff_xpm[] = { +"18 18 8 1", +" c #242523", +". c #3D3F3D", +"+ c #525451", +"@ c #666865", +"# c #80827F", +"$ c #979996", +"% c #A7A9A6", +"& c #C6C8C5", +" ", +" ", +" ", +" ", +" .@@. ", +" #&&&&# ", +" .&$ %&. ", +" &%+ .. ", +" #&&&$+ ", +" .@%&&. ", +" +#. @&@ ", +" .&$..#&+ ", +" #&&&&$ ", +" .@@+ ", +" ", +" ", +" ", +" "}; + + +#ifdef WITH_VST + + +const char *fxOff_xpm[] = { +"18 18 8 1", +" c #242523", +". c #40423F", +"+ c #4D4E4C", +"@ c #686A67", +"# c #7B7D7A", +"$ c #919390", +"% c #AEB0AD", +"& c #C1C3C0", +" ", +" ", +" ", +" ", +" ", +" ..... . . ", +" $&&&$ $% @&. ", +" $$ .&#&@ ", +" $%##. @&$ ", +" $%##. #&% ", +" $$ .&@&# ", +" $$ %$ @&. ", +" .. + +. ", +" ", +" ", +" ", +" ", +" "}; + + +const char *fxOn_xpm[] = { +"18 18 8 1", +" c #4D4F4C", +". c #565855", +"+ c #636562", +"@ c #80827F", +"# c #8E908D", +"$ c #9FA19E", +"% c #B1B3B0", +"& c #C1C3C0", +" ", +" ", +" ", +" ", +" ", +" .++++ +. +. ", +" $&&&$ $% @&. ", +" $$ .&#&@ ", +" $%##+ @&$ ", +" $%##+ #&% ", +" $$ +&@&# ", +" $$ %$ @&+ ", +" ++ .+. ++ ", +" ", +" ", +" ", +" ", +" "}; + + +const char *fxShiftUpOff_xpm[] = { +"18 18 7 1", +" c #242523", +". c #4D4F4C", +"+ c #A3A5A2", +"@ c #868885", +"# c #C1C3C0", +"$ c #313330", +"% c #626361", +" ", +" ", +" ", +" ", +" ", +" ", +" .+@ ", +" @+#. ", +" $#%+@ ", +" %# %#$ ", +" +@ $#% ", +" $#. @+ ", +" $. $. ", +" ", +" ", +" ", +" ", +" "}; + + +const char *fxShiftUpOn_xpm[] = { +"18 18 5 1", +" c #4D4F4C", +". c #70726F", +"+ c #A5A7A4", +"@ c #C1C3BF", +"# c #8E908D", +" ", +" ", +" ", +" ", +" ", +" ", +" .++ ", +" +@@. ", +" @.+# ", +" .@ .@ ", +" +# @. ", +" .@. #+ ", +" . . ", +" ", +" ", +" ", +" ", +" "}; + + +const char *fxShiftDownOff_xpm[] = { +"18 18 7 1", +" c #242523", +". c #4D4F4C", +"+ c #A3A5A2", +"@ c #313330", +"# c #626361", +"$ c #868885", +"% c #C1C3C0", +" ", +" ", +" ", +" ", +" ", +" ", +" .+@ #$ ", +" @%# +$ ", +" $+ .%@ ", +" .%@$+ ", +" +$%# ", +" #%%@ ", +" @.. ", +" ", +" ", +" ", +" ", +" "}; + + +const char *fxShiftDownOn_xpm[] = { +"18 18 5 1", +" c #4D4F4C", +". c #70726F", +"+ c #A5A7A4", +"@ c #C1C3BF", +"# c #8E908D", +" ", +" ", +" ", +" ", +" ", +" ", +" .+ .+ ", +" @. +# ", +" #+ .@. ", +" .@.#+ ", +" +#@. ", +" #@@ ", +" .. ", +" ", +" ", +" ", +" ", +" "}; + + +const char *vstLogo_xpm[] = { +"65 38 8 1", +" c #161715", +". c #2B2D2A", +"+ c #474846", +"@ c #6A6C69", +"# c #8C8E8B", +"$ c #A8AAA7", +"% c #C7C9C6", +"& c #EEF0ED", +" @#############################################################+ ", +"@#.............................................................$+", +"#. .#", +"#. .#", +"#. ...... .. .#", +"#. .@$$$####$%$#@.+&$ .#", +"#. .#$$#+. +#$%%%%$ .#", +"#. .$$#$ .#$$%$ .#", +"#. ............. ....$$$$$ ++$$%$+@@@@@@@@@@@@@@@ .#", +"#. ##$$$$$$%%%%@ %%&&&&%%$@ %&@#$$@@$%%&&&%%%&&&&& .#", +"#. +$$$$$%@ .&%####%$@ $&$.$# #$%%%& @&%& .#", +"#. +$$$$$% +&$###$%%&&$@. $&. #$%%%&. .%& .#", +"#. @$$$$%$ %##$##$%&&&&&&%#.%# #$$%%&. @& .#", +"#. $$$$$%+ #& #$$%%&&&&&&&%%$$@ #$$%%&. + .#", +"#. .$$$$$% +&+ .#%&&&&&&&&%$$#$$# #$$%%&. .#", +"#. @$$$$%$ %$ @%&&&&&&%$$###$$ #$$%%&. .#", +"#. #$$$%%@ #& . +$&&&%$####$%$ #$$%%&. .#", +"#. $$$%%% .&@ +%# .@$$$###$$% #$$$%&. .#", +"#. +%$%%%$$% +$$+ #$#$$$% @$$$%&. .#", +"#. #%%%%%&. +%$$ ##$$%$ @$$$%%. .#", +"#. $$%%%@ +%$$$. #$$$%. @$$$$%. .#", +"#. +%%%$ +%$$#$@ +$$%$ @#$$$%+ .#", +"#. @%%. +%%%$$$$#@++.++@#$$$@ @@##$$$%%%$$@ .#", +"#. #@ +&# .@@###$$$###@. @+++@@@@###$@ .#", +"#. .#", +"#. .#", +"#. .#", +"#. .#", +"#. .#", +"#. .@$$$$$$$$ .$%%%%%%# .#", +"#. ....... .@@@@@@@@@. .#", +"#. ........ @@@+@@@@@@@@@@+ .#", +"@# ......... .####@@@@@@@@@@@+ #@", +" @$$$$$$$$$$$$$$$.......... .@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ ", +" ......... .@@@@@@@@@@@@@@@. ", +" ........ @@@@@@@@@@. ", +" ........... .@@@@@@@@@ ", +" .......... .@@@@@@@@ "}; + + +const char *fxRemoveOff_xpm[] = { +"18 18 9 1", +" c None", +". c #242623", +"+ c #2F312E", +"@ c #393A38", +"# c #484A47", +"$ c #5D5F5C", +"% c #8E908D", +"& c #9B9D9A", +"* c #BDBFBC", +"..................", +"..................", +"..................", +"..................", +"..................", +".....+#@..@#+.....", +"......&*++*&......", +"......@*%%*@......", +".......$**$.......", +".......#**#.......", +"......+*&&*+......", +"......%*@@*%......", +"......@@..@@......", +"..................", +"..................", +"..................", +"..................", +".................."}; + + +const char *fxRemoveOn_xpm[] = { +"18 18 9 1", +" c None", +". c #4D4F4C", +"+ c #575956", +"@ c #5C5D5B", +"# c #666865", +"$ c #787977", +"% c #9C9E9B", +"& c #A6A8A5", +"* c #BFC1BE", +"..................", +"..................", +"..................", +"..................", +"..................", +"......#@..@#......", +"......&*++*&......", +"......@*%%*@......", +".......$**$.......", +".......#**#.......", +"......+*&&*+......", +"......%*@+*%......", +"......@+..+@......", +"..................", +"..................", +"..................", +"..................", +".................."}; +#endif // #ifdef WITH_VST + + +const char *beatsDivideOn_xpm[] = { +"13 13 13 1", +" c None", +". c #595B58", +"+ c #5B5D5A", +"@ c #5F615E", +"# c #686967", +"$ c #737572", +"% c #787A77", +"& c #80827F", +"* c #8F918E", +"= c #959794", +"- c #9A9C99", +"; c #C4C6C3", +"> c #C7C9C6", +".............", +".............", +".............", +".............", +".....#>#.....", +"....+@%@+....", +"...*>>>>>*...", +"....+@%@+....", +".....#>#.....", +".............", +".............", +".............", +"............."}; + + +const char *beatsDivideOff_xpm[] = { +"13 13 13 1", +" c None", +". c #242523", +"+ c #262825", +"@ c #2D2E2C", +"# c #3A3B39", +"$ c #494B48", +"% c #525451", +"& c #595B58", +"* c #5F615E", +"= c #787A77", +"- c #858784", +"; c #C3C5C1", +"> c #C7C9C6", +".............", +".............", +".............", +"......+......", +".....#>#.....", +"...++@%@++...", +"...=>>>>>=...", +"...++@%@++...", +".....#>#.....", +"......+......", +".............", +".............", +"............."}; + + +const char *beatsMultiplyOn_xpm[] = { +"13 13 13 1", +" c None", +". c #595B58", +"+ c #5B5D5A", +"@ c #5F615E", +"# c #686967", +"$ c #737572", +"% c #787A77", +"& c #80827F", +"* c #8F918E", +"= c #959794", +"- c #9A9C99", +"; c #C4C6C3", +"> c #C7C9C6", +".............", +".............", +".............", +"....$...$....", +"...$;&.&;$...", +"....&;-;&....", +".....->-.....", +"....&;=;&....", +"...$;&.&;$...", +"...+$...$....", +".............", +".............", +"............."}; + + +const char *beatsMultiplyOff_xpm[] = { +"13 13 12 1", +" c #242523", +". c #262825", +"+ c #2D2E2C", +"@ c #3A3B39", +"# c #494B48", +"$ c #525451", +"% c #595B58", +"& c #5F615E", +"* c #787A77", +"= c #858784", +"- c #C3C5C1", +"; c #C7C9C6", +" ", +" ", +" ", +" .# #. ", +" #-& &-# ", +" &-=-& ", +" =;= ", +" %-*-& ", +" #-% %-# ", +" .# #. ", +" ", +" ", +" "}; + + +const char *channelStop_xpm[] = { +"18 18 8 1", +" c #242523", +". c #312D2C", +"+ c #413A3A", +"@ c #615253", +"# c #73605F", +"$ c #7A6663", +"% c #9C7E7D", +"& c #B08D8E", +" ", +" ", +" ", +" ", +" ##. ", +" $&%@ ", +" $&&&%+ ", +" $&&&&&$. ", +" $&&&&&&&@ ", +" $&&&&&&&@. ", +" $&&&&&$. ", +" $&&&%+ ", +" $&&@ ", +" $#. ", +" . ", +" ", +" ", +" "}; + + + +const char *channelPlay_xpm[] = { +"18 18 8 1", +" c #4D4F4C", +". c #554E56", +"+ c #5A4D59", +"@ c #605068", +"# c #775086", +"$ c #8A509C", +"% c #9E50B5", +"& c #AD52D0", +" ", +" ", +" ", +" . ", +" $$. ", +" $&%# ", +" $&&&%@ ", +" $&&&&&$. ", +" $&&&&&&&#. ", +" $&&&&&&&#. ", +" $&&&&&$+ ", +" $&&&%@ ", +" $&&# ", +" $$. ", +" . ", +" ", +" ", +" "}; diff --git a/src/core/graphics.h b/src/core/graphics.h new file mode 100644 index 0000000..c8d9073 --- /dev/null +++ b/src/core/graphics.h @@ -0,0 +1,105 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * graphics + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GRAPHICS_H +#define GRAPHICS_H + +extern const char *giada_logo_xpm[]; + +extern const char *loopRepeat_xpm[]; +extern const char *loopBasic_xpm[]; +extern const char *loopOnce_xpm[]; +extern const char *loopOnceBar_xpm[]; +extern const char *oneshotBasic_xpm[]; +extern const char *oneshotRetrig_xpm[]; +extern const char *oneshotPress_xpm[]; +extern const char *oneshotEndless_xpm[]; + +extern const char *updirOff_xpm[]; +extern const char *updirOn_xpm[]; + +extern const char *pause_xpm[]; +extern const char *play_xpm[]; + +extern const char *zoomInOff_xpm[]; +extern const char *zoomInOn_xpm[]; +extern const char *zoomOutOff_xpm[]; +extern const char *zoomOutOn_xpm[]; + +extern const char *scrollLeftOff_xpm[]; +extern const char *scrollLeftOn_xpm[]; +extern const char *scrollRightOff_xpm[]; +extern const char *scrollRightOn_xpm[]; + +extern const char *rewindOff_xpm[]; +extern const char *rewindOn_xpm[]; + +extern const char *recOff_xpm[]; +extern const char *recOn_xpm[]; + +extern const char *metronomeOff_xpm[]; +extern const char *metronomeOn_xpm[]; + +extern const char *inputRecOn_xpm[]; +extern const char *inputRecOff_xpm[]; + +extern const char *beatsDivideOn_xpm[]; +extern const char *beatsDivideOff_xpm[]; +extern const char *beatsMultiplyOn_xpm[]; +extern const char *beatsMultiplyOff_xpm[]; + +extern const char *muteOff_xpm[]; +extern const char *muteOn_xpm[]; + +extern const char *soloOff_xpm[]; +extern const char *soloOn_xpm[]; + +extern const char *readActionOn_xpm[]; +extern const char *readActionOff_xpm[]; + +extern const char *channelStop_xpm[]; +extern const char *channelPlay_xpm[]; + +#ifdef WITH_VST +extern const char *fxOff_xpm[]; +extern const char *fxOn_xpm[]; + +extern const char *fxShiftUpOn_xpm[]; +extern const char *fxShiftUpOff_xpm[]; +extern const char *fxShiftDownOn_xpm[]; +extern const char *fxShiftDownOff_xpm[]; + +extern const char *fxRemoveOff_xpm[]; +extern const char *fxRemoveOn_xpm[]; + +extern const char *vstLogo_xpm[]; +#endif + +extern const char *giada_icon[]; + +#endif diff --git a/src/core/init.cpp b/src/core/init.cpp new file mode 100644 index 0000000..1ecbb2d --- /dev/null +++ b/src/core/init.cpp @@ -0,0 +1,201 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * init + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include "../utils/log.h" +#include "../utils/utils.h" +#include "../utils/gui_utils.h" +#include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/gd_warnings.h" +#include "init.h" +#include "mixer.h" +#include "wave.h" +#include "const.h" +#include "mixerHandler.h" +#include "patch.h" +#include "conf.h" +#include "pluginHost.h" +#include "recorder.h" +#include "midiMapConf.h" +#include "kernelMidi.h" + + +extern Mixer G_Mixer; +extern bool G_audio_status; +extern bool G_quit; +extern Patch G_Patch; +extern Conf G_Conf; +extern MidiMapConf G_MidiMap; +extern gdMainWindow *mainWin; + +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +void init_prepareParser() +{ + G_Conf.read(); + G_Patch.setDefault(); + if (!gLog_init(G_Conf.logMode)) + gLog("[init] log init failed! Using default stdout\n"); + time_t t; + time (&t); + gLog("[init] Giada "VERSIONE" - %s", ctime(&t)); + gLog("[init] configuration file ready\n"); +} + + +/* -------------------------------------------------------------------------- */ + + +void init_prepareKernelAudio() +{ + kernelAudio::openDevice( + G_Conf.soundSystem, + G_Conf.soundDeviceOut, + G_Conf.soundDeviceIn, + G_Conf.channelsOut, + G_Conf.channelsIn, + G_Conf.samplerate, + G_Conf.buffersize); + G_Mixer.init(); + recorder::init(); +} + + +/* -------------------------------------------------------------------------- */ + + +void init_prepareKernelMIDI() +{ + kernelMidi::setApi(G_Conf.midiSystem); + kernelMidi::openOutDevice(G_Conf.midiPortOut); + kernelMidi::openInDevice(G_Conf.midiPortIn); +} + + +/* -------------------------------------------------------------------------- */ + + +void init_prepareMidiMap() +{ + G_MidiMap.init(); + G_MidiMap.setDefault(); + G_MidiMap.readMap(G_Conf.midiMapPath); +} + + +/* -------------------------------------------------------------------------- */ + + +void init_startGUI(int argc, char **argv) +{ + char win_label[32]; + sprintf(win_label, "%s - %s", + VERSIONE_STR, + !strcmp(G_Patch.name, "") ? "(default patch)" : G_Patch.name); + + mainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, win_label, argc, argv); + mainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW, G_Conf.mainWindowH); + + /* never update the GUI elements if G_audio_status is bad, segfaults + * are around the corner */ + + if (G_audio_status) + gu_updateControls(); + + if (!G_audio_status) + gdAlert( + "Your soundcard isn't configured correctly.\n" + "Check the configuration and restart Giada." + ); +} + +/* -------------------------------------------------------------------------- */ + + +void init_startKernelAudio() +{ + if (G_audio_status) + kernelAudio::startStream(); + +#ifdef WITH_VST + G_PluginHost.allocBuffers(); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void init_shutdown() +{ + G_quit = true; + + /* store position and size of the main window for the next startup */ + + G_Conf.mainWindowX = mainWin->x(); + G_Conf.mainWindowY = mainWin->y(); + G_Conf.mainWindowW = mainWin->w(); + G_Conf.mainWindowH = mainWin->h(); + + /* close any open subwindow, especially before cleaning PluginHost to + * avoid mess */ + + gu_closeAllSubwindows(); + gLog("[init] all subwindows closed\n"); + + /* write configuration file */ + + if (!G_Conf.write()) + gLog("[init] error while saving configuration file!\n"); + else + gLog("[init] configuration saved\n"); + + /* if G_audio_status we close the kernelAudio FIRST, THEN the mixer. + * The opposite could cause random segfaults (even now with RtAudio?). */ + + if (G_audio_status) { + kernelAudio::closeDevice(); + G_Mixer.close(); + gLog("[init] Mixer closed\n"); + } + + recorder::clearAll(); + gLog("[init] Recorder cleaned up\n"); + +#ifdef WITH_VST + G_PluginHost.freeAllStacks(); + gLog("[init] Plugin Host cleaned up\n"); +#endif + + gLog("[init] Giada "VERSIONE" closed\n\n"); + gLog_close(); +} diff --git a/src/core/init.h b/src/core/init.h new file mode 100644 index 0000000..0824f65 --- /dev/null +++ b/src/core/init.h @@ -0,0 +1,50 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * init + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef INIT_H +#define INIT_H + + +#include +#include +#ifdef __APPLE__ + #include +#endif + + +void init_prepareParser(); +void init_startGUI(int argc, char **argv); +void init_prepareKernelAudio(); +void init_prepareKernelMIDI(); +void init_prepareMidiMap(); +void init_startKernelAudio(); +void init_shutdown(); + + +#endif diff --git a/src/core/kernelAudio.cpp b/src/core/kernelAudio.cpp new file mode 100644 index 0000000..c31d507 --- /dev/null +++ b/src/core/kernelAudio.cpp @@ -0,0 +1,468 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * KernelAudio + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../utils/log.h" +#include "../glue/glue.h" +#include "kernelAudio.h" +#include "mixer.h" + +#include "conf.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern bool G_audio_status; + + +namespace kernelAudio { + +RtAudio *system = NULL; +unsigned numDevs = 0; +bool inputEnabled = 0; +unsigned realBufsize = 0; +int api = 0; + +int openDevice( + int _api, + int outDev, + int inDev, + int outChan, + int inChan, + int samplerate, + int buffersize) +{ + api = _api; + gLog("[KA] using system 0x%x\n", api); +#if defined(__linux__) + if (api == SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK)) + system = new RtAudio(RtAudio::UNIX_JACK); + else + if (api == SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA)) + system = new RtAudio(RtAudio::LINUX_ALSA); + else + if (api == SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE)) + system = new RtAudio(RtAudio::LINUX_PULSE); +#elif defined(_WIN32) + if (api == SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS)) + system = new RtAudio(RtAudio::WINDOWS_DS); + else + if (api == SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO)) + system = new RtAudio(RtAudio::WINDOWS_ASIO); +#elif defined(__APPLE__) + if (api == SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE)) + system = new RtAudio(RtAudio::MACOSX_CORE); +#endif + else { + G_audio_status = false; + return 0; + } + + + + //gLog("[KA] %d\n", sizeof(system->rtapi_)); + + gLog("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate); + + numDevs = system->getDeviceCount(); + + if (numDevs < 1) { + gLog("[KA] no devices found with this API\n"); + closeDevice(); + G_audio_status = false; + return 0; + } + else { + gLog("[KA] %d device(s) found\n", numDevs); + for (unsigned i=0; iopenStream( + &outParams, // output params + &inParams, // input params + RTAUDIO_FLOAT32, // audio format + samplerate, // sample rate + &realBufsize, // buffer size in byte + &G_Mixer.masterPlay, // audio callback + NULL, // user data (unused) + &options); + } + else { + system->openStream( + &outParams, // output params + NULL, // input params + RTAUDIO_FLOAT32, // audio format + samplerate, // sample rate + &realBufsize, // buffer size in byte + &G_Mixer.masterPlay, // audio callback + NULL, // user data (unused) + &options); + } + G_audio_status = true; + +#if defined(__linux__) + if (api == SYS_API_JACK) + jackSetSyncCb(); +#endif + + return 1; + } + catch (RtAudioError &e) { + gLog("[KA] system init error: %s\n", e.getMessage().c_str()); + closeDevice(); + G_audio_status = false; + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int startStream() { + try { + system->startStream(); + gLog("[KA] latency = %lu\n", system->getStreamLatency()); + return 1; + } + catch (RtAudioError &e) { + gLog("[KA] Start stream error: %s\n", e.getMessage().c_str()); + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int stopStream() { + try { + system->stopStream(); + return 1; + } + catch (RtAudioError &e) { + gLog("[KA] Stop stream error\n"); + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +const char *getDeviceName(unsigned dev) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).name.c_str(); + } + catch (RtAudioError &e) { + gLog("[KA] invalid device ID = %d\n", dev); + return NULL; + } +} + + +/* ------------------------------------------------------------------ */ + + +int closeDevice() { + if (system->isStreamOpen()) { +#if defined(__linux__) || defined(__APPLE__) + system->abortStream(); // stopStream seems to lock the thread +#elif defined(_WIN32) + system->stopStream(); // on Windows it's the opposite +#endif + system->closeStream(); + delete system; + system = NULL; + } + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +unsigned getMaxInChans(int dev) { + + if (dev == -1) return 0; + + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).inputChannels; + } + catch (RtAudioError &e) { + gLog("[KA] Unable to get input channels\n"); + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +unsigned getMaxOutChans(unsigned dev) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).outputChannels; + } + catch (RtAudioError &e) { + gLog("[KA] Unable to get output channels\n"); + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +bool isProbed(unsigned dev) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).probed; + } + catch (RtAudioError &e) { + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +unsigned getDuplexChans(unsigned dev) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).duplexChannels; + } + catch (RtAudioError &e) { + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +bool isDefaultIn(unsigned dev) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultInput; + } + catch (RtAudioError &e) { + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +bool isDefaultOut(unsigned dev) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultOutput; + } + catch (RtAudioError &e) { + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int getTotalFreqs(unsigned dev) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.size(); + } + catch (RtAudioError &e) { + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int getFreq(unsigned dev, int i) { + try { + return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.at(i); + } + catch (RtAudioError &e) { + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int getDefaultIn() { + return system->getDefaultInputDevice(); +} + +int getDefaultOut() { + return system->getDefaultOutputDevice(); +} + + +/* ------------------------------------------------------------------ */ + + +int getDeviceByName(const char *name) { + for (unsigned i=0; i APIs; + RtAudio::getCompiledApi(APIs); + for (unsigned i=0; i +#include +#include + +jack_client_t *jackGetHandle() { + return (jack_client_t*) system->rtapi_->__HACK__getJackClient(); +} + +void jackStart() { + if (api == SYS_API_JACK) { + jack_client_t *client = jackGetHandle(); + jack_transport_start(client); + } +} + + +void jackStop() { + if (api == SYS_API_JACK) { + jack_client_t *client = jackGetHandle(); + jack_transport_stop(client); + } +} + + +void jackSetSyncCb() { + jack_client_t *client = jackGetHandle(); + jack_set_sync_callback(client, jackSyncCb, NULL); + //jack_set_sync_timeout(client, 8); +} + + +int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, + void *arg) +{ + switch (state) { + case JackTransportStopped: + gLog("[KA] Jack transport stopped, frame=%d\n", pos->frame); + glue_stopSeq(false); // false = not from GUI + if (pos->frame == 0) + glue_rewindSeq(); + break; + + case JackTransportRolling: + gLog("[KA] Jack transport rolling\n"); + break; + + case JackTransportStarting: + gLog("[KA] Jack transport starting, frame=%d\n", pos->frame); + glue_startSeq(false); // false = not from GUI + if (pos->frame == 0) + glue_rewindSeq(); + break; + + default: + gLog("[KA] Jack transport [unknown]\n"); + } + return 1; +} + +#endif + +} + + diff --git a/src/core/kernelAudio.h b/src/core/kernelAudio.h new file mode 100644 index 0000000..a4673f2 --- /dev/null +++ b/src/core/kernelAudio.h @@ -0,0 +1,96 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * KernelAudio + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef KERNELAUDIO_H +#define KERNELAUDIO_H + + +#include "../deps/rtaudio-mod/RtAudio.h" +#if defined(__linux__) + #include + #include + #include +#endif + + +namespace kernelAudio { + + int openDevice( + int api, + int outDev, + int inDev, + int outChan, + int inChan, + int samplerate, + int buffersize); + int closeDevice(); + + int startStream(); + int stopStream(); + + bool isProbed (unsigned dev); + bool isDefaultIn (unsigned dev); + bool isDefaultOut (unsigned dev); + const char *getDeviceName (unsigned dev); + unsigned getMaxInChans (int dev); + unsigned getMaxOutChans (unsigned dev); + unsigned getDuplexChans (unsigned dev); + int getTotalFreqs (unsigned dev); + int getFreq (unsigned dev, int i); + int getDeviceByName(const char *name); + int getDefaultOut (); + int getDefaultIn (); + bool hasAPI (int API); + + std::string getRtAudioVersion(); + +#ifdef __linux__ + jack_client_t *jackGetHandle(); + void jackStart(); + void jackStop(); + void jackSetSyncCb(); + int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg); +#endif + + /* *** how to avoid multiple definition of *** + * When you declare a variable in a header file, every source file that + * includes that header, either directly or indirectly, gets its own + * separate copy of the variable. Then when you go to link all the .o + * files together, the linker sees that the variable is instantiated + * in a bunch of .o files. Make it extern in the header file and + * instantiate it in memory.cpp. */ + + extern RtAudio *system; + extern unsigned numDevs; + extern bool inputEnabled; + extern unsigned realBufsize; // reale bufsize from the soundcard + extern int api; +} + +#endif diff --git a/src/core/kernelMidi.cpp b/src/core/kernelMidi.cpp new file mode 100644 index 0000000..d695ef8 --- /dev/null +++ b/src/core/kernelMidi.cpp @@ -0,0 +1,395 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * KernelMidi + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include "../utils/log.h" +#include "../glue/glue.h" +#include "kernelMidi.h" +#include "mixer.h" +#include "channel.h" +#include "sampleChannel.h" +#include "pluginHost.h" +#include "conf.h" +#include "midiMapConf.h" + + +extern bool G_midiStatus; +extern Conf G_Conf; +extern Mixer G_Mixer; +extern MidiMapConf G_MidiMap; + +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +namespace kernelMidi +{ + +int api = 0; // one api for both in & out +RtMidiOut *midiOut = NULL; +RtMidiIn *midiIn = NULL; +unsigned numOutPorts = 0; +unsigned numInPorts = 0; + +cb_midiLearn *cb_learn = NULL; +void *cb_data = NULL; + + +/* -------------------------------------------------------------------------- */ + + +void startMidiLearn(cb_midiLearn *cb, void *data) +{ + cb_learn = cb; + cb_data = data; +} + + +/* -------------------------------------------------------------------------- */ + + +void stopMidiLearn() +{ + cb_learn = NULL; + cb_data = NULL; +} + + +/* -------------------------------------------------------------------------- */ + + +void setApi(int _api) +{ + api = api; + gLog("[KM] using system 0x%x\n", api); +} + + +/* -------------------------------------------------------------------------- */ + + +int openOutDevice(int port) +{ + try { + midiOut = new RtMidiOut((RtMidi::Api) api, "Giada MIDI Output"); + G_midiStatus = true; + } + catch (RtMidiError &error) { + gLog("[KM] MIDI out device error: %s\n", error.getMessage().c_str()); + G_midiStatus = false; + return 0; + } + + /* print output ports */ + + numOutPorts = midiOut->getPortCount(); + gLog("[KM] %d output MIDI ports found\n", numOutPorts); + for (unsigned i=0; i 0) { + try { + midiOut->openPort(port, getOutPortName(port)); + gLog("[KM] MIDI out port %d open\n", port); + + /* for each init command of MidiMap, send the init commands to the + external world. TODO 1 - we shold do that only if there is a map loaded + and available in MidiMap. + TODO 2 - move the map initialization to another function */ + + for(int i=0; igetPortCount(); + gLog("[KM] %d input MIDI ports found\n", numInPorts); + for (unsigned i=0; i 0) { + try { + midiIn->openPort(port, getInPortName(port)); + midiIn->ignoreTypes(true, false, true); // ignore all system/time msgs, for now + gLog("[KM] MIDI in port %d open\n", port); + midiIn->setCallback(&callback); + return 1; + } + catch (RtMidiError &error) { + gLog("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str()); + G_midiStatus = false; + return 0; + } + } + else + return 2; +} + + +/* -------------------------------------------------------------------------- */ + + +bool hasAPI(int API) +{ + std::vector APIs; + RtMidi::getCompiledApi(APIs); + for (unsigned i=0; igetPortName(p).c_str(); } + catch (RtMidiError &error) { return NULL; } +} + +const char *getInPortName(unsigned p) +{ + try { return midiIn->getPortName(p).c_str(); } + catch (RtMidiError &error) { return NULL; } +} + + +/* -------------------------------------------------------------------------- */ + + +void send(uint32_t data) +{ + if (!G_midiStatus) + return; + + std::vector msg(1, getB1(data)); + msg.push_back(getB2(data)); + msg.push_back(getB3(data)); + + midiOut->sendMessage(&msg); + gLog("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]); +} + + +/* -------------------------------------------------------------------------- */ + + +void send(int b1, int b2, int b3) +{ + if (!G_midiStatus) + return; + + std::vector msg(1, b1); + + if (b2 != -1) + msg.push_back(b2); + if (b3 != -1) + msg.push_back(b3); + + midiOut->sendMessage(&msg); + //gLog("[KM] send msg=(%X %X %X)\n", b1, b2, b3); +} + + +/* -------------------------------------------------------------------------- */ + + +void callback(double t, std::vector *msg, void *data) +{ + /* 0.8.0 - for now we handle other midi signals (common and real-time + * messages) as unknown, for debugging purposes */ + + if (msg->size() < 3) { + gLog("[KM] MIDI received - unkown signal - size=%d, value=0x", (int) msg->size()); + for (unsigned i=0; isize(); i++) + gLog("%X", (int) msg->at(i)); + gLog("\n"); + return; + } + + /* in this place we want to catch two things: a) note on/note off + * from a keyboard and b) knob/wheel/slider movements from a + * controller */ + + uint32_t input = getIValue(msg->at(0), msg->at(1), msg->at(2)); + uint32_t chan = input & 0x0F000000; + uint32_t value = input & 0x0000FF00; + uint32_t pure = 0x00; + if (!G_Conf.noNoteOff) + pure = input & 0xFFFF0000; // input without 'value' byte + else + pure = input & 0xFFFFFF00; // input with 'value' byte + + gLog("[KM] MIDI received - 0x%X (chan %d)", input, chan >> 24); + + /* start dispatcher. If midi learn is on don't parse channels, just + * learn incoming midi signal. Otherwise process master events first, + * then each channel in the stack. This way incoming signals don't + * get processed by glue_* when midi learning is on. */ + + if (cb_learn) { + gLog("\n"); + cb_learn(pure, cb_data); + } + else { + + /* process master events */ + + if (pure == G_Conf.midiInRewind) { + gLog(" >>> rewind (global) (pure=0x%X)", pure); + glue_rewindSeq(); + } + else if (pure == G_Conf.midiInStartStop) { + gLog(" >>> startStop (global) (pure=0x%X)", pure); + glue_startStopSeq(); + } + else if (pure == G_Conf.midiInActionRec) { + gLog(" >>> actionRec (global) (pure=0x%X)", pure); + glue_startStopActionRec(); + } + else if (pure == G_Conf.midiInInputRec) { + gLog(" >>> inputRec (global) (pure=0x%X)", pure); + glue_startStopInputRec(false, false); // update gui, no popup messages + } + else if (pure == G_Conf.midiInMetronome) { + gLog(" >>> metronome (global) (pure=0x%X)", pure); + glue_startStopMetronome(false); + } + else if (pure == G_Conf.midiInVolumeIn) { + float vf = (value >> 8)/127.0f; + gLog(" >>> input volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf); + glue_setInVol(vf, false); + } + else if (pure == G_Conf.midiInVolumeOut) { + float vf = (value >> 8)/127.0f; + gLog(" >>> output volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf); + glue_setOutVol(vf, false); + } + else if (pure == G_Conf.midiInBeatDouble) { + gLog(" >>> sequencer x2 (global) (pure=0x%X)", pure); + glue_beatsMultiply(); + } + else if (pure == G_Conf.midiInBeatHalf) { + gLog(" >>> sequencer /2 (global) (pure=0x%X)", pure); + glue_beatsDivide(); + } + + /* process channels */ + + for (unsigned i=0; imidiIn) continue; + + if (pure == ch->midiInKeyPress) { + gLog(" >>> keyPress, ch=%d (pure=0x%X)", ch->index, pure); + glue_keyPress(ch, false, false); + } + else if (pure == ch->midiInKeyRel) { + gLog(" >>> keyRel ch=%d (pure=0x%X)", ch->index, pure); + glue_keyRelease(ch, false, false); + } + else if (pure == ch->midiInMute) { + gLog(" >>> mute ch=%d (pure=0x%X)", ch->index, pure); + glue_setMute(ch, false); + } + else if (pure == ch->midiInSolo) { + gLog(" >>> solo ch=%d (pure=0x%X)", ch->index, pure); + ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false); + } + else if (pure == ch->midiInVolume) { + float vf = (value >> 8)/127.0f; + gLog(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf); + glue_setChanVol(ch, vf, false); + } + else if (pure == ((SampleChannel*)ch)->midiInPitch) { + float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0] + gLog(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf); + glue_setPitch(NULL, (SampleChannel*)ch, vf, false); + } + else if (pure == ((SampleChannel*)ch)->midiInReadActions) { + gLog(" >>> start/stop read actions ch=%d (pure=0x%X)", ch->index, pure); + glue_startStopReadingRecs((SampleChannel*)ch, false); + } + } + gLog("\n"); + } +} + + +/* -------------------------------------------------------------------------- */ + + +std::string getRtMidiVersion() +{ + return midiOut->getVersion(); +} + + +} // namespace diff --git a/src/core/kernelMidi.h b/src/core/kernelMidi.h new file mode 100644 index 0000000..d446995 --- /dev/null +++ b/src/core/kernelMidi.h @@ -0,0 +1,104 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * KernelMidi + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef KERNELMIDI_H +#define KERNELMIDI_H + + +#include +#include +#include "channel.h" + + +namespace kernelMidi { + + extern int api; // one api for both in & out + extern unsigned numOutPorts; + extern unsigned numInPorts; + + typedef void (cb_midiLearn) (uint32_t, void *); + + /* cb_learn + * callback prepared by the gdMidiGrabber window and called by + * kernelMidi. It contains things to do once the midi message has been + * stored. */ + + extern cb_midiLearn *cb_learn; + extern void *cb_data; + + void startMidiLearn(cb_midiLearn *cb, void *data); + void stopMidiLearn(); + + inline int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; } + inline int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; } + inline int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; } + + inline uint32_t getIValue(int b1, int b2, int b3) { + return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00); + } + + /* send + * send a MIDI message 's' (uint32_t). */ + + void send(uint32_t s); + + /* send (2) + * send separate bytes of MIDI message. */ + + void send(int b1, int b2=-1, int b3=-1); + + /* setApi + * set the Api in use for both in & out messages. */ + + void setApi(int api); + + /* open/close/in/outDevice */ + + int openOutDevice(int port); + int openInDevice(int port); + int closeInDevice(); + int closeOutDevice(); + + /* getIn/OutPortName + * return the name of the port 'p'. */ + + const char *getInPortName(unsigned p); + const char *getOutPortName(unsigned p); + + bool hasAPI(int API); + + /* callback + * master callback for input events. */ + + void callback(double t, std::vector *msg, void *data); + + std::string getRtMidiVersion(); +} + +#endif diff --git a/src/core/midiChannel.cpp b/src/core/midiChannel.cpp new file mode 100644 index 0000000..dfb95f8 --- /dev/null +++ b/src/core/midiChannel.cpp @@ -0,0 +1,348 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../utils/log.h" +#include "midiChannel.h" +#include "channel.h" +#include "pluginHost.h" +#include "patch.h" +#include "conf.h" +#include "kernelMidi.h" + + +extern Patch G_Patch; +extern Mixer G_Mixer; +extern Conf G_Conf; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +MidiChannel::MidiChannel(int bufferSize) + : Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize), + midiOut (false), + midiOutChan(MIDI_CHANS[0]) +{ +#ifdef WITH_VST // init VstEvents stack + freeVstMidiEvents(true); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +MidiChannel::~MidiChannel() {} + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + +void MidiChannel::freeVstMidiEvents(bool init) +{ + if (events.numEvents == 0 && !init) + return; + memset(events.events, 0, sizeof(VstEvent*) * MAX_VST_EVENTS); + events.numEvents = 0; + events.reserved = 0; +} + +#endif + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + +void MidiChannel::addVstMidiEvent(uint32_t msg) +{ + addVstMidiEvent(G_PluginHost.createVstMidiEvent(msg)); +} + +#endif + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + +void MidiChannel::addVstMidiEvent(VstMidiEvent *e) +{ + if (events.numEvents < MAX_VST_EVENTS) { + events.events[events.numEvents] = (VstEvent*) e; + events.numEvents++; + /* + gLog("[MidiChannel] VstMidiEvent added - numEvents=%d offset=%d note=%d number=%d velo=%d\n", + events.numEvents, + e->deltaFrames, + e->midiData[0], + e->midiData[1], + e->midiData[2] + );*/ + } + else + gLog("[MidiChannel] channel %d VstEvents = %d > MAX_VST_EVENTS, nothing to do\n", index, events.numEvents); +} + +#endif + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::onBar(int frame) {} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::stop() {} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::empty() {} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::quantize(int index, int localFrame, int globalFrame) {} + + +/* -------------------------------------------------------------------------- */ + +#ifdef WITH_VST + +VstEvents *MidiChannel::getVstEvents() +{ + return (VstEvents *) &events; +} + +#endif + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::parseAction(recorder::action *a, int localFrame, int globalFrame) +{ + if (a->type == ACTION_MIDI) + sendMidi(a, localFrame/2); +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::onZero(int frame) +{ + if (status == STATUS_ENDING) { + status = STATUS_OFF; + sendMidiLplay(); + } + else + if (status == STATUS_WAIT) { + status = STATUS_PLAY; + sendMidiLplay(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::setMute(bool internal) +{ + mute = true; // internal mute does not exist for midi (for now) + if (midiOut) + kernelMidi::send(MIDI_ALL_NOTES_OFF); +#ifdef WITH_VST + addVstMidiEvent(MIDI_ALL_NOTES_OFF); +#endif + sendMidiLmute(); +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::unsetMute(bool internal) +{ + mute = false; // internal mute does not exist for midi (for now) + sendMidiLmute(); +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::process(float *buffer) +{ +#ifdef WITH_VST + G_PluginHost.processStack(vChan, PluginHost::CHANNEL, this); + freeVstMidiEvents(); +#endif + + for (int j=0; jiValue | MIDI_CHANS[midiOutChan]); + +#ifdef WITH_VST + a->event->deltaFrames = localFrame; + addVstMidiEvent(a->event); +#endif + } +} + + +void MidiChannel::sendMidi(uint32_t data) +{ + if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) { + if (midiOut) + kernelMidi::send(data | MIDI_CHANS[midiOutChan]); +#ifdef WITH_VST + addVstMidiEvent(data); +#endif + } +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::rewind() +{ + if (midiOut) + kernelMidi::send(MIDI_ALL_NOTES_OFF); +#ifdef WITH_VST + addVstMidiEvent(MIDI_ALL_NOTES_OFF); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::writePatch(FILE *fp, int i, bool isProject) +{ + Channel::writePatch(fp, i, isProject); + + fprintf(fp, "chanMidiOut%d=%u\n", i, midiOut); + fprintf(fp, "chanMidiOutChan%d=%u\n", i, midiOutChan); +} diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h new file mode 100644 index 0000000..275081c --- /dev/null +++ b/src/core/midiChannel.h @@ -0,0 +1,139 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * channel + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef MIDI_CHANNEL_H +#define MIDI_CHANNEL_H + + +#include "channel.h" + + +#ifdef WITH_VST + +/* before including aeffetx(x).h we must define __cdecl, otherwise VST + * headers can't be compiled correctly. In windows __cdecl is already + * defined. */ + + #ifdef __GNUC__ + #ifndef _WIN32 + #define __cdecl + #endif + #endif + #include "../deps/vst/aeffectx.h" + +#endif + + +class MidiChannel : public Channel { + +public: + + MidiChannel(int bufferSize); + ~MidiChannel(); + + bool midiOut; // enable midi output + uint8_t midiOutChan; // midi output channel + + void process (float *buffer); + void start (int frame, bool doQuantize); + void kill (int frame); + void empty (); + void stopBySeq (); + void stop (); + void rewind (); + void setMute (bool internal); + void unsetMute (bool internal); + int loadByPatch(const char *file, int i); + void writePatch (FILE *fp, int i, bool isProject); + void quantize (int index, int localFrame, int globalFrame); + void onZero (int frame); + void onBar (int frame); + void parseAction(recorder::action *a, int localFrame, int globalFrame); + + /* ---------------------------------------------------------------- */ + + /* sendMidi + * send Midi event to the outside world. */ + + void sendMidi(recorder::action *a, int localFrame); + void sendMidi(uint32_t data); + +#ifdef WITH_VST + + /* getVstEvents + * return a pointer to gVstEvents. */ + + VstEvents *getVstEvents(); + + /* freeVstMidiEvents + * empty vstEvents structure. Init: use the method for channel + * initialization. */ + + void freeVstMidiEvents(bool init=false); + + /* addVstMidiEvent + * take a composite MIDI event, decompose it and add it to channel. The + * other version creates a VstMidiEvent on the fly. */ + + void addVstMidiEvent(struct VstMidiEvent *e); + void addVstMidiEvent(uint32_t msg); + +#endif + + /* ---------------------------------------------------------------- */ + +#ifdef WITH_VST + + /* VST struct containing MIDI events. When ready, events are sent to + * each plugin in the channel. + * + * Anatomy of VstEvents + * -------------------- + * + * VstInt32 numEvents = number of Events in array + * VstIntPtr reserved = zero (Reserved for future use) + * VstEvent *events[n] = event pointer array, variable size + * + * Note that by default VstEvents only holds three events- if you want + * it to hold more, create an equivalent struct with a larger array, + * and then cast it to a VstEvents object when you've populated it. + * That's what we do with gVstEvents! */ + + struct gVstEvents { + VstInt32 numEvents; + VstIntPtr reserved; + VstEvent *events[MAX_VST_EVENTS]; + } events; + +#endif + +}; + + +#endif diff --git a/src/core/midiMapConf.cpp b/src/core/midiMapConf.cpp new file mode 100644 index 0000000..9a0dfc5 --- /dev/null +++ b/src/core/midiMapConf.cpp @@ -0,0 +1,223 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * midiMapConf + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#include +#include +#include +#include "midiMapConf.h" +#include "const.h" +#include "../utils/utils.h" +#include "../utils/log.h" + + +using std::string; + + +void MidiMapConf::init() +{ + midimapsPath = gGetHomePath() + "/midimaps/"; + + /* scan dir of midi maps and load the filenames into <>maps. */ + + gLog("[MidiMapConf::init] scanning midimaps directory...\n"); + + DIR *dp; + dirent *ep; + dp = opendir(midimapsPath.c_str()); + + if (!dp) { + gLog("[MidiMapConf::init] unable to scan midimaps directory!\n"); + return; + } + + while ((ep = readdir(dp))) { + if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, "..")) + continue; + + // TODO - check if is a valid midimap file (verify headers) + + gLog("[MidiMapConf::init] found midimap '%s'\n", ep->d_name); + + maps.add(ep->d_name); + } + + closedir(dp); +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiMapConf::setDefault() +{ + brand = ""; + device = ""; + + for (int i=0; i ic; + gSplit(getValue("init_commands"), ";", &ic); + for (unsigned i=0; i<(unsigned)MAX_INIT_COMMANDS && i. + * + * -------------------------------------------------------------------------- */ + + +#ifndef __MIDIMAPCONF_H__ +#define __MIDIMAPCONF_H__ + + +#include +#include +#include "dataStorage.h" +#include "../utils/utils.h" +#if defined(__APPLE__) +#include +#endif + + +using std::string; + + +class MidiMapConf : public DataStorage +{ +private: + + void close(); + void parse(string key, int *chan, uint32_t *msg, int *offset); + +public: + + static const int MAX_INIT_COMMANDS = 32; + static const int MAX_MIDI_BYTES = 4; + static const int MAX_MIDI_NIBBLES = 8; + + /* midimapsPath + * path of midimap files, different between OSes. */ + + string midimapsPath; + + /* maps + * Maps are the available .giadamap files. Each element of the vector + * represents a .giadamap filename. */ + + gVector maps; + + string brand; + string device; + + /* init_* + * init_commands. These messages are sent to the physical device as a wake up + * signal. */ + + int init_channels[MAX_INIT_COMMANDS]; + uint32_t init_messages[MAX_INIT_COMMANDS]; + + /* events + * [event]Channel: the MIDI output channel to send the event to + * [event]notePos: the byte where the note is stored ('nn' placeholder) + * [event]offset: the note offset (i.e. of 'nn' placeholder) */ + + int muteOnChan; + int muteOnOffset; + uint32_t muteOnMsg; + + int muteOffChan; + int muteOffOffset; + uint32_t muteOffMsg; + + int soloOnChan; + int soloOnOffset; + uint32_t soloOnMsg; + + int soloOffChan; + int soloOffOffset; + uint32_t soloOffMsg; + + int waitingChan; + int waitingOffset; + uint32_t waitingMsg; + + int playingChan; + int playingOffset; + uint32_t playingMsg; + + int stoppingChan; + int stoppingOffset; + uint32_t stoppingMsg; + + int stoppedChan; + int stoppedOffset; + uint32_t stoppedMsg; + + /* init + Parse the midi maps folders and find the available maps. */ + + void init(); + + /* setDefault + Set default values in case no maps are available/choosen. */ + + void setDefault(); + + /* readMap + Read a midi map from file 'file'. */ + + int readMap(string file); +}; + +#endif diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp new file mode 100644 index 0000000..2930f81 --- /dev/null +++ b/src/core/mixer.cpp @@ -0,0 +1,684 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * mixer + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../utils/log.h" +#include "../utils/gui_utils.h" +#include "mixer.h" +#include "init.h" +#include "wave.h" +#include "recorder.h" +#include "pluginHost.h" +#include "patch.h" +#include "conf.h" +#include "mixerHandler.h" +#include "channel.h" +#include "sampleChannel.h" +#include "midiChannel.h" +#include "kernelMidi.h" + + +extern Mixer G_Mixer; +extern Patch G_Patch; +extern Conf G_Conf; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +Mixer::Mixer() {} +Mixer::~Mixer() {} + + +#define TICKSIZE 38 + + +float Mixer::tock[TICKSIZE] = { + 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 Mixer::tick[TICKSIZE] = { + 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 +}; + + +/* ------------------------------------------------------------------ */ + + +void Mixer::init() { + quanto = 1; + docross = false; + rewindWait = false; + running = false; + ready = true; + waitRec = 0; + actualFrame = 0; + bpm = DEFAULT_BPM; + bars = DEFAULT_BARS; + beats = DEFAULT_BEATS; + quantize = DEFAULT_QUANTIZE; + metronome = false; + + tickTracker = 0; + tockTracker = 0; + tickPlay = false; + tockPlay = false; + + outVol = DEFAULT_OUT_VOL; + inVol = DEFAULT_IN_VOL; + peakOut = 0.0f; + peakIn = 0.0f; + chanInput = NULL; + inputTracker = 0; + + actualBeat = 0; + + midiTCstep = 0; + midiTCrate = (G_Conf.samplerate / G_Conf.midiTCfps) * 2; // dealing with stereo vals + midiTCframes = 0; + midiTCseconds = 0; + midiTCminutes = 0; + midiTChours = 0; + + /* alloc virtual input channels. vChanInput malloc is done in + * updateFrameBars, because of its variable size */ + /** TODO - set kernelAudio::realBufsize * 2 as private member */ + + vChanInput = NULL; + vChanInToOut = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float)); + + pthread_mutex_init(&mutex_recs, NULL); + pthread_mutex_init(&mutex_chans, NULL); + pthread_mutex_init(&mutex_plugins, NULL); + + updateFrameBars(); + rewind(); +} + + +/* ------------------------------------------------------------------ */ + + +Channel *Mixer::addChannel(int type) { + + Channel *ch; + int bufferSize = kernelAudio::realBufsize*2; + + if (type == CHANNEL_SAMPLE) + ch = new SampleChannel(bufferSize); + else + ch = new MidiChannel(bufferSize); + + while (true) { + int lockStatus = pthread_mutex_trylock(&mutex_chans); + if (lockStatus == 0) { + channels.add(ch); + pthread_mutex_unlock(&mutex_chans); + break; + } + } + + ch->index = getNewIndex(); + gLog("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size); + return ch; +} + + +/* ------------------------------------------------------------------ */ + + +int Mixer::getNewIndex() { + + /* always skip last channel: it's the last one just added */ + + if (channels.size == 1) + return 0; + + int index = 0; + for (unsigned i=0; iindex > index) + index = channels.at(i)->index; + } + index += 1; + return index; +} + + +/* ------------------------------------------------------------------ */ + + +int Mixer::deleteChannel(Channel *ch) { + int lockStatus; + while (true) { + lockStatus = pthread_mutex_trylock(&mutex_chans); + if (lockStatus == 0) { + channels.del(ch); + delete ch; + pthread_mutex_unlock(&mutex_chans); + return 1; + } + //else + // gLog("[mixer::deleteChannel] waiting for mutex...\n"); + } +} + + +/* ------------------------------------------------------------------ */ + + +Channel *Mixer::getChannelByIndex(int index) { + for (unsigned i=0; iindex == index) + return channels.at(i); + gLog("[mixer::getChannelByIndex] channel at index %d not found!\n", index); + return NULL; +} + + +/* ------------------------------------------------------------------ */ + + +void Mixer::sendMIDIsync() { + + if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) { + if (actualFrame % (framesPerBeat/24) == 0) + kernelMidi::send(MIDI_CLOCK, -1, -1); + } + else + if (G_Conf.midiSync == MIDI_SYNC_MTC_M) { + + /* check if a new timecode frame has passed. If so, send MIDI TC + * quarter frames. 8 quarter frames, divided in two branches: + * 1-4 and 5-8. We check timecode frame's parity: if even, send + * range 1-4, if odd send 5-8. */ + + if (actualFrame % midiTCrate == 0) { + + /* frame low nibble + * frame high nibble + * seconds low nibble + * seconds high nibble */ + + if (midiTCframes % 2 == 0) { + kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1); + kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1); + kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1); + kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1); + } + + /* minutes low nibble + * minutes high nibble + * hours low nibble + * hours high nibble SMPTE frame rate */ + + else { + kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1); + kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1); + kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1); + kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1); + } + + midiTCframes++; + + /* check if total timecode frames are greater than timecode fps: + * if so, a second has passed */ + + if (midiTCframes > G_Conf.midiTCfps) { + midiTCframes = 0; + midiTCseconds++; + if (midiTCseconds >= 60) { + midiTCminutes++; + midiTCseconds = 0; + if (midiTCminutes >= 60) { + midiTChours++; + midiTCminutes = 0; + } + } + //gLog("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes); + } + } + } +} + + +/* ------------------------------------------------------------------ */ + + +void Mixer::sendMIDIrewind() { + + midiTCframes = 0; + midiTCseconds = 0; + midiTCminutes = 0; + midiTChours = 0; + + /* For cueing the slave to a particular start point, Quarter Frame + * messages are not used. Instead, an MTC Full Frame message should + * be sent. The Full Frame is a SysEx message that encodes the entire + * SMPTE time in one message */ + + if (G_Conf.midiSync == MIDI_SYNC_MTC_M) { + kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0 + kernelMidi::send(0x01, 0x01, 0x00); // hours 0 + kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0 + kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex + } +} + +/* ------------------------------------------------------------------ */ + + +int Mixer::masterPlay( + void *out_buf, void *in_buf, unsigned n_frames, + double streamTime, RtAudioStreamStatus status, void *userData) { + return G_Mixer.__masterPlay(out_buf, in_buf, n_frames); +} + + +/* ------------------------------------------------------------------ */ + + +int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames) { + + if (!ready) + return 0; + + float *outBuf = ((float *) out_buf); + float *inBuf = ((float *) in_buf); + bufferFrames *= 2; // stereo + peakOut = 0.0f; // reset peak calculator + peakIn = 0.0f; // reset peak calculator + + /* always clean each buffer */ + + memset(outBuf, 0, sizeof(float) * bufferFrames); // out + memset(vChanInToOut, 0, sizeof(float) * bufferFrames); // inToOut vChan + + pthread_mutex_lock(&mutex_chans); + for (unsigned i=0; itype == CHANNEL_SAMPLE) + ((SampleChannel*)channels.at(i))->clear(); + pthread_mutex_unlock(&mutex_chans); + + for (unsigned j=0; j peakIn) + peakIn = inBuf[j] * inVol; + + /* "hear what you're playing" - process, copy and paste the input buffer + * onto the output buffer */ + + if (inToOut) { + vChanInToOut[j] = inBuf[j] * inVol; + vChanInToOut[j+1] = inBuf[j+1] * inVol; + } + } + + /* operations to do if the sequencer is running: + * - compute quantizer + * - time check for LOOP_REPEAT + * - reset loops at beat 0 + * - read recorded actions + * - reset actualFrame */ + + if (running) { + + /* line in recording */ + + if (chanInput != NULL && kernelAudio::inputEnabled) { + + /* delay comp: wait until waitRec reaches delayComp. WaitRec + * returns to 0 in mixerHandler, as soon as the recording ends */ + + if (waitRec < G_Conf.delayComp) + waitRec += 2; + else { + vChanInput[inputTracker] += inBuf[j] * inVol; + vChanInput[inputTracker+1] += inBuf[j+1] * inVol; + inputTracker += 2; + if (inputTracker >= totalFrames) + inputTracker = 0; + } + } + + /* quantizer computations: quantize rewind and all channels. */ + + if (quantize > 0 && quanto > 0) { + if (actualFrame % (quanto) == 0) { // is quanto! + if (rewindWait) { + rewindWait = false; + rewind(); + } + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; kquantize(k, j, actualFrame); // j == localFrame + pthread_mutex_unlock(&mutex_chans); + } + } + + /* reset LOOP_REPEAT, if a bar has passed */ + + if (actualFrame % framesPerBar == 0 && actualFrame != 0) { + if (metronome) + tickPlay = true; + + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; konBar(j); + pthread_mutex_unlock(&mutex_chans); + } + + /* reset loops on beat 0 */ + + if (actualFrame == 0) { + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; konZero(j); + pthread_mutex_unlock(&mutex_chans); + } + + /* reading all actions recorded */ + + pthread_mutex_lock(&mutex_recs); + for (unsigned y=0; ychan; + Channel *ch = getChannelByIndex(index); + ch->parseAction(recorder::global.at(y).at(z), j, actualFrame); + } + break; + } + } + pthread_mutex_unlock(&mutex_recs); + + /* increase actualFrame */ + + actualFrame += 2; + + /* if actualFrame > totalFrames the sequencer returns to frame 0, + * beat 0. This must be the last operation. */ + + if (actualFrame > totalFrames) { + actualFrame = 0; + actualBeat = 0; + } + else + if (actualFrame % framesPerBeat == 0 && actualFrame > 0) { + actualBeat++; + + /* avoid tick and tock to overlap when a new bar has passed (which + * is also a beat) */ + + if (metronome && !tickPlay) + tockPlay = true; + } + + sendMIDIsync(); + + } // if (running) + + /* sum channels, CHANNEL_SAMPLE only */ + + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; ktype == CHANNEL_SAMPLE) + ((SampleChannel*)channels.at(k))->sum(j, running); + } + pthread_mutex_unlock(&mutex_chans); + + /* metronome play */ + /** FIXME - move this one after the peak meter calculation */ + + if (tockPlay) { + outBuf[j] += tock[tockTracker]; + outBuf[j+1] += tock[tockTracker]; + tockTracker++; + if (tockTracker >= TICKSIZE-1) { + tockPlay = false; + tockTracker = 0; + } + } + if (tickPlay) { + outBuf[j] += tick[tickTracker]; + outBuf[j+1] += tick[tickTracker]; + tickTracker++; + if (tickTracker >= TICKSIZE-1) { + tickPlay = false; + tickTracker = 0; + } + } + } // end loop J + + + /* final loop: sum virtual channels and process plugins. */ + + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; kprocess(outBuf); + pthread_mutex_unlock(&mutex_chans); + + /* processing fxs master in & out, if any. */ + +#ifdef WITH_VST + pthread_mutex_lock(&mutex_plugins); + G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT); + G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN); + pthread_mutex_unlock(&mutex_plugins); +#endif + + /* post processing master fx + peak calculation. */ + + for (unsigned j=0; j peakOut) + peakOut = outBuf[j]; + + if (G_Conf.limitOutput) { + if (outBuf[j] > 1.0f) + outBuf[j] = 1.0f; + else if (outBuf[j] < -1.0f) + outBuf[j] = -1.0f; + + if (outBuf[j+1] > 1.0f) + outBuf[j+1] = 1.0f; + else if (outBuf[j+1] < -1.0f) + outBuf[j+1] = -1.0f; + } + } + + return 0; +} + + +/* ------------------------------------------------------------------ */ + + +void Mixer::updateFrameBars() { + + /* seconds ....... total time of play (in seconds) of the whole + * sequencer. 60 / bpm == how many seconds lasts one bpm + * totalFrames ... number of frames in the whole sequencer, x2 because + * it's stereo + * framesPerBar .. n. of frames within a bar + * framesPerBeat . n. of frames within a beat */ + + float seconds = (60.0f / bpm) * beats; + totalFrames = G_Conf.samplerate * seconds * 2; + framesPerBar = totalFrames / bars; + framesPerBeat = totalFrames / beats; + framesInSequencer = framesPerBeat * MAX_BEATS; + + /* big troubles if frames are odd. */ + + if (totalFrames % 2 != 0) + totalFrames--; + if (framesPerBar % 2 != 0) + framesPerBar--; + if (framesPerBeat % 2 != 0) + framesPerBeat--; + + updateQuanto(); + + /* realloc input virtual channel, if not NULL. TotalFrames is changed! */ + + if (vChanInput != NULL) + free(vChanInput); + vChanInput = (float*) malloc(totalFrames * sizeof(float)); + if (!vChanInput) + gLog("[Mixer] vChanInput realloc error!\n"); +} + + +/* ------------------------------------------------------------------ */ + + +int Mixer::close() { + running = false; + while (channels.size > 0) + deleteChannel(channels.at(0)); + free(vChanInput); + free(vChanInToOut); + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +bool Mixer::isSilent() { + for (unsigned i=0; istatus == STATUS_PLAY) + return false; + return true; +} + + +/* ------------------------------------------------------------------ */ + + +void Mixer::rewind() { + + actualFrame = 0; + actualBeat = 0; + + if (running) + for (unsigned i=0; irewind(); + + sendMIDIrewind(); +} + + +/* ------------------------------------------------------------------ */ + + +void Mixer::updateQuanto() { + + /* big troubles if frames are odd. */ + + if (quantize != 0) + quanto = framesPerBeat / quantize; + if (quanto % 2 != 0) + quanto++; +} + + +/* ------------------------------------------------------------------ */ + + +bool Mixer::hasLogicalSamples() { + for (unsigned i=0; itype == CHANNEL_SAMPLE) + if (((SampleChannel*)channels.at(i))->wave) + if (((SampleChannel*)channels.at(i))->wave->isLogical) + return true; + return false; +} + + +/* ------------------------------------------------------------------ */ + + +bool Mixer::hasEditedSamples() { + for (unsigned i=0; itype == CHANNEL_SAMPLE) + if (((SampleChannel*)channels.at(i))->wave) + if (((SampleChannel*)channels.at(i))->wave->isEdited) + return true; + return false; +} + + +/* ------------------------------------------------------------------ */ + + +bool Mixer::mergeVirtualInput() { + if (vChanInput == NULL) { + gLog("[Mixer] virtual input channel not alloc'd\n"); + return false; + } + else { +#ifdef WITH_VST + G_PluginHost.processStackOffline(vChanInput, PluginHost::MASTER_IN, 0, totalFrames); +#endif + int numFrames = totalFrames*sizeof(float); + memcpy(chanInput->wave->data, vChanInput, numFrames); + memset(vChanInput, 0, numFrames); // clear vchan + return true; + } +} diff --git a/src/core/mixer.h b/src/core/mixer.h new file mode 100644 index 0000000..02eafd4 --- /dev/null +++ b/src/core/mixer.h @@ -0,0 +1,209 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * mixer + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef MIXER_H +#define MIXER_H + +#include +#include +#include "const.h" +#include "kernelAudio.h" +#include "../utils/utils.h" + + +class Mixer { + +public: + + Mixer(); + ~Mixer(); + + void init(); + int close(); + + /* addChannel + * add a new channel without any wave inside of it. */ + + class Channel *addChannel(int type); + + /* deleteChannel + * completely remove a channel from the stack. */ + + int deleteChannel(class Channel *ch); + + /* masterPlay + * core method (callback) */ + + static int masterPlay( + void *out_buf, void *in_buf, unsigned n_frames, + double streamTime, RtAudioStreamStatus status, void *userData + ); + int __masterPlay(void *out_buf, void *in_buf, unsigned n_frames); + + /* updateFrameBars + * updates bpm, frames, beats and so on. */ + + void updateFrameBars(); + + /* isSilent + * is mixer silent? */ + + bool isSilent(); + + /* rewind + * rewind sequencer to sample 0. */ + + void rewind(); + + /* updateQuanto + * recomputes the quanto between two quantizations */ + + void updateQuanto(); + + /* hasLogicalSamples + * true if 1 or more samples are logical (memory only, such as takes) */ + + bool hasLogicalSamples(); + + /* hasEditedSamples + * true if 1 or more samples was edited via gEditor */ + + bool hasEditedSamples(); + + /* mergeVirtualInput + * memcpy the virtual channel input in the channel designed for input + * recording. Called by mixerHandler on stopInputRec() */ + + bool mergeVirtualInput(); + + /* getChannelByIndex + * return channel with given index 'i'. */ + + Channel *getChannelByIndex(int i); + + inline Channel* getLastChannel() { return channels.at(channels.size-1); } + + + /* ---------------------------------------------------------------- */ + + + enum { // const - what to do when a fadeout ends + DO_STOP = 0x01, + DO_MUTE = 0x02, + DO_MUTE_I = 0x04 + }; + + enum { // const - fade types + FADEOUT = 0x01, + XFADE = 0x02 + }; + + gVector channels; + + bool running; + bool ready; + float *vChanInput; // virtual channel for recording + float *vChanInToOut; // virtual channel in->out bridge (hear what you're playin) + int frameSize; + float outVol; + float inVol; + float peakOut; + float peakIn; + int quanto; + char quantize; + bool metronome; + float bpm; + int bars; + int beats; + int waitRec; // delayComp guard + + bool docross; // crossfade guard + bool rewindWait; // rewind guard, if quantized + + int framesPerBar; // frames in one bar + int framesPerBeat; // frames in one beat + int framesInSequencer; // frames in the whole sequencer + int totalFrames; // frames in the selected range (e.g. 4/4) + int actualFrame; + int actualBeat; + +#define TICKSIZE 38 + static float tock[TICKSIZE]; + static float tick[TICKSIZE]; + int tickTracker, tockTracker; + bool tickPlay, tockPlay; // 1 = play, 0 = stop + + /* chanInput + * the active channel during a recording. NULL = no channels active */ + + class SampleChannel *chanInput; + + /* inputTracker + * position of the sample in the input side (recording) */ + + int inputTracker; + + /* inToOut + * copy, process and paste the input into the output, in order to + * obtain a "hear what you're playing" feature. */ + + bool inToOut; + + pthread_mutex_t mutex_recs; + pthread_mutex_t mutex_chans; + pthread_mutex_t mutex_plugins; + + +private: + + int midiTCstep; // part of MTC to send (0 to 7) + int midiTCrate; // send MTC data every midiTCrate frames + int midiTCframes; + int midiTCseconds; + int midiTCminutes; + int midiTChours; + + /* getNewIndex + * compute new index value for new channels */ + + int getNewIndex(); + + /* sendMIDIsync + * generate MIDI sync output data */ + + void sendMIDIsync(); + + /* sendMIDIrewind + * rewind timecode to beat 0 and also send a MTC full frame to cue + * the slave */ + + void sendMIDIrewind(); +}; + +#endif diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp new file mode 100644 index 0000000..427808b --- /dev/null +++ b/src/core/mixerHandler.cpp @@ -0,0 +1,230 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * mixerHandler + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#if defined(__linux__) + #include + #include + #include +#endif + +#include "../utils/utils.h" +#include "../utils/log.h" +#include "../glue/glue.h" +#include "mixerHandler.h" +#include "kernelMidi.h" +#include "mixer.h" +#include "const.h" +#include "init.h" +#include "pluginHost.h" +#include "plugin.h" +#include "waveFx.h" +#include "conf.h" +#include "patch.h" +#include "recorder.h" +#include "channel.h" +#include "sampleChannel.h" +#include "wave.h" + + +extern Mixer G_Mixer; +extern Patch G_Patch; +extern Conf G_Conf; + +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +void mh_stopSequencer() +{ + G_Mixer.running = false; + for (unsigned i=0; istopBySeq(); +} + + +/* -------------------------------------------------------------------------- */ + + +void mh_clear() +{ + G_Mixer.running = false; + while (G_Mixer.channels.size > 0) + G_Mixer.channels.del(0U); // unsigned +} + + +/* -------------------------------------------------------------------------- */ + + +bool mh_uniqueSolo(Channel *ch) +{ + int solos = 0; + for (unsigned i=0; isolo) solos++; + if (solos > 1) return false; + } + return true; +} + + +/* -------------------------------------------------------------------------- */ + + +/** TODO - revision needed: mh should not call glue_addChannel */ + +void mh_loadPatch(bool isProject, const char *projPath) +{ + G_Mixer.init(); + G_Mixer.ready = false; // put it in wait mode + + int numChans = G_Patch.getNumChans(); + for (int i=0; iloadByPatch(smpPath, i); + } + + G_Mixer.outVol = G_Patch.getOutVol(); + G_Mixer.inVol = G_Patch.getInVol(); + G_Mixer.bpm = G_Patch.getBpm(); + G_Mixer.bars = G_Patch.getBars(); + G_Mixer.beats = G_Patch.getBeats(); + G_Mixer.quantize = G_Patch.getQuantize(); + G_Mixer.metronome = G_Patch.getMetronome(); + G_Patch.lastTakeId = G_Patch.getLastTakeId(); + G_Patch.samplerate = G_Patch.getSamplerate(); + + /* rewind and update frames in Mixer (it's vital) */ + + G_Mixer.rewind(); + G_Mixer.updateFrameBars(); + G_Mixer.ready = true; +} + + +/* -------------------------------------------------------------------------- */ + + +void mh_rewindSequencer() +{ + if (G_Mixer.quantize > 0 && G_Mixer.running) // quantize rewind + G_Mixer.rewindWait = true; + else + G_Mixer.rewind(); +} + + +/* -------------------------------------------------------------------------- */ + + +SampleChannel *mh_startInputRec() +{ + /* search for the next available channel */ + + SampleChannel *chan = NULL; + for (unsigned i=0; itype == CHANNEL_SAMPLE) + if (((SampleChannel*) G_Mixer.channels.at(i))->canInputRec()) { + chan = (SampleChannel*) G_Mixer.channels.at(i); + break; + } + } + + /* no chans available? */ + + if (chan == NULL) + return NULL; + + Wave *w = new Wave(); + if (!w->allocEmpty(G_Mixer.totalFrames)) + return NULL; + + /* increase lastTakeId until the sample name TAKE-[n] is unique */ + + char name[32]; + sprintf(name, "TAKE-%d", G_Patch.lastTakeId); + while (!mh_uniqueSamplename(chan, name)) { + G_Patch.lastTakeId++; + sprintf(name, "TAKE-%d", G_Patch.lastTakeId); + } + + chan->allocEmpty(G_Mixer.totalFrames, G_Patch.lastTakeId); + G_Mixer.chanInput = chan; + + /* start to write from the actualFrame, not the beginning */ + /** FIXME: move this before wave allocation*/ + + G_Mixer.inputTracker = G_Mixer.actualFrame; + + gLog( + "[mh] start input recs using chan %d with size %d, frame=%d\n", + chan->index, G_Mixer.totalFrames, G_Mixer.inputTracker + ); + + return chan; +} + + +/* -------------------------------------------------------------------------- */ + + +SampleChannel *mh_stopInputRec() +{ + gLog("[mh] stop input recs\n"); + G_Mixer.mergeVirtualInput(); + SampleChannel *ch = G_Mixer.chanInput; + G_Mixer.chanInput = NULL; + G_Mixer.waitRec = 0; // if delay compensation is in use + return ch; +} + + +/* -------------------------------------------------------------------------- */ + + +bool mh_uniqueSamplename(SampleChannel *ch, const char *name) +{ + for (unsigned i=0; itype == CHANNEL_SAMPLE) { + SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i); + if (other->wave != NULL) + if (!strcmp(name, other->wave->name.c_str())) + return false; + } + } + } + return true; +} diff --git a/src/core/mixerHandler.h b/src/core/mixerHandler.h new file mode 100644 index 0000000..f842004 --- /dev/null +++ b/src/core/mixerHandler.h @@ -0,0 +1,76 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * mixerHandler + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef MIXERHANDLER_H +#define MIXERHANDLER_H + + +#include "recorder.h" + + +/* stopSequencer + * stop the sequencer, with special case if samplesStopOnSeqHalt is + * true. */ + +void mh_stopSequencer(); + +void mh_rewindSequencer(); + +/* clear + * stop everything and clear all channels. */ + +void mh_clear(); + +/* uniqueSolo + * true if ch is the only solo'd channel in mixer. */ + +bool mh_uniqueSolo(class Channel *ch); + +/* loadPatch + * load a path or a project (if isProject) into Mixer. If isProject, path + * must contain the address of the project folder. */ + +void mh_loadPatch(bool isProject, const char *projPath=0); + +/* startInputRec - record from line in + * creates a new empty wave in the first available channels and returns + * the chan number chosen, otherwise -1 if there are no more empty + * channels available. */ + +SampleChannel *mh_startInputRec(); + +SampleChannel *mh_stopInputRec(); + +/* uniqueSamplename + * return true if samplename 'n' is unique. Requires SampleChannel *ch + * in order to skip check against itself. */ + +bool mh_uniqueSamplename(class SampleChannel *ch, const char *name); + +#endif diff --git a/src/core/patch.cpp b/src/core/patch.cpp new file mode 100644 index 0000000..3613070 --- /dev/null +++ b/src/core/patch.cpp @@ -0,0 +1,739 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * patch + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include "../utils/log.h" +#include "../utils/utils.h" +#include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/elems/ge_keyboard.h" +#include "patch.h" +#include "init.h" +#include "recorder.h" +#include "conf.h" +#include "pluginHost.h" +#include "wave.h" +#include "mixer.h" +#include "channel.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif +extern gdMainWindow *mainWin; + + +int Patch::open(const char *file) +{ + fp = fopen(file, "r"); + if (fp == NULL) + return PATCH_UNREADABLE; + + if (getValue("header") != "GIADAPTC") + return PATCH_INVALID; + + version = atof(getValue("versionf").c_str()); + gLog("[patch] open patch version %f\n", version); + + return PATCH_OPEN_OK; +} + + +/* -------------------------------------------------------------------------- */ + + +void Patch::setDefault() +{ + name[0] = '\0'; + lastTakeId = 0; + samplerate = DEFAULT_SAMPLERATE; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::close() +{ + return fclose(fp); +} + + +/* -------------------------------------------------------------------------- */ + + +void Patch::getName() +{ + std::string out = getValue("patchname"); + strncpy(name, out.c_str(), MAX_PATCHNAME_LEN); +} + + +/* -------------------------------------------------------------------------- */ + + +std::string Patch::getSamplePath(int c) +{ + char tmp[16]; + sprintf(tmp, "samplepath%d", c); + return getValue(tmp); +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getPitch(int c) +{ + char tmp[16]; + sprintf(tmp, "chanPitch%d", c); + float out = atof(getValue(tmp).c_str()); + if (out > 2.0f || out < 0.1f) + return 1.0f; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getNumChans() +{ + if (version == 0.0) // backward compatibility with version < 0.6.1 + return 32; + return atoi(getValue("channels").c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getNumColumns() +{ + return atoi(getValue("columns").c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getColumn(int c) +{ + if (version == 0.0) // backward compatibility with version < 0.6.1 + return 0; + char tmp[16]; + sprintf(tmp, "chanColumn%d", c); + return atoi(getValue(tmp).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getIndex(int c) +{ + if (version == 0.0) // backward compatibility with version < 0.6.1 + return c; + + char tmp[16]; + sprintf(tmp, "chanIndex%d", c); + return atoi(getValue(tmp).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getVol(int c) +{ + char tmp[16]; + sprintf(tmp, "chanvol%d", c); + float out = atof(getValue(tmp).c_str()); + if (out > 1.0f || out < 0.0f) + return DEFAULT_VOL; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getMode(int c) +{ + char tmp[16]; + sprintf(tmp, "chanmode%d", c); + int out = atoi(getValue(tmp).c_str()); + if (out & (LOOP_ANY | SINGLE_ANY)) + return out; + return DEFAULT_CHANMODE; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getMute(int c) +{ + char tmp[16]; + sprintf(tmp, "chanMute%d", c); + return atoi(getValue(tmp).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getMute_s(int c) +{ + char tmp[16]; + sprintf(tmp, "chanMute_s%d", c); + return atoi(getValue(tmp).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getSolo(int c) +{ + char tmp[16]; + sprintf(tmp, "chanSolo%d", c); + return atoi(getValue(tmp).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getType(int c) +{ + char tmp[16]; + sprintf(tmp, "chanType%d", c); + int out = atoi(getValue(tmp).c_str()); + if (out == 0) + return CHANNEL_SAMPLE; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getBegin(int c) +{ + char tmp[16]; + if (version < 0.73f) + sprintf(tmp, "chanstart%d", c); + else + sprintf(tmp, "chanBegin%d", c); + int out = atoi(getValue(tmp).c_str()); + if (out < 0) + return 0; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getEnd(int c, unsigned size) +{ + char tmp[16]; + sprintf(tmp, "chanend%d", c); + + /* if chanEnd doesn't exist, it returns an atoi(empty string) == 0. + * good in theory, a disaster in practice. */ + + std::string val = getValue(tmp); + if (val == "") + return size; + + unsigned out = atoi(val.c_str()); + if (out <= 0 || out > size) + return size; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getBoost(int c) +{ + char tmp[16]; + sprintf(tmp, "chanBoost%d", c); + float out = atof(getValue(tmp).c_str()); + if (out < 1.0f) + return DEFAULT_BOOST; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getPanLeft(int c) +{ + char tmp[16]; + sprintf(tmp, "chanPanLeft%d", c); + std::string val = getValue(tmp); + if (val == "") + return 1.0f; + + float out = atof(val.c_str()); + if (out < 0.0f || out > 1.0f) + return 1.0f; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getKey(int c) +{ + if (version == 0.0) // backward compatibility with version < 0.6.1 + return 0; + char tmp[16]; + sprintf(tmp, "chanKey%d", c); + return atoi(getValue(tmp).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getPanRight(int c) +{ + char tmp[16]; + sprintf(tmp, "chanPanRight%d", c); + std::string val = getValue(tmp); + if (val == "") + return 1.0f; + + float out = atof(val.c_str()); + if (out < 0.0f || out > 1.0f) + return 1.0f; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +bool Patch::getRecActive(int c) +{ + char tmp[16]; + sprintf(tmp, "chanRecActive%d", c); + return atoi(getValue(tmp).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getOutVol() +{ + return atof(getValue("outVol").c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getInVol() +{ + return atof(getValue("inVol").c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +float Patch::getBpm() +{ + float out = atof(getValue("bpm").c_str()); + if (out < 20.0f || out > 999.0f) + return DEFAULT_BPM; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getBars() +{ + int out = atoi(getValue("bars").c_str()); + if (out <= 0 || out > 32) + return DEFAULT_BARS; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getBeats() +{ + int out = atoi(getValue("beats").c_str()); + if (out <= 0 || out > 32) + return DEFAULT_BEATS; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getQuantize() +{ + int out = atoi(getValue("quantize").c_str()); + if (out < 0 || out > 8) + return DEFAULT_QUANTIZE; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +bool Patch::getMetronome() +{ + return atoi(getValue("metronome").c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getLastTakeId() +{ + return atoi(getValue("lastTakeId").c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::getSamplerate() +{ + int out = atoi(getValue("samplerate").c_str()); + if (out <= 0) + return DEFAULT_SAMPLERATE; + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +uint32_t Patch::getMidiValue(int i, const char *c) +{ + char tmp[32]; + sprintf(tmp, "chanMidi%s%d", c, i); + return strtoul(getValue(tmp).c_str(), NULL, 10); +} + + +/* -------------------------------------------------------------------------- */ + + +int Patch::readRecs() +{ + gLog("[patch] Reading recs...\n"); + + unsigned numrecs = atoi(getValue("numrecs").c_str()); + + for (unsigned i=0; istatus & ~(STATUS_WRONG | STATUS_MISSING | STATUS_EMPTY)) { + if (version < 0.83f) + recorder::rec(ch->index, type, frame, iValue_fix, fValue); + else + recorder::rec(ch->index, type, frame, iValue, fValue); + } + } + } + return 1; +} + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST +int Patch::readPlugins() +{ + gLog("[patch] Reading plugins...\n"); + + int globalOut = 1; + + /* master plugins */ + + globalOut &= readMasterPlugins(PluginHost::MASTER_IN); + globalOut &= readMasterPlugins(PluginHost::MASTER_OUT); + + /* channel plugins */ + + for (unsigned i=0; iindex); + int np = atoi(getValue(tmp).c_str()); + + for (int j=0; jindex, j); + int out = G_PluginHost.addPlugin(getValue(tmp).c_str(), PluginHost::CHANNEL, ch); + if (out != 0) { + sprintf(tmp, "chan%d_p%dnumParams", ch->index, j); + int nparam = atoi(getValue(tmp).c_str()); + Plugin *pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch); + sprintf(tmp, "chan%d_p%dbypass", ch->index, j); + pPlugin->bypass = atoi(getValue(tmp).c_str()); + for (int k=0; kindex, j, k); + float pval = atof(getValue(tmp).c_str()); + pPlugin->setParam(k, pval); + } + } + globalOut &= out; + } + } + return globalOut; +} +#endif + + +/* -------------------------------------------------------------------------- */ + + +int Patch::write(const char *file, const char *name, bool project) +{ + fp = fopen(file, "w"); + if (fp == NULL) + return 0; + + fprintf(fp, "# --- Giada patch file --- \n"); + fprintf(fp, "header=GIADAPTC\n"); + fprintf(fp, "version=%s\n", VERSIONE); + fprintf(fp, "versionf=%f\n", VERSIONE_FLOAT); + fprintf(fp, "patchname=%s\n", name); + fprintf(fp, "bpm=%f\n", G_Mixer.bpm); + fprintf(fp, "bars=%d\n", G_Mixer.bars); + fprintf(fp, "beats=%d\n", G_Mixer.beats); + fprintf(fp, "quantize=%d\n", G_Mixer.quantize); + fprintf(fp, "outVol=%f\n", G_Mixer.outVol); + fprintf(fp, "inVol=%f\n", G_Mixer.inVol); + fprintf(fp, "metronome=%d\n", G_Mixer.metronome); + fprintf(fp, "lastTakeId=%d\n", lastTakeId); + fprintf(fp, "samplerate=%d\n", G_Conf.samplerate); // original samplerate when the patch was saved + fprintf(fp, "channels=%d\n", G_Mixer.channels.size); + fprintf(fp, "columns=%d\n", mainWin->keyboard->getTotalColumns()); + + for (unsigned i=0; iwritePatch(fp, i, project); + } + + /* writing recs. Warning: channel index is not mixer.channels.at(chan), + * but mixer.channels.at(chan)->index! */ + + fprintf(fp, "# --- actions --- \n"); + fprintf(fp, "numrecs=%d\n", recorder::global.size); + for (unsigned i=0; ichan, + recorder::global.at(i).at(k)->type, + recorder::global.at(i).at(k)->fValue, + recorder::global.at(i).at(k)->iValue); + } + } + +#ifdef WITH_VST + + /* writing master VST parameters */ + + writeMasterPlugins(PluginHost::MASTER_IN); + writeMasterPlugins(PluginHost::MASTER_OUT); + + /* writing VST parameters, channels. chan%d is mixer::channels.at(%d)->index, + * not mixer::chanels.at(%d)! */ + + int numPlugs; + int numParams; + Plugin *pPlugin; + + fprintf(fp, "# --- VST / channels --- \n"); + for (unsigned i=0; iindex, numPlugs); + + for (int j=0; jstatus) { + gLog("[patch] Plugin %d is in a bad status, skip writing params\n", i); + continue; + } + fprintf(fp, "chan%d_p%dpathfile=%s\n", ch->index, j, pPlugin->pathfile); + fprintf(fp, "chan%d_p%dbypass=%d\n", ch->index, j, pPlugin->bypass); + numParams = pPlugin->getNumParams(); + fprintf(fp, "chan%d_p%dnumParams=%d\n", ch->index, j, numParams); + + for (int k=0; kindex, j, k, pPlugin->getParam(k)); + } + } + +#endif + + fclose(fp); + return 1; +} + + +/* -------------------------------------------------------------------------- */ + +#ifdef WITH_VST + +int Patch::readMasterPlugins(int type) +{ + int nmp; + char chr; + int res = 1; + + if (type == PluginHost::MASTER_IN) { + chr = 'I'; + nmp = atoi(getValue("masterIPlugins").c_str()); + } + else { + chr = 'O'; + nmp = atoi(getValue("masterOPlugins").c_str()); + } + + for (int i=0; ibypass = atoi(getValue(tmp).c_str()); + sprintf(tmp, "master%c_p%dnumParams", chr, i); + int nparam = atoi(getValue(tmp).c_str()); + for (int j=0; jsetParam(j, pval); + } + } + res &= out; + } + + return res; +} + + +/* -------------------------------------------------------------------------- */ + + +void Patch::writeMasterPlugins(int type) +{ + char chr; + + if (type == PluginHost::MASTER_IN) { + fprintf(fp, "# --- VST / master in --- \n"); + chr = 'I'; + } + else { + fprintf(fp, "# --- VST / master out --- \n"); + chr = 'O'; + } + + int nmp = G_PluginHost.countPlugins(type); + fprintf(fp, "master%cPlugins=%d\n", chr, nmp); + + for (int i=0; istatus) { + gLog("[patch] Plugin %d is in a bad status, skip writing params\n", i); + continue; + } + + fprintf(fp, "master%c_p%dpathfile=%s\n", chr, i, pPlugin->pathfile); + fprintf(fp, "master%c_p%dbypass=%d\n", chr, i, pPlugin->bypass); + int numParams = pPlugin->getNumParams(); + fprintf(fp, "master%c_p%dnumParams=%d\n", chr, i, numParams); + + for (int j=0; jgetParam(j)); + } +} + +#endif diff --git a/src/core/patch.h b/src/core/patch.h new file mode 100644 index 0000000..e431b85 --- /dev/null +++ b/src/core/patch.h @@ -0,0 +1,95 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * patch + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef __PATCH_H__ +#define __PATCH_H__ + +#include +#include +#include +#include "dataStorage.h" +#include "const.h" + + +class Patch : public DataStorage { + +private: + int readMasterPlugins(int type); + void writeMasterPlugins(int type); + +public: + + char name[MAX_PATCHNAME_LEN]; + float version; + int lastTakeId; + int samplerate; + + int open(const char *file); + void setDefault(); + int close(); + + void getName (); + int getNumChans (); + int getNumColumns (); + std::string getSamplePath (int i); + float getVol (int i); + int getMode (int i); + int getMute (int i); + int getMute_s (int i); + int getSolo (int i); + int getBegin (int i); + int getEnd (int i, unsigned sampleSize); + float getBoost (int i); + float getPanLeft (int i); + float getPanRight (int i); + float getPitch (int i); + bool getRecActive (int i); + int getColumn (int i); + int getIndex (int i); + int getType (int i); + int getKey (int i); + uint32_t getMidiValue (int i, const char *c); + float getOutVol (); + float getInVol (); + float getBpm (); + int getBars (); + int getBeats (); + int getQuantize (); + bool getMetronome (); + int getLastTakeId (); + int getSamplerate (); + + int write(const char *file, const char *name, bool isProject); + int readRecs(); +#ifdef WITH_VST + int readPlugins(); +#endif +}; + +#endif diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp new file mode 100644 index 0000000..d54c7df --- /dev/null +++ b/src/core/plugin.cpp @@ -0,0 +1,520 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifdef WITH_VST + + +#include "../utils/log.h" +#include "plugin.h" + + +int Plugin::id_generator = 0; + + +/* ------------------------------------------------------------------ */ + + +Plugin::Plugin() + : module (NULL), + entryPoint(NULL), + plugin (NULL), + id (id_generator++), + program (-1), + bypass (false), + suspended (false) +{} + + +/* ------------------------------------------------------------------ */ + + +Plugin::~Plugin() { + unload(); +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::unload() { + + if (module == NULL) + return 1; + +#if defined(_WIN32) + + FreeLibrary((HMODULE)module); // FIXME - error checking + return 1; + +#elif defined(__linux__) + + return dlclose(module) == 0 ? 1 : 0; + +#elif defined(__APPLE__) + + /* we must unload bundles but because bundles may be in use for other + plug-in types it is important (and mandatory on certain plug-ins, + e.g. Korg) to do a check on the retain count. */ + + CFIndex retainCount = CFGetRetainCount(module); + + if (retainCount == 1) { + gLog("[plugin] retainCount == 1, can unload dlyb\n"); + CFBundleUnloadExecutable(module); + CFRelease(module); + } + else + gLog("[plugin] retainCount > 1 (%d), leave dlyb alone\n", (int) retainCount); + + return 1; + +#endif +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::load(const char *fname) { + + strcpy(pathfile, fname); + +#if defined(_WIN32) + + module = LoadLibrary(pathfile); + +#elif defined(__linux__) + + module = dlopen(pathfile, RTLD_LAZY); + +#elif defined(__APPLE__) + + /* creates the path to the bundle. In OSX vsts are stored inside the + * so-called bundles, just a directory with '.vst' extension. Finally + * we open the bundle with CFBundleCreate. */ + + CFStringRef pathStr = CFStringCreateWithCString(NULL, pathfile, kCFStringEncodingASCII); + CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStr, kCFURLPOSIXPathStyle, true); + if(bundleUrl == NULL) { + gLog("[plugin] unable to create URL reference for plugin\n"); + status = 0; + return 0; + } + module = CFBundleCreate(kCFAllocatorDefault, bundleUrl); + +#endif + + if (module) { + + /* release (free) any old string */ + +#ifdef __APPLE__ + CFRelease(pathStr); + CFRelease(bundleUrl); +#endif + //strcpy(pathfile, fname); ??????????? + status = 1; + return 1; + } + else { + +#if defined(_WIN32) + + gLog("[plugin] unable to load %s, error: %d\n", fname, (int) GetLastError()); + +#elif defined(__linux__) + + gLog("[plugin] unable to load %s, error: %s\n", fname, dlerror()); + +#elif defined(__APPLE__) + + gLog("[plugin] unable to create bundle reference\n"); + CFRelease(pathStr); + CFRelease(bundleUrl); + +#endif + status = 0; + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::init(VstIntPtr VSTCALLBACK (*HostCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)) { + +#if defined(_WIN32) + + entryPoint = (vstPluginFuncPtr) GetProcAddress((HMODULE)module, "VSTPluginMain"); + if (!entryPoint) + entryPoint = (vstPluginFuncPtr) GetProcAddress((HMODULE)module, "main"); + +#elif defined(__linux__) + + /* bad stuff here: main() is a function pointer, dlsym(module, "main") + * returns a pointer to an object (void*) which should be casted to + * a pointer to function (main(), precisely). Unfortunately the standard + * forbids the conversion from void* to function pointer. So we do a raw + * mem copy from tmp to entryPoint. */ + + void *tmp; + tmp = dlsym(module, "VSTPluginMain"); + if (!tmp) + tmp = dlsym(module, "main"); + memcpy(&entryPoint, &tmp, sizeof(tmp)); + +#elif defined(__APPLE__) + + /* same also for Unix/OSX. */ + + void *tmp = NULL; + tmp = CFBundleGetFunctionPointerForName(module, CFSTR("VSTPluginMain")); + + if (!tmp) { + gLog("[plugin] entryPoint 'VSTPluginMain' not found\n"); + tmp = CFBundleGetFunctionPointerForName(module, CFSTR("main_macho")); // VST SDK < 2.4 + } + if (!tmp) { + gLog("[plugin] entryPoint 'main_macho' not found\n"); + tmp = CFBundleGetFunctionPointerForName(module, CFSTR("main")); + } + if (tmp) + memcpy(&entryPoint, &tmp, sizeof(tmp)); + else + gLog("[plugin] entryPoint 'main' not found\n"); + +#endif + + /* if entry point is found, add to plugin a pointer to hostCallback. Or + * in other words bind the callback to the plugin. */ + + if (entryPoint) { + gLog("[plugin] entryPoint found\n"); + plugin = entryPoint(HostCallback); + if (!plugin) { + gLog("[plugin] failed to create effect instance!\n"); + return 0; + } + } + else { + gLog("[plugin] entryPoint not found, unable to proceed\n"); + return 0; + } + + + /* check the magicNumber */ + /** WARNING: on Windows one can load any DLL! Why!?! */ + + if(plugin->magic == kEffectMagic) { + gLog("[plugin] magic number OK\n"); + return 1; + } + else { + gLog("[plugin] magic number is bad\n"); + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::setup(int samplerate, int frames) { + + /* init plugin through the dispatcher with some basic infos */ + + plugin->dispatcher(plugin, effOpen, 0, 0, 0, 0); + plugin->dispatcher(plugin, effSetSampleRate, 0, 0, 0, samplerate); + plugin->dispatcher(plugin, effSetBlockSize, 0, frames, 0, 0); + + /* check SDK compatibility */ + + if (getSDKVersion() != kVstVersion) + gLog("[plugin] warning: different VST version (host: %d, plugin: %d)\n", kVstVersion, getSDKVersion()); + + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +AEffect *Plugin::getPlugin() { + return plugin; +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::getId() { return id; } + + +/* ------------------------------------------------------------------ */ + +int Plugin::getSDKVersion() { + return plugin->dispatcher(plugin, effGetVstVersion, 0, 0, 0, 0); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getName(char *out) { + char tmp[128] = "\0"; + plugin->dispatcher(plugin, effGetEffectName, 0, 0, tmp, 0); + tmp[kVstMaxEffectNameLen-1] = '\0'; + strncpy(out, tmp, kVstMaxEffectNameLen); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getVendor(char *out) { + char tmp[128] = "\0"; + plugin->dispatcher(plugin, effGetVendorString, 0, 0, tmp, 0); + tmp[kVstMaxVendorStrLen-1] = '\0'; + strncpy(out, tmp, kVstMaxVendorStrLen); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getProduct(char *out) { + char tmp[128] = "\0"; + plugin->dispatcher(plugin, effGetProductString, 0, 0, tmp, 0); + tmp[kVstMaxProductStrLen-1] = '\0'; + strncpy(out, tmp, kVstMaxProductStrLen); +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::getNumPrograms() { return plugin->numPrograms; } + + +/* ------------------------------------------------------------------ */ + + +int Plugin::setProgram(int index) { + plugin->dispatcher(plugin, effBeginSetProgram, 0, 0, 0, 0); + plugin->dispatcher(plugin, effSetProgram, 0, index, 0, 0); + gLog("[plugin] program changed, index %d\n", index); + program = index; + return plugin->dispatcher(plugin, effEndSetProgram, 0, 0, 0, 0); +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::getNumParams() { return plugin->numParams; } + + +/* ------------------------------------------------------------------ */ + + +int Plugin::getNumInputs() { return plugin->numInputs; } + + +/* ------------------------------------------------------------------ */ + + +int Plugin::getNumOutputs() { return plugin->numOutputs; } + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getProgramName(int index, char *out) { + char tmp[128] = "\0"; + plugin->dispatcher(plugin, effGetProgramNameIndexed, index, 0, tmp, 0); + tmp[kVstMaxProgNameLen-1] = '\0'; + strncpy(out, tmp, kVstMaxProgNameLen); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getParamName(int index, char *out) { + char tmp[128] = "\0"; + plugin->dispatcher(plugin, effGetParamName, index, 0, tmp, 0); + tmp[kVstMaxParamStrLen-1] = '\0'; + strncpy(out, tmp, kVstMaxParamStrLen); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getParamLabel(int index, char *out) { + char tmp[128] = "\0"; + plugin->dispatcher(plugin, effGetParamLabel, index, 0, tmp, 0); + tmp[kVstMaxParamStrLen-1] = '\0'; + strncpy(out, tmp, kVstMaxParamStrLen); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getParamDisplay(int index, char *out) { + char tmp[128] = "\0"; + plugin->dispatcher(plugin, effGetParamDisplay, index, 0, tmp, 0); + tmp[kVstMaxParamStrLen-1] = '\0'; + strncpy(out, tmp, kVstMaxParamStrLen); +} + + +/* ------------------------------------------------------------------ */ + + +float Plugin::getParam(int index) { + return plugin->getParameter(plugin, index); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::setParam(int index, float value) { + plugin->setParameter(plugin, index, value); +} + + +/* ------------------------------------------------------------------ */ + + +bool Plugin::hasGui() { + return plugin->flags & effFlagsHasEditor; +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::openGui(void *w) { + long val = 0; +#ifdef __linux__ + val = (long) w; +#endif + plugin->dispatcher(plugin, effEditOpen, 0, val, w, 0); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::closeGui() { + plugin->dispatcher(plugin, effEditClose, 0, 0, 0, 0); +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::getGuiWidth() { + ERect *pErect = NULL; + plugin->dispatcher(plugin, effEditGetRect, 0, 0, &pErect, 0); + return pErect->top + pErect->right; +} + + +/* ------------------------------------------------------------------ */ + + +int Plugin::getGuiHeight() { + ERect *pErect = NULL; + plugin->dispatcher(plugin, effEditGetRect, 0, 0, &pErect, 0); + return pErect->top + pErect->bottom; +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::idle() { + plugin->dispatcher(plugin, effEditIdle, 0, 0, NULL, 0); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::processAudio(float **in, float **out, long frames) { + plugin->processReplacing(plugin, in, out, frames); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::processEvents(VstEvents *events) { + plugin->dispatcher(plugin, effProcessEvents, 0, 0, events, 0.0); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::resume() { + plugin->dispatcher(plugin, effMainsChanged, 0, 1, 0, 0); + suspended = false; +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::suspend() { + plugin->dispatcher(plugin, effMainsChanged, 0, 0, 0, 0); + suspended = true; +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::close() { + plugin->dispatcher(plugin, effClose, 0, 0, 0, 0); +} + + +/* ------------------------------------------------------------------ */ + + +void Plugin::getRect(ERect **out) { + plugin->dispatcher(plugin, effEditGetRect, 0, 0, out, 0); +} + + +#endif diff --git a/src/core/plugin.h b/src/core/plugin.h new file mode 100644 index 0000000..b03c343 --- /dev/null +++ b/src/core/plugin.h @@ -0,0 +1,168 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * plugin + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifdef WITH_VST + +#ifndef __PLUGIN_H +#define __PLUGIN_H + +#include + +/* before including aeffetx(x).h we must define __cdecl, otherwise VST + * headers can't be compiled correctly. In windows __cdecl is already + * defined. */ + +#ifdef __GNUC__ + #ifndef _WIN32 + #define __cdecl + #endif +#endif + +#include "../deps/vst/aeffectx.h" + +#if defined(_WIN32) + #include +#elif defined(__linux__) + #include + #include +#elif defined(__APPLE__) + #include +#endif + +#include // PATH_MAX + + +// Plugin's entry point +typedef AEffect* (*vstPluginFuncPtr)(audioMasterCallback host); + + +class Plugin { + +private: + +#if defined(_WIN32) || defined(__linux__) + void *module; // dll, so, ... +#elif defined(__APPLE__) + CFBundleRef module; // OSX bundle +#endif + + vstPluginFuncPtr entryPoint; // VST entry point + AEffect *plugin; // real plugin + + /* each plugin has an unique ID */ + + static int id_generator; + int id; + + /* program + * selected program. -1 if no program available */ + + int program; + + /* unload + * free plugin from memory. Calls dlclose and similars. */ + + int unload(); + +public: + Plugin(); + ~Plugin(); + + int load(const char *fname); + int init(VstIntPtr VSTCALLBACK (*HostCallback)(AEffect*, VstInt32, VstInt32, VstIntPtr, void*, float)); + int setup(int samplerate, int frames); + + AEffect *getPlugin(); + + /* get[Item]. + * Wrappers called by host when it wants info from the plugin. */ + + int getId(); + int getSDKVersion(); + void getName (char *out); + void getVendor (char *out); + void getProduct(char *out); + int getNumPrograms(); // list all programs + int setProgram(int index); // load a program + int getNumParams(); + int getNumInputs(); + int getNumOutputs(); + void getProgramName(int index, char *out); // program = preset + void getParamName(int index, char *out); + void getParamLabel(int index, char *out); // parameter's value(0, -39, ...) + void getParamDisplay(int index, char *out); // parameter's unit measurement (dB, Pan, ...) + float getParam(int index); + void getRect(ERect **out); + void setParam(int index, float value); + + bool hasGui(); + void openGui(void *w); + void closeGui(); + int getGuiWidth(); + int getGuiHeight(); + void idle(); + + void processAudio (float **in, float **out, long frames); + void processEvents(VstEvents *events); + void resume(); + void suspend(); + void close(); + + inline int getProgram() { return program; } + + /* there's a specific opcode for the bypass, but we don't trust the + * plugin's developers. */ + + bool bypass; + + /* the status of the plugin: + * 1: ok + * 0: missing (file not found) */ + + int status; + + /* suspended + * true after suspend(), false after resume(). A suspended plugin isn't + * processed by pluginHost. */ + + bool suspended; + + /* pathfile + * full filename path */ + + char pathfile[PATH_MAX]; + + /* window + * plugin must know its window in case of a resize via opcode */ + + class gWindow *window; +}; + +#endif + +#endif // #ifdef WITH_VST diff --git a/src/core/pluginHost.cpp b/src/core/pluginHost.cpp new file mode 100644 index 0000000..6202050 --- /dev/null +++ b/src/core/pluginHost.cpp @@ -0,0 +1,679 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * pluginHost + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifdef WITH_VST + + +#include "../gui/dialogs/gd_mainWindow.h" +#include "../utils/log.h" +#include "pluginHost.h" +#include "conf.h" +#include "const.h" +#include "mixer.h" +#include "channel.h" +#include "sampleChannel.h" +#include "midiChannel.h" +#include "kernelMidi.h" + + +extern Conf G_Conf; +extern Mixer G_Mixer; +extern PluginHost G_PluginHost; +extern unsigned G_beats; +extern gdMainWindow *mainWin; + + +PluginHost::PluginHost() { + + /* initially we fill vstTimeInfo with trash. Only when the plugin requests + * the opcode we load the right infos from G_Mixer. */ + + vstTimeInfo.samplePos = 0.0; + vstTimeInfo.sampleRate = G_Conf.samplerate; + vstTimeInfo.nanoSeconds = 0.0; + vstTimeInfo.ppqPos = 0.0; + vstTimeInfo.tempo = 120.0; + vstTimeInfo.barStartPos = 0.0; + vstTimeInfo.cycleStartPos = 0.0; + vstTimeInfo.cycleEndPos = 0.0; + vstTimeInfo.timeSigNumerator = 4; + vstTimeInfo.timeSigDenominator = 4; + vstTimeInfo.smpteOffset = 0; + vstTimeInfo.smpteFrameRate = 1; + vstTimeInfo.samplesToNextClock = 0; + vstTimeInfo.flags = 0; +} + + +/* ------------------------------------------------------------------ */ + + +PluginHost::~PluginHost() {} + + +/* ------------------------------------------------------------------ */ + + +int PluginHost::allocBuffers() { + + /** FIXME - ERROR CHECKING! */ + + /* never, ever use G_Conf.buffersize to alloc these chunks of memory. + * If you use JACK, that value would be meaningless. Always refer to + * kernelAudio::realBufsize. */ + + int bufSize = kernelAudio::realBufsize*sizeof(float); + + bufferI = (float **) malloc(2 * sizeof(float*)); + bufferI[0] = (float *) malloc(bufSize); + bufferI[1] = (float *) malloc(bufSize); + + bufferO = (float **) malloc(2 * sizeof(float*)); + bufferO[0] = (float *) malloc(bufSize); + bufferO[1] = (float *) malloc(bufSize); + + memset(bufferI[0], 0, bufSize); + memset(bufferI[1], 0, bufSize); + memset(bufferO[0], 0, bufSize); + memset(bufferO[1], 0, bufSize); + + gLog("[pluginHost] buffers allocated, buffersize = %d\n", 2*kernelAudio::realBufsize); + + //printOpcodes(); + + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +VstIntPtr VSTCALLBACK PluginHost::HostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt) { + return G_PluginHost.gHostCallback(effect, opcode, index, value, ptr, opt); +} + + +/* ------------------------------------------------------------------ */ + + +VstIntPtr PluginHost::gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt) { + + /* warning: VST headers compiled with DECLARE_VST_DEPRECATED. */ + + switch (opcode) { + + /* 0 - Called after a control has changed in the editor and when + * the associated parameter should be automated. Index contains the + * param, opt the value. Thanks, but we don't need it now. It will + * be useful when recording actions from VST (in the future). */ + + case audioMasterAutomate: + return 0; + + /* 1 - host version (2.4) */ + + case audioMasterVersion: + return kVstVersion; + + /* 3 - Give idle time to Host application, e.g. if plug-in editor is + * doing mouse tracking in a modal loop. This a is multithread app, + * we don't need it. */ + + case audioMasterIdle: + return 0; + + /* 6 - tells the host that the plugin is an instrument. Deprecated. */ + + case DECLARE_VST_DEPRECATED(audioMasterWantMidi): + return 0; + + /* 7 - time infos */ + + case audioMasterGetTime: + vstTimeInfo.samplePos = G_Mixer.actualFrame; + vstTimeInfo.sampleRate = G_Conf.samplerate; + vstTimeInfo.tempo = G_Mixer.bpm; + vstTimeInfo.timeSigNumerator = G_Mixer.beats; + vstTimeInfo.timeSigDenominator = G_Mixer.bars; + vstTimeInfo.ppqPos = (G_Mixer.actualFrame / (float) G_Conf.samplerate) * (float) G_Mixer.bpm / 60.0f; + return (VstIntPtr) &vstTimeInfo; + + /* ? - requires a pointer to VstEvents. No vstEvents so far (v0.5.4) */ + + case audioMasterProcessEvents: + return 0; + + /* 13 - tells that numInputs/numOutputs are changed. Not supported and + * not needed. */ + + case audioMasterIOChanged: + return false; + + /* 14 - plugin needs idle calls (outside its editor window). Deprecated */ + + case DECLARE_VST_DEPRECATED(audioMasterNeedIdle): + return 0; + + /* 15 - requests to resize the editor window. w = index, h = value*/ + + case audioMasterSizeWindow: { + gWindow *window = NULL; + for (unsigned i=0; igetPlugin() == effect) + window = masterOut.at(i)->window; + + for (unsigned i=0; igetPlugin() == effect) + window = masterIn.at(i)->window; + + for (unsigned i=0; iplugins.size && !window; j++) + if (ch->plugins.at(j)->getPlugin() == effect) + window = ch->plugins.at(j)->window; + } + + if (window) { + gLog("[pluginHost] audioMasterSizeWindow: resizing window from plugin %p\n", (void*) effect); + if (index == 1 || value == 1) + gLog("[pluginHost] warning: non-sense values!\n"); + else + window->size((int)index, (int)value); + return 1; + } + else { + gLog("[pluginHost] audioMasterSizeWindow: window from plugin %p not found\n", (void*) effect); + return 0; + } + } + + /* 16 - sample rate */ + + case audioMasterGetSampleRate: + return G_Conf.samplerate; + + /* ?? - buffer size */ + + case audioMasterGetBlockSize: + return kernelAudio::realBufsize; + + case audioMasterGetInputLatency: + gLog("[pluginHost] requested opcode 'audioMasterGetInputLatency' (%d)\n", opcode); + return 0; + + case audioMasterGetOutputLatency: + gLog("[pluginHost] requested opcode 'audioMasterGetOutputLatency' (%d)\n", opcode); + return 0; + + /* 23 - wants to know what kind of process is that. + * kVstProcessLevelRealtime = currently in audio thread (where + * process is called). */ + + case audioMasterGetCurrentProcessLevel: + return kVstProcessLevelRealtime; + + /* 32 - vendor name */ + + case audioMasterGetVendorString: + strcpy((char*)ptr, "Monocasual"); + return 1; + + /* 32 - product name */ + + case audioMasterGetProductString: + strcpy((char*)ptr, "Giada"); + return 1; + + /* 33 - product version */ + + case audioMasterGetVendorVersion: + return (int) VERSIONE_FLOAT * 100; + + + /* 37 - Plugin asks Host if it implements the feature text. */ + + case audioMasterCanDo: + gLog("[pluginHost] audioMasterCanDo: %s\n", (char*)ptr); + if (!strcmp((char*)ptr, "sizeWindow") || + !strcmp((char*)ptr, "sendVstTimeInfo") || + !strcmp((char*)ptr, "sendVstMidiEvent") || + !strcmp((char*)ptr, "sendVstMidiEventFlagIsRealtime")) + return 1; // we can do all of that + else + return 0; + + /* 42 - Something has changed, update the host's 'multi-fx' display. + * Not supported right now, return 0. This opcode deals with the program + * changes, more infos http://www.asseca.com/vst-24-specs/amUpdateDisplay.html */ + + case audioMasterUpdateDisplay: + return 0; + + case audioMasterGetLanguage: + return kVstLangEnglish; + + /* ?? */ + + case audioMasterGetAutomationState: + gLog("[pluginHost] requested opcode 'audioMasterGetAutomationState' (%d)\n", opcode); + return 0; + + /* 43 - It tells the Host that if it needs to, it has to record + * automation data for this control. In other words this opcode is fired + * when the user starts to tweak a parameter with the mouse. + * Useful when the plugin actions will be recorded. */ + + case audioMasterBeginEdit: + return 0; + + /* 44 - no more interaction for the user, started with the previous + * opcode. */ + + case audioMasterEndEdit: + return 0; + + default: + gLog("[pluginHost] FIXME: host callback called with opcode %d\n", opcode); + return 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) { + + Plugin *p = new Plugin(); + bool success = true; + + gVector *pStack; + pStack = getStack(stackType, ch); + + if (!p->load(fname)) { + //delete p; + //return 0; + success = false; + } + + /* if the load failed we add a 'dead' plugin into the stack. This is + * useful to report a missing plugin. */ + + if (!success) { + pStack->add(p); + return 0; + } + + /* otherwise let's try to initialize it. */ + + else { + + /* try to init the plugin. If fails, delete it and return error. */ + + if (!p->init(&PluginHost::HostCallback)) { + delete p; + return 0; + } + + /* plugin setup */ + + p->setup(G_Conf.samplerate, kernelAudio::realBufsize); + + /* try to add the new plugin until succeed */ + + int lockStatus; + while (true) { + lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); + if (lockStatus == 0) { + pStack->add(p); + pthread_mutex_unlock(&G_Mixer.mutex_plugins); + break; + } + } + + char name[256]; p->getName(name); + gLog("[pluginHost] plugin id=%d loaded (%s), stack type=%d, stack size=%d\n", p->getId(), name, stackType, pStack->size); + + /* p->resume() is suggested. Who knows... */ + + p->resume(); + + return 1; + } +} + + +/* ------------------------------------------------------------------ */ + + +void PluginHost::processStack(float *buffer, int stackType, Channel *ch) { + + gVector *pStack = getStack(stackType, ch); + + /* empty stack, stack not found or mixer not ready: do nothing */ + /// TODO - join evaluation + + if (!G_Mixer.ready) + return; + if (pStack == NULL) + return; + if (pStack->size == 0) + return; + + /* converting buffer from Giada to VST */ + + for (unsigned i=0; isize; i++) { + /// TODO - join evaluation + + if (pStack->at(i)->status != 1) + continue; + if (pStack->at(i)->suspended) + continue; + if (pStack->at(i)->bypass) + continue; + if (ch) { // process events if it's a channel stack + if (ch->type == CHANNEL_MIDI) { + ///gLog("events: %d\n", (((MidiChannel*)ch)->getVstEvents())->numEvents); + pStack->at(i)->processEvents(((MidiChannel*)ch)->getVstEvents()); + } + } + pStack->at(i)->processAudio(bufferI, bufferO, kernelAudio::realBufsize); + bufferI = bufferO; + } + + /* converting buffer from VST to Giada. A note for the future: if we + * overwrite (=) (as we do now) it's SEND, if we add (+) it's INSERT. */ + + for (unsigned i=0; i *pStack = getStack(stackType, ch); + for (unsigned i=0; isize; i++) { + if (pStack->at(i)->getId() == id) + return pStack->at(i); + } + return NULL; +} + + +/* ------------------------------------------------------------------ */ + + +Plugin *PluginHost::getPluginByIndex(int index, int stackType, Channel *ch) { + gVector *pStack = getStack(stackType, ch); + if (pStack->size == 0) + return NULL; + if ((unsigned) index >= pStack->size) + return NULL; + return pStack->at(index); +} + + +/* ------------------------------------------------------------------ */ + + +void PluginHost::freeStack(int stackType, Channel *ch) { + + gVector *pStack; + pStack = getStack(stackType, ch); + + if (pStack->size == 0) + return; + + int lockStatus; + while (true) { + lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); + if (lockStatus == 0) { + for (unsigned i=0; isize; i++) { + if (pStack->at(i)->status == 1) { // only if plugin is ok + pStack->at(i)->suspend(); + pStack->at(i)->close(); + } + delete pStack->at(i); + } + pStack->clear(); + pthread_mutex_unlock(&G_Mixer.mutex_plugins); + break; + } + } + +} + + +/* ------------------------------------------------------------------ */ + + +void PluginHost::freeAllStacks() { + freeStack(PluginHost::MASTER_OUT); + freeStack(PluginHost::MASTER_IN); + for (unsigned i=0; i *pStack; + pStack = getStack(stackType, ch); + + /* try to delete the plugin until succeed. G_Mixer has priority. */ + + for (unsigned i=0; isize; i++) + if (pStack->at(i)->getId() == id) { + + if (pStack->at(i)->status == 0) { // no frills if plugin is missing + delete pStack->at(i); + pStack->del(i); + return; + } + else { + int lockStatus; + while (true) { + lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); + if (lockStatus == 0) { + pStack->at(i)->suspend(); + pStack->at(i)->close(); + delete pStack->at(i); + pStack->del(i); + pthread_mutex_unlock(&G_Mixer.mutex_plugins); + gLog("[pluginHost] plugin id=%d removed\n", id); + return; + } + //else + //gLog("[pluginHost] waiting for mutex...\n"); + } + } + } + gLog("[pluginHost] plugin id=%d not found\n", id); +} + + +/* ------------------------------------------------------------------ */ + + +void PluginHost::swapPlugin(unsigned indexA, unsigned indexB, int stackType, Channel *ch) { + + gVector *pStack = getStack(stackType, ch); + + int lockStatus; + while (true) { + lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); + if (lockStatus == 0) { + pStack->swap(indexA, indexB); + pthread_mutex_unlock(&G_Mixer.mutex_plugins); + gLog("[pluginHost] plugin at index %d and %d swapped\n", indexA, indexB); + return; + } + //else + //gLog("[pluginHost] waiting for mutex...\n"); + } +} + + +/* ------------------------------------------------------------------ */ + + +int PluginHost::getPluginIndex(int id, int stackType, Channel *ch) { + + gVector *pStack = getStack(stackType, ch); + + for (unsigned i=0; isize; i++) + if (pStack->at(i)->getId() == id) + return i; + return -1; +} + + +/* ------------------------------------------------------------------ */ + + +gVector *PluginHost::getStack(int stackType, Channel *ch) { + switch(stackType) { + case MASTER_OUT: + return &masterOut; + case MASTER_IN: + return &masterIn; + case CHANNEL: + return &ch->plugins; + default: + return NULL; + } +} + + +/* ------------------------------------------------------------------ */ + + +VstMidiEvent *PluginHost::createVstMidiEvent(uint32_t msg) +{ + VstMidiEvent *e = (VstMidiEvent*) malloc(sizeof(VstMidiEvent)); + + /* type = two types of events: MIDI event and MIDI system exclusive + * (aka sysex, not implemented). */ + + e->type = kVstMidiType; + e->byteSize = sizeof(VstMidiEvent); + + /* deltaFrames = sample frames related to the current block start + * sample position. */ + + e->deltaFrames = 0; + + /* flags = kVstMidiEventIsRealtime means that this event is played + * live (not in playback from a sequencer track). This allows the + * Plug-In to handle these flagged events with higher priority, + * especially when the Plug-In has a big latency */ + + e->flags = kVstMidiEventIsRealtime; + + /* midiData = 1 to 3 MIDI bytes; midiData[3] is reserved (zero) */ + + e->midiData[0] = kernelMidi::getB1(msg); // note on/off + channel + e->midiData[1] = kernelMidi::getB2(msg); // note number + e->midiData[2] = kernelMidi::getB3(msg); // velocity + e->midiData[3] = 0; + + /* noteLength = (in sample frames) of entire note, if available, + * else 0 */ + + e->noteLength = 0; + + /* noteOffset = offset (in sample frames) into note from note start + * if available, else 0 */ + + e->noteOffset = 0; + + /* noteOffVelocity = Note Off Velocity [0, 127]. */ + + e->noteOffVelocity = 0; + + return e; +} + + +/* ------------------------------------------------------------------ */ + + +unsigned PluginHost::countPlugins(int stackType, Channel *ch) { + gVector *pStack = getStack(stackType, ch); + return pStack->size; +} + + +#endif // #ifdef WITH_VST diff --git a/src/core/pluginHost.h b/src/core/pluginHost.h new file mode 100644 index 0000000..58907ad --- /dev/null +++ b/src/core/pluginHost.h @@ -0,0 +1,132 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * pluginHost + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifdef WITH_VST + +#ifndef __PLUGIN_HOST_ +#define __PLUGIN_HOST_ + +#include "../utils/utils.h" +#include "../gui/elems/ge_window.h" +#include "plugin.h" +#include "init.h" +#include "const.h" + + +class PluginHost { + +private: + + /* VSTs have a different buffer model: + * + * buffer[0] = channel left + * buffer[1] = channel right + * buffer[0][....] = all signals from left chan + * buffer[1][....] = all signals from right chan */ + + float **bufferI; + float **bufferO; + + /* VST struct containing infos on tempo (bpm, freq, smtpe, ...). */ + + VstTimeInfo vstTimeInfo; + +public: + + /* stack types. Use them together with getStack() in order to geta + * pointer to the right stack. */ + + enum stackType { + MASTER_OUT, + MASTER_IN, + CHANNEL + }; + + /* stack of Plugins */ + + gVector masterOut; + gVector masterIn; + + PluginHost(); + ~PluginHost(); + + int allocBuffers(); + + /* The plugin can ask the host if it supports a given capability, + * which is done through the HostCallback() function. + * + * Why static? This is a callback attached to each plugin in the stack + * and C++ callback functions need to be static when declared in class. + * + * OPCODE LIST: + * base version: vstsdk2.4/pluginterfaces/aeffect.h (vst 1.x) + * enhanced v. : vstsdk2.4/pluginterfaces/effectx.h (vst 2.x) */ + + static VstIntPtr VSTCALLBACK HostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt); + VstIntPtr gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt); + + int addPlugin(const char *fname, int stackType, class Channel *ch=NULL); + + void processEvents(float *buffer, class Channel *ch); + + /* processStack + * apply the fx list to the buffer. */ + + void processStack(float *buffer, int stackType, class Channel *ch=NULL); + + /* processStackOffline + * apply the fx list to a longer chunk of data */ + + void processStackOffline(float *buffer, int stackType, class Channel *ch, int size); + + /* createVstMidiEvent + * return a pointer to a new VstMidiEvent structure. */ + + VstMidiEvent *createVstMidiEvent(uint32_t msg); + + gVector *getStack(int stackType, class Channel *ch=NULL); + + Plugin *getPluginById(int id, int stackType, class Channel *ch=NULL); + + Plugin *getPluginByIndex(int index, int stackType, class Channel *ch=NULL); + + int getPluginIndex(int id, int stackType, class Channel *ch=NULL); + + unsigned countPlugins(int stackType, class Channel *ch=NULL); + + void freeStack(int stackType, class Channel *ch=NULL); + + void freeAllStacks(); + + void freePlugin(int id, int stackType, class Channel *ch=NULL); + + void swapPlugin(unsigned indexA, unsigned indexB, int stackType, class Channel *ch=NULL); +}; +#endif + +#endif // #ifdef WITH_VST diff --git a/src/core/recorder.cpp b/src/core/recorder.cpp new file mode 100644 index 0000000..73d9b38 --- /dev/null +++ b/src/core/recorder.cpp @@ -0,0 +1,697 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * recorder + * Action recorder. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "recorder.h" +#include "const.h" +#include "mixer.h" +#include "mixerHandler.h" +#include "kernelAudio.h" +#include "pluginHost.h" +#include "kernelMidi.h" +#include "patch.h" +#include "conf.h" +#include "channel.h" +#include "sampleChannel.h" +#include "../utils/log.h" +#include "../utils/utils.h" + + +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +extern Mixer G_Mixer; +extern Patch f_patch; +extern Conf G_Conf; + + +namespace recorder +{ +gVector frames; +gVector< gVector > global; +gVector actions; + +bool active = false; +bool sortedActions = false; + +composite cmp; + + +/* ------------------------------------------------------------------ */ + + +void init() +{ + sortedActions = false; + active = false; + clearAll(); +} + + +/* ------------------------------------------------------------------ */ + + +bool canRec(Channel *ch) +{ + /* NO recording if: + * recorder is inactive + * mixer is not running + * mixer is recording a take in this channel ch + * channel is empty */ + + if (!active || !G_Mixer.running || G_Mixer.chanInput == ch || (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL)) + return 0; + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +void rec(int index, int type, int frame, uint32_t iValue, float fValue) +{ + /* make sure frame is even */ + + if (frame % 2 != 0) + frame++; + + /* allocating the action */ + + action *a = (action*) malloc(sizeof(action)); + a->chan = index; + a->type = type; + a->frame = frame; + a->iValue = iValue; + a->fValue = fValue; + + /* check if the frame exists in the stack. If it exists, we don't extend + * the stack, but we add (or push) a new action to it. */ + + int frameToExpand = frames.size; + for (int i=0; ichan == index && + ac->type == type && + ac->frame == frame && + ac->iValue == iValue && + ac->fValue == fValue) + return; + } + + global.at(frameToExpand).add(a); // expand array + } + + /* if WITH_VST create a new VST event and attach it to our action. + * Nota bene: the VST event occurs on localFrame=0: this is a + * user-generated event after all! */ + +#ifdef WITH_VST + if (type == ACTION_MIDI) + a->event = G_PluginHost.createVstMidiEvent(a->iValue); +#endif + + /* don't activate the channel (readActions == false), it's up to + * the other layers */ + + Channel *ch = G_Mixer.getChannelByIndex(index); + ch->hasActions = true; + + sortedActions = false; + + gLog("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", + a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue); + //print(); +} + + +/* ------------------------------------------------------------------ */ + + +void clearChan(int index) +{ + gLog("[REC] clearing chan %d...\n", index); + + for (unsigned i=0; ichan == index) { +#ifdef WITH_VST + if (a->type == ACTION_MIDI) + free(a->event); +#endif + free(a); + global.at(i).del(j); + } + else + j++; + } + } + + Channel *ch = G_Mixer.getChannelByIndex(index); + ch->hasActions = false; + optimize(); + //print(); +} + + +/* ------------------------------------------------------------------ */ + + +void clearAction(int index, char act) +{ + gLog("[REC] clearing action %d from chan %d...\n", act, index); + for (unsigned i=0; ichan == index && (act & a->type) == a->type) { // bitmask + free(a); + global.at(i).del(j); + } + else + j++; + } + } + Channel *ch = G_Mixer.getChannelByIndex(index); + ch->hasActions = false; /// FIXME - why this? Isn't it useless if we call chanHasActions? + optimize(); + chanHasActions(index); /// FIXME + //print(); +} + + +/* ------------------------------------------------------------------ */ + + +void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue, float fValue) +{ + /* make sure frame is even */ + + if (frame % 2 != 0) + frame++; + + /* find the frame 'frame' */ + + bool found = false; + for (unsigned i=0; ichan == chan && a->type == (type & a->type)); + if (checkValues) + doit &= (a->iValue == iValue && a->fValue == fValue); + + if (doit) { + int lockStatus = 0; + while (lockStatus == 0) { + lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_recs); + if (lockStatus == 0) { +#ifdef WITH_VST + if (type == ACTION_MIDI) + free(a->event); +#endif + free(a); + global.at(i).del(j); + pthread_mutex_unlock(&G_Mixer.mutex_recs); + found = true; + break; + } + else + gLog("[REC] delete action: waiting for mutex...\n"); + } + } + } + } + } + if (found) { + optimize(); + chanHasActions(chan); + gLog("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", + type, frame, chan, iValue, iValue, fValue); + } + else + gLog("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", + type, frame, chan, iValue, iValue, fValue); +} + + +/* ------------------------------------------------------------------ */ + + +void deleteActions(int chan, int frame_a, int frame_b, char type) +{ + sortActions(); + gVector dels; + + for (unsigned i=0; i frame_a && frames.at(i) < frame_b) + dels.add(frames.at(i)); + + for (unsigned i=0; i 0) { + for (unsigned i=0; itype == ACTION_MIDI) + free(global.at(i).at(k)->event); +#endif + free(global.at(i).at(k)); // free action + } + global.at(i).clear(); // free action container + global.del(i); + } + } + + for (unsigned i=0; ihasActions = false; + if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) + ((SampleChannel*)G_Mixer.channels.at(i))->readActions = false; + } + + global.clear(); + frames.clear(); +} + + +/* ------------------------------------------------------------------ */ + + +void optimize() +{ + /* do something until the i frame is empty. */ + + unsigned i = 0; + while (true) { + if (i == global.size) return; + if (global.at(i).size == 0) { + global.del(i); + frames.del(i); + } + else + i++; + } + + sortActions(); +} + + +/* ------------------------------------------------------------------ */ + + +void sortActions() +{ + if (sortedActions) + return; + for (unsigned i=0; i frames.at(i)) { + frames.swap(j, i); + global.swap(j, i); + } + sortedActions = true; + //print(); +} + + +/* ------------------------------------------------------------------ */ + + +void updateBpm(float oldval, float newval, int oldquanto) +{ + for (unsigned i=0; i 0 && scarto <= 6) + frames.at(i) = frames.at(i) + scarto; + } + + /* never ever have odd frames. */ + + if (frames.at(i) % 2 != 0) + frames.at(i)++; + } + + /* update structs */ + + for (unsigned i=0; iframe = frames.at(i); + } + } + + //print(); +} + + +/* ------------------------------------------------------------------ */ + + +void updateSamplerate(int systemRate, int patchRate) +{ + /* diff ratio: systemRate / patchRate + * e.g. 44100 / 96000 = 0.4... */ + + if (systemRate == patchRate) + return; + + gLog("[REC] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate); + + float ratio = systemRate / (float) patchRate; + for (unsigned i=0; iframe = frames.at(i); + } + } +} + + +/* ------------------------------------------------------------------ */ + + +void expand(int old_fpb, int new_fpb) +{ + /* this algorithm requires multiple passages if we expand from e.g. 2 + * to 16 beats, precisely 16 / 2 - 1 = 7 times (-1 is the first group, + * which exists yet). If we expand by a non-multiple, the result is zero, + * due to float->int implicit cast */ + + unsigned pass = (int) (new_fpb / old_fpb) - 1; + if (pass == 0) pass = 1; + + unsigned init_fs = frames.size; + + for (unsigned z=1; z<=pass; z++) { + for (unsigned i=0; ichan, a->type, newframe, a->iValue, a->fValue); + } + } + } + gLog("[REC] expanded recs\n"); + //print(); +} + + +/* ------------------------------------------------------------------ */ + + +void shrink(int new_fpb) +{ + /* easier than expand(): here we delete eveything beyond old_framesPerBars. */ + + unsigned i=0; + while (true) { + if (i == frames.size) break; + + if (frames.at(i) >= new_fpb) { + for (unsigned k=0; khasActions = false; + return; + } + for (unsigned i=0; ihasActions; i++) { + for (unsigned j=0; jhasActions; j++) { + if (global.at(i).at(j)->chan == index) + ch->hasActions = true; + } + } +} + + +/* ------------------------------------------------------------------ */ + + +int getNextAction(int chan, char type, int frame, action **out, uint32_t iValue) +{ + sortActions(); // mandatory + + unsigned i=0; + while (i < frames.size && frames.at(i) <= frame) i++; + + if (i == frames.size) // no further actions past 'frame' + return -1; + + for (; ichan == chan && (type & a->type) == a->type) { + if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) { + *out = global.at(i).at(j); + return 1; + } + } + } + + return -2; // no 'type' actions found +} + + +/* ------------------------------------------------------------------ */ + + +int getAction(int chan, char action, int frame, struct action **out) +{ + for (unsigned i=0; iframe && + action == global.at(i).at(j)->type && + chan == global.at(i).at(j)->chan) + { + *out = global.at(i).at(j); + return 1; + } + return 0; +} + + +/* ------------------------------------------------------------------ */ + + +void startOverdub(int index, char actionMask, int frame) +{ + /* prepare the composite struct */ + + if (actionMask == ACTION_KEYS) { + cmp.a1.type = ACTION_KEYPRESS; + cmp.a2.type = ACTION_KEYREL; + } + else { + cmp.a1.type = ACTION_MUTEON; + cmp.a2.type = ACTION_MUTEOFF; + } + cmp.a1.chan = index; + cmp.a2.chan = index; + cmp.a1.frame = frame; + // cmp.a2.frame doesn't exist yet + + /* avoid underlying action truncation: if action2.type == nextAction: + * you are in the middle of a composite action, truncation needed */ + + rec(index, cmp.a1.type, frame); + + action *act = NULL; + int res = getNextAction(index, cmp.a1.type | cmp.a2.type, cmp.a1.frame, &act); + if (res == 1) { + if (act->type == cmp.a2.type) { + int truncFrame = cmp.a1.frame-kernelAudio::realBufsize; + if (truncFrame < 0) + truncFrame = 0; + gLog("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type); + rec(index, cmp.a2.type, truncFrame); + } + } + + SampleChannel *ch = (SampleChannel*) G_Mixer.getChannelByIndex(index); + ch->readActions = false; // don't use disableRead() +} + + +/* ------------------------------------------------------------------ */ + + +void stopOverdub(int frame) +{ + cmp.a2.frame = frame; + bool ringLoop = false; + bool nullLoop = false; + + /* ring loop verification, i.e. a composite action with key_press at + * frame N and key_release at frame M, with M <= N */ + + if (cmp.a2.frame < cmp.a1.frame) { + ringLoop = true; + gLog("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame); + rec(cmp.a2.chan, cmp.a2.type, G_Mixer.totalFrames); // record at the end of the sequencer + } + else + if (cmp.a2.frame == cmp.a1.frame) { + nullLoop = true; + gLog("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame); + deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false); // false == don't check values + } + + SampleChannel *ch = (SampleChannel*) G_Mixer.getChannelByIndex(cmp.a2.chan); + ch->readActions = false; // don't use disableRead() + + /* remove any nested action between keypress----keyrel, then record */ + + if (!nullLoop) + deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type); + deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type); + + if (!ringLoop && !nullLoop) { + rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame); + + /* avoid underlying action truncation, if keyrel happens inside a + * composite action */ + + action *act = NULL; + int res = getNextAction(cmp.a2.chan, cmp.a1.type | cmp.a2.type, cmp.a2.frame, &act); + if (res == 1) { + if (act->type == cmp.a2.type) { + gLog("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type); + deleteAction(act->chan, act->frame, act->type, false); // false == don't check values + } + } + } +} + + +/* ------------------------------------------------------------------ */ + + +void print() +{ + gLog("[REC] ** print debug **\n"); + for (unsigned i=0; itype, global.at(i).at(j)->chan, global.at(i).at(j)->frame); + } + } +} + +} // namespace diff --git a/src/core/recorder.h b/src/core/recorder.h new file mode 100644 index 0000000..6c9c65e --- /dev/null +++ b/src/core/recorder.h @@ -0,0 +1,192 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * recorder + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef RECORDER_H +#define RECORDER_H + +#include +#include +#include "../utils/utils.h" +#include "const.h" +#include "mixer.h" + +#ifdef WITH_VST + +/* before including aeffetx(x).h we must define __cdecl, otherwise VST + * headers can't be compiled correctly. In windows __cdecl is already + * defined. */ + + #ifdef __GNUC__ + #ifndef _WIN32 + #define __cdecl + #endif + #endif + #include "../deps/vst/aeffectx.h" +#endif + +/* + * [global0]-->[gVector<_action*>0]-->[a0][a1][a2] 0[frames1] + * [global1]-->[gVector<_action*>1]-->[a0][a1][a2] 1[frames2] + * [global2]-->[gVector<_action*>2]-->[a0][a1][a2] 2[frames3] + * [global3]-->[gVector<_action*>3]-->[a0][a1][a2] 3[frames4] + * */ + +namespace recorder { + +/* action + * struct containing fields to describe an atomic action. Note from + * VST sdk: parameter values, like all VST parameters, are declared as + * floats with an inclusive range of 0.0 to 1.0 (fValue). */ + +struct action { + int chan; // channel index, i.e. Channel->index + int type; + int frame; // redundant info, used by helper functions + float fValue; // used only for envelopes (volumes, vst params). + uint32_t iValue; // used only for MIDI events + + /* if VST store here a pointer to a vstEvent. */ + +#ifdef WITH_VST + VstMidiEvent *event; +#endif +}; + +/* composite + * a group of two actions (keypress+keyrel, muteon+muteoff) used during + * the overdub process */ + +struct composite { + action a1; + action a2; +}; + +extern gVector frames; // frame counter (sentinel) frames.size == global.size +extern gVector< gVector > global; // container of containers of actions +extern gVector actions; // container of actions + +extern bool active; +extern bool sortedActions; // are actions sorted via sortActions()? + +/* init + * everything starts from here. */ + +void init(); + +/* chanHasActions + * Check if the channel has at least one action recorded. If false, sets + * ch->hasActions = false. Used after an action deletion. */ + +void chanHasActions(int chan); + +/* canRec + * can a channel rec an action? Call this one BEFORE rec(). */ + +bool canRec(Channel *ch); + +/* rec + * record an action. */ + +void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f); + +/* clearChan + * clear all actions from a channel. */ + +void clearChan(int chan); + +/* clearAction + * clear the 'action' action type from a channel. */ + +void clearAction(int chan, char action); + +/* deleteAction + * delete ONE action. Useful in the action editor. 'type' can be a mask. */ + +void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue=0, float fValue=0.0); + +/* deleteActions + * delete A RANGE of actions from frame_a to frame_b in channel 'chan'. + * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */ + +void deleteActions(int chan, int frame_a, int frame_b, char type); + +/* clearAll + * delete everything. */ + +void clearAll(); + +/* optimize + * clear frames without actions. */ + +void optimize(); + +/* sortActions + * sorts actions by frame, asc mode. */ + +void sortActions(); + +/* updateBpm + * reassign frames by calculating the new bpm value. */ + +void updateBpm(float oldval, float newval, int oldquanto); + +/* updateSamplerate + * reassign frames taking in account the samplerate. If f_system == + * f_patch nothing changes, otherwise the conversion is mandatory. */ + +void updateSamplerate(int systemRate, int patchRate); + +void expand(int old_fpb, int new_fpb); +void shrink(int new_fpb); + +/* getNextAction + * return the nearest action in chan 'chan' of type 'action' starting + * from 'frame'. Action can be a bitmask. If iValue != -1 search for + * next action with iValue == iValue: useful for MIDI key_release. */ + +int getNextAction(int chan, char action, int frame, struct action **out, uint32_t iValue=0); + +/* getAction + * return a pointer to action in chan 'chan' of type 'action' at frame + * 'frame'. */ + +int getAction(int chan, char action, int frame, struct action **out); + +/* start/endOverdub */ + +void startOverdub(int chan, char action, int frame); +void stopOverdub(int frame); + +/* print + * debug of the frame stack. */ + +void print(); + +} // namespace + +#endif diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp new file mode 100644 index 0000000..81ba29f --- /dev/null +++ b/src/core/sampleChannel.cpp @@ -0,0 +1,1036 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include "../utils/log.h" +#include "sampleChannel.h" +#include "patch.h" +#include "conf.h" +#include "wave.h" +#include "pluginHost.h" +#include "waveFx.h" +#include "mixerHandler.h" +#include "kernelMidi.h" + + +extern Patch G_Patch; +extern Mixer G_Mixer; +extern Conf G_Conf; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +SampleChannel::SampleChannel(int bufferSize) + : Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize), + frameRewind (-1), + wave (NULL), + tracker (0), + begin (0), + end (0), + pitch (gDEFAULT_PITCH), + boost (1.0f), + mode (DEFAULT_CHANMODE), + qWait (false), + fadeinOn (false), + fadeinVol (1.0f), + fadeoutOn (false), + fadeoutVol (1.0f), + fadeoutTracker (0), + fadeoutStep (DEFAULT_FADEOUT_STEP), + readActions (true), + midiInReadActions(0x0), + midiInPitch (0x0) +{ + rsmp_state = src_new(SRC_LINEAR, 2, NULL); + pChan = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float)); +} + + +/* -------------------------------------------------------------------------- */ + + +SampleChannel::~SampleChannel() +{ + if (wave) + delete wave; + src_delete(rsmp_state); + free(pChan); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::clear() +{ + /** TODO - these memsets can be done only if status PLAY (if below), + * but it would require extra clearPChan calls when samples stop */ + + memset(vChan, 0, sizeof(float) * bufferSize); + memset(pChan, 0, sizeof(float) * bufferSize); + + if (status & (STATUS_PLAY | STATUS_ENDING)) { + tracker = fillChan(vChan, tracker, 0); + if (fadeoutOn && fadeoutType == XFADE) { + gLog("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker); + fadeoutTracker = fillChan(pChan, fadeoutTracker, 0); + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::calcVolumeEnv(int frame) +{ + /* method: check this frame && next frame, then calculate delta */ + + recorder::action *a0 = NULL; + recorder::action *a1 = NULL; + int res; + + /* get this action on frame 'frame'. It's unlikely that the action + * is not found. */ + + res = recorder::getAction(index, ACTION_VOLUME, frame, &a0); + if (res == 0) + return; + + /* get the action next to this one. + * res == -1: a1 not found, this is the last one. Rewind the search + * and use action at frame number 0 (actions[0]). + * res == -2 ACTION_VOLUME not found. This should never happen */ + + res = recorder::getNextAction(index, ACTION_VOLUME, frame, &a1); + + if (res == -1) + res = recorder::getAction(index, ACTION_VOLUME, 0, &a1); + + volume_i = a0->fValue; + volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::hardStop(int frame) +{ + if (frame != 0) // clear data in range [frame, bufferSize-1] + clearChan(vChan, frame); + status = STATUS_OFF; + sendMidiLplay(); + reset(frame); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::onBar(int frame) +{ + ///if (mode == LOOP_REPEAT && status == STATUS_PLAY) + /// //setXFade(frame); + /// reset(frame); + + if (mode == LOOP_REPEAT) { + if (status == STATUS_PLAY) + //setXFade(frame); + reset(frame); + } + else + if (mode == LOOP_ONCE_BAR) { + if (status == STATUS_WAIT) { + status = STATUS_PLAY; + tracker = fillChan(vChan, tracker, frame); + sendMidiLplay(); + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +int SampleChannel::save(const char *path) +{ + return wave->writeData(path); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setBegin(unsigned v) +{ + begin = v; + tracker = begin; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setEnd(unsigned v) +{ + end = v; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setPitch(float v) +{ + pitch = v; + rsmp_data.src_ratio = 1/pitch; + + /* if status is off don't slide between frequencies */ + + if (status & (STATUS_OFF | STATUS_WAIT)) + src_set_ratio(rsmp_state, 1/pitch); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::rewind() +{ + /* rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode */ + + if (wave != NULL) { + if ((mode & LOOP_ANY) || (recStatus == REC_READING && (mode & SINGLE_ANY))) + reset(0); // rewind is user-generated events, always on frame 0 + } +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::parseAction(recorder::action *a, int localFrame, int globalFrame) +{ + if (readActions == false) + return; + + switch (a->type) { + case ACTION_KEYPRESS: + if (mode & SINGLE_ANY) + start(localFrame, false); + break; + case ACTION_KEYREL: + if (mode & SINGLE_ANY) + stop(); + break; + case ACTION_KILLCHAN: + if (mode & SINGLE_ANY) + kill(localFrame); + break; + case ACTION_MUTEON: + setMute(true); // internal mute + break; + case ACTION_MUTEOFF: + unsetMute(true); // internal mute + break; + case ACTION_VOLUME: + calcVolumeEnv(globalFrame); + break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::sum(int frame, bool running) +{ + if (wave == NULL || status & ~(STATUS_PLAY | STATUS_ENDING)) + return; + + if (frame != frameRewind) { + + /* volume envelope, only if seq is running */ + + if (running) { + volume_i += volume_d; + if (volume_i < 0.0f) + volume_i = 0.0f; + else + if (volume_i > 1.0f) + volume_i = 1.0f; + } + + /* fadein or fadeout processes. If mute, delete any signal. */ + + /** TODO - big issue: fade[in/out]Vol * internal_volume might be a + * bad choice: it causes glitches when muting on and off during a + * volume envelope. */ + + if (mute || mute_i) { + vChan[frame] = 0.0f; + vChan[frame+1] = 0.0f; + } + else + if (fadeinOn) { + if (fadeinVol < 1.0f) { + vChan[frame] *= fadeinVol * volume_i; + vChan[frame+1] *= fadeinVol * volume_i; + fadeinVol += 0.01f; + } + else { + fadeinOn = false; + fadeinVol = 0.0f; + } + } + else + if (fadeoutOn) { + if (fadeoutVol > 0.0f) { // fadeout ongoing + if (fadeoutType == XFADE) { + vChan[frame] *= volume_i; + vChan[frame+1] *= volume_i; + vChan[frame] = pChan[frame] * fadeoutVol * volume_i; + vChan[frame+1] = pChan[frame+1] * fadeoutVol * volume_i; + } + else { + vChan[frame] *= fadeoutVol * volume_i; + vChan[frame+1] *= fadeoutVol * volume_i; + } + fadeoutVol -= fadeoutStep; + } + else { // fadeout end + fadeoutOn = false; + fadeoutVol = 1.0f; + + /* QWait ends with the end of the xfade */ + + if (fadeoutType == XFADE) { + qWait = false; + } + else { + if (fadeoutEnd == DO_MUTE) + mute = true; + else + if (fadeoutEnd == DO_MUTE_I) + mute_i = true; + else // DO_STOP + hardStop(frame); + } + } + } + else { + vChan[frame] *= volume_i; + vChan[frame+1] *= volume_i; + } + } + else { + + if (mode & (SINGLE_BASIC | SINGLE_PRESS | SINGLE_RETRIG) || + (mode == SINGLE_ENDLESS && status == STATUS_ENDING) || + (mode & LOOP_ANY && !running)) // stop loops when the seq is off + { + status = STATUS_OFF; + sendMidiLplay(); + } + + /* temporary stop LOOP_ONCE not in ENDING status, otherwise they + * would return in wait, losing the ENDING status */ + + //if (mode == LOOP_ONCE && status != STATUS_ENDING) + if ((mode & (LOOP_ONCE | LOOP_ONCE_BAR)) && status != STATUS_ENDING) { + status = STATUS_WAIT; + sendMidiLplay(); + } + + /* check for end of samples. SINGLE_ENDLESS runs forever unless + * it's in ENDING mode. */ + + reset(frame); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::onZero(int frame) +{ + if (wave == NULL) + return; + + if (mode & LOOP_ANY) { + + /* do a crossfade if the sample is playing. Regular chanReset + * instead if it's muted, otherwise a click occurs */ + + if (status == STATUS_PLAY) { + /* + if (mute || mute_i) + reset(frame); + else + setXFade(frame); + */ + reset(frame); + } + else + if (status == STATUS_ENDING) + hardStop(frame); + } + + if (status == STATUS_WAIT) { /// FIXME - should be inside previous if! + status = STATUS_PLAY; + sendMidiLplay(); + tracker = fillChan(vChan, tracker, frame); + } + + if (recStatus == REC_ENDING) { + recStatus = REC_STOPPED; + setReadActions(false); // rec stop + } + else + if (recStatus == REC_WAITING) { + recStatus = REC_READING; + setReadActions(true); // rec start + } +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::quantize(int index, int localFrame, int globalFrame) +{ + /* skip if LOOP_ANY or not in quantizer-wait mode */ + + if ((mode & LOOP_ANY) || !qWait) + return; + + /* no fadeout if the sample starts for the first time (from a + * STATUS_OFF), it would be meaningless. */ + + if (status == STATUS_OFF) { + status = STATUS_PLAY; + sendMidiLplay(); + qWait = false; + tracker = fillChan(vChan, tracker, localFrame); /// FIXME: ??? + } + else + //setXFade(localFrame); + reset(localFrame); + + /* this is the moment in which we record the keypress, if the + * quantizer is on. SINGLE_PRESS needs overdub */ + + if (recorder::canRec(this)) { + if (mode == SINGLE_PRESS) + recorder::startOverdub(index, ACTION_KEYS, globalFrame); + else + recorder::rec(index, ACTION_KEYPRESS, globalFrame); + } +} + + +/* -------------------------------------------------------------------------- */ + + +int SampleChannel::getPosition() +{ + if (status & ~(STATUS_EMPTY | STATUS_MISSING | STATUS_OFF)) // if is not (...) + return tracker - begin; + else + return -1; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setMute(bool internal) +{ + if (internal) { + + /* global mute is on? don't waste time with fadeout, just mute it + * internally */ + + if (mute) + mute_i = true; + else { + if (isPlaying()) + setFadeOut(DO_MUTE_I); + else + mute_i = true; + } + } + else { + + /* internal mute is on? don't waste time with fadeout, just mute it + * globally */ + + if (mute_i) + mute = true; + else { + + /* sample in play? fadeout needed. Else, just mute it globally */ + + if (isPlaying()) + setFadeOut(DO_MUTE); + else + mute = true; + } + } + + sendMidiLmute(); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::unsetMute(bool internal) +{ + if (internal) { + if (mute) + mute_i = false; + else { + if (isPlaying()) + setFadeIn(internal); + else + mute_i = false; + } + } + else { + if (mute_i) + mute = false; + else { + if (isPlaying()) + setFadeIn(internal); + else + mute = false; + } + } + + sendMidiLmute(); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::calcFadeoutStep() +{ + if (end - tracker < (1 / DEFAULT_FADEOUT_STEP) * 2) + fadeoutStep = ceil((end - tracker) / volume) * 2; /// or volume_i ??? + else + fadeoutStep = DEFAULT_FADEOUT_STEP; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setReadActions(bool v) +{ + if (v) + readActions = true; + else { + readActions = false; + if (G_Conf.recsStopOnChanHalt) + kill(0); /// FIXME - wrong frame value + } +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setFadeIn(bool internal) +{ + if (internal) mute_i = false; // remove mute before fading in + else mute = false; + fadeinOn = true; + fadeinVol = 0.0f; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setFadeOut(int actionPostFadeout) +{ + calcFadeoutStep(); + fadeoutOn = true; + fadeoutVol = 1.0f; + fadeoutType = FADEOUT; + fadeoutEnd = actionPostFadeout; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::setXFade(int frame) +{ + gLog("[xFade] frame=%d tracker=%d\n", frame, tracker); + + calcFadeoutStep(); + fadeoutOn = true; + fadeoutVol = 1.0f; + fadeoutType = XFADE; + fadeoutTracker = fillChan(pChan, tracker, 0, false); + reset(frame); +} + + +/* -------------------------------------------------------------------------- */ + + +/* on reset, if frame > 0 and in play, fill again pChan to create + * something like this: + * + * |abcdefabcdefab*abcdefabcde| + * [old data-----]*[new data--] + * + * */ + +void SampleChannel::reset(int frame) +{ + //fadeoutTracker = tracker; // store old frame number for xfade + tracker = begin; + mute_i = false; + if (frame > 0 && status & (STATUS_PLAY | STATUS_ENDING)) + tracker = fillChan(vChan, tracker, frame); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::empty() +{ + status = STATUS_OFF; + if (wave) { + delete wave; + wave = NULL; + } + status = STATUS_EMPTY; + sendMidiLplay(); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::pushWave(Wave *w) +{ + wave = w; + status = STATUS_OFF; + sendMidiLplay(); + begin = 0; + end = wave->size; +} + + +/* -------------------------------------------------------------------------- */ + + +bool SampleChannel::allocEmpty(int frames, int takeId) +{ + Wave *w = new Wave(); + if (!w->allocEmpty(frames)) + return false; + + char wname[32]; + sprintf(wname, "TAKE-%d", takeId); + + w->pathfile = gGetCurrentPath()+"/"+wname; // FIXME - use gGetSlash() in utils.h + w->name = wname; + wave = w; + status = STATUS_OFF; + begin = 0; + end = wave->size; + + sendMidiLplay(); + + return true; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::process(float *buffer) +{ +#ifdef WITH_VST + G_PluginHost.processStack(vChan, PluginHost::CHANNEL, this); +#endif + + for (int j=0; j FILENAME_MAX) + return SAMPLE_PATH_TOO_LONG; + + Wave *w = new Wave(); + + if (!w->open(file)) { + gLog("[SampleChannel] %s: read error\n", file); + delete w; + return SAMPLE_READ_ERROR; + } + + if (w->channels() > 2) { + gLog("[SampleChannel] %s: unsupported multichannel wave\n", file); + delete w; + return SAMPLE_MULTICHANNEL; + } + + if (!w->readData()) { + delete w; + return SAMPLE_READ_ERROR; + } + + if (w->channels() == 1) /** FIXME: error checking */ + wfx_monoToStereo(w); + + if (w->rate() != G_Conf.samplerate) { + gLog("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n", + w->rate(), G_Conf.samplerate); + w->resample(G_Conf.rsmpQuality, G_Conf.samplerate); + } + + pushWave(w); + + /* sample name must be unique. Start from k = 1, zero is too nerdy */ + + std::string oldName = wave->name; + int k = 1; + while (!mh_uniqueSamplename(this, wave->name.c_str())) { + wave->updateName((oldName + "-" + gItoa(k)).c_str()); + k++; + } + + gLog("[SampleChannel] %s loaded in channel %d\n", file, index); + return SAMPLE_LOADED_OK; +} + + +/* -------------------------------------------------------------------------- */ + + +int SampleChannel::loadByPatch(const char *f, int i) +{ + int res = load(f); + + volume = G_Patch.getVol(i); + key = G_Patch.getKey(i); + index = G_Patch.getIndex(i); + mode = G_Patch.getMode(i); + mute = G_Patch.getMute(i); + mute_s = G_Patch.getMute_s(i); + solo = G_Patch.getSolo(i); + boost = G_Patch.getBoost(i); + panLeft = G_Patch.getPanLeft(i); + panRight = G_Patch.getPanRight(i); + readActions = G_Patch.getRecActive(i); + recStatus = readActions ? REC_READING : REC_STOPPED; + + readPatchMidiIn(i); + midiInReadActions = G_Patch.getMidiValue(i, "InReadActions"); + midiInPitch = G_Patch.getMidiValue(i, "InPitch"); + readPatchMidiOut(i); + + if (res == SAMPLE_LOADED_OK) { + setBegin(G_Patch.getBegin(i)); + setEnd (G_Patch.getEnd(i, wave->size)); + setPitch(G_Patch.getPitch(i)); + } + else { + // volume = DEFAULT_VOL; + // mode = DEFAULT_CHANMODE; + // status = STATUS_WRONG; + // key = 0; + + if (res == SAMPLE_LEFT_EMPTY) + status = STATUS_EMPTY; + else + if (res == SAMPLE_READ_ERROR) + status = STATUS_MISSING; + sendMidiLplay(); + } + + return res; +} + + +/* -------------------------------------------------------------------------- */ + + +bool SampleChannel::canInputRec() + { + return wave == NULL; +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::start(int frame, bool doQuantize) +{ + switch (status) { + case STATUS_EMPTY: + case STATUS_MISSING: + case STATUS_WRONG: + { + return; + } + + case STATUS_OFF: + { + if (mode & LOOP_ANY) { + status = STATUS_WAIT; + sendMidiLplay(); + } + else { + if (G_Mixer.quantize > 0 && G_Mixer.running && doQuantize) + qWait = true; + else { + + /* fillChan only if frame != 0. If you call fillChan on frame == 0 + * a duplicate call to fillChan occurs with loss of data. */ + + status = STATUS_PLAY; + sendMidiLplay(); + if (frame != 0) + tracker = fillChan(vChan, tracker, frame); + } + } + break; + } + + case STATUS_PLAY: + { + if (mode == SINGLE_BASIC) + setFadeOut(DO_STOP); + else + if (mode == SINGLE_RETRIG) { + if (G_Mixer.quantize > 0 && G_Mixer.running && doQuantize) + qWait = true; + else + reset(frame); + } + else + if (mode & (LOOP_ANY | SINGLE_ENDLESS)) { + status = STATUS_ENDING; + sendMidiLplay(); + } + break; + } + + case STATUS_WAIT: + { + status = STATUS_OFF; + sendMidiLplay(); + break; + } + + case STATUS_ENDING: + { + status = STATUS_PLAY; + sendMidiLplay(); + break; + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::writePatch(FILE *fp, int i, bool isProject) +{ + Channel::writePatch(fp, i, isProject); + + const char *path = ""; + if (wave != NULL) { + path = wave->pathfile.c_str(); + if (isProject) + path = gBasename(path).c_str(); // make it portable + } + + fprintf(fp, "samplepath%d=%s\n", i, path); + fprintf(fp, "chanKey%d=%d\n", i, key); + //fprintf(fp, "columnIndex%d=%d\n", i, index); + fprintf(fp, "chanmode%d=%d\n", i, mode); + fprintf(fp, "chanBegin%d=%d\n", i, begin); + fprintf(fp, "chanend%d=%d\n", i, end); + fprintf(fp, "chanBoost%d=%f\n", i, boost); + fprintf(fp, "chanRecActive%d=%d\n", i, readActions); + fprintf(fp, "chanPitch%d=%f\n", i, pitch); + + fprintf(fp, "chanMidiInReadActions%d=%u\n", i, midiInReadActions); + fprintf(fp, "chanMidiInPitch%d=%u\n", i, midiInPitch); +} + + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::clearChan(float *dest, int start) +{ + memset(dest+start, 0, sizeof(float)*(bufferSize-start)); +} + + +/* -------------------------------------------------------------------------- */ + + +int SampleChannel::fillChan(float *dest, int start, int offset, bool rewind) +{ + int position; // return value: the new position + + if (pitch == 1.0f) { + + /* case 1: 'dest' lies within the original sample boundaries (start- + * end) */ + + if (start+bufferSize-offset <= end) { + memcpy(dest+offset, wave->data+start, (bufferSize-offset)*sizeof(float)); + position = start+bufferSize-offset; + if (rewind) + frameRewind = -1; + } + + /* case2: 'dest' lies outside the end of the sample, OR the sample + * is smaller than 'dest' */ + + else { + memcpy(dest+offset, wave->data+start, (end-start)*sizeof(float)); + position = end; + if (rewind) + frameRewind = end-start+offset; + } + } + else { + + rsmp_data.data_in = wave->data+start; // source data + rsmp_data.input_frames = (end-start)/2; // how many readable bytes + rsmp_data.data_out = dest+offset; // destination (processed data) + rsmp_data.output_frames = (bufferSize-offset)/2; // how many bytes to process + rsmp_data.end_of_input = false; + + src_process(rsmp_state, &rsmp_data); + int gen = rsmp_data.output_frames_gen*2; // frames generated by this call + + position = start + rsmp_data.input_frames_used*2; // position goes forward of frames_used (i.e. read from wave) + + if (rewind) { + if (gen == bufferSize-offset) + frameRewind = -1; + else + frameRewind = gen+offset; + } + } + return position; +} diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h new file mode 100644 index 0000000..736063a --- /dev/null +++ b/src/core/sampleChannel.h @@ -0,0 +1,210 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * sampleChannel + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef SAMPLE_CHANNEL_H +#define SAMPLE_CHANNEL_H + + +#include +#include "channel.h" + + +class SampleChannel : public Channel { + +private: + + /* rsmp_state, rsmp_data + * structs from libsamplerate */ + + SRC_STATE *rsmp_state; + SRC_DATA rsmp_data; + + /* pChan + * extra virtual channel for processing resampled data. */ + + float *pChan; + + /* frameRewind + * exact frame in which a rewind occurs */ + + int frameRewind; + + /* fillChan + * copy from wave to *dest and resample data from wave, if necessary. + * Start to fill pChan from byte 'offset'. If rewind=false don't + * rewind internal tracker. Returns new sample position, in frames */ + + int fillChan(float *dest, int start, int offset, bool rewind=true); + + /* clearChan + * set data to zero from start to bufferSize-1. */ + + void clearChan(float *dest, int start); + + /* calcFadeoutStep + * how many frames are left before the end of the sample? Is there + * enough room for a complete fadeout? Should we shorten it? */ + + void calcFadeoutStep(); + + /* calcVolumeEnv + * compute any changes in volume done via envelope tool */ + + void calcVolumeEnv(int frame); + +public: + + SampleChannel(int bufferSize); + ~SampleChannel(); + + void clear (); + void process (float *buffer); + void start (int frame, bool doQuantize); + void kill (int frame); + void empty (); + void stopBySeq (); + void stop (); + void rewind (); + void setMute (bool internal); + void unsetMute (bool internal); + void reset (int frame); + int load (const char *file); + int loadByPatch(const char *file, int i); + void writePatch (FILE *fp, int i, bool isProject); + void quantize (int index, int localFrame, int globalFrame); + void onZero (int frame); + void onBar (int frame); + void parseAction(recorder::action *a, int localFrame, int globalFrame); + + /* fade methods + * prepare channel for fade, mixer will take care of the process + * during master play. */ + + void setFadeIn (bool internal); + void setFadeOut (int actionPostFadeout); + void setXFade (int frame); + + /* pushWave + * add a new wave to an existing channel. */ + + void pushWave(class Wave *w); + + /* getPosition + * returns the position of an active sample. If EMPTY o MISSING + * returns -1. */ + + int getPosition(); + + /* sum + * add sample frames to virtual channel. Frame = processed frame in + * Mixer. Running = is Mixer in play? */ + + void sum(int frame, bool running); + + /* setPitch + * updates the pitch value and chanStart+chanEnd accordingly. */ + + void setPitch(float v); + + /* setStart/end + * change begin/end read points in sample. */ + + void setBegin(unsigned v); + void setEnd (unsigned v); + + /* save + * save sample to file. */ + + int save(const char *path); + + /* hardStop + * stop the channel immediately, no further checks. */ + + void hardStop(int frame); + + /* allocEmpty + * alloc an empty wave used in input recordings. */ + + bool allocEmpty(int frames, int takeId); + + /* canInputRec + * true if channel can host a new wave from input recording. */ + + bool canInputRec(); + + /* setReadActions + * if enabled, recorder will read actions from this channel */ + + void setReadActions(bool v); + + /* ---------------------------------------------------------------- */ + + class Wave *wave; + int tracker; // chan position + int begin; + int end; + float pitch; + float boost; + int mode; // mode: see const.h + bool qWait; // quantizer wait + bool fadeinOn; + float fadeinVol; + bool fadeoutOn; + float fadeoutVol; // fadeout volume + int fadeoutTracker; // tracker fadeout, xfade only + float fadeoutStep; // fadeout decrease + int fadeoutType; // xfade or fadeout + int fadeoutEnd; // what to do when fadeout ends + + /* recorder:: stuff */ + + bool readActions; // read actions or not + + /* midi stuff */ + + uint32_t midiInReadActions; + uint32_t midiInPitch; + + /* const - what to do when a fadeout ends */ + + enum { + DO_STOP = 0x01, + DO_MUTE = 0x02, + DO_MUTE_I = 0x04 + }; + + /* const - fade types */ + + enum { + FADEOUT = 0x01, + XFADE = 0x02 + }; +}; + +#endif diff --git a/src/core/wave.cpp b/src/core/wave.cpp new file mode 100644 index 0000000..ee81c6d --- /dev/null +++ b/src/core/wave.cpp @@ -0,0 +1,245 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * wave + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include +#include // memcpy +#include +#include "../utils/utils.h" +#include "../utils/log.h" +#include "wave.h" +#include "conf.h" +#include "init.h" + + +extern Conf G_Conf; + + +Wave::Wave() + : data (NULL), + size (0), + isLogical(0), + isEdited (0) {} + + +/* ------------------------------------------------------------------ */ + + +Wave::~Wave() { + clear(); +} + + +/* ------------------------------------------------------------------ */ + + +int Wave::open(const char *f) { + + pathfile = f; + name = gStripExt(gBasename(f).c_str()); + fileIn = sf_open(f, SFM_READ, &inHeader); + + if (fileIn == NULL) { + gLog("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn)); + pathfile = ""; + name = ""; + return 0; + } + + isLogical = false; + isEdited = false; + + return 1; +} + + +/* ------------------------------------------------------------------ */ + +/* how to read and write with libsndfile: + * + * a frame consists of all items (samples) that belong to the same + * point in time. So in each frame there are as many items as there + * are channels. + * + * Quindi: + * frame = [item, item, ...] + * In pratica: + * frame1 = [itemLeft, itemRight] + * frame2 = [itemLeft, itemRight] + * ... + */ + +int Wave::readData() { + size = inHeader.frames * inHeader.channels; + data = (float *) malloc(size * sizeof(float)); + if (data == NULL) { + gLog("[wave] unable to allocate memory\n"); + return 0; + } + + if (sf_read_float(fileIn, data, size) != size) + gLog("[wave] warning: incomplete read!\n"); + + sf_close(fileIn); + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +int Wave::writeData(const char *f) { + + /* prepare the header for output file */ + + outHeader.samplerate = inHeader.samplerate; + outHeader.channels = inHeader.channels; + outHeader.format = inHeader.format; + + fileOut = sf_open(f, SFM_WRITE, &outHeader); + if (fileOut == NULL) { + gLog("[wave] unable to open %s for exporting\n", f); + return 0; + } + + int out = sf_write_float(fileOut, data, size); + if (out != (int) size) { + gLog("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut)); + return 0; + } + + isLogical = false; + isEdited = false; + sf_close(fileOut); + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +void Wave::clear() { + if (data != NULL) { + free(data); + data = NULL; + pathfile = ""; + size = 0; + } +} + + +/* ------------------------------------------------------------------ */ + + +int Wave::allocEmpty(unsigned __size) { + + /* the caller must pass a __size for stereo values */ + + /// FIXME - this way if malloc fails size becomes wrong + size = __size; + data = (float *) malloc(size * sizeof(float)); + if (data == NULL) { + gLog("[wave] unable to allocate memory\n"); + return 0; + } + + memset(data, 0, sizeof(float) * size); /// FIXME - is it useful? + + inHeader.samplerate = G_Conf.samplerate; + inHeader.channels = 2; + inHeader.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; // wave only + + isLogical = true; + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +int Wave::resample(int quality, int newRate) { + + float ratio = newRate / (float) inHeader.samplerate; + int newSize = ceil(size * ratio); + if (newSize % 2 != 0) // libsndfile goes crazy with odd size in case of saving + newSize++; + + float *tmp = (float *) malloc(newSize * sizeof(float)); + if (!tmp) { + gLog("[wave] unable to allocate memory for resampling\n"); + return -1; + } + + SRC_DATA src_data; + src_data.data_in = data; + src_data.input_frames = size/2; // in frames, i.e. /2 (stereo) + src_data.data_out = tmp; + src_data.output_frames = newSize/2; // in frames, i.e. /2 (stereo) + src_data.src_ratio = ratio; + + gLog("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2); + + int ret = src_simple(&src_data, quality, 2); + if (ret != 0) { + gLog("[wave] resampling error: %s\n", src_strerror(ret)); + return 0; + } + + free(data); + data = tmp; + size = newSize; + inHeader.samplerate = newRate; + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +std::string Wave::basename() { + return gStripExt(gBasename(pathfile.c_str()).c_str()); +} + +std::string Wave::extension() { + return gGetExt(pathfile.c_str()); +} + + +/* ------------------------------------------------------------------ */ + + +void Wave::updateName(const char *n) { + std::string ext = gGetExt(pathfile.c_str()); + name = gStripExt(gBasename(n).c_str()); + pathfile = gDirname(pathfile.c_str()) + gGetSlash() + name + "." + ext; + isLogical = true; + + /* a wave with updated name must become logical, since the underlying + * file does not exist yet. */ +} diff --git a/src/core/wave.h b/src/core/wave.h new file mode 100644 index 0000000..9ce81df --- /dev/null +++ b/src/core/wave.h @@ -0,0 +1,88 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * wave + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef WAVE_H +#define WAVE_H + + +#include +#include +#include + + +class Wave { + +private: + + SNDFILE *fileIn; + SNDFILE *fileOut; + SF_INFO inHeader; + SF_INFO outHeader; + +public: + + Wave(); + ~Wave(); + + std::string pathfile; // full path + sample name + std::string name; // sample name (changeable) + + float *data; + int size; // wave size (size in stereo: size / 2) + bool isLogical; // memory only (a take) + bool isEdited; // edited via editor + + inline int rate () { return inHeader.samplerate; } + inline int channels() { return inHeader.channels; } + inline int frames () { return inHeader.frames; } + inline void rate (int v) { inHeader.samplerate = v; } + inline void channels(int v) { inHeader.channels = v; } + inline void frames (int v) { inHeader.frames = v; } + + std::string basename (); + std::string extension(); + + void updateName(const char *n); + int open (const char *f); + int readData (); + int writeData (const char *f); + void clear (); + + /* allocEmpty + * alloc an empty waveform. */ + + int allocEmpty(unsigned size); + + /* resample + * simple algorithm for one-shot resampling. */ + + int resample(int quality, int newRate); +}; + +#endif diff --git a/src/core/waveFx.cpp b/src/core/waveFx.cpp new file mode 100644 index 0000000..368bd79 --- /dev/null +++ b/src/core/waveFx.cpp @@ -0,0 +1,224 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * waveFx + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../utils/log.h" +#include "waveFx.h" +#include "channel.h" +#include "mixer.h" +#include "wave.h" + + +extern Mixer G_Mixer; + + +float wfx_normalizeSoft(Wave *w) { + float peak = 0.0f; + float abs = 0.0f; + for (int i=0; isize; i++) { // i++: both L and R samples + abs = fabs(w->data[i]); + if (abs > peak) + peak = abs; + } + + /* peak == 0.0f: don't normalize the silence + * peak > 1.0f: don't reduce the amplitude, just leave it alone */ + + if (peak == 0.0f || peak > 1.0f) + return 1.0f; + + return 1.0f / peak; +} + + +/* ------------------------------------------------------------------ */ + + +bool wfx_monoToStereo(Wave *w) { + + unsigned newSize = w->size * 2; + float *dataNew = (float *) malloc(newSize * sizeof(float)); + if (dataNew == NULL) { + gLog("[wfx] unable to allocate memory for mono>stereo conversion\n"); + return 0; + } + + for (int i=0, j=0; isize; i++) { + dataNew[j] = w->data[i]; + dataNew[j+1] = w->data[i]; + j+=2; + } + + free(w->data); + w->data = dataNew; + w->size = newSize; + w->frames(w->frames()*2); + w->channels(2); + + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +void wfx_silence(Wave *w, int a, int b) { + + /* stereo values */ + a = a * 2; + b = b * 2; + + gLog("[wfx] silencing from %d to %d\n", a, b); + + for (int i=a; idata[i] = 0.0f; + w->data[i+1] = 0.0f; + } + + w->isEdited = true; + + return; +} + + +/* ------------------------------------------------------------------ */ + + +int wfx_cut(Wave *w, int a, int b) { + a = a * 2; + b = b * 2; + + if (a < 0) a = 0; + if (b > w->size) b = w->size; + + /* create a new temp wave and copy there the original one, skipping + * the a-b range */ + + unsigned newSize = w->size-(b-a); + float *temp = (float *) malloc(newSize * sizeof(float)); + if (temp == NULL) { + gLog("[wfx] unable to allocate memory for cutting\n"); + return 0; + } + + gLog("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", a, b, newSize, newSize/2); + + for (int i=0, k=0; isize; i++) { + if (i < a || i >= b) { // left margin always included, in order to keep + temp[k] = w->data[i]; // the stereo pair + k++; + } + } + + free(w->data); + w->data = temp; + w->size = newSize; + //w->inHeader.frames -= b-a; + w->frames(w->frames() - b - a); + w->isEdited = true; + + gLog("[wfx] cutting done\n"); + + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +int wfx_trim(Wave *w, int a, int b) { + a = a * 2; + b = b * 2; + + if (a < 0) a = 0; + if (b > w->size) b = w->size; + + int newSize = b - a; + float *temp = (float *) malloc(newSize * sizeof(float)); + if (temp == NULL) { + gLog("[wfx] unable to allocate memory for trimming\n"); + return 0; + } + + gLog("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a); + + for (int i=a, k=0; idata[i]; + + free(w->data); + w->data = temp; + w->size = newSize; + //w->inHeader.frames = b-a; + w->frames(b - a); + w->isEdited = true; + + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +void wfx_fade(Wave *w, int a, int b, int type) { + + float m = type == 0 ? 0.0f : 1.0f; + float d = 1.0f/(float)(b-a); + if (type == 1) + d = -d; + + a *= 2; + b *= 2; + + for (int i=a; idata[i] *= m; + w->data[i+1] *= m; + m += d; + } +} + + +/* ------------------------------------------------------------------ */ + + +void wfx_smooth(Wave *w, int a, int b) { + + int d = 32; // 64 if stereo data + + /* do nothing if fade edges (both of 32 samples) are > than selected + * portion of wave. d*2 => count both edges, (b-a)*2 => stereo + * values. */ + + if (d*2 > (b-a)*2) { + gLog("[WFX] selection is too small, nothing to do\n"); + return; + } + + wfx_fade(w, a, a+d, 0); + wfx_fade(w, b-d, b, 1); +} diff --git a/src/core/waveFx.h b/src/core/waveFx.h new file mode 100644 index 0000000..ebdef4a --- /dev/null +++ b/src/core/waveFx.h @@ -0,0 +1,59 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * waveFx + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef WAVEFX_H +#define WAVEFX_H + + +/* normalizeSoft + * normalize the wave by returning the dB value for the boost volume. It + * doesn't deal with data in memory. */ + +float wfx_normalizeSoft(class Wave *w); + +bool wfx_monoToStereo(class Wave *w); + +void wfx_silence(class Wave *w, int a, int b); + +int wfx_cut(class Wave *w, int a, int b); + +int wfx_trim(class Wave *w, int a, int b); + +/* fade + * fade in or fade out selection. Fade In = type 0, Fade Out = type 1 */ + +void wfx_fade(class Wave *w, int a, int b, int type); + +/* smooth + * smooth edges of selection. */ + +void wfx_smooth(class Wave *w, int a, int b); + + +#endif diff --git a/src/dataStorage.cpp b/src/dataStorage.cpp deleted file mode 100644 index 43729e8..0000000 --- a/src/dataStorage.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * dataStorage - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include -#include "dataStorage.h" -#include "const.h" -#include "log.h" - - -std::string DataStorage::getValue(const char *in) { - - /* on each call reset the pointe to the beginning of the file. Not so - * good but necessary if you want to pick up random values from the - * file. */ - - fseek(fp, 0L, SEEK_SET); - std::string out = ""; - - while (!feof(fp)) { - - char buffer[MAX_LINE_LEN]; - if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) { - gLog("[PATCH] get_value error (key=%s)\n", in); - return ""; - } - - if (buffer[0] == '#') - continue; - - unsigned len = strlen(in); - if (strncmp(buffer, in, len) == 0) { - - for (unsigned i=len+1; i. - * - * ------------------------------------------------------------------ */ - - -#ifndef __DATA_STORAGE_H__ -#define __DATA_STORAGE_H__ - -#include -#include -#include - -#define MAX_LINE_LEN 1024 - - -class DataStorage { - -protected: - - FILE *fp; - std::string getValue(const char *in); -}; - -#endif diff --git a/src/deps/rtaudio-mod/Makefile.in b/src/deps/rtaudio-mod/Makefile.in new file mode 100644 index 0000000..89cacdc --- /dev/null +++ b/src/deps/rtaudio-mod/Makefile.in @@ -0,0 +1,75 @@ +### Do not edit -- Generated by 'configure --with-whatever' from Makefile.in +### RtAudio library Makefile + +RM = /bin/rm +LN = /bin/ln + +OBJECTS = RtAudio.o @objects@ + +LIBNAME = librtaudio +STATIC = $(LIBNAME).a +SHARED = @sharedlib@ +RELEASE = 4.1.1 +MAJOR = 4 +LIBRARIES = $(STATIC) $(SHARED) + +CC = @CXX@ +AR = @AR@ +RANLIB = @RANLIB@ + +DEFS = @CPPFLAGS@ +CFLAGS = @CXXFLAGS@ -Iinclude -fPIC + +PREFIX = @prefix@ + +all : $(LIBRARIES) + +tests: + cd tests && $(MAKE) all + +$(LIBRARIES): $(OBJECTS) + $(AR) ruv $(STATIC) $(OBJECTS) + ranlib $(STATIC) + $(CC) -fPIC @libflags@ $(OBJECTS) @LIBS@ + $(LN) -sf @sharedname@ $(SHARED) + $(LN) -sf @sharedname@ $(SHARED).$(MAJOR) + +%.o : %.cpp + $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@ + +%.o : include/%.cpp + $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@ + +install: + install --mode=755 $(STATIC) $(PREFIX)/lib/ + install --mode=755 @sharedname@ $(PREFIX)/lib/ + $(LN) -sf @sharedname@ $(PREFIX)/lib/$(SHARED) + $(LN) -sf @sharedname@ $(PREFIX)/lib/$(SHARED).$(MAJOR) + install --mode=644 $(LIBNAME).pc $(PREFIX)/lib/pkgconfig + install --mode=644 RtAudio.h $(PREFIX)/include/ + install --mode=755 rtaudio-config $(PREFIX)/bin/ + +uninstall: + -@rm -vf $(patsubst %,$(PREFIX)/lib/%, $(LIBRARIES) $(SHARED).$(MAJOR) $(SHARED).$(RELEASE)) + -@rm -vf $(PREFIX)/lib/pkgconfig/$(LIBNAME).pc + -@rm -vf $(PREFIX)/bin/rtaudio-config + +clean : + $(RM) -f $(LIBRARIES) @sharedname@ $(SHARED)* + $(RM) -f $(OBJECTS) + $(RM) -f *~ + cd tests && $(MAKE) clean + +distclean: + $(RM) -f $(LIBRARIES) @sharedname@ $(SHARED)* + $(RM) -f $(OBJECTS) + $(RM) -f *~ + $(RM) -rf config.log config.status autom4te.cache Makefile rtaudio-config $(LIBNAME).pc + cd tests && $(MAKE) distclean + +strip : + strip $(LIBRARIES) + ranlib $(LIBRARIES) + cd tests && $(MAKE) strip + +.PHONY: clean distclean strip install uninstall diff --git a/src/deps/rtaudio-mod/RtAudio.cpp b/src/deps/rtaudio-mod/RtAudio.cpp new file mode 100644 index 0000000..5716c59 --- /dev/null +++ b/src/deps/rtaudio-mod/RtAudio.cpp @@ -0,0 +1,10145 @@ +/************************************************************************/ +/*! \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 WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ + + RtAudio: realtime audio i/o C++ classes + Copyright (c) 2001-2014 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 4.1.1 + +#include "RtAudio.h" +#include +#include +#include +#include + +// 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) +#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 ) throw() +{ + return RTAUDIO_VERSION; +} + +void RtAudio :: getCompiledApi( std::vector &apis ) throw() +{ + apis.clear(); + + // The order here will control the order of RtAudio's API search in + // the constructor. +#if defined(__UNIX_JACK__) + apis.push_back( UNIX_JACK ); +#endif +#if defined(__LINUX_ALSA__) + apis.push_back( LINUX_ALSA ); +#endif +#if defined(__LINUX_PULSE__) + apis.push_back( LINUX_PULSE ); +#endif +#if defined(__LINUX_OSS__) + apis.push_back( LINUX_OSS ); +#endif +#if defined(__WINDOWS_ASIO__) + apis.push_back( WINDOWS_ASIO ); +#endif +#if defined(__WINDOWS_WASAPI__) + apis.push_back( WINDOWS_WASAPI ); +#endif +#if defined(__WINDOWS_DS__) + apis.push_back( WINDOWS_DS ); +#endif +#if defined(__MACOSX_CORE__) + apis.push_back( MACOSX_CORE ); +#endif +#if defined(__RTAUDIO_DUMMY__) + apis.push_back( RTAUDIO_DUMMY ); +#endif +} + +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; igetDeviceCount() ) 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() throw() +{ + 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; +} + +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 ) { + 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; imBuffers[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; imBuffers[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 minimumRate ) minimumRate = rangeList[i].mMinimum; + if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; + } + } + + if ( haveValueRange ) { + for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) + info.sampleRates.push_back( 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; ixrun[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; iStreammBuffers[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; iStreammBuffers[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; iStreammBuffers[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 > physicalFormats; + formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger; + physicalFormats.push_back( std::pair( 32, formatFlags ) ); + formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; + physicalFormats.push_back( std::pair( 32, formatFlags ) ); + physicalFormats.push_back( std::pair( 24, formatFlags ) ); // 24-bit packed + formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh ); + physicalFormats.push_back( std::pair( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low + formatFlags |= kAudioFormatFlagIsAlignedHigh; + physicalFormats.push_back( std::pair( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high + formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; + physicalFormats.push_back( std::pair( 16, formatFlags ) ); + physicalFormats.push_back( std::pair( 8, formatFlags ) ); + + bool setPhysicalFormat = false; + for( unsigned int i=0; iflags & 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 ( stream_.state == STREAM_RUNNING ) + AudioDeviceStop( handle->id[0], callbackHandler ); +#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) + AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] ); +#else + // deprecated in favor of AudioDeviceDestroyIOProcID() + AudioDeviceRemoveIOProc( handle->id[0], callbackHandler ); +#endif + } + + if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { + if ( stream_.state == STREAM_RUNNING ) + AudioDeviceStop( handle->id[1], callbackHandler ); +#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) + AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] ); +#else + // deprecated in favor of AudioDeviceDestroyIOProcID() + 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; + } + + OSStatus result = noErr; + CoreHandle *handle = (CoreHandle *) stream_.apiHandle; + if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { + + result = AudioDeviceStart( handle->id[0], callbackHandler ); + 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] ) ) { + + result = AudioDeviceStart( handle->id[1], callbackHandler ); + 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 + } + + result = AudioDeviceStop( handle->id[0], callbackHandler ); + 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] ) ) { + + result = AudioDeviceStop( handle->id[1], callbackHandler ); + 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; inStreams[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; imBuffers[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; inStreams[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; idrainCounter ) { + 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; imBuffers[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; inStreams[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 +#include +#include + +// 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; } +}; + +/* --- Monocasual hack ---------------------------------------------- */ +#ifdef __linux__ +void *RtApi :: __HACK__getJackClient() { + JackHandle *handle = (JackHandle *) stream_.apiHandle; + return (void*) handle->client; +} +#endif +/* ------------------------------------------------------------------ */ + +static void jackSilentError( const char * ) {}; + +RtApiJack :: RtApiJack() +{ + // 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, NULL, 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, NULL, 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.sampleRates.push_back( jack_get_sample_rate( client ) ); + + // 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(), NULL, 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(), NULL, 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, NULL, 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; + } + + // Count the available ports containing the client name as device + // channels. Jack "input ports" equal RtAudio output channels. + unsigned int nChannels = 0; + unsigned long flag = JackPortIsInput; + if ( mode == INPUT ) flag = JackPortIsOutput; + ports = jack_get_ports( client, deviceName.c_str(), NULL, 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(), NULL, 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 *) &handle ); + jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); + } + + // Register our ports. + char label[64]; + if ( mode == OUTPUT ) { + for ( unsigned int i=0; iports[0][i] = jack_port_register( handle->client, (const char *)label, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + } + } + else { + for ( unsigned int i=0; iports[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 ); + + 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; + } + + 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 ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { + result = 1; + ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, 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; iclient, 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 ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { + result = 1; + ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, 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; iclient, 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; iports[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; iports[0][i], (jack_nframes_t) nframes ); + memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); + } + } + else { // no buffer conversion + for ( unsigned int i=0; iports[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; iports[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; iports[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 + +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 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; isaveDeviceInfo(); + + 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; + } + } + + // Check the device channel count. + long inputChannels, outputChannels; + result = ASIOGetChannels( &inputChannels, &outputChannels ); + if ( result != ASE_OK ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) || + ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ")."; + errorText_ = errorStream_.str(); + return FAILURE; + } + 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 ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ")."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + // Get the current sample rate + ASIOSampleRate currentRate; + result = ASIOGetSampleRate( ¤tRate ); + if ( result != ASE_OK ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + // Set the sample rate only if necessary + if ( currentRate != sampleRate ) { + result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); + if ( result != ASE_OK ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ")."; + errorText_ = errorStream_.str(); + return FAILURE; + } + } + + // 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 ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + // 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 ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + // 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 ) { + drivers.removeCurrentDriver(); + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + 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; + } + + if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) { + drivers.removeCurrentDriver(); + errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!"; + return FAILURE; + } + + 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. + AsioHandle *handle = (AsioHandle *) stream_.apiHandle; + if ( handle == 0 ) { + try { + handle = new AsioHandle; + } + catch ( std::bad_alloc& ) { + //if ( handle == NULL ) { + drivers.removeCurrentDriver(); + errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory."; + return FAILURE; + } + 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. + long inputLatency, outputLatency; + if ( mode == INPUT && stream_.mode == OUTPUT ) { + ASIODisposeBuffers(); + if ( handle->bufferInfos ) free( handle->bufferInfos ); + } + + // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. + bool buffersAllocated = false; + 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; iisInput = ASIOFalse; + infos->channelNum = i + stream_.channelOffset[0]; + infos->buffers[0] = infos->buffers[1] = 0; + } + for ( i=0; iisInput = ASIOTrue; + infos->channelNum = i + stream_.channelOffset[1]; + infos->buffers[0] = infos->buffers[1] = 0; + } + + // 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 ) { + errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers."; + errorText_ = errorStream_.str(); + goto error; + } + buffersAllocated = true; + + // 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 ( 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_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory."; + goto error; + } + } + } + + stream_.sampleRate = sampleRate; + stream_.device[mode] = device; + stream_.state = STREAM_STOPPED; + asioCallbackInfo = &stream_.callbackInfo; + stream_.callbackInfo.object = (void *) this; + if ( stream_.mode == OUTPUT && mode == INPUT ) + // We had already set up an output stream. + stream_.mode = DUPLEX; + else + stream_.mode = mode; + + // Determine device latencies + 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 ( buffersAllocated ) + ASIODisposeBuffers(); + drivers.removeCurrentDriver(); + + 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; + } + + 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; + } + + 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; ibufferInfos[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; ibufferInfos[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; ibufferInfos[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; ibufferInfos[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; ibufferInfos[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 , 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 +#include +#include +#include + +//============================================================================= + +#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() { + delete buffer_; + } + + // sets the length of the internal ring buffer + void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) { + delete 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_; + } + + // "in" index can end on the "out" index but cannot begin at it + 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_; + } + + // "out" index can begin at and end on 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 convertBufferWasapi function is used to perform this conversion +// between HwIn->UserIn and UserOut->HwOut during the stream callback loop. +// This sample rate converter favors speed over quality, and works best with conversions between +// one rate and its multiple. +void convertBufferWasapi( char* outBuffer, + const char* inBuffer, + const unsigned int& channelCount, + const unsigned int& inSampleRate, + const unsigned int& outSampleRate, + const unsigned int& inSampleCount, + unsigned int& outSampleCount, + const RtAudioFormat& format ) +{ + // calculate the new outSampleCount and relative sampleStep + float sampleRatio = ( float ) outSampleRate / inSampleRate; + float sampleStep = 1.0f / sampleRatio; + float inSampleFraction = 0.0f; + + outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio ); + + // frame-by-frame, copy each relative input sample into it's corresponding output sample + for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ ) + { + unsigned int inSample = ( unsigned int ) inSampleFraction; + + switch ( format ) + { + case RTAUDIO_SINT8: + memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) ); + break; + case RTAUDIO_SINT16: + memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) ); + break; + case RTAUDIO_SINT24: + memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) ); + break; + case RTAUDIO_SINT32: + memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) ); + break; + case RTAUDIO_FLOAT32: + memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) ); + break; + case RTAUDIO_FLOAT64: + memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) ); + break; + } + + // jump to next in sample + inSampleFraction += sampleStep; + } +} + +//----------------------------------------------------------------------------- + +// 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 ( FAILED( hr ) ) { + errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator"; + error( RtAudioError::DRIVER_ERROR ); + } +} + +//----------------------------------------------------------------------------- + +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; + + // 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::wstring deviceName; + 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; + } + + deviceName = defaultDeviceNameProp.pwszVal; + defaultDeviceName = std::string( deviceName.begin(), deviceName.end() ); + + // 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; + } + + deviceName = deviceNameProp.pwszVal; + info.name = std::string( deviceName.begin(), deviceName.end() ); + + // 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] ); + } + + // 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; + } + + // 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 ); + + // stop capture client if applicable + if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) { + HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop(); + if ( FAILED( hr ) ) { + errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream."; + error( RtAudioError::DRIVER_ERROR ); + return; + } + } + + // stop render client if applicable + if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) { + HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop(); + if ( FAILED( hr ) ) { + errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream."; + error( RtAudioError::DRIVER_ERROR ); + return; + } + } + + // 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 ); + } + + // stop capture client if applicable + if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) { + HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop(); + if ( FAILED( hr ) ) { + errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream."; + error( RtAudioError::DRIVER_ERROR ); + return; + } + } + + // stop render client if applicable + if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) { + HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop(); + if ( FAILED( hr ) ) { + errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream."; + error( RtAudioError::DRIVER_ERROR ); + return; + } + } + + // 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; + } + + // determine whether index falls within capture or render 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 device audio client."; + goto Exit; + } + + hr = captureAudioClient->GetMixFormat( &deviceFormat ); + if ( FAILED( hr ) ) { + errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format."; + goto Exit; + } + + stream_.nDeviceChannels[mode] = deviceFormat->nChannels; + captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); + } + else { + if ( mode != OUTPUT ) { + errorType = RtAudioError::INVALID_USE; + errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device."; + goto Exit; + } + + // retrieve renderAudioClient from devicePtr + IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; + + 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 device audio client."; + goto Exit; + } + + hr = renderAudioClient->GetMixFormat( &deviceFormat ); + if ( FAILED( hr ) ) { + errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve 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 != stream_.nDeviceChannels ) + 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, 0 ); + + // 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; + + // declare local stream variables + RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback; + BYTE* streamBuffer = NULL; + unsigned long captureFlags = 0; + unsigned int bufferFrameCount = 0; + unsigned int numFramesPadding = 0; + unsigned int convBufferSize = 0; + bool callbackPushed = false; + 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; + + errorText_.clear(); + 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 ) 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; + } + + captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate ); + + // initialize capture stream according to desire buffer size + float desiredBufferSize = stream_.bufferSize * captureSrRatio; + REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec ); + + if ( !captureClient ) { + hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + desiredBufferPeriod, + desiredBufferPeriod, + 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; + } + + // 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 )->captureClient = captureClient; + ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent; + } + + 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 ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT]; + inBufferSize *= stream_.nDeviceChannels[INPUT]; + + // set captureBuffer size + captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) ); + + // 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; + } + } + + // 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; + } + + renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate ); + + // initialize render stream according to desire buffer size + float desiredBufferSize = stream_.bufferSize * renderSrRatio; + REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec ); + + if ( !renderClient ) { + hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + desiredBufferPeriod, + desiredBufferPeriod, + 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; + } + + 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 ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT]; + outBufferSize *= stream_.nDeviceChannels[OUTPUT]; + + // set renderBuffer size + renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) ); + + // 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; + } + } + + if ( stream_.mode == INPUT ) { + convBuffSize = ( size_t ) ( 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 ) ( 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 ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), + ( size_t ) ( 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] ) ); + } + + convBuffer = ( char* ) malloc( convBuffSize ); + stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize ); + 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 ) { + // Pull callback buffer from inputBuffer + callbackPulled = captureBuffer.pullBuffer( convBuffer, + ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT], + stream_.deviceFormat[INPUT] ); + + if ( callbackPulled ) { + // Convert callback buffer to user sample rate + convertBufferWasapi( stream_.deviceBuffer, + convBuffer, + stream_.nDeviceChannels[INPUT], + captureFormat->nSamplesPerSec, + stream_.sampleRate, + ( unsigned int ) ( stream_.bufferSize * captureSrRatio ), + convBufferSize, + stream_.deviceFormat[INPUT] ); + + 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 ); + + // 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 ( stream_.doConvertBuffer[OUTPUT] ) { + // Convert callback buffer to stream format + convertBuffer( stream_.deviceBuffer, + stream_.userBuffer[OUTPUT], + stream_.convertInfo[OUTPUT] ); + + } + + // Convert callback buffer to stream sample rate + convertBufferWasapi( convBuffer, + stream_.deviceBuffer, + stream_.nDeviceChannels[OUTPUT], + stream_.sampleRate, + renderFormat->nSamplesPerSec, + stream_.bufferSize, + convBufferSize, + stream_.deviceFormat[OUTPUT] ); + + // 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( 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 ) { + callbackPulled = false; + } + + // tick stream time + RtApi::tickStreamTime(); + } + +Exit: + // clean up + CoTaskMemFree( captureFormat ); + CoTaskMemFree( renderFormat ); + + free ( convBuffer ); + + CoUninitialize(); + + // update stream state + stream_.state = STREAM_STOPPED; + + if ( errorText_.empty() ) + return; + else + 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 +#include +#include + +#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* 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 ( coInitialized_ ) CoUninitialize(); // balanced call. + if ( stream_.state != STREAM_CLOSED ) closeStream(); +} + +// 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 indices; + for ( unsigned int i=0; i(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= (unsigned int) outCaps.dwMinSecondarySampleRate && + SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) + info.sampleRates.push_back( 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 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 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; + } + + 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(); + 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(); + 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(); + 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(); + error( RtAudioError::SYSTEM_ERROR ); + return; + } + if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break; + Sleep( 1 ); + } + + //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); + + handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; + if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; + handle->bufferPointer[1] = safeReadPointer; + } + else if ( stream_.mode == OUTPUT ) { + + // Set the proper nextWritePosition after initial startup. + LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; + result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); + if ( FAILED( result ) ) { + errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; + errorText_ = errorStream_.str(); + 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; idsBufferSize[0]; + nextWritePointer = handle->bufferPointer[0]; + + DWORD endWrite, leadPointer; + while ( true ) { + // Find out where the read and "safe write" pointers are. + result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); + if ( FAILED( result ) ) { + errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; + errorText_ = errorStream_.str(); + 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(); + 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(); + error( RtAudioError::SYSTEM_ERROR ); + return; + } + nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; + handle->bufferPointer[0] = nextWritePointer; + } + + // Don't bother draining input + if ( handle->drainCounter ) { + handle->drainCounter++; + goto unlock; + } + + if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { + + // Setup parameters. + if ( stream_.doConvertBuffer[1] ) { + buffer = stream_.deviceBuffer; + bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1]; + bufferBytes *= formatBytes( stream_.deviceFormat[1] ); + } + else { + buffer = stream_.userBuffer[1]; + bufferBytes = stream_.bufferSize * stream_.nUserChannels[1]; + bufferBytes *= formatBytes( stream_.userFormat ); + } + + LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; + long nextReadPointer = handle->bufferPointer[1]; + DWORD dsBufferSize = handle->dsBufferSize[1]; + + // Find out where the write and "safe read" pointers are. + result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); + if ( FAILED( result ) ) { + errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; + errorText_ = errorStream_.str(); + error( RtAudioError::SYSTEM_ERROR ); + return; + } + + if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset + DWORD endRead = nextReadPointer + bufferBytes; + + // Handling depends on whether we are INPUT or DUPLEX. + // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode, + // then a wait here will drag the write pointers into the forbidden zone. + // + // In DUPLEX mode, rather than wait, we will back off the read pointer until + // it's in a safe position. This causes dropouts, but it seems to be the only + // practical way to sync up the read and write pointers reliably, given the + // the very complex relationship between phase and increment of the read and write + // pointers. + // + // In order to minimize audible dropouts in DUPLEX mode, we will + // provide a pre-roll period of 0.5 seconds in which we return + // zeros from the read buffer while the pointers sync up. + + if ( stream_.mode == DUPLEX ) { + if ( safeReadPointer < endRead ) { + if ( duplexPrerollBytes <= 0 ) { + // Pre-roll time over. Be more agressive. + int adjustment = endRead-safeReadPointer; + + handle->xrun[1] = true; + // Two cases: + // - large adjustments: we've probably run out of CPU cycles, so just resync exactly, + // and perform fine adjustments later. + // - small adjustments: back off by twice as much. + if ( adjustment >= 2*bufferBytes ) + nextReadPointer = safeReadPointer-2*bufferBytes; + else + nextReadPointer = safeReadPointer-bufferBytes-adjustment; + + if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; + + } + else { + // In pre=roll time. Just do it. + nextReadPointer = safeReadPointer - bufferBytes; + while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; + } + endRead = nextReadPointer + bufferBytes; + } + } + else { // mode == INPUT + while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) { + // See comments for playback. + double millis = (endRead - safeReadPointer) * 1000.0; + millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate); + if ( millis < 1.0 ) millis = 1.0; + Sleep( (DWORD) millis ); + + // Wake up and find out where we are now. + result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); + if ( FAILED( result ) ) { + errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; + errorText_ = errorStream_.str(); + 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(); + 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(); + 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; jobject; + bool* isRunning = &info->isRunning; + + while ( *isRunning == true ) { + object->callbackEvent(); + } + + _endthreadex( 0 ); + return 0; +} + +#include "tchar.h" + +static std::string convertTChar( LPCTSTR name ) +{ +#if defined( UNICODE ) || defined( _UNICODE ) + int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL); + std::string s( length-1, '\0' ); + WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL); +#else + std::string s( name ); +#endif + + return s; +} + +static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, + LPCTSTR description, + LPCTSTR /*module*/, + LPVOID lpContext ) +{ + struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext; + std::vector& 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 = convertTChar( 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 +#include + + // 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; + + // 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 ) { + 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: + 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; + + // 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::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: + snd_ctl_close( chandle ); + snd_card_next( &card ); + } + + result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); + if ( result == 0 ) { + if ( nDevices == device ) { + strcpy( name, "default" ); + goto foundDevice; + } + nDevices++; + } + + if ( nDevices == 0 ) { + errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!"; + error( RtAudioError::INVALID_USE ); + return info; + } + + if ( device >= nDevices ) { + errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!"; + error( RtAudioError::INVALID_USE ); + return info; + } + + foundDevice: + + // If a stream is already open, we cannot probe the stream devices. + // Thus, use the saved results. + if ( stream_.state != STREAM_CLOSED && + ( stream_.device[0] == device || stream_.device[1] == device ) ) { + snd_ctl_close( chandle ); + if ( device >= devices_.size() ) { + errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened."; + error( RtAudioError::WARNING ); + return info; + } + return devices_[ device ]; + } + + int openMode = SND_PCM_ASYNC; + snd_pcm_stream_t stream; + snd_pcm_info_t *pcminfo; + snd_pcm_info_alloca( &pcminfo ); + snd_pcm_t *phandle; + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca( ¶ms ); + + // First try for playback unless default device (which has subdev -1) + stream = SND_PCM_STREAM_PLAYBACK; + snd_pcm_info_set_stream( pcminfo, stream ); + if ( subdevice != -1 ) { + snd_pcm_info_set_device( pcminfo, subdevice ); + snd_pcm_info_set_subdevice( pcminfo, 0 ); + + result = snd_ctl_pcm_info( chandle, pcminfo ); + if ( result < 0 ) { + // Device probably doesn't support playback. + goto captureProbe; + } + } + + result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK ); + if ( result < 0 ) { + errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + goto captureProbe; + } + + // The device is open ... fill the parameter structure. + result = snd_pcm_hw_params_any( phandle, params ); + if ( result < 0 ) { + snd_pcm_close( phandle ); + errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + goto captureProbe; + } + + // Get output channel information. + unsigned int value; + result = snd_pcm_hw_params_get_channels_max( params, &value ); + if ( result < 0 ) { + snd_pcm_close( phandle ); + errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + goto captureProbe; + } + info.outputChannels = value; + snd_pcm_close( phandle ); + + captureProbe: + stream = SND_PCM_STREAM_CAPTURE; + snd_pcm_info_set_stream( pcminfo, stream ); + + // Now try for capture unless default device (with subdev = -1) + if ( subdevice != -1 ) { + result = snd_ctl_pcm_info( chandle, pcminfo ); + snd_ctl_close( chandle ); + if ( result < 0 ) { + // Device probably doesn't support capture. + if ( info.outputChannels == 0 ) return info; + goto probeParameters; + } + } + else + snd_ctl_close( chandle ); + + result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); + if ( result < 0 ) { + errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + if ( info.outputChannels == 0 ) return info; + goto probeParameters; + } + + // The device is open ... fill the parameter structure. + result = snd_pcm_hw_params_any( phandle, params ); + if ( result < 0 ) { + snd_pcm_close( phandle ); + errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + if ( info.outputChannels == 0 ) return info; + goto probeParameters; + } + + result = snd_pcm_hw_params_get_channels_max( params, &value ); + if ( result < 0 ) { + snd_pcm_close( phandle ); + errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + if ( info.outputChannels == 0 ) return info; + goto probeParameters; + } + info.inputChannels = value; + snd_pcm_close( phandle ); + + // If device opens for both playback and capture, we determine the channels. + if ( info.outputChannels > 0 && info.inputChannels > 0 ) + info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; + + // ALSA doesn't provide default devices so we'll use the first available one. + if ( device == 0 && info.outputChannels > 0 ) + info.isDefaultOutput = true; + if ( device == 0 && info.inputChannels > 0 ) + info.isDefaultInput = true; + + probeParameters: + // At this point, we just need to figure out the supported data + // formats and sample rates. We'll proceed by opening the device in + // the direction with the maximum number of channels, or playback if + // they are equal. This might limit our sample rate options, but so + // be it. + + if ( info.outputChannels >= info.inputChannels ) + stream = SND_PCM_STREAM_PLAYBACK; + else + stream = SND_PCM_STREAM_CAPTURE; + snd_pcm_info_set_stream( pcminfo, stream ); + + result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); + if ( result < 0 ) { + errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + return info; + } + + // The device is open ... fill the parameter structure. + result = snd_pcm_hw_params_any( phandle, params ); + if ( result < 0 ) { + snd_pcm_close( phandle ); + errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + error( RtAudioError::WARNING ); + return info; + } + + // Test our discrete set of sample rate values. + info.sampleRates.clear(); + for ( unsigned int i=0; i= 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; iflags & 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" ); + goto foundDevice; + } + nDevices++; + } + + 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 (eg: NetBSD 1.6.x with GNU Pthread) + if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { + // We previously attempted to increase the audio callback priority + // to SCHED_RR here via the attributes. However, while no errors + // were reported in doing so, it did not work. So, now this is + // done in the alsaCallbackHandler function. + stream_.callbackInfo.doRealtime = true; + 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; + stream_.callbackInfo.priority = priority; + } +#endif + + stream_.callbackInfo.isRunning = true; + result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo ); + pthread_attr_destroy( &attr ); + 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 ); + + 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; ixrun[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; ixrun[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 { + 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 (eg: NetBSD 1.6.x with GNU Pthread) + if ( &info->doRealtime ) { + pthread_t tID = pthread_self(); // ID of this thread + sched_param prio = { info->priority }; // scheduling priority of thread + pthread_setschedparam( tID, SCHED_RR, &prio ); + } +#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 +#include +#include + +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.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32; + + return info; +} + +static void *pulseaudio_callback( void * user ) +{ + CallbackInfo *cbi = static_cast( user ); + RtApiPulse *context = static_cast( cbi->object ); + volatile bool *isRunning = &cbi->isRunning; + + while ( *isRunning ) { + pthread_testcancel(); + context->callbackEvent(); + } + + pthread_exit( NULL ); +} + +void RtApiPulse::closeStream( void ) +{ + PulseAudioHandle *pah = static_cast( 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( 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( 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 ); + + 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( 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->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( 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->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; + + // 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( stream_.apiHandle ); + + int error; + if ( !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, "RtAudio", 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_.callbackInfo.isRunning = true; + if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) { + errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread."; + goto error; + } + } + + stream_.state = STREAM_STOPPED; + return true; + + 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; + } + + return FAILURE; +} + +//******************** End of __LINUX_PULSE__ *********************// +#endif + +#if defined(__LINUX_OSS__) + +#include +#include +#include +#include +#include +#include +#include + +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; + if ( mask & AFMT_FLOAT ) + info.nativeFormats |= RTAUDIO_FLOAT32; + 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= (int) SAMPLE_RATES[k] ) + info.sampleRates.push_back( 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 - 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 (eg: NetBSD 1.6.x with GNU Pthread) + if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { + 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; + pthread_attr_setschedparam( &attr, ¶m ); + pthread_attr_setschedpolicy( &attr, SCHED_RR ); + } + 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 ) { + 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; + } + + 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 ); + + 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; iid[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; + + 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 0 ) { + if ( stream_.deviceInterleaved[mode] ) { + if ( mode == OUTPUT ) { + for ( int k=0; k> 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> 8); + } + in += info.inJump; + out += info.outJump; + } + } + else if (info.inFormat == RTAUDIO_SINT32) { + Int32 *in = (Int32 *)inBuffer; + for (unsigned int i=0; i> 16) & 0x0000ffff); + } + in += info.inJump; + out += info.outJump; + } + } + else if (info.inFormat == RTAUDIO_FLOAT32) { + Float32 *in = (Float32 *)inBuffer; + for (unsigned int i=0; i> 8) & 0x00ff); + } + in += info.inJump; + out += info.outJump; + } + } + else if (info.inFormat == RTAUDIO_SINT24) { + Int24 *in = (Int24 *)inBuffer; + for (unsigned int i=0; i> 16); + } + in += info.inJump; + out += info.outJump; + } + } + else if (info.inFormat == RTAUDIO_SINT32) { + Int32 *in = (Int32 *)inBuffer; + for (unsigned int i=0; i> 24) & 0x000000ff); + } + in += info.inJump; + out += info.outJump; + } + } + else if (info.inFormat == RTAUDIO_FLOAT32) { + Float32 *in = (Float32 *)inBuffer; + for (unsigned int i=0; i>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 ) +{ + register char val; + register char *ptr; + + ptr = buffer; + if ( format == RTAUDIO_SINT16 ) { + for ( unsigned int i=0; i +#include + +/* --- Monocasual hack ---------------------------------------------- */ +#if defined(__linux__) +#include +#endif +/* ------------------------------------------------------------------ */ + +#include +#include + +/*! \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). + + 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 Direct Sound, 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. +*/ +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). + +/*! \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). + + 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 RtAudioError : public std::exception +{ + 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 ) throw() : message_(message), type_(type) {} + + //! The destructor. + virtual ~RtAudioError( void ) throw() {} + + //! Prints thrown error message to stderr. + virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } + + //! Returns the thrown error message type. + virtual const Type& getType(void) const throw() { return type_; } + + //! Returns the thrown error message string. + virtual const std::string& getMessage(void) const throw() { return message_; } + + //! Returns the thrown error message as a c-style string. + virtual const char* what( void ) const throw() { return message_.c_str(); } + + protected: + std::string message_; + 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 +{ + 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 Direct Sound API. */ + RTAUDIO_DUMMY /*!< A compilable but non-functional API. */ + }; + + //! 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 sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ + RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ + + // Default constructor. + DeviceInfo() + :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), + isDefaultOutput(false), isDefaultInput(false), 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 Direct Sound, 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 ) throw(); + + //! 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 &apis ) throw(); + + //! 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() throw(); + + //! Returns the audio API specifier for the current instance of RtAudio. + RtAudio::Api getCurrentApi( void ) throw(); + + //! 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 ) throw(); + + //! 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 ) throw(); + + //! 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 ) throw(); + + //! 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 ) throw(); + + //! 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 throw(); + + //! Returns true if the stream is running and false if it is stopped or not open. + bool isStreamRunning( void ) const throw(); + + //! 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 ) throw(); + +/* --- Monocasual hack ---------------------------------------------- */ + //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 + #include + + 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 + + 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) {} +}; + +// **************************************************************** // +// +// 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 S24& v ) { *this = v; } + 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 +#endif + +#include + +class RtApi +{ +public: + +/* --- Monocasual hack ---------------------------------------------- */ +#ifdef __linux__ + void *__HACK__getJackClient(); +#endif +/* ------------------------------------------------------------------ */ + + 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 inOffset; + std::vector 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 ) throw() { return rtapi_->getCurrentApi(); } +inline unsigned int RtAudio :: getDeviceCount( void ) throw() { return rtapi_->getDeviceCount(); } +inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); } +inline unsigned int RtAudio :: getDefaultInputDevice( void ) throw() { return rtapi_->getDefaultInputDevice(); } +inline unsigned int RtAudio :: getDefaultOutputDevice( void ) throw() { return rtapi_->getDefaultOutputDevice(); } +inline void RtAudio :: closeStream( void ) throw() { 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 throw() { return rtapi_->isStreamOpen(); } +inline bool RtAudio :: isStreamRunning( void ) const throw() { 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 ) throw() { rtapi_->showWarnings( value ); } + +// RtApi Subclass prototypes. + +#if defined(__MACOSX_CORE__) + +#include + +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 ); + long getStreamLatency( 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 ); + long getStreamLatency( 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 ); +}; + +#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 ); + long getStreamLatency( 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 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 ); + long getStreamLatency( 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 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(); + ~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 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 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-mod/config/config.guess b/src/deps/rtaudio-mod/config/config.guess new file mode 100644 index 0000000..313be34 --- /dev/null +++ b/src/deps/rtaudio-mod/config/config.guess @@ -0,0 +1,1371 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2004-02-26' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 99, 2000 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break + fi + done + rm -f $dummy.c $dummy.o $dummy.rel + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 8/24/94.) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # Netbsd (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + case "${HPUX_REV}" in + 11.[0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + esac ;; + esac + fi ;; + esac + if [ "${HP_ARCH}" = "" ]; then + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + cat >$dummy.c < /* for printf() prototype */ +int main (int argc, char *argv[]) { +#else +int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __MIPSEB__ + printf ("%s-unknown-linux-gnu\n", argv[1]); +#endif +#ifdef __MIPSEL__ + printf ("%sel-unknown-linux-gnu\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + ;; + ppc:Linux:*:*) + # Determine Lib Version + cat >$dummy.c < +#if defined(__GLIBC__) +extern char __libc_version[]; +extern char __libc_release[]; +#endif +main(argc, argv) + int argc; + char *argv[]; +{ +#if defined(__GLIBC__) + printf("%s %s\n", __libc_version, __libc_release); +#else + printf("unknown\n"); +#endif + return 0; +} +EOF + LIBC="" + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null + if test "$?" = 0 ; then + ./$dummy | grep 1\.99 > /dev/null + if test "$?" = 0 ; then LIBC="libc1" ; fi + fi + rm -f $dummy.c $dummy + echo powerpc-unknown-linux-gnu${LIBC} + exit 0 ;; + alpha:Linux:*:*) + cat <$dummy.s + .data + \$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + .text + .globl main + .align 4 + .ent main + main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + LIBC="" + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) UNAME_MACHINE="alpha" ;; + 1-0) UNAME_MACHINE="alphaev5" ;; + 1-1) UNAME_MACHINE="alphaev56" ;; + 1-101) UNAME_MACHINE="alphapca56" ;; + 2-303) UNAME_MACHINE="alphaev6" ;; + 2-307) UNAME_MACHINE="alphaev67" ;; + esac + objdump --private-headers $dummy | \ + grep ld.so.1 > /dev/null + if test "$?" = 0 ; then + LIBC="libc1" + fi + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_emulations=`cd /; ld --help 2>&1 \ + | sed -ne '/supported emulations:/!d + s/[ ][ ]*/ /g + s/.*supported emulations: *// + s/ .*// + p'` + case "$ld_supported_emulations" in + i*86linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 + ;; + elf_i*86) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + i*86coff) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 + ;; + esac + # Either a pre-BFD a.out linker (linux-gnuoldld) + # or one that does not give us useful --help. + # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout. + # If ld does not provide *any* "supported emulations:" + # that means it is gnuoldld. + test -z "$ld_supported_emulations" && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0 + case "${UNAME_MACHINE}" in + i*86) + VENDOR=pc; + ;; + *) + VENDOR=unknown; + ;; + esac + # Determine whether the default compiler is a.out or elf + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-${VENDOR}-linux-gnu\n", argv[1]); +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +#else + printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; +# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions +# are messed up and put the nodename in both sysname and nodename. + i*86:DYNIX/ptx:4*:*) + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:7*) + # Fixed at (any) Pentium or better + UNAME_MACHINE=i586 + if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then + echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} + fi + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/src/deps/rtaudio-mod/config/config.sub b/src/deps/rtaudio-mod/config/config.sub new file mode 100755 index 0000000..9a7d59a --- /dev/null +++ b/src/deps/rtaudio-mod/config/config.sub @@ -0,0 +1,1366 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2012-11-19' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | storm-chaos* | os2-emx*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \ + | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \ + | pyramid | mn10200 | mn10300 | tron | a29k \ + | 580 | i960 | h8300 \ + | x86 | ppcbe | mipsbe | mipsle | shbe | shle \ + | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \ + | hppa64 \ + | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \ + | alphaev6[78] \ + | we32k | ns16k | clipper | i370 | sh | sh[34] \ + | powerpc | powerpc64 | powerpcle \ + | 1750a | dsp16xx | pdp10 | pdp11 \ + | mips16 | mips64 | mipsel | mips64el \ + | mips64orion | mips64orionel | mipstx39 | mipstx39el \ + | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \ + | mips64vr5000 | miprs64vr5000el | mcore | s390 | s390x \ + | sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \ + | v850 | c4x \ + | thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \ + | pj | pjl | h8500) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | w65) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + # FIXME: clean up the formatting here. + vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \ + | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \ + | arm-* | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \ + | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \ + | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \ + | xmp-* | ymp-* \ + | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \ + | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \ + | hppa2.0n-* | hppa64-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \ + | alphaev6[78]-* \ + | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \ + | clipper-* | orion-* \ + | sparclite-* | pdp10-* | pdp11-* | sh-* | powerpc-* | powerpc64-* | powerpcle-* \ + | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \ + | mips16-* | mips64-* | mipsel-* \ + | mips64el-* | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \ + | mipstx39-* | mipstx39el-* | mcore-* \ + | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \ + | [cjt]90-* \ + | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \ + | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \ + | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + os=-linux-gnu + ;; + mips*-linux*) + basic_machine=mips-unknown + os=-linux-gnu + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + if [ x$os = x-linux-gnu ]; then + basic_machine=mips-unknown + else + basic_machine=mips-mips + fi + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4) + basic_machine=sh-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/src/deps/rtaudio-mod/config/install.sh b/src/deps/rtaudio-mod/config/install.sh new file mode 100755 index 0000000..e69de29 diff --git a/src/deps/rtaudio-mod/configure b/src/deps/rtaudio-mod/configure new file mode 100755 index 0000000..9f3d3f8 --- /dev/null +++ b/src/deps/rtaudio-mod/configure @@ -0,0 +1,5888 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for RtAudio 4.1. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: gary@music.mcgill.ca about your system, including any +$0: error possibly output before this message. Then install +$0: a modern shell, or manually run the script under such a +$0: shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='RtAudio' +PACKAGE_TARNAME='rtaudio' +PACKAGE_VERSION='4.1' +PACKAGE_STRING='RtAudio 4.1' +PACKAGE_BUGREPORT='gary@music.mcgill.ca' +PACKAGE_URL='' + +ac_unique_file="RtAudio.cpp" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +objects +PULSE_LIBS +PULSE_CFLAGS +req +api +libflags +sharedname +sharedlib +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +object_path +cxxflag +cppflag +EGREP +GREP +CPP +ac_ct_CC +CFLAGS +CC +AR +RANLIB +OBJEXT +EXEEXT +ac_ct_CXX +CPPFLAGS +LDFLAGS +CXXFLAGS +CXX +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG +GXX +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_debug +with_jack +with_alsa +with_pulse +with_oss +with_core +with_asio +with_ds +with_wasapi +' + ac_precious_vars='build_alias +host_alias +target_alias +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +CXX +CXXFLAGS +LDFLAGS +LIBS +CPPFLAGS +CCC +CC +CFLAGS +CPP +PULSE_CFLAGS +PULSE_LIBS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures RtAudio 4.1 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/rtaudio] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of RtAudio 4.1:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-debug = enable various debug output + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-jack = choose JACK server support (mac and linux only) + --with-alsa = choose native ALSA API support (linux only) + --with-pulse = choose PulseAudio API support (linux only) + --with-oss = choose OSS API support (linux only) + --with-jack = choose JACK server support (unix only) + --with-core = choose CoreAudio API support (mac only) + --with-asio = choose ASIO API support (windoze only) + --with-ds = choose DirectSound API support (windoze only) + --with-wasapi = choose Windows Audio Session API support (windoze only) + +Some influential environment variables: + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CC C compiler command + CFLAGS C compiler flags + CPP C preprocessor + PULSE_CFLAGS + C compiler flags for PULSE, overriding pkg-config + PULSE_LIBS linker flags for PULSE, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +RtAudio configure 4.1 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ----------------------------------- ## +## Report this to gary@music.mcgill.ca ## +## ----------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by RtAudio $as_me 4.1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_aux_dir= +for ac_dir in config "$srcdir"/config; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in config \"$srcdir\"/config" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +ac_config_files="$ac_config_files rtaudio-config librtaudio.pc Makefile tests/Makefile" + + +# Fill GXX with something before test. +GXX="no" + + + + + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + + + +# Checks for programs. +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ CC c++ cxx + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ CC c++ cxx +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 +$as_echo_n "checking whether the C++ compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C++ compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 +$as_echo_n "checking for C++ compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +# Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $AR in + [\\/]* | ?:[\\/]*) + ac_cv_path_AR="$AR" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_AR" && ac_cv_path_AR="no" + ;; +esac +fi +AR=$ac_cv_path_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +if [ $AR = "no" ] ; then + as_fn_error $? "\"Could not find ar - needed to create a library\"" "$LINENO" 5; +fi + +# Checks for header files. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in sys/ioctl.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Check for debug +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to compile debug version" >&5 +$as_echo_n "checking whether to compile debug version... " >&6; } +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; cppflag=-D__RTAUDIO_DEBUG__ + cxxflag=-g + object_path=Debug + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + cppflag= + cxxflag=-O2 + object_path=Release + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# Checks for functions +ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" +if test "x$ac_cv_func_gettimeofday" = xyes; then : + cppflag="$cppflag -DHAVE_GETTIMEOFDAY" +fi + + +# Set paths if prefix is defined +if test "x$prefix" != "x" && test "x$prefix" != "xNONE"; then + LIBS="$LIBS -L$prefix/lib" + CPPFLAGS="$CPPFLAGS -I$prefix/include" +fi + +# For -I and -D flags +CPPFLAGS="$CPPFLAGS $cppflag" + +# For debugging and optimization ... overwrite default because it has both -g and -O2 +#CXXFLAGS="$CXXFLAGS $cxxflag" +CXXFLAGS="$cxxflag" + +# Check compiler and use -Wall if gnu. +if test $GXX = "yes" ; then + cxxflag="-Wall -Wextra" + +fi + +CXXFLAGS="$CXXFLAGS $cxxflag" + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + +sharedlib="librtaudio.so" + +sharedname="librtaudio.so.\$(RELEASE)" + +libflags="-shared -Wl,-soname,\$(SHARED).\$(MAJOR) -o \$(SHARED).\$(RELEASE)" + +case $host in + *-apple*) + sharedlib="librtaudio.dylib" + + sharedname="librtaudio.\$(RELEASE).dylib" + + libflags="-dynamiclib -o librtaudio.\$(RELEASE).dylib" + +esac + +# Checks for package options and external software +api="" + +req="" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for audio API" >&5 +$as_echo_n "checking for audio API... " >&6; } +case $host in + *-*-netbsd*) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using OSS" >&5 +$as_echo "using OSS" >&6; } + api="$api -D__LINUX_OSS__" + LIBS="$LIBS -lossaudio" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_pthread_create+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_pthread_create=yes +else + ac_cv_lib_pthread_pthread_create=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPTHREAD 1 +_ACEOF + + LIBS="-lpthread $LIBS" + +else + as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5 +fi + + ;; + + *-*-linux*) + +# Check whether --with-jack was given. +if test "${with_jack+set}" = set; then : + withval=$with_jack; + api="$api -D__UNIX_JACK__" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using JACK" >&5 +$as_echo "using JACK" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for jack_client_open in -ljack" >&5 +$as_echo_n "checking for jack_client_open in -ljack... " >&6; } +if ${ac_cv_lib_jack_jack_client_open+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljack $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char jack_client_open (); +int +main () +{ +return jack_client_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_jack_jack_client_open=yes +else + ac_cv_lib_jack_jack_client_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jack_jack_client_open" >&5 +$as_echo "$ac_cv_lib_jack_jack_client_open" >&6; } +if test "x$ac_cv_lib_jack_jack_client_open" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBJACK 1 +_ACEOF + + LIBS="-ljack $LIBS" + +else + as_fn_error $? "JACK support requires the jack library!" "$LINENO" 5 +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5 +$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; } +if ${ac_cv_lib_asound_snd_pcm_open+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lasound $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char snd_pcm_open (); +int +main () +{ +return snd_pcm_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_asound_snd_pcm_open=yes +else + ac_cv_lib_asound_snd_pcm_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5 +$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; } +if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBASOUND 1 +_ACEOF + + LIBS="-lasound $LIBS" + +else + as_fn_error $? "Jack support also requires the asound library!" "$LINENO" 5 +fi + +fi + + + # Look for ALSA flag + +# Check whether --with-alsa was given. +if test "${with_alsa+set}" = set; then : + withval=$with_alsa; + api="$api -D__LINUX_ALSA__" + req="$req alsa" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ALSA" >&5 +$as_echo "using ALSA" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5 +$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; } +if ${ac_cv_lib_asound_snd_pcm_open+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lasound $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char snd_pcm_open (); +int +main () +{ +return snd_pcm_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_asound_snd_pcm_open=yes +else + ac_cv_lib_asound_snd_pcm_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5 +$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; } +if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBASOUND 1 +_ACEOF + + LIBS="-lasound $LIBS" + +else + as_fn_error $? "ALSA support requires the asound library!" "$LINENO" 5 +fi + +fi + + + # Look for PULSE flag + +# Check whether --with-pulse was given. +if test "${with_pulse+set}" = set; then : + withval=$with_pulse; + api="$api -D__LINUX_PULSE__" + req="$req libpulse-simple" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using PulseAudio" >&5 +$as_echo "using PulseAudio" >&6; } + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PULSE" >&5 +$as_echo_n "checking for PULSE... " >&6; } + +if test -n "$PULSE_CFLAGS"; then + pkg_cv_PULSE_CFLAGS="$PULSE_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse-simple\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libpulse-simple") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_PULSE_CFLAGS=`$PKG_CONFIG --cflags "libpulse-simple" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$PULSE_LIBS"; then + pkg_cv_PULSE_LIBS="$PULSE_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse-simple\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libpulse-simple") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_PULSE_LIBS=`$PKG_CONFIG --libs "libpulse-simple" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + PULSE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libpulse-simple" 2>&1` + else + PULSE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libpulse-simple" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$PULSE_PKG_ERRORS" >&5 + + as_fn_error $? "PulseAudio support requires the pulse-simple library!" "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "PulseAudio support requires the pulse-simple library!" "$LINENO" 5 +else + PULSE_CFLAGS=$pkg_cv_PULSE_CFLAGS + PULSE_LIBS=$pkg_cv_PULSE_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi + LIBS="$LIBS `pkg-config --libs libpulse-simple`" +fi + + + # Look for OSS flag + +# Check whether --with-oss was given. +if test "${with_oss+set}" = set; then : + withval=$with_oss; + api="$api -D__LINUX_OSS__" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using OSS" >&5 +$as_echo "using OSS" >&6; } +fi + + + # If no audio api flags specified, use ALSA + if test "$api" == ""; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ALSA" >&5 +$as_echo "using ALSA" >&6; } + api=-D__LINUX_ALSA__ + + req="$req alsa" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5 +$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; } +if ${ac_cv_lib_asound_snd_pcm_open+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lasound $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char snd_pcm_open (); +int +main () +{ +return snd_pcm_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_asound_snd_pcm_open=yes +else + ac_cv_lib_asound_snd_pcm_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5 +$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; } +if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBASOUND 1 +_ACEOF + + LIBS="-lasound $LIBS" + +else + as_fn_error $? "ALSA support requires the asound library!" "$LINENO" 5 +fi + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_pthread_create+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_pthread_create=yes +else + ac_cv_lib_pthread_pthread_create=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPTHREAD 1 +_ACEOF + + LIBS="-lpthread $LIBS" + +else + as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5 +fi + + ;; + + *-apple*) + +# Check whether --with-jack was given. +if test "${with_jack+set}" = set; then : + withval=$with_jack; + api="$api -D__UNIX_JACK__" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using JACK" >&5 +$as_echo "using JACK" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for jack_client_open in -ljack" >&5 +$as_echo_n "checking for jack_client_open in -ljack... " >&6; } +if ${ac_cv_lib_jack_jack_client_open+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljack $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char jack_client_open (); +int +main () +{ +return jack_client_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_jack_jack_client_open=yes +else + ac_cv_lib_jack_jack_client_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jack_jack_client_open" >&5 +$as_echo "$ac_cv_lib_jack_jack_client_open" >&6; } +if test "x$ac_cv_lib_jack_jack_client_open" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBJACK 1 +_ACEOF + + LIBS="-ljack $LIBS" + +else + as_fn_error $? "JACK support requires the jack library!" "$LINENO" 5 +fi + +fi + + +# AC_CHECK_HEADER(jack/jack.h, [], [AC_MSG_ERROR(Jack header file not found!)] ) +# LIBS="$LIBS -framework jackmp" ], ) + + + # Look for Core flag + +# Check whether --with-core was given. +if test "${with_core+set}" = set; then : + withval=$with_core; + api="$api -D__MACOSX_CORE__" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using CoreAudio" >&5 +$as_echo "using CoreAudio" >&6; } + ac_fn_c_check_header_mongrel "$LINENO" "CoreAudio/CoreAudio.h" "ac_cv_header_CoreAudio_CoreAudio_h" "$ac_includes_default" +if test "x$ac_cv_header_CoreAudio_CoreAudio_h" = xyes; then : + +else + as_fn_error $? "CoreAudio header files not found!" "$LINENO" 5 +fi + + + LIBS="$LIBS -framework CoreAudio -framework CoreFoundation" +fi + + + # If no audio api flags specified, use CoreAudio + if test "$api" == ""; then + api=-D__MACOSX_CORE__ + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using CoreAudio" >&5 +$as_echo "using CoreAudio" >&6; } + ac_fn_c_check_header_mongrel "$LINENO" "CoreAudio/CoreAudio.h" "ac_cv_header_CoreAudio_CoreAudio_h" "$ac_includes_default" +if test "x$ac_cv_header_CoreAudio_CoreAudio_h" = xyes; then : + +else + as_fn_error $? "CoreAudio header files not found!" "$LINENO" 5 +fi + + + LIBS="-framework CoreAudio -framework CoreFoundation" + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_pthread_create+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_pthread_create=yes +else + ac_cv_lib_pthread_pthread_create=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPTHREAD 1 +_ACEOF + + LIBS="-lpthread $LIBS" + +else + as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5 +fi + + ;; + + *-mingw32*) + +# Check whether --with-asio was given. +if test "${with_asio+set}" = set; then : + withval=$with_asio; + api="$api -D__WINDOWS_ASIO__" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ASIO" >&5 +$as_echo "using ASIO" >&6; } + objects="asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o" + +fi + + + # Look for DirectSound flag + +# Check whether --with-ds was given. +if test "${with_ds+set}" = set; then : + withval=$with_ds; + api="$api -D__WINDOWS_DS__" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using DirectSound" >&5 +$as_echo "using DirectSound" >&6; } + LIBS="-ldsound -lwinmm $LIBS" +fi + + + # Look for WASAPI flag + +# Check whether --with-wasapi was given. +if test "${with_wasapi+set}" = set; then : + withval=$with_wasapi; + api="$api -D__WINDOWS_WASAPI__" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using WASAPI" >&5 +$as_echo "using WASAPI" >&6; } + LIBS="-lwinmm -luuid -lksuser $LIBS" +fi + + + # If no audio api flags specified, use DS + if test "$api" == ""; then + api=-D__WINDOWS_DS__ + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: using DirectSound" >&5 +$as_echo "using DirectSound" >&6; } + LIBS="-ldsound -lwinmm $LIBS" + fi + + LIBS="-lole32 $LIBS" + ;; + + *) + # Default case for unknown realtime systems. + as_fn_error $? "Unknown system type for realtime support!" "$LINENO" 5 + ;; +esac + +CPPFLAGS="$CPPFLAGS $api" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +:mline +/\\$/{ + N + s,\\\n,, + b mline +} +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by RtAudio $as_me 4.1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +RtAudio config.status 4.1 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "rtaudio-config") CONFIG_FILES="$CONFIG_FILES rtaudio-config" ;; + "librtaudio.pc") CONFIG_FILES="$CONFIG_FILES librtaudio.pc" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +chmod oug+x rtaudio-config diff --git a/src/deps/rtaudio-mod/configure.ac b/src/deps/rtaudio-mod/configure.ac new file mode 100644 index 0000000..fa45967 --- /dev/null +++ b/src/deps/rtaudio-mod/configure.ac @@ -0,0 +1,197 @@ +# Process this file with autoconf to produce a configure script. +AC_INIT(RtAudio, 4.1, gary@music.mcgill.ca, rtaudio) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_SRCDIR(RtAudio.cpp) +AC_CONFIG_FILES([rtaudio-config librtaudio.pc Makefile tests/Makefile]) + +# Fill GXX with something before test. +AC_SUBST( GXX, ["no"] ) + +dnl Check for pkg-config program, used for configuring some libraries. +m4_define_default([PKG_PROG_PKG_CONFIG], +[AC_MSG_CHECKING([pkg-config]) +AC_MSG_RESULT([no])]) + +PKG_PROG_PKG_CONFIG + +dnl If the pkg-config autoconf support isn't installed, define its +dnl autoconf macro to disable any packages depending on it. +m4_define_default([PKG_CHECK_MODULES], +[AC_MSG_CHECKING([$1]) +AC_MSG_RESULT([no]) +$4]) + +# Checks for programs. +AC_PROG_CXX(g++ CC c++ cxx) +AC_PROG_RANLIB +AC_PATH_PROG(AR, ar, no) +if [[ $AR = "no" ]] ; then + AC_MSG_ERROR("Could not find ar - needed to create a library"); +fi + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(sys/ioctl.h unistd.h) + +# Check for debug +AC_MSG_CHECKING(whether to compile debug version) +AC_ARG_ENABLE(debug, + [ --enable-debug = enable various debug output], + [AC_SUBST( cppflag, [-D__RTAUDIO_DEBUG__] ) AC_SUBST( cxxflag, [-g] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)], + [AC_SUBST( cppflag, [] ) AC_SUBST( cxxflag, [-O2] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)]) + + +# Checks for functions +AC_CHECK_FUNC(gettimeofday, [cppflag="$cppflag -DHAVE_GETTIMEOFDAY"], ) + +# Set paths if prefix is defined +if test "x$prefix" != "x" && test "x$prefix" != "xNONE"; then + LIBS="$LIBS -L$prefix/lib" + CPPFLAGS="$CPPFLAGS -I$prefix/include" +fi + +# For -I and -D flags +CPPFLAGS="$CPPFLAGS $cppflag" + +# For debugging and optimization ... overwrite default because it has both -g and -O2 +#CXXFLAGS="$CXXFLAGS $cxxflag" +CXXFLAGS="$cxxflag" + +# Check compiler and use -Wall if gnu. +if [test $GXX = "yes" ;] then + AC_SUBST( cxxflag, ["-Wall -Wextra"] ) +fi + +CXXFLAGS="$CXXFLAGS $cxxflag" + +AC_CANONICAL_HOST + +AC_SUBST( sharedlib, ["librtaudio.so"] ) +AC_SUBST( sharedname, ["librtaudio.so.\$(RELEASE)"] ) +AC_SUBST( libflags, ["-shared -Wl,-soname,\$(SHARED).\$(MAJOR) -o \$(SHARED).\$(RELEASE)"] ) +case $host in + *-apple*) + AC_SUBST( sharedlib, ["librtaudio.dylib"] ) + AC_SUBST( sharedname, ["librtaudio.\$(RELEASE).dylib"] ) + AC_SUBST( libflags, ["-dynamiclib -o librtaudio.\$(RELEASE).dylib"] ) +esac + +# Checks for package options and external software +AC_SUBST( api, [""] ) +AC_SUBST( req, [""] ) +AC_MSG_CHECKING(for audio API) +case $host in + *-*-netbsd*) + AC_MSG_RESULT(using OSS) + api="$api -D__LINUX_OSS__" + LIBS="$LIBS -lossaudio" + AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!)) + ;; + + *-*-linux*) + AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (mac and linux only)], [ + api="$api -D__UNIX_JACK__" + AC_MSG_RESULT(using JACK) + AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!)) + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(Jack support also requires the asound library!))], ) + + # Look for ALSA flag + AC_ARG_WITH(alsa, [ --with-alsa = choose native ALSA API support (linux only)], [ + api="$api -D__LINUX_ALSA__" + req="$req alsa" + AC_MSG_RESULT(using ALSA) + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))], ) + + # Look for PULSE flag + AC_ARG_WITH(pulse, [ --with-pulse = choose PulseAudio API support (linux only)], [ + api="$api -D__LINUX_PULSE__" + req="$req libpulse-simple" + AC_MSG_RESULT(using PulseAudio) + PKG_CHECK_MODULES([PULSE], [libpulse-simple], , AC_MSG_ERROR(PulseAudio support requires the pulse-simple library!)) + LIBS="$LIBS `pkg-config --libs libpulse-simple`" ], ) + + # Look for OSS flag + AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [ + api="$api -D__LINUX_OSS__" + AC_MSG_RESULT(using OSS)], ) + + # If no audio api flags specified, use ALSA + if [test "$api" == "";] then + AC_MSG_RESULT(using ALSA) + AC_SUBST( api, [-D__LINUX_ALSA__] ) + req="$req alsa" + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!)) + fi + + AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!)) + ;; + + *-apple*) + AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (unix only)], [ + api="$api -D__UNIX_JACK__" + AC_MSG_RESULT(using JACK) + AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!))], ) + +# AC_CHECK_HEADER(jack/jack.h, [], [AC_MSG_ERROR(Jack header file not found!)] ) +# LIBS="$LIBS -framework jackmp" ], ) + + + # Look for Core flag + AC_ARG_WITH(core, [ --with-core = choose CoreAudio API support (mac only)], [ + api="$api -D__MACOSX_CORE__" + AC_MSG_RESULT(using CoreAudio) + AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] ) + LIBS="$LIBS -framework CoreAudio -framework CoreFoundation" ], ) + + # If no audio api flags specified, use CoreAudio + if [test "$api" == ""; ] then + AC_SUBST( api, [-D__MACOSX_CORE__] ) + AC_MSG_RESULT(using CoreAudio) + AC_CHECK_HEADER(CoreAudio/CoreAudio.h, + [], + [AC_MSG_ERROR(CoreAudio header files not found!)] ) + AC_SUBST( LIBS, ["-framework CoreAudio -framework CoreFoundation"] ) + fi + + AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!)) + ;; + + *-mingw32*) + AC_ARG_WITH(asio, [ --with-asio = choose ASIO API support (windoze only)], [ + api="$api -D__WINDOWS_ASIO__" + AC_MSG_RESULT(using ASIO) + AC_SUBST( objects, ["asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o"] ) ], ) + + # Look for DirectSound flag + AC_ARG_WITH(ds, [ --with-ds = choose DirectSound API support (windoze only)], [ + api="$api -D__WINDOWS_DS__" + AC_MSG_RESULT(using DirectSound) + LIBS="-ldsound -lwinmm $LIBS" ], ) + + # Look for WASAPI flag + AC_ARG_WITH(wasapi, [ --with-wasapi = choose Windows Audio Session API support (windoze only)], [ + api="$api -D__WINDOWS_WASAPI__" + AC_MSG_RESULT(using WASAPI) + LIBS="-lwinmm -luuid -lksuser $LIBS" ], ) + + # If no audio api flags specified, use DS + if [test "$api" == "";] then + AC_SUBST( api, [-D__WINDOWS_DS__] ) + AC_MSG_RESULT(using DirectSound) + LIBS="-ldsound -lwinmm $LIBS" + fi + + LIBS="-lole32 $LIBS" + ;; + + *) + # Default case for unknown realtime systems. + AC_MSG_ERROR(Unknown system type for realtime support!) + ;; +esac + +CPPFLAGS="$CPPFLAGS $api" + +AC_OUTPUT + +chmod oug+x rtaudio-config diff --git a/src/deps/rtaudio-mod/include/FunctionDiscoveryKeys_devpkey.h b/src/deps/rtaudio-mod/include/FunctionDiscoveryKeys_devpkey.h new file mode 100644 index 0000000..854244d --- /dev/null +++ b/src/deps/rtaudio-mod/include/FunctionDiscoveryKeys_devpkey.h @@ -0,0 +1,212 @@ +#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 + +// +// _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-mod/include/asio.cpp b/src/deps/rtaudio-mod/include/asio.cpp new file mode 100644 index 0000000..b241663 --- /dev/null +++ b/src/deps/rtaudio-mod/include/asio.cpp @@ -0,0 +1,257 @@ +/* + Steinberg Audio Stream I/O API + (c) 1996, Steinberg Soft- und Hardware GmbH + + asio.cpp + + asio functions entries which translate the + asio interface to the asiodrvr class methods +*/ + +#include +#include "asiosys.h" // platform definition +#include "asio.h" + +#if MAC +#include "asiodrvr.h" + +#pragma export on + +AsioDriver *theAsioDriver = 0; + +extern "C" +{ + +long main() +{ + return 'ASIO'; +} + +#elif WINDOWS + +#include "windows.h" +#include "iasiodrv.h" +#include "asiodrivers.h" + +IASIO *theAsioDriver = 0; +extern AsioDrivers *asioDrivers; + +#elif SGI || SUN || BEOS || LINUX +#include "asiodrvr.h" +static AsioDriver *theAsioDriver = 0; +#endif + +//----------------------------------------------------------------------------------------------------- +ASIOError ASIOInit(ASIODriverInfo *info) +{ +#if MAC || SGI || SUN || BEOS || LINUX + if(theAsioDriver) + { + delete theAsioDriver; + theAsioDriver = 0; + } + info->driverVersion = 0; + strcpy(info->name, "No ASIO Driver"); + theAsioDriver = getDriver(); + if(!theAsioDriver) + { + strcpy(info->errorMessage, "Not enough memory for the ASIO driver!"); + return ASE_NotPresent; + } + if(!theAsioDriver->init(info->sysRef)) + { + theAsioDriver->getErrorMessage(info->errorMessage); + delete theAsioDriver; + theAsioDriver = 0; + return ASE_NotPresent; + } + strcpy(info->errorMessage, "No ASIO Driver Error"); + theAsioDriver->getDriverName(info->name); + info->driverVersion = theAsioDriver->getDriverVersion(); + return ASE_OK; + +#else + + info->driverVersion = 0; + strcpy(info->name, "No ASIO Driver"); + if(theAsioDriver) // must be loaded! + { + if(!theAsioDriver->init(info->sysRef)) + { + theAsioDriver->getErrorMessage(info->errorMessage); + theAsioDriver = 0; + return ASE_NotPresent; + } + + strcpy(info->errorMessage, "No ASIO Driver Error"); + theAsioDriver->getDriverName(info->name); + info->driverVersion = theAsioDriver->getDriverVersion(); + return ASE_OK; + } + return ASE_NotPresent; + +#endif // !MAC +} + +ASIOError ASIOExit(void) +{ + if(theAsioDriver) + { +#if WINDOWS + asioDrivers->removeCurrentDriver(); +#else + delete theAsioDriver; +#endif + } + theAsioDriver = 0; + return ASE_OK; +} + +ASIOError ASIOStart(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->start(); +} + +ASIOError ASIOStop(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->stop(); +} + +ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels) +{ + if(!theAsioDriver) + { + *numInputChannels = *numOutputChannels = 0; + return ASE_NotPresent; + } + return theAsioDriver->getChannels(numInputChannels, numOutputChannels); +} + +ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency) +{ + if(!theAsioDriver) + { + *inputLatency = *outputLatency = 0; + return ASE_NotPresent; + } + return theAsioDriver->getLatencies(inputLatency, outputLatency); +} + +ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) +{ + if(!theAsioDriver) + { + *minSize = *maxSize = *preferredSize = *granularity = 0; + return ASE_NotPresent; + } + return theAsioDriver->getBufferSize(minSize, maxSize, preferredSize, granularity); +} + +ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->canSampleRate(sampleRate); +} + +ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->getSampleRate(currentRate); +} + +ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->setSampleRate(sampleRate); +} + +ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources) +{ + if(!theAsioDriver) + { + *numSources = 0; + return ASE_NotPresent; + } + return theAsioDriver->getClockSources(clocks, numSources); +} + +ASIOError ASIOSetClockSource(long reference) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->setClockSource(reference); +} + +ASIOError ASIOGetSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->getSamplePosition(sPos, tStamp); +} + +ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info) +{ + if(!theAsioDriver) + { + info->channelGroup = -1; + info->type = ASIOSTInt16MSB; + strcpy(info->name, "None"); + return ASE_NotPresent; + } + return theAsioDriver->getChannelInfo(info); +} + +ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks) +{ + if(!theAsioDriver) + { + ASIOBufferInfo *info = bufferInfos; + for(long i = 0; i < numChannels; i++, info++) + info->buffers[0] = info->buffers[1] = 0; + return ASE_NotPresent; + } + return theAsioDriver->createBuffers(bufferInfos, numChannels, bufferSize, callbacks); +} + +ASIOError ASIODisposeBuffers(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->disposeBuffers(); +} + +ASIOError ASIOControlPanel(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->controlPanel(); +} + +ASIOError ASIOFuture(long selector, void *opt) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->future(selector, opt); +} + +ASIOError ASIOOutputReady(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->outputReady(); +} + +#if MAC +} // extern "C" +#pragma export off +#endif + + diff --git a/src/deps/rtaudio-mod/include/asio.h b/src/deps/rtaudio-mod/include/asio.h new file mode 100644 index 0000000..8ec811f --- /dev/null +++ b/src/deps/rtaudio-mod/include/asio.h @@ -0,0 +1,1054 @@ +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +/* + Steinberg Audio Stream I/O API + (c) 1997 - 2005, Steinberg Media Technologies GmbH + + ASIO Interface Specification v 2.1 + + 2005 - Added support for DSD sample data (in cooperation with Sony) + + + basic concept is an i/o synchronous double-buffer scheme: + + on bufferSwitch(index == 0), host will read/write: + + after ASIOStart(), the + read first input buffer A (index 0) + | will be invalid (empty) + * ------------------------ + |------------------------|-----------------------| + | | | + | Input Buffer A (0) | Input Buffer B (1) | + | | | + |------------------------|-----------------------| + | | | + | Output Buffer A (0) | Output Buffer B (1) | + | | | + |------------------------|-----------------------| + * ------------------------- + | before calling ASIOStart(), + write host will have filled output + buffer B (index 1) already + + *please* take special care of proper statement of input + and output latencies (see ASIOGetLatencies()), these + control sequencer sync accuracy + +*/ + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +/* + +prototypes summary: + +ASIOError ASIOInit(ASIODriverInfo *info); +ASIOError ASIOExit(void); +ASIOError ASIOStart(void); +ASIOError ASIOStop(void); +ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels); +ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency); +ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); +ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate); +ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate); +ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate); +ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources); +ASIOError ASIOSetClockSource(long reference); +ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp); +ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info); +ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks); +ASIOError ASIODisposeBuffers(void); +ASIOError ASIOControlPanel(void); +void *ASIOFuture(long selector, void *params); +ASIOError ASIOOutputReady(void); + +*/ + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +#ifndef __ASIO_H +#define __ASIO_H + +// force 4 byte alignment +#if defined(_MSC_VER) && !defined(__MWERKS__) +#pragma pack(push,4) +#elif PRAGMA_ALIGN_SUPPORTED +#pragma options align = native +#endif + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Type definitions +//- - - - - - - - - - - - - - - - - - - - - - - - - + +// number of samples data type is 64 bit integer +#if NATIVE_INT64 + typedef long long int ASIOSamples; +#else + typedef struct ASIOSamples { + unsigned long hi; + unsigned long lo; + } ASIOSamples; +#endif + +// Timestamp data type is 64 bit integer, +// Time format is Nanoseconds. +#if NATIVE_INT64 + typedef long long int ASIOTimeStamp ; +#else + typedef struct ASIOTimeStamp { + unsigned long hi; + unsigned long lo; + } ASIOTimeStamp; +#endif + +// Samplerates are expressed in IEEE 754 64 bit double float, +// native format as host computer +#if IEEE754_64FLOAT + typedef double ASIOSampleRate; +#else + typedef struct ASIOSampleRate { + char ieee[8]; + } ASIOSampleRate; +#endif + +// Boolean values are expressed as long +typedef long ASIOBool; +enum { + ASIOFalse = 0, + ASIOTrue = 1 +}; + +// Sample Types are expressed as long +typedef long ASIOSampleType; +enum { + ASIOSTInt16MSB = 0, + ASIOSTInt24MSB = 1, // used for 20 bits as well + ASIOSTInt32MSB = 2, + ASIOSTFloat32MSB = 3, // IEEE 754 32 bit float + ASIOSTFloat64MSB = 4, // IEEE 754 64 bit double float + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can be more easily used with these + ASIOSTInt32MSB16 = 8, // 32 bit data with 16 bit alignment + ASIOSTInt32MSB18 = 9, // 32 bit data with 18 bit alignment + ASIOSTInt32MSB20 = 10, // 32 bit data with 20 bit alignment + ASIOSTInt32MSB24 = 11, // 32 bit data with 24 bit alignment + + ASIOSTInt16LSB = 16, + ASIOSTInt24LSB = 17, // used for 20 bits as well + ASIOSTInt32LSB = 18, + ASIOSTFloat32LSB = 19, // IEEE 754 32 bit float, as found on Intel x86 architecture + ASIOSTFloat64LSB = 20, // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + ASIOSTInt32LSB16 = 24, // 32 bit data with 18 bit alignment + ASIOSTInt32LSB18 = 25, // 32 bit data with 18 bit alignment + ASIOSTInt32LSB20 = 26, // 32 bit data with 20 bit alignment + ASIOSTInt32LSB24 = 27, // 32 bit data with 24 bit alignment + + // ASIO DSD format. + ASIOSTDSDInt8LSB1 = 32, // DSD 1 bit data, 8 samples per byte. First sample in Least significant bit. + ASIOSTDSDInt8MSB1 = 33, // DSD 1 bit data, 8 samples per byte. First sample in Most significant bit. + ASIOSTDSDInt8NER8 = 40, // DSD 8 bit data, 1 sample per byte. No Endianness required. + + ASIOSTLastEntry +}; + +/*----------------------------------------------------------------------------- +// DSD operation and buffer layout +// Definition by Steinberg/Sony Oxford. +// +// We have tried to treat DSD as PCM and so keep a consistant structure across +// the ASIO interface. +// +// DSD's sample rate is normally referenced as a multiple of 44.1Khz, so +// the standard sample rate is refered to as 64Fs (or 2.8224Mhz). We looked +// at making a special case for DSD and adding a field to the ASIOFuture that +// would allow the user to select the Over Sampleing Rate (OSR) as a seperate +// entity but decided in the end just to treat it as a simple value of +// 2.8224Mhz and use the standard interface to set it. +// +// The second problem was the "word" size, in PCM the word size is always a +// greater than or equal to 8 bits (a byte). This makes life easy as we can +// then pack the samples into the "natural" size for the machine. +// In DSD the "word" size is 1 bit. This is not a major problem and can easily +// be dealt with if we ensure that we always deal with a multiple of 8 samples. +// +// DSD brings with it another twist to the Endianness religion. How are the +// samples packed into the byte. It would be nice to just say the most significant +// bit is always the first sample, however there would then be a performance hit +// on little endian machines. Looking at how some of the processing goes... +// Little endian machines like the first sample to be in the Least Significant Bit, +// this is because when you write it to memory the data is in the correct format +// to be shifted in and out of the words. +// Big endian machine prefer the first sample to be in the Most Significant Bit, +// again for the same reasion. +// +// And just when things were looking really muddy there is a proposed extension to +// DSD that uses 8 bit word sizes. It does not care what endianness you use. +// +// Switching the driver between DSD and PCM mode +// ASIOFuture allows for extending the ASIO API quite transparently. +// See kAsioSetIoFormat, kAsioGetIoFormat, kAsioCanDoIoFormat +// +//-----------------------------------------------------------------------------*/ + + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Error codes +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef long ASIOError; +enum { + ASE_OK = 0, // This value will be returned whenever the call succeeded + ASE_SUCCESS = 0x3f4847a0, // unique success return value for ASIOFuture calls + ASE_NotPresent = -1000, // hardware input or output is not present or available + ASE_HWMalfunction, // hardware is malfunctioning (can be returned by any ASIO function) + ASE_InvalidParameter, // input parameter invalid + ASE_InvalidMode, // hardware is in a bad mode or used in a bad mode + ASE_SPNotAdvancing, // hardware is not running when sample position is inquired + ASE_NoClock, // sample clock or rate cannot be determined or is not present + ASE_NoMemory // not enough memory for completing the request +}; + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Time Info support +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIOTimeCode +{ + double speed; // speed relation (fraction of nominal speed) + // optional; set to 0. or 1. if not supported + ASIOSamples timeCodeSamples; // time in samples + unsigned long flags; // some information flags (see below) + char future[64]; +} ASIOTimeCode; + +typedef enum ASIOTimeCodeFlags +{ + kTcValid = 1, + kTcRunning = 1 << 1, + kTcReverse = 1 << 2, + kTcOnspeed = 1 << 3, + kTcStill = 1 << 4, + + kTcSpeedValid = 1 << 8 +} ASIOTimeCodeFlags; + +typedef struct AsioTimeInfo +{ + double speed; // absolute speed (1. = nominal) + ASIOTimeStamp systemTime; // system time related to samplePosition, in nanoseconds + // on mac, must be derived from Microseconds() (not UpTime()!) + // on windows, must be derived from timeGetTime() + ASIOSamples samplePosition; + ASIOSampleRate sampleRate; // current rate + unsigned long flags; // (see below) + char reserved[12]; +} AsioTimeInfo; + +typedef enum AsioTimeInfoFlags +{ + kSystemTimeValid = 1, // must always be valid + kSamplePositionValid = 1 << 1, // must always be valid + kSampleRateValid = 1 << 2, + kSpeedValid = 1 << 3, + + kSampleRateChanged = 1 << 4, + kClockSourceChanged = 1 << 5 +} AsioTimeInfoFlags; + +typedef struct ASIOTime // both input/output +{ + long reserved[4]; // must be 0 + struct AsioTimeInfo timeInfo; // required + struct ASIOTimeCode timeCode; // optional, evaluated if (timeCode.flags & kTcValid) +} ASIOTime; + +/* + +using time info: +it is recommended to use the new method with time info even if the asio +device does not support timecode; continuous calls to ASIOGetSamplePosition +and ASIOGetSampleRate are avoided, and there is a more defined relationship +between callback time and the time info. + +see the example below. +to initiate time info mode, after you have received the callbacks pointer in +ASIOCreateBuffers, you will call the asioMessage callback with kAsioSupportsTimeInfo +as the argument. if this returns 1, host has accepted time info mode. +now host expects the new callback bufferSwitchTimeInfo to be used instead +of the old bufferSwitch method. the ASIOTime structure is assumed to be valid +and accessible until the callback returns. + +using time code: +if the device supports reading time code, it will call host's asioMessage callback +with kAsioSupportsTimeCode as the selector. it may then fill the according +fields and set the kTcValid flag. +host will call the future method with the kAsioEnableTimeCodeRead selector when +it wants to enable or disable tc reading by the device. you should also support +the kAsioCanTimeInfo and kAsioCanTimeCode selectors in ASIOFuture (see example). + +note: +the AsioTimeInfo/ASIOTimeCode pair is supposed to work in both directions. +as a matter of convention, the relationship between the sample +position counter and the time code at buffer switch time is +(ignoring offset between tc and sample pos when tc is running): + +on input: sample 0 -> input buffer sample 0 -> time code 0 +on output: sample 0 -> output buffer sample 0 -> time code 0 + +this means that for 'real' calculations, one has to take into account +the according latencies. + +example: + +ASIOTime asioTime; + +in createBuffers() +{ + memset(&asioTime, 0, sizeof(ASIOTime)); + AsioTimeInfo* ti = &asioTime.timeInfo; + ti->sampleRate = theSampleRate; + ASIOTimeCode* tc = &asioTime.timeCode; + tc->speed = 1.; + timeInfoMode = false; + canTimeCode = false; + if(callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0) == 1) + { + timeInfoMode = true; +#if kCanTimeCode + if(callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0) == 1) + canTimeCode = true; +#endif + } +} + +void switchBuffers(long doubleBufferIndex, bool processNow) +{ + if(timeInfoMode) + { + AsioTimeInfo* ti = &asioTime.timeInfo; + ti->flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid; + ti->systemTime = theNanoSeconds; + ti->samplePosition = theSamplePosition; + if(ti->sampleRate != theSampleRate) + ti->flags |= kSampleRateChanged; + ti->sampleRate = theSampleRate; + +#if kCanTimeCode + if(canTimeCode && timeCodeEnabled) + { + ASIOTimeCode* tc = &asioTime.timeCode; + tc->timeCodeSamples = tcSamples; // tc in samples + tc->flags = kTcValid | kTcRunning | kTcOnspeed; // if so... + } + ASIOTime* bb = callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse); +#else + callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse); +#endif + } + else + callbacks->bufferSwitch(doubleBufferIndex, ASIOFalse); +} + +ASIOError ASIOFuture(long selector, void *params) +{ + switch(selector) + { + case kAsioEnableTimeCodeRead: + timeCodeEnabled = true; + return ASE_SUCCESS; + case kAsioDisableTimeCodeRead: + timeCodeEnabled = false; + return ASE_SUCCESS; + case kAsioCanTimeInfo: + return ASE_SUCCESS; + #if kCanTimeCode + case kAsioCanTimeCode: + return ASE_SUCCESS; + #endif + } + return ASE_NotPresent; +}; + +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// application's audio stream handler callbacks +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIOCallbacks +{ + void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess); + // bufferSwitch indicates that both input and output are to be processed. + // the current buffer half index (0 for A, 1 for B) determines + // - the output buffer that the host should start to fill. the other buffer + // will be passed to output hardware regardless of whether it got filled + // in time or not. + // - the input buffer that is now filled with incoming data. Note that + // because of the synchronicity of i/o, the input always has at + // least one buffer latency in relation to the output. + // directProcess suggests to the host whether it should immedeately + // start processing (directProcess == ASIOTrue), or whether its process + // should be deferred because the call comes from a very low level + // (for instance, a high level priority interrupt), and direct processing + // would cause timing instabilities for the rest of the system. If in doubt, + // directProcess should be set to ASIOFalse. + // Note: bufferSwitch may be called at interrupt time for highest efficiency. + + void (*sampleRateDidChange) (ASIOSampleRate sRate); + // gets called when the AudioStreamIO detects a sample rate change + // If sample rate is unknown, 0 is passed (for instance, clock loss + // when externally synchronized). + + long (*asioMessage) (long selector, long value, void* message, double* opt); + // generic callback for various purposes, see selectors below. + // note this is only present if the asio version is 2 or higher + + ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess); + // new callback with time info. makes ASIOGetSamplePosition() and various + // calls to ASIOGetSampleRate obsolete, + // and allows for timecode sync etc. to be preferred; will be used if + // the driver calls asioMessage with selector kAsioSupportsTimeInfo. +} ASIOCallbacks; + +// asioMessage selectors +enum +{ + kAsioSelectorSupported = 1, // selector in , returns 1L if supported, + // 0 otherwise + kAsioEngineVersion, // returns engine (host) asio implementation version, + // 2 or higher + kAsioResetRequest, // request driver reset. if accepted, this + // will close the driver (ASIO_Exit() ) and + // re-open it again (ASIO_Init() etc). some + // drivers need to reconfigure for instance + // when the sample rate changes, or some basic + // changes have been made in ASIO_ControlPanel(). + // returns 1L; note the request is merely passed + // to the application, there is no way to determine + // if it gets accepted at this time (but it usually + // will be). + kAsioBufferSizeChange, // not yet supported, will currently always return 0L. + // for now, use kAsioResetRequest instead. + // once implemented, the new buffer size is expected + // in , and on success returns 1L + kAsioResyncRequest, // the driver went out of sync, such that + // the timestamp is no longer valid. this + // is a request to re-start the engine and + // slave devices (sequencer). returns 1 for ok, + // 0 if not supported. + kAsioLatenciesChanged, // the drivers latencies have changed. The engine + // will refetch the latencies. + kAsioSupportsTimeInfo, // if host returns true here, it will expect the + // callback bufferSwitchTimeInfo to be called instead + // of bufferSwitch + kAsioSupportsTimeCode, // + kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands + kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this + kAsioSupportsInputGain, // unused and undefined + kAsioSupportsInputMeter, // unused and undefined + kAsioSupportsOutputGain, // unused and undefined + kAsioSupportsOutputMeter, // unused and undefined + kAsioOverload, // driver detected an overload + + kAsioNumMessageSelectors +}; + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// (De-)Construction +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIODriverInfo +{ + long asioVersion; // currently, 2 + long driverVersion; // driver specific + char name[32]; + char errorMessage[124]; + void *sysRef; // on input: system reference + // (Windows: application main window handle, Mac & SGI: 0) +} ASIODriverInfo; + +ASIOError ASIOInit(ASIODriverInfo *info); +/* Purpose: + Initialize the AudioStreamIO. + Parameter: + info: pointer to an ASIODriver structure: + - asioVersion: + - on input, the host version. *** Note *** this is 0 for earlier asio + implementations, and the asioMessage callback is implemeted + only if asioVersion is 2 or greater. sorry but due to a design fault + the driver doesn't have access to the host version in ASIOInit :-( + added selector for host (engine) version in the asioMessage callback + so we're ok from now on. + - on return, asio implementation version. + older versions are 1 + if you support this version (namely, ASIO_outputReady() ) + this should be 2 or higher. also see the note in + ASIO_getTimeStamp() ! + - version: on return, the driver version (format is driver specific) + - name: on return, a null-terminated string containing the driver's name + - error message: on return, should contain a user message describing + the type of error that occured during ASIOInit(), if any. + - sysRef: platform specific + Returns: + If neither input nor output is present ASE_NotPresent + will be returned. + ASE_NoMemory, ASE_HWMalfunction are other possible error conditions +*/ + +ASIOError ASIOExit(void); +/* Purpose: + Terminates the AudioStreamIO. + Parameter: + None. + Returns: + If neither input nor output is present ASE_NotPresent + will be returned. + Notes: this implies ASIOStop() and ASIODisposeBuffers(), + meaning that no host callbacks must be accessed after ASIOExit(). +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Start/Stop +//- - - - - - - - - - - - - - - - - - - - - - - - - + +ASIOError ASIOStart(void); +/* Purpose: + Start input and output processing synchronously. + This will + - reset the sample counter to zero + - start the hardware (both input and output) + The first call to the hosts' bufferSwitch(index == 0) then tells + the host to read from input buffer A (index 0), and start + processing to output buffer A while output buffer B (which + has been filled by the host prior to calling ASIOStart()) + is possibly sounding (see also ASIOGetLatencies()) + Parameter: + None. + Returns: + If neither input nor output is present, ASE_NotPresent + will be returned. + If the hardware fails to start, ASE_HWMalfunction will be returned. + Notes: + There is no restriction on the time that ASIOStart() takes + to perform (that is, it is not considered a realtime trigger). +*/ + +ASIOError ASIOStop(void); +/* Purpose: + Stops input and output processing altogether. + Parameter: + None. + Returns: + If neither input nor output is present ASE_NotPresent + will be returned. + Notes: + On return from ASIOStop(), the driver must in no + case call the hosts' bufferSwitch() routine. +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Inquiry methods and sample rate +//- - - - - - - - - - - - - - - - - - - - - - - - - + +ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels); +/* Purpose: + Returns number of individual input/output channels. + Parameter: + numInputChannels will hold the number of available input channels + numOutputChannels will hold the number of available output channels + Returns: + If no input/output is present ASE_NotPresent will be returned. + If only inputs, or only outputs are available, the according + other parameter will be zero, and ASE_OK is returned. +*/ + +ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency); +/* Purpose: + Returns the input and output latencies. This includes + device specific delays, like FIFOs etc. + Parameter: + inputLatency will hold the 'age' of the first sample frame + in the input buffer when the hosts reads it in bufferSwitch() + (this is theoretical, meaning it does not include the overhead + and delay between the actual physical switch, and the time + when bufferSitch() enters). + This will usually be the size of one block in sample frames, plus + device specific latencies. + + outputLatency will specify the time between the buffer switch, + and the time when the next play buffer will start to sound. + The next play buffer is defined as the one the host starts + processing after (or at) bufferSwitch(), indicated by the + index parameter (0 for buffer A, 1 for buffer B). + It will usually be either one block, if the host writes directly + to a dma buffer, or two or more blocks if the buffer is 'latched' by + the driver. As an example, on ASIOStart(), the host will have filled + the play buffer at index 1 already; when it gets the callback (with + the parameter index == 0), this tells it to read from the input + buffer 0, and start to fill the play buffer 0 (assuming that now + play buffer 1 is already sounding). In this case, the output + latency is one block. If the driver decides to copy buffer 1 + at that time, and pass it to the hardware at the next slot (which + is most commonly done, but should be avoided), the output latency + becomes two blocks instead, resulting in a total i/o latency of at least + 3 blocks. As memory access is the main bottleneck in native dsp processing, + and to acheive less latency, it is highly recommended to try to avoid + copying (this is also why the driver is the owner of the buffers). To + summarize, the minimum i/o latency can be acheived if the input buffer + is processed by the host into the output buffer which will physically + start to sound on the next time slice. Also note that the host expects + the bufferSwitch() callback to be accessed for each time slice in order + to retain sync, possibly recursively; if it fails to process a block in + time, it will suspend its operation for some time in order to recover. + Returns: + If no input/output is present ASE_NotPresent will be returned. +*/ + +ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); +/* Purpose: + Returns min, max, and preferred buffer sizes for input/output + Parameter: + minSize will hold the minimum buffer size + maxSize will hold the maxium possible buffer size + preferredSize will hold the preferred buffer size (a size which + best fits performance and hardware requirements) + granularity will hold the granularity at which buffer sizes + may differ. Usually, the buffer size will be a power of 2; + in this case, granularity will hold -1 on return, signalling + possible buffer sizes starting from minSize, increased in + powers of 2 up to maxSize. + Returns: + If no input/output is present ASE_NotPresent will be returned. + Notes: + When minimum and maximum buffer size are equal, + the preferred buffer size has to be the same value as well; granularity + should be 0 in this case. +*/ + +ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate); +/* Purpose: + Inquires the hardware for the available sample rates. + Parameter: + sampleRate is the rate in question. + Returns: + If the inquired sample rate is not supported, ASE_NoClock will be returned. + If no input/output is present ASE_NotPresent will be returned. +*/ +ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate); +/* Purpose: + Get the current sample Rate. + Parameter: + currentRate will hold the current sample rate on return. + Returns: + If sample rate is unknown, sampleRate will be 0 and ASE_NoClock will be returned. + If no input/output is present ASE_NotPresent will be returned. + Notes: +*/ + +ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate); +/* Purpose: + Set the hardware to the requested sample Rate. If sampleRate == 0, + enable external sync. + Parameter: + sampleRate: on input, the requested rate + Returns: + If sampleRate is unknown ASE_NoClock will be returned. + If the current clock is external, and sampleRate is != 0, + ASE_InvalidMode will be returned + If no input/output is present ASE_NotPresent will be returned. + Notes: +*/ + +typedef struct ASIOClockSource +{ + long index; // as used for ASIOSetClockSource() + long associatedChannel; // for instance, S/PDIF or AES/EBU + long associatedGroup; // see channel groups (ASIOGetChannelInfo()) + ASIOBool isCurrentSource; // ASIOTrue if this is the current clock source + char name[32]; // for user selection +} ASIOClockSource; + +ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources); +/* Purpose: + Get the available external audio clock sources + Parameter: + clocks points to an array of ASIOClockSource structures: + - index: this is used to identify the clock source + when ASIOSetClockSource() is accessed, should be + an index counting from zero + - associatedInputChannel: the first channel of an associated + input group, if any. + - associatedGroup: the group index of that channel. + groups of channels are defined to seperate for + instance analog, S/PDIF, AES/EBU, ADAT connectors etc, + when present simultaniously. Note that associated channel + is enumerated according to numInputs/numOutputs, means it + is independant from a group (see also ASIOGetChannelInfo()) + inputs are associated to a clock if the physical connection + transfers both data and clock (like S/PDIF, AES/EBU, or + ADAT inputs). if there is no input channel associated with + the clock source (like Word Clock, or internal oscillator), both + associatedChannel and associatedGroup should be set to -1. + - isCurrentSource: on exit, ASIOTrue if this is the current clock + source, ASIOFalse else + - name: a null-terminated string for user selection of the available sources. + numSources: + on input: the number of allocated array members + on output: the number of available clock sources, at least + 1 (internal clock generator). + Returns: + If no input/output is present ASE_NotPresent will be returned. + Notes: +*/ + +ASIOError ASIOSetClockSource(long index); +/* Purpose: + Set the audio clock source + Parameter: + index as obtained from an inquiry to ASIOGetClockSources() + Returns: + If no input/output is present ASE_NotPresent will be returned. + If the clock can not be selected because an input channel which + carries the current clock source is active, ASE_InvalidMode + *may* be returned (this depends on the properties of the driver + and/or hardware). + Notes: + Should *not* return ASE_NoClock if there is no clock signal present + at the selected source; this will be inquired via ASIOGetSampleRate(). + It should call the host callback procedure sampleRateHasChanged(), + if the switch causes a sample rate change, or if no external clock + is present at the selected source. +*/ + +ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp); +/* Purpose: + Inquires the sample position/time stamp pair. + Parameter: + sPos will hold the sample position on return. The sample + position is reset to zero when ASIOStart() gets called. + tStamp will hold the system time when the sample position + was latched. + Returns: + If no input/output is present, ASE_NotPresent will be returned. + If there is no clock, ASE_SPNotAdvancing will be returned. + Notes: + + in order to be able to synchronise properly, + the sample position / time stamp pair must refer to the current block, + that is, the engine will call ASIOGetSamplePosition() in its bufferSwitch() + callback and expect the time for the current block. thus, when requested + in the very first bufferSwitch after ASIO_Start(), the sample position + should be zero, and the time stamp should refer to the very time where + the stream was started. it also means that the sample position must be + block aligned. the driver must ensure proper interpolation if the system + time can not be determined for the block position. the driver is responsible + for precise time stamps as it usually has most direct access to lower + level resources. proper behaviour of ASIO_GetSamplePosition() and ASIO_GetLatencies() + are essential for precise media synchronization! +*/ + +typedef struct ASIOChannelInfo +{ + long channel; // on input, channel index + ASIOBool isInput; // on input + ASIOBool isActive; // on exit + long channelGroup; // dto + ASIOSampleType type; // dto + char name[32]; // dto +} ASIOChannelInfo; + +ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info); +/* Purpose: + retreive information about the nature of a channel + Parameter: + info: pointer to a ASIOChannelInfo structure with + - channel: on input, the channel index of the channel in question. + - isInput: on input, ASIOTrue if info for an input channel is + requested, else output + - channelGroup: on return, the channel group that the channel + belongs to. For drivers which support different types of + channels, like analog, S/PDIF, AES/EBU, ADAT etc interfaces, + there should be a reasonable grouping of these types. Groups + are always independant form a channel index, that is, a channel + index always counts from 0 to numInputs/numOutputs regardless + of the group it may belong to. + There will always be at least one group (group 0). Please + also note that by default, the host may decide to activate + channels 0 and 1; thus, these should belong to the most + useful type (analog i/o, if present). + - type: on return, contains the sample type of the channel + - isActive: on return, ASIOTrue if channel is active as it was + installed by ASIOCreateBuffers(), ASIOFalse else + - name: describing the type of channel in question. Used to allow + for user selection, and enabling of specific channels. examples: + "Analog In", "SPDIF Out" etc + Returns: + If no input/output is present ASE_NotPresent will be returned. + Notes: + If possible, the string should be organised such that the first + characters are most significantly describing the nature of the + port, to allow for identification even if the view showing the + port name is too small to display more than 8 characters, for + instance. +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Buffer preparation +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIOBufferInfo +{ + ASIOBool isInput; // on input: ASIOTrue: input, else output + long channelNum; // on input: channel index + void *buffers[2]; // on output: double buffer addresses +} ASIOBufferInfo; + +ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks); + +/* Purpose: + Allocates input/output buffers for all input and output channels to be activated. + Parameter: + bufferInfos is a pointer to an array of ASIOBufferInfo structures: + - isInput: on input, ASIOTrue if the buffer is to be allocated + for an input, output buffer else + - channelNum: on input, the index of the channel in question + (counting from 0) + - buffers: on exit, 2 pointers to the halves of the channels' double-buffer. + the size of the buffer(s) of course depend on both the ASIOSampleType + as obtained from ASIOGetChannelInfo(), and bufferSize + numChannels is the sum of all input and output channels to be created; + thus bufferInfos is a pointer to an array of numChannels ASIOBufferInfo + structures. + bufferSize selects one of the possible buffer sizes as obtained from + ASIOGetBufferSizes(). + callbacks is a pointer to an ASIOCallbacks structure. + Returns: + If not enough memory is available ASE_NoMemory will be returned. + If no input/output is present ASE_NotPresent will be returned. + If bufferSize is not supported, or one or more of the bufferInfos elements + contain invalid settings, ASE_InvalidMode will be returned. + Notes: + If individual channel selection is not possible but requested, + the driver has to handle this. namely, bufferSwitch() will only + have filled buffers of enabled outputs. If possible, processing + and buss activities overhead should be avoided for channels which + were not enabled here. +*/ + +ASIOError ASIODisposeBuffers(void); +/* Purpose: + Releases all buffers for the device. + Parameter: + None. + Returns: + If no buffer were ever prepared, ASE_InvalidMode will be returned. + If no input/output is present ASE_NotPresent will be returned. + Notes: + This implies ASIOStop(). +*/ + +ASIOError ASIOControlPanel(void); +/* Purpose: + request the driver to start a control panel component + for device specific user settings. This will not be + accessed on some platforms (where the component is accessed + instead). + Parameter: + None. + Returns: + If no panel is available ASE_NotPresent will be returned. + Actually, the return code is ignored. + Notes: + if the user applied settings which require a re-configuration + of parts or all of the enigine and/or driver (such as a change of + the block size), the asioMessage callback can be used (see + ASIO_Callbacks). +*/ + +ASIOError ASIOFuture(long selector, void *params); +/* Purpose: + various + Parameter: + selector: operation Code as to be defined. zero is reserved for + testing purposes. + params: depends on the selector; usually pointer to a structure + for passing and retreiving any type and amount of parameters. + Returns: + the return value is also selector dependant. if the selector + is unknown, ASE_InvalidParameter should be returned to prevent + further calls with this selector. on success, ASE_SUCCESS + must be returned (note: ASE_OK is *not* sufficient!) + Notes: + see selectors defined below. +*/ + +enum +{ + kAsioEnableTimeCodeRead = 1, // no arguments + kAsioDisableTimeCodeRead, // no arguments + kAsioSetInputMonitor, // ASIOInputMonitor* in params + kAsioTransport, // ASIOTransportParameters* in params + kAsioSetInputGain, // ASIOChannelControls* in params, apply gain + kAsioGetInputMeter, // ASIOChannelControls* in params, fill meter + kAsioSetOutputGain, // ASIOChannelControls* in params, apply gain + kAsioGetOutputMeter, // ASIOChannelControls* in params, fill meter + kAsioCanInputMonitor, // no arguments for kAsioCanXXX selectors + kAsioCanTimeInfo, + kAsioCanTimeCode, + kAsioCanTransport, + kAsioCanInputGain, + kAsioCanInputMeter, + kAsioCanOutputGain, + kAsioCanOutputMeter, + + // DSD support + // The following extensions are required to allow switching + // and control of the DSD subsystem. + kAsioSetIoFormat = 0x23111961, /* ASIOIoFormat * in params. */ + kAsioGetIoFormat = 0x23111983, /* ASIOIoFormat * in params. */ + kAsioCanDoIoFormat = 0x23112004, /* ASIOIoFormat * in params. */ +}; + +typedef struct ASIOInputMonitor +{ + long input; // this input was set to monitor (or off), -1: all + long output; // suggested output for monitoring the input (if so) + long gain; // suggested gain, ranging 0 - 0x7fffffffL (-inf to +12 dB) + ASIOBool state; // ASIOTrue => on, ASIOFalse => off + long pan; // suggested pan, 0 => all left, 0x7fffffff => right +} ASIOInputMonitor; + +typedef struct ASIOChannelControls +{ + long channel; // on input, channel index + ASIOBool isInput; // on input + long gain; // on input, ranges 0 thru 0x7fffffff + long meter; // on return, ranges 0 thru 0x7fffffff + char future[32]; +} ASIOChannelControls; + +typedef struct ASIOTransportParameters +{ + long command; // see enum below + ASIOSamples samplePosition; + long track; + long trackSwitches[16]; // 512 tracks on/off + char future[64]; +} ASIOTransportParameters; + +enum +{ + kTransStart = 1, + kTransStop, + kTransLocate, // to samplePosition + kTransPunchIn, + kTransPunchOut, + kTransArmOn, // track + kTransArmOff, // track + kTransMonitorOn, // track + kTransMonitorOff, // track + kTransArm, // trackSwitches + kTransMonitor // trackSwitches +}; + +/* +// DSD support +// Some notes on how to use ASIOIoFormatType. +// +// The caller will fill the format with the request types. +// If the board can do the request then it will leave the +// values unchanged. If the board does not support the +// request then it will change that entry to Invalid (-1) +// +// So to request DSD then +// +// ASIOIoFormat NeedThis={kASIODSDFormat}; +// +// if(ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat,&NeedThis) ){ +// // If the board did not accept one of the parameters then the +// // whole call will fail and the failing parameter will +// // have had its value changes to -1. +// } +// +// Note: Switching between the formats need to be done before the "prepared" +// state (see ASIO 2 documentation) is entered. +*/ +typedef long int ASIOIoFormatType; +enum ASIOIoFormatType_e +{ + kASIOFormatInvalid = -1, + kASIOPCMFormat = 0, + kASIODSDFormat = 1, +}; + +typedef struct ASIOIoFormat_s +{ + ASIOIoFormatType FormatType; + char future[512-sizeof(ASIOIoFormatType)]; +} ASIOIoFormat; + + +ASIOError ASIOOutputReady(void); +/* Purpose: + this tells the driver that the host has completed processing + the output buffers. if the data format required by the hardware + differs from the supported asio formats, but the hardware + buffers are DMA buffers, the driver will have to convert + the audio stream data; as the bufferSwitch callback is + usually issued at dma block switch time, the driver will + have to convert the *previous* host buffer, which increases + the output latency by one block. + when the host finds out that ASIOOutputReady() returns + true, it will issue this call whenever it completed + output processing. then the driver can convert the + host data directly to the dma buffer to be played next, + reducing output latency by one block. + another way to look at it is, that the buffer switch is called + in order to pass the *input* stream to the host, so that it can + process the input into the output, and the output stream is passed + to the driver when the host has completed its process. + Parameter: + None + Returns: + only if the above mentioned scenario is given, and a reduction + of output latency can be acheived by this mechanism, should + ASE_OK be returned. otherwise (and usually), ASE_NotPresent + should be returned in order to prevent further calls to this + function. note that the host may want to determine if it is + to use this when the system is not yet fully initialized, so + ASE_OK should always be returned if the mechanism makes sense. + Notes: + please remeber to adjust ASIOGetLatencies() according to + whether ASIOOutputReady() was ever called or not, if your + driver supports this scenario. + also note that the engine may fail to call ASIO_OutputReady() + in time in overload cases. as already mentioned, bufferSwitch + should be called for every block regardless of whether a block + could be processed in time. +*/ + +// restore old alignment +#if defined(_MSC_VER) && !defined(__MWERKS__) +#pragma pack(pop) +#elif PRAGMA_ALIGN_SUPPORTED +#pragma options align = reset +#endif + +#endif + diff --git a/src/deps/rtaudio-mod/include/asiodrivers.cpp b/src/deps/rtaudio-mod/include/asiodrivers.cpp new file mode 100644 index 0000000..5f56454 --- /dev/null +++ b/src/deps/rtaudio-mod/include/asiodrivers.cpp @@ -0,0 +1,186 @@ +#include +#include "asiodrivers.h" + +AsioDrivers* asioDrivers = 0; + +bool loadAsioDriver(char *name); + +bool loadAsioDriver(char *name) +{ + if(!asioDrivers) + asioDrivers = new AsioDrivers(); + if(asioDrivers) + return asioDrivers->loadDriver(name); + return false; +} + +//------------------------------------------------------------------------------------ + +#if MAC + +bool resolveASIO(unsigned long aconnID); + +AsioDrivers::AsioDrivers() : CodeFragments("ASIO Drivers", 'AsDr', 'Asio') +{ + connID = -1; + curIndex = -1; +} + +AsioDrivers::~AsioDrivers() +{ + removeCurrentDriver(); +} + +bool AsioDrivers::getCurrentDriverName(char *name) +{ + if(curIndex >= 0) + return getName(curIndex, name); + return false; +} + +long AsioDrivers::getDriverNames(char **names, long maxDrivers) +{ + for(long i = 0; i < getNumFragments() && i < maxDrivers; i++) + getName(i, names[i]); + return getNumFragments() < maxDrivers ? getNumFragments() : maxDrivers; +} + +bool AsioDrivers::loadDriver(char *name) +{ + char dname[64]; + unsigned long newID; + + for(long i = 0; i < getNumFragments(); i++) + { + if(getName(i, dname) && !strcmp(name, dname)) + { + if(newInstance(i, &newID)) + { + if(resolveASIO(newID)) + { + if(connID != -1) + removeInstance(curIndex, connID); + curIndex = i; + connID = newID; + return true; + } + } + break; + } + } + return false; +} + +void AsioDrivers::removeCurrentDriver() +{ + if(connID != -1) + removeInstance(curIndex, connID); + connID = -1; + curIndex = -1; +} + +//------------------------------------------------------------------------------------ + +#elif WINDOWS + +#include "iasiodrv.h" + +extern IASIO* theAsioDriver; + +AsioDrivers::AsioDrivers() : AsioDriverList() +{ + curIndex = -1; +} + +AsioDrivers::~AsioDrivers() +{ +} + +bool AsioDrivers::getCurrentDriverName(char *name) +{ + if(curIndex >= 0) + return asioGetDriverName(curIndex, name, 32) == 0 ? true : false; + name[0] = 0; + return false; +} + +long AsioDrivers::getDriverNames(char **names, long maxDrivers) +{ + for(long i = 0; i < asioGetNumDev() && i < maxDrivers; i++) + asioGetDriverName(i, names[i], 32); + return asioGetNumDev() < maxDrivers ? asioGetNumDev() : maxDrivers; +} + +bool AsioDrivers::loadDriver(char *name) +{ + char dname[64]; + char curName[64]; + + for(long i = 0; i < asioGetNumDev(); i++) + { + if(!asioGetDriverName(i, dname, 32) && !strcmp(name, dname)) + { + curName[0] = 0; + getCurrentDriverName(curName); // in case we fail... + removeCurrentDriver(); + + if(!asioOpenDriver(i, (void **)&theAsioDriver)) + { + curIndex = i; + return true; + } + else + { + theAsioDriver = 0; + if(curName[0] && strcmp(dname, curName)) + loadDriver(curName); // try restore + } + break; + } + } + return false; +} + +void AsioDrivers::removeCurrentDriver() +{ + if(curIndex != -1) + asioCloseDriver(curIndex); + curIndex = -1; +} + +#elif SGI || BEOS + +#include "asiolist.h" + +AsioDrivers::AsioDrivers() + : AsioDriverList() +{ + curIndex = -1; +} + +AsioDrivers::~AsioDrivers() +{ +} + +bool AsioDrivers::getCurrentDriverName(char *name) +{ + return false; +} + +long AsioDrivers::getDriverNames(char **names, long maxDrivers) +{ + return 0; +} + +bool AsioDrivers::loadDriver(char *name) +{ + return false; +} + +void AsioDrivers::removeCurrentDriver() +{ +} + +#else +#error implement me +#endif diff --git a/src/deps/rtaudio-mod/include/asiodrivers.h b/src/deps/rtaudio-mod/include/asiodrivers.h new file mode 100644 index 0000000..2ddf7ad --- /dev/null +++ b/src/deps/rtaudio-mod/include/asiodrivers.h @@ -0,0 +1,41 @@ +#ifndef __AsioDrivers__ +#define __AsioDrivers__ + +#include "ginclude.h" + +#if MAC +#include "CodeFragments.hpp" + +class AsioDrivers : public CodeFragments + +#elif WINDOWS +#include +#include "asiolist.h" + +class AsioDrivers : public AsioDriverList + +#elif SGI || BEOS +#include "asiolist.h" + +class AsioDrivers : public AsioDriverList + +#else +#error implement me +#endif + +{ +public: + AsioDrivers(); + ~AsioDrivers(); + + bool getCurrentDriverName(char *name); + long getDriverNames(char **names, long maxDrivers); + bool loadDriver(char *name); + void removeCurrentDriver(); + long getCurrentDriverIndex() {return curIndex;} +protected: + unsigned long connID; + long curIndex; +}; + +#endif diff --git a/src/deps/rtaudio-mod/include/asiodrvr.h b/src/deps/rtaudio-mod/include/asiodrvr.h new file mode 100644 index 0000000..663f75a --- /dev/null +++ b/src/deps/rtaudio-mod/include/asiodrvr.h @@ -0,0 +1,76 @@ +/* + Steinberg Audio Stream I/O API + (c) 1996, Steinberg Soft- und Hardware GmbH + charlie (May 1996) + + asiodrvr.h + c++ superclass to implement asio functionality. from this, + you can derive whatever required +*/ + +#ifndef _asiodrvr_ +#define _asiodrvr_ + +// cpu and os system we are running on +#include "asiosys.h" +// basic "C" interface +#include "asio.h" + +class AsioDriver; +extern AsioDriver *getDriver(); // for generic constructor + +#if WINDOWS +#include +#include "combase.h" +#include "iasiodrv.h" +class AsioDriver : public IASIO ,public CUnknown +{ +public: + AsioDriver(LPUNKNOWN pUnk, HRESULT *phr); + + DECLARE_IUNKNOWN + // Factory method + static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr); + // IUnknown + virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface(REFIID riid,void **ppvObject); + +#else + +class AsioDriver +{ +public: + AsioDriver(); +#endif + virtual ~AsioDriver(); + + virtual ASIOBool init(void* sysRef); + virtual void getDriverName(char *name); // max 32 bytes incl. terminating zero + virtual long getDriverVersion(); + virtual void getErrorMessage(char *string); // max 124 bytes incl. + + virtual ASIOError start(); + virtual ASIOError stop(); + + virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels); + virtual ASIOError getLatencies(long *inputLatency, long *outputLatency); + virtual ASIOError getBufferSize(long *minSize, long *maxSize, + long *preferredSize, long *granularity); + + virtual ASIOError canSampleRate(ASIOSampleRate sampleRate); + virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate); + virtual ASIOError setSampleRate(ASIOSampleRate sampleRate); + virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources); + virtual ASIOError setClockSource(long reference); + + virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp); + virtual ASIOError getChannelInfo(ASIOChannelInfo *info); + + virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks); + virtual ASIOError disposeBuffers(); + + virtual ASIOError controlPanel(); + virtual ASIOError future(long selector, void *opt); + virtual ASIOError outputReady(); +}; +#endif diff --git a/src/deps/rtaudio-mod/include/asiolist.cpp b/src/deps/rtaudio-mod/include/asiolist.cpp new file mode 100644 index 0000000..e4c73c2 --- /dev/null +++ b/src/deps/rtaudio-mod/include/asiolist.cpp @@ -0,0 +1,308 @@ +#include +#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-mod/include/asiolist.h b/src/deps/rtaudio-mod/include/asiolist.h new file mode 100644 index 0000000..01c64f0 --- /dev/null +++ b/src/deps/rtaudio-mod/include/asiolist.h @@ -0,0 +1,46 @@ +#ifndef __asiolist__ +#define __asiolist__ + +#define DRVERR -5000 +#define DRVERR_INVALID_PARAM DRVERR-1 +#define DRVERR_DEVICE_ALREADY_OPEN DRVERR-2 +#define DRVERR_DEVICE_NOT_FOUND DRVERR-3 + +#define MAXPATHLEN 512 +#define MAXDRVNAMELEN 128 + +struct asiodrvstruct +{ + int drvID; + CLSID clsid; + char dllpath[MAXPATHLEN]; + char drvname[MAXDRVNAMELEN]; + LPVOID asiodrv; + struct asiodrvstruct *next; +}; + +typedef struct asiodrvstruct ASIODRVSTRUCT; +typedef ASIODRVSTRUCT *LPASIODRVSTRUCT; + +class AsioDriverList { +public: + AsioDriverList(); + ~AsioDriverList(); + + LONG asioOpenDriver (int,VOID **); + LONG asioCloseDriver (int); + + // nice to have + LONG asioGetNumDev (VOID); + LONG asioGetDriverName (int,char *,int); + LONG asioGetDriverPath (int,char *,int); + LONG asioGetDriverCLSID (int,CLSID *); + + // or use directly access + LPASIODRVSTRUCT lpdrvlist; + int numdrv; +}; + +typedef class AsioDriverList *LPASIODRIVERLIST; + +#endif diff --git a/src/deps/rtaudio-mod/include/asiosys.h b/src/deps/rtaudio-mod/include/asiosys.h new file mode 100644 index 0000000..37f7a48 --- /dev/null +++ b/src/deps/rtaudio-mod/include/asiosys.h @@ -0,0 +1,82 @@ +#ifndef __asiosys__ + #define __asiosys__ + + #ifdef WIN32 + #undef MAC + #define PPC 0 + #define WINDOWS 1 + #define SGI 0 + #define SUN 0 + #define LINUX 0 + #define BEOS 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #elif BEOS + #define MAC 0 + #define PPC 0 + #define WINDOWS 0 + #define PC 0 + #define SGI 0 + #define SUN 0 + #define LINUX 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #ifndef DEBUG + #define DEBUG 0 + #if DEBUG + void DEBUGGERMESSAGE(char *string); + #else + #define DEBUGGERMESSAGE(a) + #endif + #endif + + #elif SGI + #define MAC 0 + #define PPC 0 + #define WINDOWS 0 + #define PC 0 + #define SUN 0 + #define LINUX 0 + #define BEOS 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #ifndef DEBUG + #define DEBUG 0 + #if DEBUG + void DEBUGGERMESSAGE(char *string); + #else + #define DEBUGGERMESSAGE(a) + #endif + #endif + + #else // MAC + + #define MAC 1 + #define PPC 1 + #define WINDOWS 0 + #define PC 0 + #define SGI 0 + #define SUN 0 + #define LINUX 0 + #define BEOS 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #ifndef DEBUG + #define DEBUG 0 + #if DEBUG + void DEBUGGERMESSAGE(char *string); + #else + #define DEBUGGERMESSAGE(a) + #endif + #endif + #endif + +#endif diff --git a/src/deps/rtaudio-mod/include/dsound.h b/src/deps/rtaudio-mod/include/dsound.h new file mode 100644 index 0000000..cb19cca --- /dev/null +++ b/src/deps/rtaudio-mod/include/dsound.h @@ -0,0 +1,2369 @@ +/*==========================================================================; + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * File: dsound.h + * Content: DirectSound include file + * + **************************************************************************/ + +#define COM_NO_WINDOWS_H +#include +#include + +#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-mod/include/ginclude.h b/src/deps/rtaudio-mod/include/ginclude.h new file mode 100644 index 0000000..b627dc2 --- /dev/null +++ b/src/deps/rtaudio-mod/include/ginclude.h @@ -0,0 +1,38 @@ +#ifndef __gInclude__ +#define __gInclude__ + +#if SGI + #undef BEOS + #undef MAC + #undef WINDOWS + // + #define ASIO_BIG_ENDIAN 1 + #define ASIO_CPU_MIPS 1 +#elif defined WIN32 + #undef BEOS + #undef MAC + #undef SGI + #define WINDOWS 1 + #define ASIO_LITTLE_ENDIAN 1 + #define ASIO_CPU_X86 1 +#elif BEOS + #undef MAC + #undef SGI + #undef WINDOWS + #define ASIO_LITTLE_ENDIAN 1 + #define ASIO_CPU_X86 1 + // +#else + #define MAC 1 + #undef BEOS + #undef WINDOWS + #undef SGI + #define ASIO_BIG_ENDIAN 1 + #define ASIO_CPU_PPC 1 +#endif + +// always +#define NATIVE_INT64 0 +#define IEEE754_64FLOAT 1 + +#endif // __gInclude__ diff --git a/src/deps/rtaudio-mod/include/iasiodrv.h b/src/deps/rtaudio-mod/include/iasiodrv.h new file mode 100644 index 0000000..64d2dbb --- /dev/null +++ b/src/deps/rtaudio-mod/include/iasiodrv.h @@ -0,0 +1,37 @@ +#include "asiosys.h" +#include "asio.h" + +/* Forward Declarations */ + +#ifndef __ASIODRIVER_FWD_DEFINED__ +#define __ASIODRIVER_FWD_DEFINED__ +typedef interface IASIO IASIO; +#endif /* __ASIODRIVER_FWD_DEFINED__ */ + +interface IASIO : public IUnknown +{ + + virtual ASIOBool init(void *sysHandle) = 0; + virtual void getDriverName(char *name) = 0; + virtual long getDriverVersion() = 0; + virtual void getErrorMessage(char *string) = 0; + virtual ASIOError start() = 0; + virtual ASIOError stop() = 0; + virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0; + virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0; + virtual ASIOError getBufferSize(long *minSize, long *maxSize, + long *preferredSize, long *granularity) = 0; + virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0; + virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0; + virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0; + virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0; + virtual ASIOError setClockSource(long reference) = 0; + virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; + virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0; + virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks) = 0; + virtual ASIOError disposeBuffers() = 0; + virtual ASIOError controlPanel() = 0; + virtual ASIOError future(long selector,void *opt) = 0; + virtual ASIOError outputReady() = 0; +}; diff --git a/src/deps/rtaudio-mod/include/iasiothiscallresolver.cpp b/src/deps/rtaudio-mod/include/iasiothiscallresolver.cpp new file mode 100644 index 0000000..08c55ea --- /dev/null +++ b/src/deps/rtaudio-mod/include/iasiothiscallresolver.cpp @@ -0,0 +1,572 @@ +/* + 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 + + 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 +#include + +// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is +// #include'd before it in client code, we do NOT want to do this test here. +#define iasiothiscallresolver_sourcefile 1 +#include "iasiothiscallresolver.h" +#undef iasiothiscallresolver_sourcefile + +// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want +// this macro defined in this translation unit. +#undef ASIOInit + + +// theAsioDriver is a global pointer to the current IASIO instance which the +// ASIO SDK uses to perform all actions on the IASIO interface. We substitute +// our own forwarding interface into this pointer. +extern IASIO* theAsioDriver; + + +// The following macros define the inline assembler for BORLAND first then gcc + +#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__) + + +#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\ + void *this_ = (thisPtr); \ + __asm { \ + mov ecx, this_ ; \ + mov eax, [ecx] ; \ + call [eax+funcOffset] ; \ + mov resultName, eax ; \ + } + + +#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\ + void *this_ = (thisPtr); \ + __asm { \ + mov eax, param1 ; \ + push eax ; \ + mov ecx, this_ ; \ + mov eax, [ecx] ; \ + call [eax+funcOffset] ; \ + } + + +#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\ + void *this_ = (thisPtr); \ + __asm { \ + mov eax, param1 ; \ + push eax ; \ + mov ecx, this_ ; \ + mov eax, [ecx] ; \ + call [eax+funcOffset] ; \ + mov resultName, eax ; \ + } + + +#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\ + void *this_ = (thisPtr); \ + void *doubleParamPtr_ (¶m1); \ + __asm { \ + mov eax, doubleParamPtr_ ; \ + push [eax+4] ; \ + push [eax] ; \ + mov ecx, this_ ; \ + mov eax, [ecx] ; \ + call [eax+funcOffset] ; \ + mov resultName, eax ; \ + } + + +#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\ + void *this_ = (thisPtr); \ + __asm { \ + mov eax, param2 ; \ + push eax ; \ + mov eax, param1 ; \ + push eax ; \ + mov ecx, this_ ; \ + mov eax, [ecx] ; \ + call [eax+funcOffset] ; \ + mov resultName, eax ; \ + } + + +#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ + void *this_ = (thisPtr); \ + __asm { \ + mov eax, param4 ; \ + push eax ; \ + mov eax, param3 ; \ + push eax ; \ + mov eax, param2 ; \ + push eax ; \ + mov eax, param1 ; \ + push eax ; \ + mov ecx, this_ ; \ + mov eax, [ecx] ; \ + call [eax+funcOffset] ; \ + mov resultName, eax ; \ + } + + +#elif defined(__GNUC__) + + +#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \ + __asm__ __volatile__ ("movl (%1), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"c"(thisPtr) /* Input Operands */ \ + : "%edx" /* Clobbered Registers */ \ + ); \ + + +#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \ + __asm__ __volatile__ ("pushl %0\n\t" \ + "movl (%1), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + : /* Output Operands */ \ + :"r"(param1), /* Input Operands */ \ + "c"(thisPtr) \ + : "%edx" /* Clobbered Registers */ \ + ); \ + + +#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \ + __asm__ __volatile__ ("pushl %1\n\t" \ + "movl (%2), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"r"(param1), /* Input Operands */ \ + "c"(thisPtr) \ + : "%edx" /* Clobbered Registers */ \ + ); \ + + +#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \ + do { \ + double param1f64 = param1; /* Cast explicitly to double */ \ + double *param1f64Ptr = ¶m1f64; /* Make pointer to address */ \ + __asm__ __volatile__ ("pushl 4(%1)\n\t" \ + "pushl (%1)\n\t" \ + "movl (%2), %%edx\n\t" \ + "call *"#funcOffset"(%%edx);\n\t" \ + : "=a"(resultName) /* Output Operands */ \ + : "r"(param1f64Ptr), /* Input Operands */ \ + "c"(thisPtr), \ + "m"(*param1f64Ptr) /* Using address */ \ + : "%edx" /* Clobbered Registers */ \ + ); \ + } while (0); \ + + +#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \ + __asm__ __volatile__ ("pushl %1\n\t" \ + "pushl %2\n\t" \ + "movl (%3), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"r"(param2), /* Input Operands */ \ + "r"(param1), \ + "c"(thisPtr) \ + : "%edx" /* Clobbered Registers */ \ + ); \ + + +#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ + __asm__ __volatile__ ("pushl %1\n\t" \ + "pushl %2\n\t" \ + "pushl %3\n\t" \ + "pushl %4\n\t" \ + "movl (%5), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"r"(param4), /* Input Operands */ \ + "r"(param3), \ + "r"(param2), \ + "r"(param1), \ + "c"(thisPtr) \ + : "%edx" /* Clobbered Registers */ \ + ); \ + +#endif + + + +// Our static singleton instance. +IASIOThiscallResolver IASIOThiscallResolver::instance; + +// Constructor called to initialize static Singleton instance above. Note that +// it is important not to clear that_ incase it has already been set by the call +// to placement new in ASIOInit(). +IASIOThiscallResolver::IASIOThiscallResolver() +{ +} + +// Constructor called from ASIOInit() below +IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that) +: that_( that ) +{ +} + +// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not +// really a COM object, just a wrapper which will work with the ASIO SDK. +// If you wanted to use ASIO without the SDK you might want to implement COM +// aggregation in these methods. +HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv) +{ + (void)riid; // suppress unused variable warning + + assert( false ); // this function should never be called by the ASIO SDK. + + *ppv = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef() +{ + assert( false ); // this function should never be called by the ASIO SDK. + + return 1; +} + +ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release() +{ + assert( false ); // this function should never be called by the ASIO SDK. + + return 1; +} + + +// Implement the IASIO interface methods by performing the vptr manipulation +// described above then delegating to the real implementation. +ASIOBool IASIOThiscallResolver::init(void *sysHandle) +{ + ASIOBool result; + CALL_THISCALL_1( result, that_, 12, sysHandle ); + return result; +} + +void IASIOThiscallResolver::getDriverName(char *name) +{ + CALL_VOID_THISCALL_1( that_, 16, name ); +} + +long IASIOThiscallResolver::getDriverVersion() +{ + ASIOBool result; + CALL_THISCALL_0( result, that_, 20 ); + return result; +} + +void IASIOThiscallResolver::getErrorMessage(char *string) +{ + CALL_VOID_THISCALL_1( that_, 24, string ); +} + +ASIOError IASIOThiscallResolver::start() +{ + ASIOBool result; + CALL_THISCALL_0( result, that_, 28 ); + return result; +} + +ASIOError IASIOThiscallResolver::stop() +{ + ASIOBool result; + CALL_THISCALL_0( result, that_, 32 ); + return result; +} + +ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels) +{ + ASIOBool result; + CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels ); + return result; +} + +ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency) +{ + ASIOBool result; + CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency ); + return result; +} + +ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize, + long *preferredSize, long *granularity) +{ + ASIOBool result; + CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity ); + return result; +} + +ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate) +{ + ASIOBool result; + CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate ); + return result; +} + +ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate) +{ + ASIOBool result; + CALL_THISCALL_1( result, that_, 52, sampleRate ); + return result; +} + +ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate) +{ + ASIOBool result; + CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate ); + return result; +} + +ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources) +{ + ASIOBool result; + CALL_THISCALL_2( result, that_, 60, clocks, numSources ); + return result; +} + +ASIOError IASIOThiscallResolver::setClockSource(long reference) +{ + ASIOBool result; + CALL_THISCALL_1( result, that_, 64, reference ); + return result; +} + +ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) +{ + ASIOBool result; + CALL_THISCALL_2( result, that_, 68, sPos, tStamp ); + return result; +} + +ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info) +{ + ASIOBool result; + CALL_THISCALL_1( result, that_, 72, info ); + return result; +} + +ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos, + long numChannels, long bufferSize, ASIOCallbacks *callbacks) +{ + ASIOBool result; + CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks ); + return result; +} + +ASIOError IASIOThiscallResolver::disposeBuffers() +{ + ASIOBool result; + CALL_THISCALL_0( result, that_, 80 ); + return result; +} + +ASIOError IASIOThiscallResolver::controlPanel() +{ + ASIOBool result; + CALL_THISCALL_0( result, that_, 84 ); + return result; +} + +ASIOError IASIOThiscallResolver::future(long selector,void *opt) +{ + ASIOBool result; + CALL_THISCALL_2( result, that_, 88, selector, opt ); + return result; +} + +ASIOError IASIOThiscallResolver::outputReady() +{ + ASIOBool result; + CALL_THISCALL_0( result, that_, 92 ); + return result; +} + + +// Implement our substitute ASIOInit() method +ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info) +{ + // To ensure that our instance's vptr is correctly constructed, even if + // ASIOInit is called prior to main(), we explicitly call its constructor + // (potentially over the top of an existing instance). Note that this is + // pretty ugly, and is only safe because IASIOThiscallResolver has no + // destructor and contains no objects with destructors. + new((void*)&instance) IASIOThiscallResolver( theAsioDriver ); + + // Interpose between ASIO client code and the real driver. + theAsioDriver = &instance; + + // Note that we never need to switch theAsioDriver back to point to the + // real driver because theAsioDriver is reset to zero in ASIOExit(). + + // Delegate to the real ASIOInit + return ::ASIOInit(info); +} + + +#endif /* !defined(_MSC_VER) */ + +#endif /* Win32 */ + diff --git a/src/deps/rtaudio-mod/include/iasiothiscallresolver.h b/src/deps/rtaudio-mod/include/iasiothiscallresolver.h new file mode 100644 index 0000000..63e91ca --- /dev/null +++ b/src/deps/rtaudio-mod/include/iasiothiscallresolver.h @@ -0,0 +1,202 @@ +// **************************************************************************** +// +// Changed: I have modified this file slightly (includes) to work with +// RtAudio. RtAudio.cpp must include this file after asio.h. +// +// File: IASIOThiscallResolver.h +// Description: The IASIOThiscallResolver class implements the IASIO +// interface and acts as a proxy to the real IASIO interface by +// calling through its vptr table using the thiscall calling +// convention. To put it another way, we interpose +// IASIOThiscallResolver between ASIO SDK code and the driver. +// This is necessary because most non-Microsoft compilers don't +// implement the thiscall calling convention used by IASIO. +// +// iasiothiscallresolver.cpp contains the background of this +// problem plus a technical description of the vptr +// manipulations. +// +// In order to use this mechanism one simply has to add +// iasiothiscallresolver.cpp to the list of files to compile +// and #include +// +// Note that this #include must come after the other ASIO SDK +// #includes, for example: +// +// #include +// #include +// #include +// #include +// #include +// +// Actually the important thing is to #include +// after . We have +// incorporated a test to enforce this ordering. +// +// The code transparently takes care of the interposition by +// using macro substitution to intercept calls to ASIOInit() +// and ASIOExit(). We save the original ASIO global +// "theAsioDriver" in our "that" variable, and then set +// "theAsioDriver" to equal our IASIOThiscallResolver instance. +// +// Whilst this method of resolving the thiscall problem requires +// the addition of #include to client +// code it has the advantage that it does not break the terms +// of the ASIO licence by publishing it. We are NOT modifying +// any Steinberg code here, we are merely implementing the IASIO +// interface in the same way that we would need to do if we +// wished to provide an open source ASIO driver. +// +// For compilation with MinGW -lole32 needs to be added to the +// linker options. For BORLAND, linking with Import32.lib is +// sufficient. +// +// The dependencies are with: CoInitialize, CoUninitialize, +// CoCreateInstance, CLSIDFromString - used by asiolist.cpp +// and are required on Windows whether ThiscallResolver is used +// or not. +// +// Searching for the above strings in the root library path +// of your compiler should enable the correct libraries to be +// identified if they aren't immediately obvious. +// +// Note that the current implementation of IASIOThiscallResolver +// is not COM compliant - it does not correctly implement the +// IUnknown interface. Implementing it is not necessary because +// it is not called by parts of the ASIO SDK which call through +// theAsioDriver ptr. The IUnknown methods are implemented as +// assert(false) to ensure that the code fails if they are +// ever called. +// Restrictions: None. Public Domain & Open Source distribute freely +// You may use IASIOThiscallResolver commercially as well as +// privately. +// You the user assume the responsibility for the use of the +// files, binary or text, and there is no guarantee or warranty, +// expressed or implied, including but not limited to the +// implied warranties of merchantability and fitness for a +// particular purpose. You assume all responsibility and agree +// to hold no entity, copyright holder or distributors liable +// for any loss of data or inaccurate representations of data +// as a result of using IASIOThiscallResolver. +// Version: 1.4 Added separate macro CALL_THISCALL_1_DOUBLE from +// Andrew Baldwin, and volatile for whole gcc asm blocks, +// both for compatibility with newer gcc versions. Cleaned up +// Borland asm to use one less register. +// 1.3 Switched to including assert.h for better compatibility. +// Wrapped entire .h and .cpp contents with a check for +// _MSC_VER to provide better compatibility with MS compilers. +// Changed Singleton implementation to use static instance +// instead of freestore allocated instance. Removed ASIOExit +// macro as it is no longer needed. +// 1.2 Removed semicolons from ASIOInit and ASIOExit macros to +// allow them to be embedded in expressions (if statements). +// Cleaned up some comments. Removed combase.c dependency (it +// doesn't compile with BCB anyway) by stubbing IUnknown. +// 1.1 Incorporated comments from Ross Bencina including things +// such as changing name from ThiscallResolver to +// IASIOThiscallResolver, tidying up the constructor, fixing +// a bug in IASIOThiscallResolver::ASIOExit() and improving +// portability through the use of conditional compilation +// 1.0 Initial working version. +// Created: 6/09/2003 +// Authors: Fraser Adams +// Ross Bencina +// Rene G. Ceballos +// Martin Fay +// Antti Silvast +// Andrew Baldwin +// +// **************************************************************************** + + +#ifndef included_iasiothiscallresolver_h +#define included_iasiothiscallresolver_h + +// 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__) +#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) + + +// The following is in order to ensure that this header is only included after +// the other ASIO headers (except for the case of iasiothiscallresolver.cpp). +// We need to do this because IASIOThiscallResolver works by eclipsing the +// original definition of ASIOInit() with a macro (see below). +#if !defined(iasiothiscallresolver_sourcefile) + #if !defined(__ASIO_H) + #error iasiothiscallresolver.h must be included AFTER asio.h + #endif +#endif + +#include +#include "iasiodrv.h" /* From ASIO SDK */ + + +class IASIOThiscallResolver : public IASIO { +private: + IASIO* that_; // Points to the real IASIO + + static IASIOThiscallResolver instance; // Singleton instance + + // Constructors - declared private so construction is limited to + // our Singleton instance + IASIOThiscallResolver(); + IASIOThiscallResolver(IASIO* that); +public: + + // Methods from the IUnknown interface. We don't fully implement IUnknown + // because the ASIO SDK never calls these methods through theAsioDriver ptr. + // These methods are implemented as assert(false). + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // Methods from the IASIO interface, implemented as forwarning calls to that. + virtual ASIOBool init(void *sysHandle); + virtual void getDriverName(char *name); + virtual long getDriverVersion(); + virtual void getErrorMessage(char *string); + virtual ASIOError start(); + virtual ASIOError stop(); + virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels); + virtual ASIOError getLatencies(long *inputLatency, long *outputLatency); + virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); + virtual ASIOError canSampleRate(ASIOSampleRate sampleRate); + virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate); + virtual ASIOError setSampleRate(ASIOSampleRate sampleRate); + virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources); + virtual ASIOError setClockSource(long reference); + virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp); + virtual ASIOError getChannelInfo(ASIOChannelInfo *info); + virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks); + virtual ASIOError disposeBuffers(); + virtual ASIOError controlPanel(); + virtual ASIOError future(long selector,void *opt); + virtual ASIOError outputReady(); + + // Class method, see ASIOInit() macro below. + static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit +}; + + +// Replace calls to ASIOInit with our interposing version. +// This macro enables us to perform thiscall resolution simply by #including +// after the asio #includes (this file _must_ be +// included _after_ the asio #includes) + +#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name)) + + +#endif /* !defined(_MSC_VER) */ + +#endif /* Win32 */ + +#endif /* included_iasiothiscallresolver_h */ + + diff --git a/src/deps/rtaudio-mod/include/soundcard.h b/src/deps/rtaudio-mod/include/soundcard.h new file mode 100644 index 0000000..2cf3a2c --- /dev/null +++ b/src/deps/rtaudio-mod/include/soundcard.h @@ -0,0 +1,1878 @@ +/* + * 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 +#include +#ifndef _IOWR +#include +#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 // for strncpy + +//------------------------------------------------------------------------------------------------------- +// VST Version +//------------------------------------------------------------------------------------------------------- + +/** Define SDK Version (you can generate different versions (from 2.0 to 2.4) of this SDK by setting the unwanted extensions to 0). */ +#define VST_2_1_EXTENSIONS 1 ///< Version 2.1 extensions (08-06-2000) +#define VST_2_2_EXTENSIONS 1 ///< Version 2.2 extensions (08-06-2001) +#define VST_2_3_EXTENSIONS 1 ///< Version 2.3 extensions (20-05-2003) +#ifndef VST_2_4_EXTENSIONS +#define VST_2_4_EXTENSIONS 1 ///< Version 2.4 extensions (01-01-2006) +#endif + +/** Current VST Version */ +#if VST_2_4_EXTENSIONS + #define kVstVersion 2400 +#elif VST_2_3_EXTENSIONS + #define kVstVersion 2300 +#elif VST_2_2_EXTENSIONS + #define kVstVersion 2200 +#elif VST_2_1_EXTENSIONS + #define kVstVersion 2100 +#else + #define kVstVersion 2 +#endif + +/** Disable for Hosts to serve Plug-ins below VST 2.4 */ +#ifndef VST_FORCE_DEPRECATED +#define VST_FORCE_DEPRECATED VST_2_4_EXTENSIONS +#endif + +/** Declares identifier as deprecated. */ +#if VST_FORCE_DEPRECATED +#define DECLARE_VST_DEPRECATED(identifier) __##identifier##Deprecated +#else +#define DECLARE_VST_DEPRECATED(identifier) identifier +#endif + +/** Define for 64 Bit Platform. */ +#ifndef VST_64BIT_PLATFORM +#define VST_64BIT_PLATFORM _WIN64 || __LP64__ +#endif + +//------------------------------------------------------------------------------------------------------- +// Integral Types +//------------------------------------------------------------------------------------------------------- + +#ifdef WIN32 +typedef short VstInt16; ///< 16 bit integer type +typedef int VstInt32; ///< 32 bit integer type +typedef __int64 VstInt64; ///< 64 bit integer type +#else +#include +typedef int16_t VstInt16; ///< 16 bit integer type +typedef int32_t VstInt32; ///< 32 bit integer type +typedef int64_t VstInt64; ///< 64 bit integer type +#endif + +//------------------------------------------------------------------------------------------------------- +// Generic Types +//------------------------------------------------------------------------------------------------------- + +#if VST_64BIT_PLATFORM +typedef VstInt64 VstIntPtr; ///< platform-dependent integer type, same size as pointer +#else +typedef VstInt32 VstIntPtr; ///< platform-dependent integer type, same size as pointer +#endif + +//------------------------------------------------------------------------------------------------------- +// Misc. Definition +//------------------------------------------------------------------------------------------------------- +#undef CCONST +struct AEffect; + +/// @cond ignore +typedef VstIntPtr (VSTCALLBACK *audioMasterCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); +typedef VstIntPtr (VSTCALLBACK *AEffectDispatcherProc) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); +typedef void (VSTCALLBACK *AEffectProcessProc) (AEffect* effect, float** inputs, float** outputs, VstInt32 sampleFrames); +typedef void (VSTCALLBACK *AEffectProcessDoubleProc) (AEffect* effect, double** inputs, double** outputs, VstInt32 sampleFrames); +typedef void (VSTCALLBACK *AEffectSetParameterProc) (AEffect* effect, VstInt32 index, float parameter); +typedef float (VSTCALLBACK *AEffectGetParameterProc) (AEffect* effect, VstInt32 index); +/// @endcond + +/** Four Character Constant (for AEffect->uniqueID) */ +#define CCONST(a, b, c, d) \ + ((((VstInt32)a) << 24) | (((VstInt32)b) << 16) | (((VstInt32)c) << 8) | (((VstInt32)d) << 0)) + +/** AEffect magic number */ +#define kEffectMagic CCONST ('V', 's', 't', 'P') + +//------------------------------------------------------------------------------------------------------- +/** Basic VST Effect "C" Interface. */ +//------------------------------------------------------------------------------------------------------- +struct AEffect +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 magic; ///< must be #kEffectMagic ('VstP') + + /** Host to Plug-in dispatcher @see AudioEffect::dispatcher */ + AEffectDispatcherProc dispatcher; + + /** \deprecated Accumulating process mode is deprecated in VST 2.4! Use AEffect::processReplacing instead! */ + AEffectProcessProc DECLARE_VST_DEPRECATED (process); + + /** Set new value of automatable parameter @see AudioEffect::setParameter */ + AEffectSetParameterProc setParameter; + + /** Returns current value of automatable parameter @see AudioEffect::getParameter*/ + AEffectGetParameterProc getParameter; + + VstInt32 numPrograms; ///< number of programs + VstInt32 numParams; ///< all programs are assumed to have numParams parameters + VstInt32 numInputs; ///< number of audio inputs + VstInt32 numOutputs; ///< number of audio outputs + + VstInt32 flags; ///< @see VstAEffectFlags + + VstIntPtr resvd1; ///< reserved for Host, must be 0 + VstIntPtr resvd2; ///< reserved for Host, must be 0 + + VstInt32 initialDelay; ///< for algorithms which need input in the first place (Group delay or latency in Samples). This value should be initialized in a resume state. + + VstInt32 DECLARE_VST_DEPRECATED (realQualities); ///< \deprecated unused member + VstInt32 DECLARE_VST_DEPRECATED (offQualities); ///< \deprecated unused member + float DECLARE_VST_DEPRECATED (ioRatio); ///< \deprecated unused member + + void* object; ///< #AudioEffect class pointer + void* user; ///< user-defined pointer + + VstInt32 uniqueID; ///< registered unique identifier (register it at Steinberg 3rd party support Web). This is used to identify a plug-in during save+load of preset and project. + VstInt32 version; ///< plug-in version (example 1100 for version 1.1.0.0) + + /** Process audio samples in replacing mode @see AudioEffect::processReplacing */ + AEffectProcessProc processReplacing; + +#if VST_2_4_EXTENSIONS + /** Process double-precision audio samples in replacing mode @see AudioEffect::processDoubleReplacing */ + AEffectProcessDoubleProc processDoubleReplacing; + + char future[56]; ///< reserved for future use (please zero) +#else + char future[60]; ///< reserved for future use (please zero) +#endif +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** AEffect flags */ +//------------------------------------------------------------------------------------------------------- +enum VstAEffectFlags +{ +//------------------------------------------------------------------------------------------------------- + effFlagsHasEditor = 1 << 0, ///< set if the plug-in provides a custom editor + effFlagsCanReplacing = 1 << 4, ///< supports replacing process mode (which should the default mode in VST 2.4) + effFlagsProgramChunks = 1 << 5, ///< program data is handled in formatless chunks + effFlagsIsSynth = 1 << 8, ///< plug-in is a synth (VSTi), Host may assign mixer channels for its outputs + effFlagsNoSoundInStop = 1 << 9, ///< plug-in does not produce sound when input is all silence + +#if VST_2_4_EXTENSIONS + effFlagsCanDoubleReplacing = 1 << 12, ///< plug-in supports double precision processing +#endif + + DECLARE_VST_DEPRECATED (effFlagsHasClip) = 1 << 1, ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effFlagsHasVu) = 1 << 2, ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effFlagsCanMono) = 1 << 3, ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effFlagsExtIsAsync) = 1 << 10, ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effFlagsExtHasBuffer) = 1 << 11 ///< \deprecated deprecated in VST 2.4 +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Basic dispatcher Opcodes (Host to Plug-in) */ +//------------------------------------------------------------------------------------------------------- +enum AEffectOpcodes +{ + effOpen = 0, ///< no arguments @see AudioEffect::open + effClose, ///< no arguments @see AudioEffect::close + + effSetProgram, ///< [value]: new program number @see AudioEffect::setProgram + effGetProgram, ///< [return value]: current program number @see AudioEffect::getProgram + effSetProgramName, ///< [ptr]: char* with new program name, limited to #kVstMaxProgNameLen @see AudioEffect::setProgramName + effGetProgramName, ///< [ptr]: char buffer for current program name, limited to #kVstMaxProgNameLen @see AudioEffect::getProgramName + + effGetParamLabel, ///< [ptr]: char buffer for parameter label, limited to #kVstMaxParamStrLen @see AudioEffect::getParameterLabel + effGetParamDisplay, ///< [ptr]: char buffer for parameter display, limited to #kVstMaxParamStrLen @see AudioEffect::getParameterDisplay + effGetParamName, ///< [ptr]: char buffer for parameter name, limited to #kVstMaxParamStrLen @see AudioEffect::getParameterName + + DECLARE_VST_DEPRECATED (effGetVu), ///< \deprecated deprecated in VST 2.4 + + effSetSampleRate, ///< [opt]: new sample rate for audio processing @see AudioEffect::setSampleRate + effSetBlockSize, ///< [value]: new maximum block size for audio processing @see AudioEffect::setBlockSize + effMainsChanged, ///< [value]: 0 means "turn off", 1 means "turn on" @see AudioEffect::suspend @see AudioEffect::resume + + effEditGetRect, ///< [ptr]: #ERect** receiving pointer to editor size @see ERect @see AEffEditor::getRect + effEditOpen, ///< [ptr]: system dependent Window pointer, e.g. HWND on Windows @see AEffEditor::open + effEditClose, ///< no arguments @see AEffEditor::close + + DECLARE_VST_DEPRECATED (effEditDraw), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effEditMouse), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effEditKey), ///< \deprecated deprecated in VST 2.4 + + effEditIdle, ///< no arguments @see AEffEditor::idle + + DECLARE_VST_DEPRECATED (effEditTop), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effEditSleep), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (effIdentify), ///< \deprecated deprecated in VST 2.4 + + effGetChunk, ///< [ptr]: void** for chunk data address [index]: 0 for bank, 1 for program @see AudioEffect::getChunk + effSetChunk, ///< [ptr]: chunk data [value]: byte size [index]: 0 for bank, 1 for program @see AudioEffect::setChunk + + effNumOpcodes +}; + +//------------------------------------------------------------------------------------------------------- +/** Basic dispatcher Opcodes (Plug-in to Host) */ +//------------------------------------------------------------------------------------------------------- +enum AudioMasterOpcodes +{ +//------------------------------------------------------------------------------------------------------- + audioMasterAutomate = 0, ///< [index]: parameter index [opt]: parameter value @see AudioEffect::setParameterAutomated + audioMasterVersion, ///< [return value]: Host VST version (for example 2400 for VST 2.4) @see AudioEffect::getMasterVersion + audioMasterCurrentId, ///< [return value]: current unique identifier on shell plug-in @see AudioEffect::getCurrentUniqueId + audioMasterIdle, ///< no arguments @see AudioEffect::masterIdle + DECLARE_VST_DEPRECATED (audioMasterPinConnected) ///< \deprecated deprecated in VST 2.4 r2 +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** String length limits (in characters excl. 0 byte) */ +//------------------------------------------------------------------------------------------------------- +enum VstStringConstants +{ +//------------------------------------------------------------------------------------------------------- + kVstMaxProgNameLen = 24, ///< used for #effGetProgramName, #effSetProgramName, #effGetProgramNameIndexed + kVstMaxParamStrLen = 8, ///< used for #effGetParamLabel, #effGetParamDisplay, #effGetParamName + kVstMaxVendorStrLen = 64, ///< used for #effGetVendorString, #audioMasterGetVendorString + kVstMaxProductStrLen = 64, ///< used for #effGetProductString, #audioMasterGetProductString + kVstMaxEffectNameLen = 32 ///< used for #effGetEffectName +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** String copy taking care of null terminator. */ +//------------------------------------------------------------------------------------------------------- +inline char* vst_strncpy (char* dst, const char* src, size_t maxLen) +{ + char* result = strncpy (dst, src, maxLen); + dst[maxLen] = 0; + return result; +} + +//------------------------------------------------------------------------------------------------------- +/** String concatenation taking care of null terminator. */ +//------------------------------------------------------------------------------------------------------- +inline char* vst_strncat (char* dst, const char* src, size_t maxLen) +{ + char* result = strncat (dst, src, maxLen); + dst[maxLen] = 0; + return result; +} + +//------------------------------------------------------------------------------------------------------- +/** Cast #VstIntPtr to pointer. */ +//------------------------------------------------------------------------------------------------------- +template inline T* FromVstPtr (VstIntPtr& arg) +{ + T** address = (T**)&arg; + return *address; +} + +//------------------------------------------------------------------------------------------------------- +/** Cast pointer to #VstIntPtr. */ +//------------------------------------------------------------------------------------------------------- +template inline VstIntPtr ToVstPtr (T* ptr) +{ + VstIntPtr* address = (VstIntPtr*)&ptr; + return *address; +} + +//------------------------------------------------------------------------------------------------------- +/** Structure used for #effEditGetRect. */ +//------------------------------------------------------------------------------------------------------- +struct ERect +{ +//------------------------------------------------------------------------------------------------------- + VstInt16 top; ///< top coordinate + VstInt16 left; ///< left coordinate + VstInt16 bottom; ///< bottom coordinate + VstInt16 right; ///< right coordinate +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +#if TARGET_API_MAC_CARBON + #pragma options align=reset +#elif defined(WIN32) || defined(__FLAT__) || defined(__GNUC__) + #pragma pack(pop) +#elif defined __BORLANDC__ + #pragma -a- +#endif + +#endif // __aeffect__ diff --git a/src/deps/vst/aeffectx.h b/src/deps/vst/aeffectx.h new file mode 100644 index 0000000..8bf7043 --- /dev/null +++ b/src/deps/vst/aeffectx.h @@ -0,0 +1,1143 @@ +//------------------------------------------------------------------------------------------------------- +// VST Plug-Ins SDK +// Version 2.4 $Date: 2006/06/20 12:43:42 $ +// +// Category : VST 2.x Interfaces +// Filename : aeffectx.h +// Created by : Steinberg Media Technologies +// Description : Definition of auxiliary structures, extensions from VST 1.0 to VST 2.4 +// +// © 2006, Steinberg Media Technologies, All Rights Reserved +//------------------------------------------------------------------------------------------------------- + +#ifndef __aeffectx__ +#define __aeffectx__ + +#ifndef __aeffect__ +#include "aeffect.h" +#endif + +//------------------------------------------------------------------------------------------------------- +#if TARGET_API_MAC_CARBON + #ifdef __LP64__ + #pragma options align=power + #else + #pragma options align=mac68k + #endif +#elif defined __BORLANDC__ + #pragma -a8 +#elif defined(__GNUC__) + #pragma pack(push,8) +#elif defined(WIN32) || defined(__FLAT__) + #pragma pack(push) + #pragma pack(8) +#endif +//------------------------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------------------------- +/** String length limits (in characters excl. 0 byte). */ +//------------------------------------------------------------------------------------------------------- +enum Vst2StringConstants +{ +//------------------------------------------------------------------------------------------------------- + kVstMaxNameLen = 64, ///< used for #MidiProgramName, #MidiProgramCategory, #MidiKeyName, #VstSpeakerProperties, #VstPinProperties + kVstMaxLabelLen = 64, ///< used for #VstParameterProperties->label, #VstPinProperties->label + kVstMaxShortLabelLen = 8, ///< used for #VstParameterProperties->shortLabel, #VstPinProperties->shortLabel + kVstMaxCategLabelLen = 24, ///< used for #VstParameterProperties->label + kVstMaxFileNameLen = 100 ///< used for #VstAudioFile->name +//------------------------------------------------------------------------------------------------------- +}; +//------------------------------------------------------------------------------------------------------- +// VstEvent +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- +/** A generic timestamped event. */ +//------------------------------------------------------------------------------------------------------- +struct VstEvent +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 type; ///< @see VstEventTypes + VstInt32 byteSize; ///< size of this event, excl. type and byteSize + VstInt32 deltaFrames; ///< sample frames related to the current block start sample position + VstInt32 flags; ///< generic flags, none defined yet + + char data[16]; ///< data size may vary, depending on event type +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** VstEvent Types used by #VstEvent. */ +//------------------------------------------------------------------------------------------------------- +enum VstEventTypes +{ +//------------------------------------------------------------------------------------------------------- + kVstMidiType = 1, ///< MIDI event @see VstMidiEvent + DECLARE_VST_DEPRECATED (kVstAudioType), ///< \deprecated unused event type + DECLARE_VST_DEPRECATED (kVstVideoType), ///< \deprecated unused event type + DECLARE_VST_DEPRECATED (kVstParameterType), ///< \deprecated unused event type + DECLARE_VST_DEPRECATED (kVstTriggerType), ///< \deprecated unused event type + kVstSysExType ///< MIDI system exclusive @see VstMidiSysexEvent +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** A block of events for the current processed audio block. */ +//------------------------------------------------------------------------------------------------------- +struct VstEvents +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 numEvents; ///< number of Events in array + VstIntPtr reserved; ///< zero (Reserved for future use) + VstEvent* events[2]; ///< event pointer array, variable size +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** MIDI Event (to be casted from VstEvent). */ +//------------------------------------------------------------------------------------------------------- +struct VstMidiEvent +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 type; ///< #kVstMidiType + VstInt32 byteSize; ///< sizeof (VstMidiEvent) + VstInt32 deltaFrames; ///< sample frames related to the current block start sample position + VstInt32 flags; ///< @see VstMidiEventFlags + VstInt32 noteLength; ///< (in sample frames) of entire note, if available, else 0 + VstInt32 noteOffset; ///< offset (in sample frames) into note from note start if available, else 0 + char midiData[4]; ///< 1 to 3 MIDI bytes; midiData[3] is reserved (zero) + char detune; ///< -64 to +63 cents; for scales other than 'well-tempered' ('microtuning') + char noteOffVelocity; ///< Note Off Velocity [0, 127] + char reserved1; ///< zero (Reserved for future use) + char reserved2; ///< zero (Reserved for future use) +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Flags used in #VstMidiEvent. */ +//------------------------------------------------------------------------------------------------------- +enum VstMidiEventFlags +{ +//------------------------------------------------------------------------------------------------------- + kVstMidiEventIsRealtime = 1 << 0 ///< means that this event is played life (not in playback from a sequencer track).\n This allows the Plug-In to handle these flagged events with higher priority, especially when the Plug-In has a big latency (AEffect::initialDelay) +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** MIDI Sysex Event (to be casted from #VstEvent). */ +//------------------------------------------------------------------------------------------------------- +struct VstMidiSysexEvent +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 type; ///< #kVstSysexType + VstInt32 byteSize; ///< sizeof (VstMidiSysexEvent) + VstInt32 deltaFrames; ///< sample frames related to the current block start sample position + VstInt32 flags; ///< none defined yet (should be zero) + VstInt32 dumpBytes; ///< byte size of sysexDump + VstIntPtr resvd1; ///< zero (Reserved for future use) + char* sysexDump; ///< sysex dump + VstIntPtr resvd2; ///< zero (Reserved for future use) +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +// VstTimeInfo +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- +/** VstTimeInfo requested via #audioMasterGetTime. @see AudioEffectX::getTimeInfo + +\note VstTimeInfo::samplePos :Current Position. It must always be valid, and should not cost a lot to ask for. The sample position is ahead of the time displayed to the user. In sequencer stop mode, its value does not change. A 32 bit integer is too small for sample positions, and it's a double to make it easier to convert between ppq and samples. +\note VstTimeInfo::ppqPos : At tempo 120, 1 quarter makes 1/2 second, so 2.0 ppq translates to 48000 samples at 48kHz sample rate. +.25 ppq is one sixteenth note then. if you need something like 480ppq, you simply multiply ppq by that scaler. +\note VstTimeInfo::barStartPos : Say we're at bars/beats readout 3.3.3. That's 2 bars + 2 q + 2 sixteenth, makes 2 * 4 + 2 + .25 = 10.25 ppq. at tempo 120, that's 10.25 * .5 = 5.125 seconds, times 48000 = 246000 samples (if my calculator servers me well :-). +\note VstTimeInfo::samplesToNextClock : MIDI Clock Resolution (24 per Quarter Note), can be negative the distance to the next midi clock (24 ppq, pulses per quarter) in samples. unless samplePos falls precicely on a midi clock, this will either be negative such that the previous MIDI clock is addressed, or positive when referencing the following (future) MIDI clock. +*/ +//------------------------------------------------------------------------------------------------------- +struct VstTimeInfo +{ +//------------------------------------------------------------------------------------------------------- + double samplePos; ///< current Position in audio samples (always valid) + double sampleRate; ///< current Sample Rate in Herz (always valid) + double nanoSeconds; ///< System Time in nanoseconds (10^-9 second) + double ppqPos; ///< Musical Position, in Quarter Note (1.0 equals 1 Quarter Note) + double tempo; ///< current Tempo in BPM (Beats Per Minute) + double barStartPos; ///< last Bar Start Position, in Quarter Note + double cycleStartPos; ///< Cycle Start (left locator), in Quarter Note + double cycleEndPos; ///< Cycle End (right locator), in Quarter Note + VstInt32 timeSigNumerator; ///< Time Signature Numerator (e.g. 3 for 3/4) + VstInt32 timeSigDenominator; ///< Time Signature Denominator (e.g. 4 for 3/4) + VstInt32 smpteOffset; ///< SMPTE offset (in SMPTE subframes (bits; 1/80 of a frame)). The current SMPTE position can be calculated using #samplePos, #sampleRate, and #smpteFrameRate. + VstInt32 smpteFrameRate; ///< @see VstSmpteFrameRate + VstInt32 samplesToNextClock; ///< MIDI Clock Resolution (24 Per Quarter Note), can be negative (nearest clock) + VstInt32 flags; ///< @see VstTimeInfoFlags +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Flags used in #VstTimeInfo. */ +//------------------------------------------------------------------------------------------------------- +enum VstTimeInfoFlags +{ +//------------------------------------------------------------------------------------------------------- + kVstTransportChanged = 1, ///< indicates that play, cycle or record state has changed + kVstTransportPlaying = 1 << 1, ///< set if Host sequencer is currently playing + kVstTransportCycleActive = 1 << 2, ///< set if Host sequencer is in cycle mode + kVstTransportRecording = 1 << 3, ///< set if Host sequencer is in record mode + kVstAutomationWriting = 1 << 6, ///< set if automation write mode active (record parameter changes) + kVstAutomationReading = 1 << 7, ///< set if automation read mode active (play parameter changes) + kVstNanosValid = 1 << 8, ///< VstTimeInfo::nanoSeconds valid + kVstPpqPosValid = 1 << 9, ///< VstTimeInfo::ppqPos valid + kVstTempoValid = 1 << 10, ///< VstTimeInfo::tempo valid + kVstBarsValid = 1 << 11, ///< VstTimeInfo::barStartPos valid + kVstCyclePosValid = 1 << 12, ///< VstTimeInfo::cycleStartPos and VstTimeInfo::cycleEndPos valid + kVstTimeSigValid = 1 << 13, ///< VstTimeInfo::timeSigNumerator and VstTimeInfo::timeSigDenominator valid + kVstSmpteValid = 1 << 14, ///< VstTimeInfo::smpteOffset and VstTimeInfo::smpteFrameRate valid + kVstClockValid = 1 << 15 ///< VstTimeInfo::samplesToNextClock valid +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** SMPTE Frame Rates. */ +//------------------------------------------------------------------------------------------------------- +enum VstSmpteFrameRate +{ +//------------------------------------------------------------------------------------------------------- + kVstSmpte24fps = 0, ///< 24 fps + kVstSmpte25fps = 1, ///< 25 fps + kVstSmpte2997fps = 2, ///< 29.97 fps + kVstSmpte30fps = 3, ///< 30 fps + kVstSmpte2997dfps = 4, ///< 29.97 drop + kVstSmpte30dfps = 5, ///< 30 drop + + kVstSmpteFilm16mm = 6, ///< Film 16mm + kVstSmpteFilm35mm = 7, ///< Film 35mm + kVstSmpte239fps = 10, ///< HDTV: 23.976 fps + kVstSmpte249fps = 11, ///< HDTV: 24.976 fps + kVstSmpte599fps = 12, ///< HDTV: 59.94 fps + kVstSmpte60fps = 13 ///< HDTV: 60 fps +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Variable IO for Offline Processing. */ +//------------------------------------------------------------------------------------------------------- +struct VstVariableIo +{ +//------------------------------------------------------------------------------------------------------- + float** inputs; ///< input audio buffers + float** outputs; ///< output audio buffers + VstInt32 numSamplesInput; ///< number of incoming samples + VstInt32 numSamplesOutput; ///< number of outgoing samples + VstInt32* numSamplesInputProcessed; ///< number of samples actually processed of input + VstInt32* numSamplesOutputProcessed; ///< number of samples actually processed of output +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Language code returned by audioMasterGetLanguage. */ +//------------------------------------------------------------------------------------------------------- +enum VstHostLanguage +{ +//------------------------------------------------------------------------------------------------------- + kVstLangEnglish = 1, ///< English + kVstLangGerman, ///< German + kVstLangFrench, ///< French + kVstLangItalian, ///< Italian + kVstLangSpanish, ///< Spanish + kVstLangJapanese ///< Japanese +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** VST 2.x dispatcher Opcodes (Plug-in to Host). Extension of #AudioMasterOpcodes */ +//------------------------------------------------------------------------------------------------------- +enum AudioMasterOpcodesX +{ +//------------------------------------------------------------------------------------------------------- + DECLARE_VST_DEPRECATED (audioMasterWantMidi) = DECLARE_VST_DEPRECATED (audioMasterPinConnected) + 2, ///< \deprecated deprecated in VST 2.4 + + audioMasterGetTime, ///< [return value]: #VstTimeInfo* or null if not supported [value]: request mask @see VstTimeInfoFlags @see AudioEffectX::getTimeInfo + audioMasterProcessEvents, ///< [ptr]: pointer to #VstEvents @see VstEvents @see AudioEffectX::sendVstEventsToHost + + DECLARE_VST_DEPRECATED (audioMasterSetTime), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (audioMasterTempoAt), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (audioMasterGetNumAutomatableParameters), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (audioMasterGetParameterQuantization), ///< \deprecated deprecated in VST 2.4 + + audioMasterIOChanged, ///< [return value]: 1 if supported @see AudioEffectX::ioChanged + + DECLARE_VST_DEPRECATED (audioMasterNeedIdle), ///< \deprecated deprecated in VST 2.4 + + audioMasterSizeWindow, ///< [index]: new width [value]: new height [return value]: 1 if supported @see AudioEffectX::sizeWindow + audioMasterGetSampleRate, ///< [return value]: current sample rate @see AudioEffectX::updateSampleRate + audioMasterGetBlockSize, ///< [return value]: current block size @see AudioEffectX::updateBlockSize + audioMasterGetInputLatency, ///< [return value]: input latency in audio samples @see AudioEffectX::getInputLatency + audioMasterGetOutputLatency, ///< [return value]: output latency in audio samples @see AudioEffectX::getOutputLatency + + DECLARE_VST_DEPRECATED (audioMasterGetPreviousPlug), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (audioMasterGetNextPlug), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (audioMasterWillReplaceOrAccumulate), ///< \deprecated deprecated in VST 2.4 + + audioMasterGetCurrentProcessLevel, ///< [return value]: current process level @see VstProcessLevels + audioMasterGetAutomationState, ///< [return value]: current automation state @see VstAutomationStates + + audioMasterOfflineStart, ///< [index]: numNewAudioFiles [value]: numAudioFiles [ptr]: #VstAudioFile* @see AudioEffectX::offlineStart + audioMasterOfflineRead, ///< [index]: bool readSource [value]: #VstOfflineOption* @see VstOfflineOption [ptr]: #VstOfflineTask* @see VstOfflineTask @see AudioEffectX::offlineRead + audioMasterOfflineWrite, ///< @see audioMasterOfflineRead @see AudioEffectX::offlineRead + audioMasterOfflineGetCurrentPass, ///< @see AudioEffectX::offlineGetCurrentPass + audioMasterOfflineGetCurrentMetaPass, ///< @see AudioEffectX::offlineGetCurrentMetaPass + + DECLARE_VST_DEPRECATED (audioMasterSetOutputSampleRate), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (audioMasterGetOutputSpeakerArrangement), ///< \deprecated deprecated in VST 2.4 + + audioMasterGetVendorString, ///< [ptr]: char buffer for vendor string, limited to #kVstMaxVendorStrLen @see AudioEffectX::getHostVendorString + audioMasterGetProductString, ///< [ptr]: char buffer for vendor string, limited to #kVstMaxProductStrLen @see AudioEffectX::getHostProductString + audioMasterGetVendorVersion, ///< [return value]: vendor-specific version @see AudioEffectX::getHostVendorVersion + audioMasterVendorSpecific, ///< no definition, vendor specific handling @see AudioEffectX::hostVendorSpecific + + DECLARE_VST_DEPRECATED (audioMasterSetIcon), ///< \deprecated deprecated in VST 2.4 + + audioMasterCanDo, ///< [ptr]: "can do" string [return value]: 1 for supported + audioMasterGetLanguage, ///< [return value]: language code @see VstHostLanguage + + DECLARE_VST_DEPRECATED (audioMasterOpenWindow), ///< \deprecated deprecated in VST 2.4 + DECLARE_VST_DEPRECATED (audioMasterCloseWindow), ///< \deprecated deprecated in VST 2.4 + + audioMasterGetDirectory, ///< [return value]: FSSpec on MAC, else char* @see AudioEffectX::getDirectory + audioMasterUpdateDisplay, ///< no arguments + audioMasterBeginEdit, ///< [index]: parameter index @see AudioEffectX::beginEdit + audioMasterEndEdit, ///< [index]: parameter index @see AudioEffectX::endEdit + audioMasterOpenFileSelector, ///< [ptr]: VstFileSelect* [return value]: 1 if supported @see AudioEffectX::openFileSelector + audioMasterCloseFileSelector, ///< [ptr]: VstFileSelect* @see AudioEffectX::closeFileSelector + + DECLARE_VST_DEPRECATED (audioMasterEditFile), ///< \deprecated deprecated in VST 2.4 + + DECLARE_VST_DEPRECATED (audioMasterGetChunkFile), ///< \deprecated deprecated in VST 2.4 [ptr]: char[2048] or sizeof (FSSpec) [return value]: 1 if supported @see AudioEffectX::getChunkFile + + DECLARE_VST_DEPRECATED (audioMasterGetInputSpeakerArrangement) ///< \deprecated deprecated in VST 2.4 +}; + +//------------------------------------------------------------------------------------------------------- +/** VST 2.x dispatcher Opcodes (Host to Plug-in). Extension of #AEffectOpcodes */ +//------------------------------------------------------------------------------------------------------- +enum AEffectXOpcodes +{ +//------------------------------------------------------------------------------------------------------- + effProcessEvents = effSetChunk + 1 ///< [ptr]: #VstEvents* @see AudioEffectX::processEvents + + , effCanBeAutomated ///< [index]: parameter index [return value]: 1=true, 0=false @see AudioEffectX::canParameterBeAutomated + , effString2Parameter ///< [index]: parameter index [ptr]: parameter string [return value]: true for success @see AudioEffectX::string2parameter + + , DECLARE_VST_DEPRECATED (effGetNumProgramCategories) ///< \deprecated deprecated in VST 2.4 + + , effGetProgramNameIndexed ///< [index]: program index [ptr]: buffer for program name, limited to #kVstMaxProgNameLen [return value]: true for success @see AudioEffectX::getProgramNameIndexed + + , DECLARE_VST_DEPRECATED (effCopyProgram) ///< \deprecated deprecated in VST 2.4 + , DECLARE_VST_DEPRECATED (effConnectInput) ///< \deprecated deprecated in VST 2.4 + , DECLARE_VST_DEPRECATED (effConnectOutput) ///< \deprecated deprecated in VST 2.4 + + , effGetInputProperties ///< [index]: input index [ptr]: #VstPinProperties* [return value]: 1 if supported @see AudioEffectX::getInputProperties + , effGetOutputProperties ///< [index]: output index [ptr]: #VstPinProperties* [return value]: 1 if supported @see AudioEffectX::getOutputProperties + , effGetPlugCategory ///< [return value]: category @see VstPlugCategory @see AudioEffectX::getPlugCategory + + , DECLARE_VST_DEPRECATED (effGetCurrentPosition) ///< \deprecated deprecated in VST 2.4 + , DECLARE_VST_DEPRECATED (effGetDestinationBuffer) ///< \deprecated deprecated in VST 2.4 + + , effOfflineNotify ///< [ptr]: #VstAudioFile array [value]: count [index]: start flag @see AudioEffectX::offlineNotify + , effOfflinePrepare ///< [ptr]: #VstOfflineTask array [value]: count @see AudioEffectX::offlinePrepare + , effOfflineRun ///< [ptr]: #VstOfflineTask array [value]: count @see AudioEffectX::offlineRun + + , effProcessVarIo ///< [ptr]: #VstVariableIo* @see AudioEffectX::processVariableIo + , effSetSpeakerArrangement ///< [value]: input #VstSpeakerArrangement* [ptr]: output #VstSpeakerArrangement* @see AudioEffectX::setSpeakerArrangement + + , DECLARE_VST_DEPRECATED (effSetBlockSizeAndSampleRate) ///< \deprecated deprecated in VST 2.4 + + , effSetBypass ///< [value]: 1 = bypass, 0 = no bypass @see AudioEffectX::setBypass + , effGetEffectName ///< [ptr]: buffer for effect name, limited to #kVstMaxEffectNameLen @see AudioEffectX::getEffectName + + , DECLARE_VST_DEPRECATED (effGetErrorText) ///< \deprecated deprecated in VST 2.4 + + , effGetVendorString ///< [ptr]: buffer for effect vendor string, limited to #kVstMaxVendorStrLen @see AudioEffectX::getVendorString + , effGetProductString ///< [ptr]: buffer for effect vendor string, limited to #kVstMaxProductStrLen @see AudioEffectX::getProductString + , effGetVendorVersion ///< [return value]: vendor-specific version @see AudioEffectX::getVendorVersion + , effVendorSpecific ///< no definition, vendor specific handling @see AudioEffectX::vendorSpecific + , effCanDo ///< [ptr]: "can do" string [return value]: 0: "don't know" -1: "no" 1: "yes" @see AudioEffectX::canDo + , effGetTailSize ///< [return value]: tail size (for example the reverb time of a reverb plug-in); 0 is default (return 1 for 'no tail') + + , DECLARE_VST_DEPRECATED (effIdle) ///< \deprecated deprecated in VST 2.4 + , DECLARE_VST_DEPRECATED (effGetIcon) ///< \deprecated deprecated in VST 2.4 + , DECLARE_VST_DEPRECATED (effSetViewPosition) ///< \deprecated deprecated in VST 2.4 + + , effGetParameterProperties ///< [index]: parameter index [ptr]: #VstParameterProperties* [return value]: 1 if supported @see AudioEffectX::getParameterProperties + + , DECLARE_VST_DEPRECATED (effKeysRequired) ///< \deprecated deprecated in VST 2.4 + + , effGetVstVersion ///< [return value]: VST version @see AudioEffectX::getVstVersion + +#if VST_2_1_EXTENSIONS + , effEditKeyDown ///< [index]: ASCII character [value]: virtual key [opt]: modifiers [return value]: 1 if key used @see AEffEditor::onKeyDown + , effEditKeyUp ///< [index]: ASCII character [value]: virtual key [opt]: modifiers [return value]: 1 if key used @see AEffEditor::onKeyUp + , effSetEditKnobMode ///< [value]: knob mode 0: circular, 1: circular relativ, 2: linear (CKnobMode in VSTGUI) @see AEffEditor::setKnobMode + + , effGetMidiProgramName ///< [index]: MIDI channel [ptr]: #MidiProgramName* [return value]: number of used programs, 0 if unsupported @see AudioEffectX::getMidiProgramName + , effGetCurrentMidiProgram ///< [index]: MIDI channel [ptr]: #MidiProgramName* [return value]: index of current program @see AudioEffectX::getCurrentMidiProgram + , effGetMidiProgramCategory ///< [index]: MIDI channel [ptr]: #MidiProgramCategory* [return value]: number of used categories, 0 if unsupported @see AudioEffectX::getMidiProgramCategory + , effHasMidiProgramsChanged ///< [index]: MIDI channel [return value]: 1 if the #MidiProgramName(s) or #MidiKeyName(s) have changed @see AudioEffectX::hasMidiProgramsChanged + , effGetMidiKeyName ///< [index]: MIDI channel [ptr]: #MidiKeyName* [return value]: true if supported, false otherwise @see AudioEffectX::getMidiKeyName + + , effBeginSetProgram ///< no arguments @see AudioEffectX::beginSetProgram + , effEndSetProgram ///< no arguments @see AudioEffectX::endSetProgram +#endif // VST_2_1_EXTENSIONS + +#if VST_2_3_EXTENSIONS + , effGetSpeakerArrangement ///< [value]: input #VstSpeakerArrangement* [ptr]: output #VstSpeakerArrangement* @see AudioEffectX::getSpeakerArrangement + , effShellGetNextPlugin ///< [ptr]: buffer for plug-in name, limited to #kVstMaxProductStrLen [return value]: next plugin's uniqueID @see AudioEffectX::getNextShellPlugin + + , effStartProcess ///< no arguments @see AudioEffectX::startProcess + , effStopProcess ///< no arguments @see AudioEffectX::stopProcess + , effSetTotalSampleToProcess ///< [value]: number of samples to process, offline only! @see AudioEffectX::setTotalSampleToProcess + , effSetPanLaw ///< [value]: pan law [opt]: gain @see VstPanLawType @see AudioEffectX::setPanLaw + + , effBeginLoadBank ///< [ptr]: #VstPatchChunkInfo* [return value]: -1: bank can't be loaded, 1: bank can be loaded, 0: unsupported @see AudioEffectX::beginLoadBank + , effBeginLoadProgram ///< [ptr]: #VstPatchChunkInfo* [return value]: -1: prog can't be loaded, 1: prog can be loaded, 0: unsupported @see AudioEffectX::beginLoadProgram +#endif // VST_2_3_EXTENSIONS + +#if VST_2_4_EXTENSIONS + , effSetProcessPrecision ///< [value]: @see VstProcessPrecision @see AudioEffectX::setProcessPrecision + , effGetNumMidiInputChannels ///< [return value]: number of used MIDI input channels (1-15) @see AudioEffectX::getNumMidiInputChannels + , effGetNumMidiOutputChannels ///< [return value]: number of used MIDI output channels (1-15) @see AudioEffectX::getNumMidiOutputChannels +#endif // VST_2_4_EXTENSIONS +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Symbolic precision constants used for effSetProcessPrecision. */ +//------------------------------------------------------------------------------------------------------- +enum VstProcessPrecision +{ + kVstProcessPrecision32 = 0, ///< single precision float (32bits) + kVstProcessPrecision64 ///< double precision (64bits) +}; + +//------------------------------------------------------------------------------------------------------- +/** Parameter Properties used in #effGetParameterProperties. */ +//------------------------------------------------------------------------------------------------------- +struct VstParameterProperties +{ +//------------------------------------------------------------------------------------------------------- + float stepFloat; ///< float step + float smallStepFloat; ///< small float step + float largeStepFloat; ///< large float step + char label[kVstMaxLabelLen];///< parameter label + VstInt32 flags; ///< @see VstParameterFlags + VstInt32 minInteger; ///< integer minimum + VstInt32 maxInteger; ///< integer maximum + VstInt32 stepInteger; ///< integer step + VstInt32 largeStepInteger; ///< large integer step + char shortLabel[kVstMaxShortLabelLen]; ///< short label, recommended: 6 + delimiter + + // The following are for remote controller display purposes. + // Note that the kVstParameterSupportsDisplayIndex flag must be set. + // Host can scan all parameters, and find out in what order + // to display them: + + VstInt16 displayIndex; ///< index where this parameter should be displayed (starting with 0) + + // Host can also possibly display the parameter group (category), such as... + // --------------------------- + // Osc 1 + // Wave Detune Octave Mod + // --------------------------- + // ...if the plug-in supports it (flag #kVstParameterSupportsDisplayCategory) + + VstInt16 category; ///< 0: no category, else group index + 1 + VstInt16 numParametersInCategory; ///< number of parameters in category + VstInt16 reserved; ///< zero + char categoryLabel[kVstMaxCategLabelLen]; ///< category label, e.g. "Osc 1" + + char future[16]; ///< reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Flags used in #VstParameterProperties. */ +//------------------------------------------------------------------------------------------------------- +enum VstParameterFlags +{ +//------------------------------------------------------------------------------------------------------- + kVstParameterIsSwitch = 1 << 0, ///< parameter is a switch (on/off) + kVstParameterUsesIntegerMinMax = 1 << 1, ///< minInteger, maxInteger valid + kVstParameterUsesFloatStep = 1 << 2, ///< stepFloat, smallStepFloat, largeStepFloat valid + kVstParameterUsesIntStep = 1 << 3, ///< stepInteger, largeStepInteger valid + kVstParameterSupportsDisplayIndex = 1 << 4, ///< displayIndex valid + kVstParameterSupportsDisplayCategory = 1 << 5, ///< category, etc. valid + kVstParameterCanRamp = 1 << 6 ///< set if parameter value can ramp up/down +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Pin Properties used in #effGetInputProperties and #effGetOutputProperties. */ +//------------------------------------------------------------------------------------------------------- +struct VstPinProperties +{ +//------------------------------------------------------------------------------------------------------- + char label[kVstMaxLabelLen]; ///< pin name + VstInt32 flags; ///< @see VstPinPropertiesFlags + VstInt32 arrangementType; ///< @see VstSpeakerArrangementType + char shortLabel[kVstMaxShortLabelLen]; ///< short name (recommended: 6 + delimiter) + + char future[48]; ///< reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Flags used in #VstPinProperties. */ +//------------------------------------------------------------------------------------------------------- +enum VstPinPropertiesFlags +{ +//------------------------------------------------------------------------------------------------------- + kVstPinIsActive = 1 << 0, ///< pin is active, ignored by Host + kVstPinIsStereo = 1 << 1, ///< pin is first of a stereo pair + kVstPinUseSpeaker = 1 << 2 ///< #VstPinProperties::arrangementType is valid and can be used to get the wanted arrangement +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Plug-in Categories. */ +//------------------------------------------------------------------------------------------------------- +enum VstPlugCategory +{ +//------------------------------------------------------------------------------------------------------- + kPlugCategUnknown = 0, ///< Unknown, category not implemented + kPlugCategEffect, ///< Simple Effect + kPlugCategSynth, ///< VST Instrument (Synths, samplers,...) + kPlugCategAnalysis, ///< Scope, Tuner, ... + kPlugCategMastering, ///< Dynamics, ... + kPlugCategSpacializer, ///< Panners, ... + kPlugCategRoomFx, ///< Delays and Reverbs + kPlugSurroundFx, ///< Dedicated surround processor + kPlugCategRestoration, ///< Denoiser, ... + kPlugCategOfflineProcess, ///< Offline Process + kPlugCategShell, ///< Plug-in is container of other plug-ins @see effShellGetNextPlugin + kPlugCategGenerator, ///< ToneGenerator, ... + + kPlugCategMaxCount ///< Marker to count the categories +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +// MIDI Programs +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- +/** MIDI Program Description. */ +//------------------------------------------------------------------------------------------------------- +struct MidiProgramName +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 thisProgramIndex; ///< 0 or greater: fill struct for this program index + char name[kVstMaxNameLen]; ///< program name + char midiProgram; ///< -1:off, 0-127 + char midiBankMsb; ///< -1:off, 0-127 + char midiBankLsb; ///< -1:off, 0-127 + char reserved; ///< zero + VstInt32 parentCategoryIndex; ///< -1:no parent category + VstInt32 flags; ///< omni etc. @see VstMidiProgramNameFlags +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Flags used in MidiProgramName. */ +//------------------------------------------------------------------------------------------------------- +enum VstMidiProgramNameFlags +{ +//------------------------------------------------------------------------------------------------------- + kMidiIsOmni = 1 ///< default is multi. for omni mode, channel 0 is used for inquiries and program changes +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** MIDI Program Category. */ +//------------------------------------------------------------------------------------------------------- +struct MidiProgramCategory +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 thisCategoryIndex; ///< 0 or greater: fill struct for this category index. + char name[kVstMaxNameLen]; ///< name + VstInt32 parentCategoryIndex; ///< -1:no parent category + VstInt32 flags; ///< reserved, none defined yet, zero. +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** MIDI Key Description. */ +//------------------------------------------------------------------------------------------------------- +struct MidiKeyName +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 thisProgramIndex; ///< 0 or greater: fill struct for this program index. + VstInt32 thisKeyNumber; ///< 0 - 127. fill struct for this key number. + char keyName[kVstMaxNameLen]; ///< key name, empty means regular key names + VstInt32 reserved; ///< zero + VstInt32 flags; ///< reserved, none defined yet, zero. +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +// Surround Setup +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- +/** Speaker Properties. + The origin for azimuth is right (as by math conventions dealing with radians). + The elevation origin is also right, visualizing a rotation of a circle across the + -pi/pi axis of the horizontal circle. Thus, an elevation of -pi/2 corresponds + to bottom, and a speaker standing on the left, and 'beaming' upwards would have + an azimuth of -pi, and an elevation of pi/2. + For user interface representation, grads are more likely to be used, and the + origins will obviously 'shift' accordingly. */ +//------------------------------------------------------------------------------------------------------- +struct VstSpeakerProperties +{ +//------------------------------------------------------------------------------------------------------- + float azimuth; ///< unit: rad, range: -PI...PI, exception: 10.f for LFE channel + float elevation; ///< unit: rad, range: -PI/2...PI/2, exception: 10.f for LFE channel + float radius; ///< unit: meter, exception: 0.f for LFE channel + float reserved; ///< zero (reserved for future use) + char name[kVstMaxNameLen]; ///< for new setups, new names should be given (L/R/C... won't do) + VstInt32 type; ///< @see VstSpeakerType + + char future[28]; ///< reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Speaker Arrangement. */ +//------------------------------------------------------------------------------------------------------- +struct VstSpeakerArrangement +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 type; ///< e.g. #kSpeakerArr51 for 5.1 @see VstSpeakerArrangementType + VstInt32 numChannels; ///< number of channels in this speaker arrangement + VstSpeakerProperties speakers[8]; ///< variable sized speaker array +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Speaker Types. */ +//------------------------------------------------------------------------------------------------------- +enum VstSpeakerType +{ +//------------------------------------------------------------------------------------------------------- + kSpeakerUndefined = 0x7fffffff, ///< Undefined + kSpeakerM = 0, ///< Mono (M) + kSpeakerL, ///< Left (L) + kSpeakerR, ///< Right (R) + kSpeakerC, ///< Center (C) + kSpeakerLfe, ///< Subbass (Lfe) + kSpeakerLs, ///< Left Surround (Ls) + kSpeakerRs, ///< Right Surround (Rs) + kSpeakerLc, ///< Left of Center (Lc) + kSpeakerRc, ///< Right of Center (Rc) + kSpeakerS, ///< Surround (S) + kSpeakerCs = kSpeakerS, ///< Center of Surround (Cs) = Surround (S) + kSpeakerSl, ///< Side Left (Sl) + kSpeakerSr, ///< Side Right (Sr) + kSpeakerTm, ///< Top Middle (Tm) + kSpeakerTfl, ///< Top Front Left (Tfl) + kSpeakerTfc, ///< Top Front Center (Tfc) + kSpeakerTfr, ///< Top Front Right (Tfr) + kSpeakerTrl, ///< Top Rear Left (Trl) + kSpeakerTrc, ///< Top Rear Center (Trc) + kSpeakerTrr, ///< Top Rear Right (Trr) + kSpeakerLfe2 ///< Subbass 2 (Lfe2) +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** User-defined speaker types, to be extended in the negative range. + Will be handled as their corresponding speaker types with abs values: + e.g abs(#kSpeakerU1) == #kSpeakerL, abs(#kSpeakerU2) == #kSpeakerR) */ +//------------------------------------------------------------------------------------------------------- +enum VstUserSpeakerType +{ +//------------------------------------------------------------------------------------------------------- + kSpeakerU32 = -32, + kSpeakerU31, + kSpeakerU30, + kSpeakerU29, + kSpeakerU28, + kSpeakerU27, + kSpeakerU26, + kSpeakerU25, + kSpeakerU24, + kSpeakerU23, + kSpeakerU22, + kSpeakerU21, + kSpeakerU20, ///< == #kSpeakerLfe2 + kSpeakerU19, ///< == #kSpeakerTrr + kSpeakerU18, ///< == #kSpeakerTrc + kSpeakerU17, ///< == #kSpeakerTrl + kSpeakerU16, ///< == #kSpeakerTfr + kSpeakerU15, ///< == #kSpeakerTfc + kSpeakerU14, ///< == #kSpeakerTfl + kSpeakerU13, ///< == #kSpeakerTm + kSpeakerU12, ///< == #kSpeakerSr + kSpeakerU11, ///< == #kSpeakerSl + kSpeakerU10, ///< == #kSpeakerCs + kSpeakerU9, ///< == #kSpeakerS + kSpeakerU8, ///< == #kSpeakerRc + kSpeakerU7, ///< == #kSpeakerLc + kSpeakerU6, ///< == #kSpeakerRs + kSpeakerU5, ///< == #kSpeakerLs + kSpeakerU4, ///< == #kSpeakerLfe + kSpeakerU3, ///< == #kSpeakerC + kSpeakerU2, ///< == #kSpeakerR + kSpeakerU1 ///< == #kSpeakerL +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Speaker Arrangement Types*/ +//------------------------------------------------------------------------------------------------------- +enum VstSpeakerArrangementType +{ +//------------------------------------------------------------------------------------------------------- + kSpeakerArrUserDefined = -2,///< user defined + kSpeakerArrEmpty = -1, ///< empty arrangement + kSpeakerArrMono = 0, ///< M + kSpeakerArrStereo, ///< L R + kSpeakerArrStereoSurround, ///< Ls Rs + kSpeakerArrStereoCenter, ///< Lc Rc + kSpeakerArrStereoSide, ///< Sl Sr + kSpeakerArrStereoCLfe, ///< C Lfe + kSpeakerArr30Cine, ///< L R C + kSpeakerArr30Music, ///< L R S + kSpeakerArr31Cine, ///< L R C Lfe + kSpeakerArr31Music, ///< L R Lfe S + kSpeakerArr40Cine, ///< L R C S (LCRS) + kSpeakerArr40Music, ///< L R Ls Rs (Quadro) + kSpeakerArr41Cine, ///< L R C Lfe S (LCRS+Lfe) + kSpeakerArr41Music, ///< L R Lfe Ls Rs (Quadro+Lfe) + kSpeakerArr50, ///< L R C Ls Rs + kSpeakerArr51, ///< L R C Lfe Ls Rs + kSpeakerArr60Cine, ///< L R C Ls Rs Cs + kSpeakerArr60Music, ///< L R Ls Rs Sl Sr + kSpeakerArr61Cine, ///< L R C Lfe Ls Rs Cs + kSpeakerArr61Music, ///< L R Lfe Ls Rs Sl Sr + kSpeakerArr70Cine, ///< L R C Ls Rs Lc Rc + kSpeakerArr70Music, ///< L R C Ls Rs Sl Sr + kSpeakerArr71Cine, ///< L R C Lfe Ls Rs Lc Rc + kSpeakerArr71Music, ///< L R C Lfe Ls Rs Sl Sr + kSpeakerArr80Cine, ///< L R C Ls Rs Lc Rc Cs + kSpeakerArr80Music, ///< L R C Ls Rs Cs Sl Sr + kSpeakerArr81Cine, ///< L R C Lfe Ls Rs Lc Rc Cs + kSpeakerArr81Music, ///< L R C Lfe Ls Rs Cs Sl Sr + kSpeakerArr102, ///< L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2 + kNumSpeakerArr +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +// Offline Processing +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- +/** Offline Task Description. */ +//------------------------------------------------------------------------------------------------------- +struct VstOfflineTask +{ +//------------------------------------------------------------------------------------------------------- + char processName[96]; ///< set by plug-in + + // audio access + double readPosition; ///< set by plug-in/Host + double writePosition; ///< set by plug-in/Host + VstInt32 readCount; ///< set by plug-in/Host + VstInt32 writeCount; ///< set by plug-in + VstInt32 sizeInputBuffer; ///< set by Host + VstInt32 sizeOutputBuffer; ///< set by Host + void* inputBuffer; ///< set by Host + void* outputBuffer; ///< set by Host + double positionToProcessFrom; ///< set by Host + double numFramesToProcess; ///< set by Host + double maxFramesToWrite; ///< set by plug-in + + // other data access + void* extraBuffer; ///< set by plug-in + VstInt32 value; ///< set by Host or plug-in + VstInt32 index; ///< set by Host or plug-in + + // file attributes + double numFramesInSourceFile; ///< set by Host + double sourceSampleRate; ///< set by Host or plug-in + double destinationSampleRate; ///< set by Host or plug-in + VstInt32 numSourceChannels; ///< set by Host or plug-in + VstInt32 numDestinationChannels;///< set by Host or plug-in + VstInt32 sourceFormat; ///< set by Host + VstInt32 destinationFormat; ///< set by plug-in + char outputText[512]; ///< set by plug-in or Host + + // progress notification + double progress; ///< set by plug-in + VstInt32 progressMode; ///< Reserved for future use + char progressText[100]; ///< set by plug-in + + VstInt32 flags; ///< set by Host and plug-in; see enum #VstOfflineTaskFlags + VstInt32 returnValue; ///< Reserved for future use + void* hostOwned; ///< set by Host + void* plugOwned; ///< set by plug-in + + char future[1024]; ///< Reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Flags used in #VstOfflineTask. */ +//------------------------------------------------------------------------------------------------------- +enum VstOfflineTaskFlags +{ +//------------------------------------------------------------------------------------------------------- + kVstOfflineUnvalidParameter = 1 << 0, ///< set by Host + kVstOfflineNewFile = 1 << 1, ///< set by Host + + kVstOfflinePlugError = 1 << 10, ///< set by plug-in + kVstOfflineInterleavedAudio = 1 << 11, ///< set by plug-in + kVstOfflineTempOutputFile = 1 << 12, ///< set by plug-in + kVstOfflineFloatOutputFile = 1 << 13, ///< set by plug-in + kVstOfflineRandomWrite = 1 << 14, ///< set by plug-in + kVstOfflineStretch = 1 << 15, ///< set by plug-in + kVstOfflineNoThread = 1 << 16 ///< set by plug-in +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Option passed to #offlineRead/#offlineWrite. */ +//------------------------------------------------------------------------------------------------------- +enum VstOfflineOption +{ +//------------------------------------------------------------------------------------------------------- + kVstOfflineAudio, ///< reading/writing audio samples + kVstOfflinePeaks, ///< reading graphic representation + kVstOfflineParameter, ///< reading/writing parameters + kVstOfflineMarker, ///< reading/writing marker + kVstOfflineCursor, ///< reading/moving edit cursor + kVstOfflineSelection, ///< reading/changing selection + kVstOfflineQueryFiles ///< to request the Host to call asynchronously #offlineNotify +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Structure passed to #offlineNotify and #offlineStart */ +//------------------------------------------------------------------------------------------------------- +struct VstAudioFile +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 flags; ///< see enum #VstAudioFileFlags + void* hostOwned; ///< any data private to Host + void* plugOwned; ///< any data private to plug-in + char name[kVstMaxFileNameLen]; ///< file title + VstInt32 uniqueId; ///< uniquely identify a file during a session + double sampleRate; ///< file sample rate + VstInt32 numChannels; ///< number of channels (1 for mono, 2 for stereo...) + double numFrames; ///< number of frames in the audio file + VstInt32 format; ///< Reserved for future use + double editCursorPosition; ///< -1 if no such cursor + double selectionStart; ///< frame index of first selected frame, or -1 + double selectionSize; ///< number of frames in selection, or 0 + VstInt32 selectedChannelsMask; ///< 1 bit per channel + VstInt32 numMarkers; ///< number of markers in the file + VstInt32 timeRulerUnit; ///< see doc for possible values + double timeRulerOffset; ///< offset in time ruler (positive or negative) + double tempo; ///< as BPM (Beats Per Minute) + VstInt32 timeSigNumerator; ///< time signature numerator + VstInt32 timeSigDenominator; ///< time signature denominator + VstInt32 ticksPerBlackNote; ///< resolution + VstInt32 smpteFrameRate; ///< SMPTE rate (set as in #VstTimeInfo) + + char future[64]; ///< Reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Flags used in #VstAudioFile. */ +//------------------------------------------------------------------------------------------------------- +enum VstAudioFileFlags +{ +//------------------------------------------------------------------------------------------------------- + kVstOfflineReadOnly = 1 << 0, ///< set by Host (in call #offlineNotify) + kVstOfflineNoRateConversion = 1 << 1, ///< set by Host (in call #offlineNotify) + kVstOfflineNoChannelChange = 1 << 2, ///< set by Host (in call #offlineNotify) + + kVstOfflineCanProcessSelection = 1 << 10, ///< set by plug-in (in call #offlineStart) + kVstOfflineNoCrossfade = 1 << 11, ///< set by plug-in (in call #offlineStart) + kVstOfflineWantRead = 1 << 12, ///< set by plug-in (in call #offlineStart) + kVstOfflineWantWrite = 1 << 13, ///< set by plug-in (in call #offlineStart) + kVstOfflineWantWriteMarker = 1 << 14, ///< set by plug-in (in call #offlineStart) + kVstOfflineWantMoveCursor = 1 << 15, ///< set by plug-in (in call #offlineStart) + kVstOfflineWantSelect = 1 << 16 ///< set by plug-in (in call #offlineStart) +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Audio file marker. */ +//------------------------------------------------------------------------------------------------------- +struct VstAudioFileMarker +{ +//------------------------------------------------------------------------------------------------------- + double position; ///< marker position + char name[32]; ///< marker name + VstInt32 type; ///< marker type + VstInt32 id; ///< marker identifier + VstInt32 reserved; ///< reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +// Others +//------------------------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------------------------- +/** \deprecated Structure used for #openWindow and #closeWindow (deprecated in VST 2.4). */ +//------------------------------------------------------------------------------------------------------- +struct DECLARE_VST_DEPRECATED (VstWindow) +{ +//------------------------------------------------------------------------------------------------------- + char title[128]; + VstInt16 xPos; + VstInt16 yPos; + VstInt16 width; + VstInt16 height; + VstInt32 style; + void* parent; + void* userHandle; + void* winHandle; + + char future[104]; +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Structure used for keyUp/keyDown. */ +//------------------------------------------------------------------------------------------------------- +struct VstKeyCode +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 character; ///< ASCII character + unsigned char virt; ///< @see VstVirtualKey + unsigned char modifier; ///< @see VstModifierKey +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Platform-independent definition of Virtual Keys (used in #VstKeyCode). */ +//------------------------------------------------------------------------------------------------------- +enum VstVirtualKey +{ +//------------------------------------------------------------------------------------------------------- + VKEY_BACK = 1, + VKEY_TAB, + VKEY_CLEAR, + VKEY_RETURN, + VKEY_PAUSE, + VKEY_ESCAPE, + VKEY_SPACE, + VKEY_NEXT, + VKEY_END, + VKEY_HOME, + VKEY_LEFT, + VKEY_UP, + VKEY_RIGHT, + VKEY_DOWN, + VKEY_PAGEUP, + VKEY_PAGEDOWN, + VKEY_SELECT, + VKEY_PRINT, + VKEY_ENTER, + VKEY_SNAPSHOT, + VKEY_INSERT, + VKEY_DELETE, + VKEY_HELP, + VKEY_NUMPAD0, + VKEY_NUMPAD1, + VKEY_NUMPAD2, + VKEY_NUMPAD3, + VKEY_NUMPAD4, + VKEY_NUMPAD5, + VKEY_NUMPAD6, + VKEY_NUMPAD7, + VKEY_NUMPAD8, + VKEY_NUMPAD9, + VKEY_MULTIPLY, + VKEY_ADD, + VKEY_SEPARATOR, + VKEY_SUBTRACT, + VKEY_DECIMAL, + VKEY_DIVIDE, + VKEY_F1, + VKEY_F2, + VKEY_F3, + VKEY_F4, + VKEY_F5, + VKEY_F6, + VKEY_F7, + VKEY_F8, + VKEY_F9, + VKEY_F10, + VKEY_F11, + VKEY_F12, + VKEY_NUMLOCK, + VKEY_SCROLL, + VKEY_SHIFT, + VKEY_CONTROL, + VKEY_ALT, + VKEY_EQUALS +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Modifier flags used in #VstKeyCode. */ +//------------------------------------------------------------------------------------------------------- +enum VstModifierKey +{ +//------------------------------------------------------------------------------------------------------- + MODIFIER_SHIFT = 1<<0, ///< Shift + MODIFIER_ALTERNATE = 1<<1, ///< Alt + MODIFIER_COMMAND = 1<<2, ///< Control on Mac + MODIFIER_CONTROL = 1<<3 ///< Ctrl on PC, Apple on Mac +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** File filter used in #VstFileSelect. */ +//------------------------------------------------------------------------------------------------------- +struct VstFileType +{ +//------------------------------------------------------------------------------------------------------- + char name[128]; ///< display name + char macType[8]; ///< MacOS type + char dosType[8]; ///< Windows file extension + char unixType[8]; ///< Unix file extension + char mimeType1[128]; ///< MIME type + char mimeType2[128]; ///< additional MIME type + + VstFileType (const char* _name = 0, const char* _macType = 0, const char* _dosType = 0, + const char* _unixType = 0, const char* _mimeType1 = 0, const char* _mimeType2 = 0) + { + vst_strncpy (name, _name ? _name : "", 127); + vst_strncpy (macType, _macType ? _macType : "", 7); + vst_strncpy (dosType, _dosType ? _dosType : "", 7); + vst_strncpy (unixType, _unixType ? _unixType : "", 7); + vst_strncpy (mimeType1, _mimeType1 ? _mimeType1 : "", 127); + vst_strncpy (mimeType2, _mimeType2 ? _mimeType2 : "", 127); + } +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** File Selector Description used in #audioMasterOpenFileSelector. */ +//------------------------------------------------------------------------------------------------------- +struct VstFileSelect +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 command; ///< @see VstFileSelectCommand + VstInt32 type; ///< @see VstFileSelectType + VstInt32 macCreator; ///< optional: 0 = no creator + VstInt32 nbFileTypes; ///< number of fileTypes + VstFileType* fileTypes; ///< list of fileTypes @see VstFileType + char title[1024]; ///< text to display in file selector's title + char* initialPath; ///< initial path + char* returnPath; ///< use with #kVstFileLoad and #kVstDirectorySelect. null: Host allocates memory, plug-in must call #closeOpenFileSelector! + VstInt32 sizeReturnPath; ///< size of allocated memory for return paths + char** returnMultiplePaths; ///< use with kVstMultipleFilesLoad. Host allocates memory, plug-in must call #closeOpenFileSelector! + VstInt32 nbReturnPath; ///< number of selected paths + VstIntPtr reserved; ///< reserved for Host application + + char future[116]; ///< reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Command constants used in #VstFileSelect structure. */ +//------------------------------------------------------------------------------------------------------- +enum VstFileSelectCommand +{ +//------------------------------------------------------------------------------------------------------- + kVstFileLoad = 0, ///< for loading a file + kVstFileSave, ///< for saving a file + kVstMultipleFilesLoad, ///< for loading multiple files + kVstDirectorySelect ///< for selecting a directory/folder +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Types used in #VstFileSelect structure. */ +//------------------------------------------------------------------------------------------------------- +enum VstFileSelectType +{ +//------------------------------------------------------------------------------------------------------- + kVstFileType = 0 ///< regular file selector +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Structure used for #effBeginLoadBank/#effBeginLoadProgram. */ +//------------------------------------------------------------------------------------------------------- +struct VstPatchChunkInfo +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 version; ///< Format Version (should be 1) + VstInt32 pluginUniqueID; ///< UniqueID of the plug-in + VstInt32 pluginVersion; ///< Plug-in Version + VstInt32 numElements; ///< Number of Programs (Bank) or Parameters (Program) + + char future[48]; ///< Reserved for future use +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** PanLaw Type. */ +//------------------------------------------------------------------------------------------------------- +enum VstPanLawType +{ +//------------------------------------------------------------------------------------------------------- + kLinearPanLaw = 0, ///< L = pan * M; R = (1 - pan) * M; + kEqualPowerPanLaw ///< L = pow (pan, 0.5) * M; R = pow ((1 - pan), 0.5) * M; +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Process Levels returned by #audioMasterGetCurrentProcessLevel. */ +//------------------------------------------------------------------------------------------------------- +enum VstProcessLevels +{ +//------------------------------------------------------------------------------------------------------- + kVstProcessLevelUnknown = 0, ///< not supported by Host + kVstProcessLevelUser, ///< 1: currently in user thread (GUI) + kVstProcessLevelRealtime, ///< 2: currently in audio thread (where process is called) + kVstProcessLevelPrefetch, ///< 3: currently in 'sequencer' thread (MIDI, timer etc) + kVstProcessLevelOffline ///< 4: currently offline processing and thus in user thread +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Automation States returned by #audioMasterGetAutomationState. */ +//------------------------------------------------------------------------------------------------------- +enum VstAutomationStates +{ +//------------------------------------------------------------------------------------------------------- + kVstAutomationUnsupported = 0, ///< not supported by Host + kVstAutomationOff, ///< off + kVstAutomationRead, ///< read + kVstAutomationWrite, ///< write + kVstAutomationReadWrite ///< read and write +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +#if TARGET_API_MAC_CARBON + #pragma options align=reset +#elif defined(WIN32) || defined(__FLAT__) || defined(__GNUC__) + #pragma pack(pop) +#elif defined __BORLANDC__ + #pragma -a- +#endif +//------------------------------------------------------------------------------------------------------- + +#endif //__aeffectx__ diff --git a/src/deps/vst/vstfxstore.h b/src/deps/vst/vstfxstore.h new file mode 100644 index 0000000..d7d60f4 --- /dev/null +++ b/src/deps/vst/vstfxstore.h @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------------------------------- +// VST Plug-Ins SDK +// Version 2.4 $Date: 2006/02/09 11:05:51 $ +// +// Category : VST 2.x Interfaces +// Filename : vstfxstore.h +// Created by : Steinberg Media Technologies +// Description : Definition of Program (fxp) and Bank (fxb) structures +// +// © 2006, Steinberg Media Technologies, All Rights Reserved +//------------------------------------------------------------------------------------------------------- + +#ifndef __vstfxstore__ +#define __vstfxstore__ + +#ifndef __aeffect__ +#include "aeffect.h" +#endif + +//------------------------------------------------------------------------------------------------------- +/** Root chunk identifier for Programs (fxp) and Banks (fxb). */ +#define cMagic 'CcnK' + +/** Regular Program (fxp) identifier. */ +#define fMagic 'FxCk' + +/** Regular Bank (fxb) identifier. */ +#define bankMagic 'FxBk' + +/** Program (fxp) identifier for opaque chunk data. */ +#define chunkPresetMagic 'FPCh' + +/** Bank (fxb) identifier for opaque chunk data. */ +#define chunkBankMagic 'FBCh' + +/* + Note: The C data structures below are for illustration only. You can not read/write them directly. + The byte order on disk of fxp and fxb files is Big Endian. You have to swap integer + and floating-point values on Little Endian platforms (Windows, MacIntel)! +*/ + +//------------------------------------------------------------------------------------------------------- +/** Program (fxp) structure. */ +//------------------------------------------------------------------------------------------------------- +struct fxProgram +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 chunkMagic; ///< 'CcnK' + VstInt32 byteSize; ///< size of this chunk, excl. magic + byteSize + + VstInt32 fxMagic; ///< 'FxCk' (regular) or 'FPCh' (opaque chunk) + VstInt32 version; ///< format version (currently 1) + VstInt32 fxID; ///< fx unique ID + VstInt32 fxVersion; ///< fx version + + VstInt32 numParams; ///< number of parameters + char prgName[28]; ///< program name (null-terminated ASCII string) + + union + { + float params[1]; ///< variable sized array with parameter values + struct + { + VstInt32 size; ///< size of program data + char chunk[1]; ///< variable sized array with opaque program data + } data; ///< program chunk data + } content; ///< program content depending on fxMagic +//------------------------------------------------------------------------------------------------------- +}; + +//------------------------------------------------------------------------------------------------------- +/** Bank (fxb) structure. */ +//------------------------------------------------------------------------------------------------------- +struct fxBank +{ +//------------------------------------------------------------------------------------------------------- + VstInt32 chunkMagic; ///< 'CcnK' + VstInt32 byteSize; ///< size of this chunk, excl. magic + byteSize + + VstInt32 fxMagic; ///< 'FxBk' (regular) or 'FBCh' (opaque chunk) + VstInt32 version; ///< format version (1 or 2) + VstInt32 fxID; ///< fx unique ID + VstInt32 fxVersion; ///< fx version + + VstInt32 numPrograms; ///< number of programs + +#if VST_2_4_EXTENSIONS + VstInt32 currentProgram; ///< version 2: current program number + char future[124]; ///< reserved, should be zero +#else + char future[128]; ///< reserved, should be zero +#endif + + union + { + fxProgram programs[1]; ///< variable number of programs + struct + { + VstInt32 size; ///< size of bank data + char chunk[1]; ///< variable sized array with opaque bank data + } data; ///< bank chunk data + } content; ///< bank content depending on fxMagic +//------------------------------------------------------------------------------------------------------- +}; + +#endif // __vstfxstore__ diff --git a/src/ext/giada.ico b/src/ext/giada.ico new file mode 100644 index 0000000..e0a90bd Binary files /dev/null and b/src/ext/giada.ico differ diff --git a/src/ext/resource.h b/src/ext/resource.h new file mode 100644 index 0000000..d771ba8 --- /dev/null +++ b/src/ext/resource.h @@ -0,0 +1 @@ +#define IDI_ICON1 101 \ No newline at end of file diff --git a/src/ext/resource.rc b/src/ext/resource.rc new file mode 100644 index 0000000..fb0be40 --- /dev/null +++ b/src/ext/resource.rc @@ -0,0 +1,2 @@ +#include "resource.h" +IDI_ICON1 ICON DISCARDABLE "giada.ico" \ No newline at end of file diff --git a/src/gd_about.cpp b/src/gd_about.cpp deleted file mode 100644 index 8caa7a0..0000000 --- a/src/gd_about.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_about - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "gd_about.h" -#include "conf.h" -#include "const.h" -#include "kernelAudio.h" -#include "kernelMidi.h" -#include "ge_mixed.h" -#include "graphics.h" -#include "gui_utils.h" - - -extern Conf G_Conf; - - -gdAbout::gdAbout() -#ifdef WITH_VST -: gWindow(340, 405, "About Giada") { -#else -: gWindow(340, 320, "About Giada") { -#endif - - if (G_Conf.aboutX) - resize(G_Conf.aboutX, G_Conf.aboutY, w(), h()); - - set_modal(); - - logo = new gBox(8, 10, 324, 86); - text = new gBox(8, 120, 324, 145); - close = new gClick(252, h()-28, 80, 20, "Close"); -#ifdef WITH_VST - vstLogo = new gBox(8, 265, 324, 50); - vstText = new gBox(8, 315, 324, 46); -#endif - end(); - - logo->image(new Fl_Pixmap(giada_logo_xpm)); - text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP); - - char message[512]; - sprintf( - message, - "Version " VERSIONE " (" __DATE__ ")\n\n" - "Developed by Monocasual\n" - "Based on FLTK (%d.%d.%d), RtAudio (%s),\n" - "RtMidi (%s), libsamplerate and libsndfile\n\n" - "Released under the terms of the GNU General\n" - "Public License (GPL v3)\n\n" - "News, infos, contacts and documentation:\n" - "www.giadamusic.com", - FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION, - kernelAudio::getRtAudioVersion().c_str(), - kernelMidi::getRtMidiVersion().c_str()); - - int tw = 0; - int th = 0; - fl_measure(message, tw, th); - text->copy_label(message); - text->size(text->w(), th); - -#ifdef WITH_VST - vstLogo->image(new Fl_Pixmap(vstLogo_xpm)); - vstLogo->position(vstLogo->x(), text->y()+text->h()+8); - vstText->label( - "VST Plug-In Technology by Steinberg\n" - "VST is a trademark of Steinberg\nMedia Technologies GmbH" - ); - vstText->position(vstText->x(), vstLogo->y()+vstLogo->h()); - -#endif - - close->callback(cb_close, (void*)this); - gu_setFavicon(this); - setId(WID_ABOUT); - show(); -} - - -/* ------------------------------------------------------------------ */ - - -gdAbout::~gdAbout() { - G_Conf.aboutX = x(); - G_Conf.aboutY = y(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdAbout::cb_close(Fl_Widget *w, void *p) { ((gdAbout*)p)->__cb_close(); } - - -/* ------------------------------------------------------------------ */ - - -void gdAbout::__cb_close() { - do_callback(); -} diff --git a/src/gd_about.h b/src/gd_about.h deleted file mode 100644 index 695a91e..0000000 --- a/src/gd_about.h +++ /dev/null @@ -1,58 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_about - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GD_ABOUT_H -#define GD_ABOUT_H - -#include -#include -#include "ge_window.h" - - -class gdAbout : public gWindow { -private: - class gBox *logo; - class gBox *text; - class gClick *close; - -#ifdef WITH_VST - class gBox *vstText; - class gBox *vstLogo; -#endif - -public: - gdAbout(); - ~gdAbout(); - - static void cb_close(Fl_Widget *w, void *p); - inline void __cb_close(); - -}; - -#endif diff --git a/src/gd_actionEditor.cpp b/src/gd_actionEditor.cpp deleted file mode 100644 index 9737e9a..0000000 --- a/src/gd_actionEditor.cpp +++ /dev/null @@ -1,472 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_actionEditor - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "gd_actionEditor.h" -#include "ge_actionChannel.h" -#include "ge_muteChannel.h" -#include "ge_envelopeChannel.h" -#include "ge_pianoRoll.h" -#include "gui_utils.h" -#include "graphics.h" -#include "mixer.h" -#include "recorder.h" -#include "conf.h" -#include "ge_mixed.h" -#include "channel.h" -#include "sampleChannel.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; - - -gdActionEditor::gdActionEditor(Channel *chan) - : gWindow(640, 284), - chan (chan), - zoom (100), - coverX (0) -{ - if (G_Conf.actionEditorW) { - resize(G_Conf.actionEditorX, G_Conf.actionEditorY, G_Conf.actionEditorW, G_Conf.actionEditorH); - zoom = G_Conf.actionEditorZoom; - } - - totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom); - - /* 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(); - - if (chan->type == CHANNEL_SAMPLE) { - actionType = new gChoice(8, 8, 80, 20); - gridTool = new gGridTool(actionType->x()+actionType->w()+4, 8, this); - actionType->add("key press"); - actionType->add("key release"); - actionType->add("kill chan"); - actionType->value(0); - - SampleChannel *ch = (SampleChannel*) chan; - if (ch->mode == SINGLE_PRESS || ch->mode & LOOP_ANY) - actionType->deactivate(); - } - else { - gridTool = new gGridTool(8, 8, this); - } - - gBox *b1 = new gBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons - zoomIn = new gClick(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm); - zoomOut = new gClick(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm); - upperArea->end(); - upperArea->resizable(b1); - - zoomIn->callback(cb_zoomIn, (void*)this); - zoomOut->callback(cb_zoomOut, (void*)this); - - /* main scroller: contains all widgets */ - - scroller = new gScroll(8, 36, w()-16, h()-44); - - if (chan->type == CHANNEL_SAMPLE) { - - SampleChannel *ch = (SampleChannel*) chan; - - ac = new gActionChannel (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch); - mc = new gMuteChannel (scroller->x(), ac->y()+ac->h()+8, this); - vc = new gEnvelopeChannel (scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume"); - scroller->add(ac); - //scroller->add(new gResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8)); - scroller->add(mc); - //scroller->add(new gResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8)); - scroller->add(vc); - //scroller->add(new gResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8)); - - /* fill volume envelope with actions from recorder */ - - vc->fill(); - - /* if channel is LOOP_ANY, deactivate it: a loop mode channel cannot - * hold keypress/keyrelease actions */ - - if (ch->mode & LOOP_ANY) - ac->deactivate(); - } - else { - pr = new gPianoRollContainer(scroller->x(), upperArea->y()+upperArea->h()+8, this); - scroller->add(pr); - scroller->add(new gResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8)); - } - - end(); - - /* compute values */ - - update(); - gridTool->calc(); - - gu_setFavicon(this); - - char buf[256]; - sprintf(buf, "Edit Actions in Channel %d", chan->index+1); - label(buf); - - set_non_modal(); - size_range(640, 284); - resizable(scroller); - - show(); -} - - -/* ------------------------------------------------------------------ */ - - -gdActionEditor::~gdActionEditor() { - G_Conf.actionEditorX = x(); - G_Conf.actionEditorY = y(); - G_Conf.actionEditorW = w(); - G_Conf.actionEditorH = h(); - G_Conf.actionEditorZoom = zoom; - - /** CHECKME - missing clear() ? */ - -} - - -/* ------------------------------------------------------------------ */ - - -void gdActionEditor::cb_zoomIn(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomIn(); } -void gdActionEditor::cb_zoomOut(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomOut(); } - - -/* ------------------------------------------------------------------ */ - - -void gdActionEditor::__cb_zoomIn() { - - /* zoom 50: empirical value, to avoid a totalWidth > 16 bit signed - * (32767 max), unsupported by FLTK 1.3.x */ - - if (zoom <= 50) - return; - - zoom /= 2; - - update(); - - if (chan->type == CHANNEL_SAMPLE) { - ac->size(totalWidth, ac->h()); - mc->size(totalWidth, mc->h()); - vc->size(totalWidth, vc->h()); - ac->updateActions(); - mc->updateActions(); - vc->updateActions(); - } - else { - pr->size(totalWidth, pr->h()); - pr->updateActions(); - } - - /* scroll to pointer */ - - int shift = Fl::event_x() + scroller->xposition(); - scroller->scroll_to(scroller->xposition() + shift, scroller->yposition()); - - /* update all underlying widgets */ - - gridTool->calc(); - scroller->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdActionEditor::__cb_zoomOut() { - - zoom *= 2; - - update(); - - if (chan->type == CHANNEL_SAMPLE) { - ac->size(totalWidth, ac->h()); - mc->size(totalWidth, mc->h()); - vc->size(totalWidth, vc->h()); - ac->updateActions(); - mc->updateActions(); - vc->updateActions(); - } - else { - pr->size(totalWidth, pr->h()); - pr->updateActions(); - } - - /* scroll to pointer */ - - int shift = (Fl::event_x() + scroller->xposition()) / -2; - if (scroller->xposition() + shift < 0) - shift = 0; - scroller->scroll_to(scroller->xposition() + shift, scroller->yposition()); - - /* update all underlying widgets */ - - gridTool->calc(); - scroller->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdActionEditor::update() { - totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom); - if (totalWidth < scroller->w()) { - totalWidth = scroller->w(); - zoom = (int) ceilf(G_Mixer.framesInSequencer / (float) totalWidth); - scroller->scroll_to(0, scroller->yposition()); - } -} - - -/* ------------------------------------------------------------------ */ - - -int gdActionEditor::handle(int e) { - int ret = Fl_Group::handle(e); - switch (e) { - case FL_MOUSEWHEEL: { - Fl::event_dy() == -1 ? __cb_zoomIn() : __cb_zoomOut(); - ret = 1; - break; - } - } - return ret; -} - - -/* ------------------------------------------------------------------ */ - - -int gdActionEditor::getActionType() { - if (actionType->value() == 0) - return ACTION_KEYPRESS; - else - if (actionType->value() == 1) - return ACTION_KEYREL; - else - if (actionType->value() == 2) - return ACTION_KILLCHAN; - else - return -1; -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gGridTool::gGridTool(int x, int y, gdActionEditor *parent) - : Fl_Group(x, y, 80, 20), parent(parent) -{ - gridType = new gChoice(x, y, 40, 20); - gridType->add("1"); - gridType->add("2"); - gridType->add("3"); - gridType->add("4"); - gridType->add("6"); - gridType->add("8"); - gridType->add("16"); - gridType->add("32"); - gridType->value(0); - gridType->callback(cb_changeType, (void*)this); - - active = new gCheck (x+44, y+4, 12, 12); - - gridType->value(G_Conf.actionEditorGridVal); - active->value(G_Conf.actionEditorGridOn); - - end(); -} - - -/* ------------------------------------------------------------------ */ - - -gGridTool::~gGridTool() { - G_Conf.actionEditorGridVal = gridType->value(); - G_Conf.actionEditorGridOn = active->value(); -} - - -/* ------------------------------------------------------------------ */ - - -void gGridTool::cb_changeType(Fl_Widget *w, void *p) { ((gGridTool*)p)->__cb_changeType(); } - - -/* ------------------------------------------------------------------ */ - - -void gGridTool::__cb_changeType() { - calc(); - parent->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -bool gGridTool::isOn() { - return active->value(); -} - - -/* ------------------------------------------------------------------ */ - - -int gGridTool::getValue() { - switch (gridType->value()) { - case 0: return 1; - case 1: return 2; - case 2: return 3; - case 3: return 4; - case 4: return 6; - case 5: return 8; - case 6: return 16; - case 7: return 32; - } - return 0; -} - - -/* ------------------------------------------------------------------ */ - - -void gGridTool::calc() { - - points.clear(); - frames.clear(); - bars.clear(); - beats.clear(); - - /* find beats, bars and grid. The method is the same of the waveform in sample - * editor. Take totalwidth (the width in pixel of the area to draw), knowing - * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth, - * put a concentrate of each block (which is totalFrames / zoom) */ - - int j = 0; - int fpgc = floor(G_Mixer.framesPerBeat / getValue()); // frames per grid cell - - for (int i=1; itotalWidth; i++) { // if i=0, step=0 -> useless cycle - int step = parent->zoom*i; - while (j < step && j < G_Mixer.totalFrames) { - if (j % fpgc == 0) { - points.add(i); - frames.add(j); - } - if (j % G_Mixer.framesPerBeat == 0) - beats.add(i); - if (j % G_Mixer.framesPerBar == 0 && i != 1) - bars.add(i); - if (j == G_Mixer.totalFrames-1) - parent->coverX = i; - j++; - } - j = step; - } - - /* fix coverX if == 0, which means G_Mixer.beats == 32 */ - - if (G_Mixer.beats == 32) - parent->coverX = parent->totalWidth; -} - - -/* ------------------------------------------------------------------ */ - - -int gGridTool::getSnapPoint(int v) { - - if (v == 0) return 0; - - for (int i=0; i<(int)points.size; i++) { - - if (i == (int) points.size-1) - return points.at(i); - - int gp = points.at(i); - int gpn = points.at(i+1); - - if (v >= gp && v < gpn) - return gp; - } - return v; // default value -} - - -/* ------------------------------------------------------------------ */ - - -int gGridTool::getSnapFrame(int v) { - - v *= parent->zoom; // transformation pixel -> frame - - for (int i=0; i<(int)frames.size; i++) { - - if (i == (int) frames.size-1) - return frames.at(i); - - int gf = frames.at(i); // grid frame - int gfn = frames.at(i+1); // grid frame next - - if (v >= gf && v < gfn) { - - /* which one is the closest? gf < v < gfn */ - - if ((gfn - v) < (v - gf)) - return gfn; - else - return gf; - } - } - return v; // default value -} - - -/* ------------------------------------------------------------------ */ - - -int gGridTool::getCellSize() { - return (parent->coverX - parent->ac->x()) / G_Mixer.beats / getValue(); -} diff --git a/src/gd_actionEditor.h b/src/gd_actionEditor.h deleted file mode 100644 index 71c37fd..0000000 --- a/src/gd_actionEditor.h +++ /dev/null @@ -1,131 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_actionEditor - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GD_ACTIONEDITOR_H -#define GD_ACTIONEDITOR_H - -#include -#include -#include -#include "ge_window.h" - - -/* gActionEditor - * main window which contains the tools for dealing with actions. - * This class calculates chan, zoom, frames per beat, and so on. Each - * sub-widget contains a pointer to this window to query those data. */ - -class gdActionEditor : public gWindow { - -private: - - /* update - * compute total width, in pixel. */ - - void update(); - -public: - - gdActionEditor(class Channel *chan); - ~gdActionEditor(); - - int handle(int e); - - int getActionType(); - - static void cb_zoomIn(Fl_Widget *w, void *p); - static void cb_zoomOut(Fl_Widget *w, void *p); - inline void __cb_zoomIn(); - inline void __cb_zoomOut(); - - class gChoice *actionType; - class gGridTool *gridTool; - class gClick *zoomIn; - class gClick *zoomOut; - class gScroll *scroller; // widget container - - class gActionChannel *ac; - class gMuteChannel *mc; - class gEnvelopeChannel *vc; - class gPianoRollContainer *pr; - - gVector widgets; - - class Channel *chan; - - int zoom; - int totalWidth; // total width of the widget, in pixel (zoom affected) - int coverX; // x1 of the unused area (x2 = totalWidth) -}; - - -/* ------------------------------------------------------------------ */ - - -class gGridTool : public Fl_Group { - -private: - class gChoice *gridType; - class gCheck *active; - - class gdActionEditor *parent; - - static void cb_changeType(Fl_Widget *w, void *p); - inline void __cb_changeType(); - -public: - - gGridTool(int x, int y, gdActionEditor *parent); - ~gGridTool(); - - int getValue(); - bool isOn(); - void calc(); - - /* getSnapPoint - * given a cursor position in input, return the x coordinates of the - * nearest snap point (in pixel, clean, ie. not x()-shifted) */ - - int getSnapPoint(int v); - int getSnapFrame(int v); - - /* getCellSize - * return the size in pixel of a single cell of the grid. */ - - int getCellSize(); - - gVector points; // points of the grid - gVector frames; // frames of the grid - - gVector bars; - gVector beats; -}; - - -#endif diff --git a/src/gd_beatsInput.cpp b/src/gd_beatsInput.cpp deleted file mode 100644 index 0c381c9..0000000 --- a/src/gd_beatsInput.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_beatsInput - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "gd_beatsInput.h" -#include "gd_mainWindow.h" -#include "gui_utils.h" -#include "patch.h" -#include "glue.h" -#include "mixer.h" -#include "conf.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern gdMainWindow *mainWin; - - -gdBeatsInput::gdBeatsInput() - : gWindow(164, 60, "Beats") -{ - if (G_Conf.beatsX) - resize(G_Conf.beatsX, G_Conf.beatsY, w(), h()); - - set_modal(); - - beats = new gInput(8, 8, 35, 20); - bars = new gInput(47, 8, 35, 20); - ok = new gClick(86, 8, 70, 20, "Ok"); - resizeRec = new gCheck(8, 40, 12, 12, "resize recorded actions"); - end(); - - char buf_bars[3]; sprintf(buf_bars, "%d", G_Mixer.bars); - char buf_beats[3]; sprintf(buf_beats, "%d", G_Mixer.beats); - beats->maximum_size(2); - beats->value(buf_beats); - beats->type(FL_INT_INPUT); - bars->maximum_size(2); - bars->value(buf_bars); - bars->type(FL_INT_INPUT); - ok->shortcut(FL_Enter); - ok->callback(cb_update_batt, (void*)this); - resizeRec->value(G_Conf.resizeRecordings); - - gu_setFavicon(this); - setId(WID_BEATS); - show(); -} - - -/* ------------------------------------------------------------------ */ - - -gdBeatsInput::~gdBeatsInput() -{ - G_Conf.beatsX = x(); - G_Conf.beatsY = y(); - G_Conf.resizeRecordings = resizeRec->value(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdBeatsInput::cb_update_batt(Fl_Widget *w, void *p) { ((gdBeatsInput*)p)->__cb_update_batt(); } - - -/* ------------------------------------------------------------------ */ - - -void gdBeatsInput::__cb_update_batt() -{ - if (!strcmp(beats->value(), "") || !strcmp(bars->value(), "")) - return; - glue_setBeats(atoi(beats->value()), atoi(bars->value()), resizeRec->value()); - do_callback(); -} diff --git a/src/gd_beatsInput.h b/src/gd_beatsInput.h deleted file mode 100644 index 8a5ccca..0000000 --- a/src/gd_beatsInput.h +++ /dev/null @@ -1,56 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_beatsInput - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GD_BEATSINPUT_H -#define GD_BEATSINPUT_H - -#include -#include -#include "ge_window.h" - - -class gdBeatsInput : public gWindow -{ -private: - - static void cb_update_batt(Fl_Widget *w, void *p); - inline void __cb_update_batt(); - - class gInput *beats; - class gInput *bars; - class gClick *ok; - class gCheck *resizeRec; - -public: - - gdBeatsInput(); - ~gdBeatsInput(); -}; - - -#endif diff --git a/src/gd_bpmInput.cpp b/src/gd_bpmInput.cpp deleted file mode 100644 index 048fe0a..0000000 --- a/src/gd_bpmInput.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_bpmInput - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "gd_bpmInput.h" -#include "gd_mainWindow.h" -#include "conf.h" -#include "ge_mixed.h" -#include "mixer.h" -#include "gui_utils.h" -#include "glue.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern gdMainWindow *mainWin; - - -gdBpmInput::gdBpmInput(const char *label) -: gWindow(144, 36, "Bpm") { - - if (G_Conf.bpmX) - resize(G_Conf.bpmX, G_Conf.bpmY, w(), h()); - - set_modal(); - - input_a = new gInput(8, 8, 30, 20); - input_b = new gInput(42, 8, 20, 20); - ok = new gClick(66, 8, 70, 20, "Ok"); - end(); - - char a[4]; - snprintf(a, 4, "%d", (int) G_Mixer.bpm); - char b[2]; - for (unsigned i=0; imaximum_size(3); - input_a->type(FL_INT_INPUT); - input_a->value(a); - input_b->maximum_size(1); - input_b->type(FL_INT_INPUT); - input_b->value(b); - - ok->shortcut(FL_Enter); - ok->callback(cb_update_bpm, (void*)this); - - gu_setFavicon(this); - setId(WID_BPM); - show(); -} - - -/* ------------------------------------------------------------------ */ - - -gdBpmInput::~gdBpmInput() { - G_Conf.bpmX = x(); - G_Conf.bpmY = y(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdBpmInput::cb_update_bpm(Fl_Widget *w, void *p) { ((gdBpmInput*)p)->__cb_update_bpm(); } - - -/* ------------------------------------------------------------------ */ - - -void gdBpmInput::__cb_update_bpm() { - if (strcmp(input_a->value(), "") == 0) - return; - glue_setBpm(input_a->value(), input_b->value()); - do_callback(); -} diff --git a/src/gd_bpmInput.h b/src/gd_bpmInput.h deleted file mode 100644 index e3bc211..0000000 --- a/src/gd_bpmInput.h +++ /dev/null @@ -1,51 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_bpmInput - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GD_BPMINPUT_H -#define GD_BPMINPUT_H - -#include -#include -#include "ge_window.h" - - -class gdBpmInput : public gWindow { -private: - static void cb_update_bpm(Fl_Widget *w, void *p); - inline void __cb_update_bpm(); - - class gInput *input_a; - class gInput *input_b; - class gClick *ok; - -public: - gdBpmInput(const char *label); // pointer to mainWin->timing->bpm->label() - ~gdBpmInput(); -}; - -#endif diff --git a/src/gd_browser.cpp b/src/gd_browser.cpp deleted file mode 100644 index 188a6c0..0000000 --- a/src/gd_browser.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_browser - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "gd_browser.h" -#include "ge_browser.h" -#include "gd_pluginList.h" -#include "gd_mainWindow.h" -#include "gg_keyboard.h" -#include "gd_warnings.h" -#include "ge_channel.h" -#include "mixer.h" -#include "graphics.h" -#include "wave.h" -#include "glue.h" -#include "pluginHost.h" -#include "channel.h" -#include "sampleChannel.h" -#include "patch.h" -#include "conf.h" - - -extern Patch G_Patch; -extern Conf G_Conf; -extern Mixer G_Mixer; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif -extern gdMainWindow *mainWin; - - -gdBrowser::gdBrowser(const char *title, const char *initPath, Channel *ch, int type, int stackType) - : gWindow (396, 302, title), - ch (ch), - type (type), - stackType(stackType) -{ - set_non_modal(); - - browser = new gBrowser(8, 36, 380, 230); - Fl_Group *group_btn = new Fl_Group(8, 274, 380, 20); - gBox *b = new gBox(8, 274, 204, 20); // spacer window border <-> buttons - ok = new gClick(308, 274, 80, 20); - cancel = new gClick(220, 274, 80, 20, "Cancel"); - status = new gProgress(8, 274, 204, 20); - status->minimum(0); - status->maximum(1); - status->hide(); // show the bar only if necessary - group_btn->resizable(b); - group_btn->end(); - - Fl_Group *group_upd = new Fl_Group(8, 8, 380, 25); - if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please! - name = new gInput(208, 8, 152, 20); - if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please! - where = new gInput(8, 8, 192, 20); - else - where = new gInput(8, 8, 352, 20); - updir = new gClick(368, 8, 20, 20, "", updirOff_xpm, updirOn_xpm); - group_upd->resizable(where); - group_upd->end(); - - end(); - - resizable(browser); - size_range(w(), h(), 0, 0); - - where->readonly(true); - where->cursor_color(COLOR_BG_DARK); - - if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please! - ok->label("Save"); - else - ok->label("Load"); - - if (type == BROWSER_LOAD_PATCH) - ok->callback(cb_load_patch, (void*)this); - else - if (type == BROWSER_LOAD_SAMPLE) - ok->callback(cb_load_sample, (void*)this); - else - if (type == BROWSER_SAVE_PATCH) { - ok->callback(cb_save_patch, (void*)this); - name->value(G_Patch.name[0] == '\0' ? "my_patch.gptc" : G_Patch.name); - name->maximum_size(MAX_PATCHNAME_LEN+5); // +5 for ".gptc" - } - else - if (type == BROWSER_SAVE_SAMPLE) { - ok->callback(cb_save_sample, (void*)this); - name->value(((SampleChannel*)ch)->wave->name.c_str()); - } - else - if (type == BROWSER_SAVE_PROJECT) { - ok->callback(cb_save_project, (void*)this); - name->value(gStripExt(G_Patch.name).c_str()); - } -#ifdef WITH_VST - else - if (type == BROWSER_LOAD_PLUGIN) { - ok->callback(cb_loadPlugin, (void*)this); - } -#endif - - ok->shortcut(FL_Enter); - - updir->callback(cb_up, (void*)this); - cancel->callback(cb_close, (void*)this); - browser->callback(cb_down, this); - browser->path_obj = where; - browser->init(initPath); - - if (G_Conf.browserW) - resize(G_Conf.browserX, G_Conf.browserY, G_Conf.browserW, G_Conf.browserH); - - gu_setFavicon(this); - show(); -} - - -/* -------------------------------------------------------------------------- */ - - -gdBrowser::~gdBrowser() { - G_Conf.browserX = x(); - G_Conf.browserY = y(); - G_Conf.browserW = w(); - G_Conf.browserH = h(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::cb_load_patch (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_load_patch(); } -void gdBrowser::cb_load_sample (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_load_sample(); } -void gdBrowser::cb_save_sample (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_sample(); } -void gdBrowser::cb_save_patch (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_patch(); } -void gdBrowser::cb_save_project(Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_project(); } -void gdBrowser::cb_down (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_down(); } -void gdBrowser::cb_up (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_up(); } -void gdBrowser::cb_close (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_close(); } -#ifdef WITH_VST -void gdBrowser::cb_loadPlugin (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_loadPlugin(); } -#endif - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_load_patch() { - - if (browser->text(browser->value()) == NULL) - return; - - /* patchFile is the file to open. - * For patches: browser->get_selected_item() - * for projects: browser->get_selected_item() without extention + - * patch name appended */ - - std::string patchFile = browser->get_selected_item();; - bool isProject; - - if (gIsProject(browser->get_selected_item())) { - std::string patchName = gGetProjectName(browser->get_selected_item()); -#if defined(__linux__) || defined(__APPLE__) - patchFile = patchFile+"/"+patchName+".gptc"; -#elif defined(_WIN32) - patchFile = patchFile+"\\"+patchName+".gptc"; -#endif - isProject = true; - } - else - isProject = false; - - int res = glue_loadPatch(patchFile.c_str(), browser->path_obj->value(), status, isProject); - - if (res == PATCH_UNREADABLE) { - status->hide(); - if (isProject) - gdAlert("This project is unreadable."); - else - gdAlert("This patch is unreadable."); - } - else if (res == PATCH_INVALID) { - status->hide(); - if (isProject) - gdAlert("This project is not valid."); - else - gdAlert("This patch is not valid."); - } - else - do_callback(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_save_sample() { - - if (strcmp(name->value(), "") == 0) { /// FIXME glue business - gdAlert("Please choose a file name."); - return; - } - - /* bruteforce check extension. */ - - std::string filename = gStripExt(name->value()); - char fullpath[PATH_MAX]; - sprintf(fullpath, "%s/%s.wav", where->value(), filename.c_str()); - - if (gFileExists(fullpath)) - if (!gdConfirmWin("Warning", "File exists: overwrite?")) - return; - - if (((SampleChannel*)ch)->save(fullpath)) - do_callback(); - else - gdAlert("Unable to save this sample!"); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_load_sample() { - if (browser->text(browser->value()) == NULL) - return; - - int res = glue_loadChannel((SampleChannel*) ch, browser->get_selected_item()); - - if (res == SAMPLE_LOADED_OK) { - do_callback(); - mainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open - } - else - mainWin->keyboard->printChannelMessage(res); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_down() { - const char *path = browser->get_selected_item(); - if (!path) // when click on an empty area - return; - if (!gIsDir(path)) { - - /* set the name of the patch/sample/project as the selected item */ - - if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) { - if (gIsProject(path)) { - std::string tmp = browser->text(browser->value()); - tmp.erase(0, 4); - name->value(tmp.c_str()); - } - else - name->value(browser->text(browser->value())); - } - return; - } - browser->clear(); - browser->down_dir(path); - browser->sort(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_up() { - browser->clear(); - browser->up_dir(); - browser->sort(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_save_patch() { - - if (strcmp(name->value(), "") == 0) { /// FIXME glue business - gdAlert("Please choose a file name."); - return; - } - - /* if name->value() contains ".gptc" */ - - char ext[6] = ".gptc"; - if (strstr(name->value(), ".gptc") != NULL) - ext[0] = '\0'; - - char fullpath[PATH_MAX]; - sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext); - if (gFileExists(fullpath)) - if (!gdConfirmWin("Warning", "File exists: overwrite?")) - return; - - if (glue_savePatch(fullpath, name->value(), false)) // false == not a project - do_callback(); - else - gdAlert("Unable to save the patch!"); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_save_project() { - - if (strcmp(name->value(), "") == 0) { /// FIXME glue business - gdAlert("Please choose a project name."); - return; - } - - /* check if name->value() contains ".gprj" */ - - char ext[6] = ".gprj"; - if (strstr(name->value(), ".gprj") != NULL) - ext[0] = '\0'; - - char fullpath[PATH_MAX]; -#if defined(_WIN32) - sprintf(fullpath, "%s\\%s%s", where->value(), name->value(), ext); -#else - sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext); -#endif - - if (gIsProject(fullpath) && !gdConfirmWin("Warning", "Project exists: overwrite?")) - return; - - if (glue_saveProject(fullpath, name->value())) - do_callback(); - else - gdAlert("Unable to save the project!"); -} - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST -void gdBrowser::__cb_loadPlugin() { - - if (browser->text(browser->value()) == NULL) - return; - - int res = G_PluginHost.addPlugin(browser->get_selected_item(), stackType, ch); - - /* store the folder path inside G_Conf, in order to reuse it the - * next time. */ - - G_Conf.setPath(G_Conf.pluginPath, where->value()); - - if (res) - do_callback(); - else - gdAlert("Unable to load the selected plugin!"); -} -#endif - - -/* -------------------------------------------------------------------------- */ - - -void gdBrowser::__cb_close() { - do_callback(); -} diff --git a/src/gd_browser.h b/src/gd_browser.h deleted file mode 100644 index 61f482d..0000000 --- a/src/gd_browser.h +++ /dev/null @@ -1,102 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_browser - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GD_BROWSER_H -#define GD_BROWSER_H - - -#include -#include -#include "ge_window.h" - - -/* TODO - this class must be subclassed into gdPluginBrowser, gdFileBrowser, - * and so on. It's a real mess right now. */ - -class gdBrowser : public gWindow { - -private: - static void cb_down(Fl_Widget *v, void *p); - static void cb_up (Fl_Widget *v, void *p); - static void cb_load_sample (Fl_Widget *v, void *p); - static void cb_save_sample (Fl_Widget *v, void *p); - static void cb_load_patch (Fl_Widget *v, void *p); - static void cb_save_patch (Fl_Widget *v, void *p); - static void cb_save_project(Fl_Widget *v, void *p); - static void cb_close (Fl_Widget *w, void *p); -#ifdef WITH_VST - static void cb_loadPlugin (Fl_Widget *v, void *p); -#endif - - inline void __cb_down(); - inline void __cb_up(); - inline void __cb_load_sample(); - inline void __cb_save_sample(); - inline void __cb_save_project(); - inline void __cb_load_patch(); - inline void __cb_save_patch(); - inline void __cb_close(); -#ifdef WITH_VST - inline void __cb_loadPlugin(); -#endif - - class gBrowser *browser; - class gClick *ok; - class gClick *cancel; - class gInput *where; - class gInput *name; - class gClick *updir; - class gProgress *status; - - class Channel *ch; - - /* browser type: see const.h */ - - /** FIXME internal enum: - * enum browserType { - * TYPE_A, - * TYPE_B, - * .... - * }; */ - int type; - - /* PluginHost stack type. Used only when loading plugins */ - - int stackType; - - char selectedFile[FILENAME_MAX]; - -public: - gdBrowser(const char *title, const char *initPath, class Channel *ch, int type, int stackType=0); - ~gdBrowser(); - - char* SelectedFile(); -}; - -#endif diff --git a/src/gd_config.cpp b/src/gd_config.cpp deleted file mode 100644 index ab34fc6..0000000 --- a/src/gd_config.cpp +++ /dev/null @@ -1,863 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_config - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "gd_config.h" -#include "gd_keyGrabber.h" -#include "gd_devInfo.h" -#include "gd_browser.h" -#include "ge_mixed.h" -#include "conf.h" -#include "midiMapConf.h" -#include "log.h" -#include "gui_utils.h" -#include "patch.h" -#include "kernelAudio.h" -#include "kernelMidi.h" - - -extern Patch G_Patch; -extern Conf G_Conf; -extern bool G_audio_status; -extern MidiMapConf G_MidiMap; - - -/* -------------------------------------------------------------------------- */ - - -gTabMisc::gTabMisc(int X, int Y, int W, int H) - : Fl_Group(X, Y, W, H, "Misc") -{ - begin(); - debugMsg = new gChoice(x()+92, y()+9, 253, 20, "Debug messages"); - end(); - - debugMsg->add("(disabled)"); - debugMsg->add("To standard output"); - debugMsg->add("To file"); - - labelsize(11); - - switch (G_Conf.logMode) { - case LOG_MODE_MUTE: - debugMsg->value(0); - break; - case LOG_MODE_STDOUT: - debugMsg->value(1); - break; - case LOG_MODE_FILE: - debugMsg->value(2); - break; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabMisc::save() -{ - switch(debugMsg->value()) { - case 0: - G_Conf.logMode = LOG_MODE_MUTE; - break; - case 1: - G_Conf.logMode = LOG_MODE_STDOUT; - break; - case 2: - G_Conf.logMode = LOG_MODE_FILE; - break; - } -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gTabAudio::gTabAudio(int X, int Y, int W, int H) - : Fl_Group(X, Y, W, H, "Sound System") -{ - begin(); - soundsys = new gChoice(x()+92, y()+9, 253, 20, "System"); - buffersize = new gChoice(x()+92, y()+37, 55, 20, "Buffer size"); - samplerate = new gChoice(x()+290, y()+37, 55, 20, "Sample rate"); - sounddevOut = new gChoice(x()+92, y()+65, 225, 20, "Output device"); - devOutInfo = new gClick (x()+325, y()+65, 20, 20, "?"); - channelsOut = new gChoice(x()+92, y()+93, 55, 20, "Output channels"); - limitOutput = new gCheck (x()+155, y()+97, 55, 20, "Limit output"); - sounddevIn = new gChoice(x()+92, y()+121, 225, 20, "Input device"); - devInInfo = new gClick (x()+325, y()+121, 20, 20, "?"); - channelsIn = new gChoice(x()+92, y()+149, 55, 20, "Input channels"); - delayComp = new gInput (x()+290, y()+149, 55, 20, "Rec delay comp."); - rsmpQuality = new gChoice(x()+92, y()+177, 253, 20, "Resampling"); - new gBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92, "Restart Giada for the changes to take effect."); - end(); - labelsize(11); - -#if defined(__linux__) - - if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA)) - soundsys->add("ALSA"); - if (kernelAudio::hasAPI(RtAudio::UNIX_JACK)) - soundsys->add("Jack"); - if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE)) - soundsys->add("PulseAudio"); - - switch (G_Conf.soundSystem) { - case SYS_API_ALSA: - soundsys->show("ALSA"); - break; - case SYS_API_JACK: - soundsys->show("Jack"); - buffersize->deactivate(); - samplerate->deactivate(); - break; - case SYS_API_PULSE: - soundsys->show("PulseAudio"); - break; - } - soundsysInitValue = soundsys->value(); - -#elif defined(_WIN32) - - if (kernelAudio::hasAPI(RtAudio::WINDOWS_DS)) - soundsys->add("DirectSound"); - if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO)) - soundsys->add("ASIO"); - soundsys->show(G_Conf.soundSystem == SYS_API_DS ? "DirectSound" : "ASIO"); - soundsysInitValue = soundsys->value(); - -#elif defined (__APPLE__) - - if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE)) - soundsys->add("CoreAudio"); - soundsys->show("CoreAudio"); - soundsysInitValue = soundsys->value(); - -#endif - - sounddevIn->callback(cb_fetchInChans, this); - sounddevOut->callback(cb_fetchOutChans, this); - - devOutInfo->callback(cb_showOutputInfo, this); - devInInfo->callback(cb_showInputInfo, this); - - fetchSoundDevs(); - - fetchOutChans(sounddevOut->value()); - fetchInChans(sounddevIn->value()); - - buffersize->add("8"); - buffersize->add("16"); - buffersize->add("32"); - buffersize->add("64"); - buffersize->add("128"); - buffersize->add("256"); - buffersize->add("512"); - buffersize->add("1024"); - buffersize->add("2048"); - buffersize->add("4096"); - - char buf[8]; - sprintf(buf, "%d", G_Conf.buffersize); - buffersize->show(buf); - - /* fill frequency dropdown menu */ - - int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value()); - for (int i=0; ivalue(), i); - sprintf(buf, "%d", freq); - samplerate->add(buf); - if (freq == G_Conf.samplerate) - samplerate->value(i); - } - - rsmpQuality->add("Sinc best quality (very slow)"); - rsmpQuality->add("Sinc medium quality (slow)"); - rsmpQuality->add("Sinc basic quality (medium)"); - rsmpQuality->add("Zero Order Hold (fast)"); - rsmpQuality->add("Linear (very fast)"); - rsmpQuality->value(G_Conf.rsmpQuality); - - buf[0] = '\0'; - sprintf(buf, "%d", G_Conf.delayComp); - delayComp->value(buf); - delayComp->type(FL_INT_INPUT); - delayComp->maximum_size(5); - - limitOutput->value(G_Conf.limitOutput); - soundsys->callback(cb_deactivate_sounddev, (void*)this); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::cb_deactivate_sounddev(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_deactivate_sounddev(); } -void gTabAudio::cb_fetchInChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchInChans(); } -void gTabAudio::cb_fetchOutChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchOutChans(); } -void gTabAudio::cb_showInputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showInputInfo(); } -void gTabAudio::cb_showOutputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showOutputInfo(); } - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::__cb_fetchInChans() -{ - fetchInChans(sounddevIn->value()); - channelsIn->value(0); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::__cb_fetchOutChans() -{ - fetchOutChans(sounddevOut->value()); - channelsOut->value(0); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::__cb_showInputInfo() -{ - unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); - new gdDevInfo(dev); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::__cb_showOutputInfo() -{ - unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); - new gdDevInfo(dev); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::__cb_deactivate_sounddev() -{ - /* if the user changes sound system (eg ALSA->JACK) device menu deactivates. - * If it returns to the original sound system, we re-fill the list by - * querying kernelAudio. */ - - if (soundsysInitValue == soundsys->value()) { - sounddevOut->clear(); - sounddevIn->clear(); - - fetchSoundDevs(); - - /* the '?' button is added by fetchSoundDevs */ - - fetchOutChans(sounddevOut->value()); - sounddevOut->activate(); - channelsOut->activate(); - - /* chan menus and '?' button are activated by fetchInChans(...) */ - - fetchInChans(sounddevIn->value()); - sounddevIn->activate(); - } - else { - sounddevOut->deactivate(); - sounddevOut->clear(); - sounddevOut->add("-- restart to fetch device(s) --"); - sounddevOut->value(0); - channelsOut->deactivate(); - devOutInfo->deactivate(); - - sounddevIn->deactivate(); - sounddevIn->clear(); - sounddevIn->add("-- restart to fetch device(s) --"); - sounddevIn->value(0); - channelsIn->deactivate(); - devInInfo->deactivate(); - } -} - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::fetchInChans(int menuItem) -{ - /* if menuItem==0 device in input is disabled. */ - - if (menuItem == 0) { - devInInfo ->deactivate(); - channelsIn->deactivate(); - delayComp ->deactivate(); - return; - } - - devInInfo ->activate(); - channelsIn->activate(); - delayComp ->activate(); - - channelsIn->clear(); - - unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); - unsigned chs = kernelAudio::getMaxInChans(dev); - - if (chs == 0) { - channelsIn->add("none"); - channelsIn->value(0); - return; - } - for (unsigned i=0; iadd(str); - } - channelsIn->value(G_Conf.channelsIn); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::fetchOutChans(int menuItem) -{ - channelsOut->clear(); - - unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); - unsigned chs = kernelAudio::getMaxOutChans(dev); - - if (chs == 0) { - channelsOut->add("none"); - channelsOut->value(0); - return; - } - for (unsigned i=0; iadd(str); - } - channelsOut->value(G_Conf.channelsOut); -} - - -/* -------------------------------------------------------------------------- */ - - -int gTabAudio::findMenuDevice(gChoice *m, int device) -{ - if (device == -1) - return 0; - - if (G_audio_status == false) - return 0; - - for (int i=0; isize(); i++) { - if (kernelAudio::getDeviceName(device) == NULL) - continue; - if (m->text(i) == NULL) - continue; - if (strcmp(m->text(i), kernelAudio::getDeviceName(device))==0) - return i; - } - - return 0; -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::fetchSoundDevs() -{ - if (kernelAudio::numDevs == 0) { - sounddevOut->add("-- no devices found --"); - sounddevOut->value(0); - sounddevIn->add("-- no devices found --"); - sounddevIn->value(0); - devInInfo ->deactivate(); - devOutInfo->deactivate(); - } - else { - - devInInfo ->activate(); - devOutInfo->activate(); - - /* input device may be disabled: now device number -1 is the disabled - * one. KernelAudio knows how to handle it. */ - - sounddevIn->add("(disabled)"); - - for (unsigned i=0; i 0) - sounddevOut->add(tmp.c_str()); - - if (kernelAudio::getMaxInChans(i) > 0) - sounddevIn->add(tmp.c_str()); - } - - /* we show the device saved in the configuration file. */ - - if (sounddevOut->size() == 0) { - sounddevOut->add("-- no devices found --"); - sounddevOut->value(0); - devOutInfo->deactivate(); - } - else { - int outMenuValue = findMenuDevice(sounddevOut, G_Conf.soundDeviceOut); - sounddevOut->value(outMenuValue); - } - - if (sounddevIn->size() == 0) { - sounddevIn->add("-- no devices found --"); - sounddevIn->value(0); - devInInfo->deactivate(); - } - else { - int inMenuValue = findMenuDevice(sounddevIn, G_Conf.soundDeviceIn); - sounddevIn->value(inMenuValue); - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabAudio::save() -{ - /** FIXME - wrong, if API is missing! Right way in gTabMidi::save */ - -#ifdef __linux__ - if (soundsys->value() == 0) G_Conf.soundSystem = SYS_API_ALSA; - else if (soundsys->value() == 1) G_Conf.soundSystem = SYS_API_JACK; - else if (soundsys->value() == 2) G_Conf.soundSystem = SYS_API_PULSE; -#else -#ifdef _WIN32 - if (soundsys->value() == 0) G_Conf.soundSystem = SYS_API_DS; - else if (soundsys->value() == 1) G_Conf.soundSystem = SYS_API_ASIO; -#endif -#endif - - /* use the device name to search into the drop down menu's */ - - G_Conf.soundDeviceOut = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); - G_Conf.soundDeviceIn = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); - G_Conf.channelsOut = channelsOut->value(); - G_Conf.channelsIn = channelsIn->value(); - G_Conf.limitOutput = limitOutput->value(); - G_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 (G_Conf.soundDeviceOut == -1) - G_Conf.soundDeviceOut = 0; - - int bufsize = atoi(buffersize->text()); - if (bufsize % 2 != 0) bufsize++; - if (bufsize < 8) bufsize = 8; - if (bufsize > 8192) bufsize = 8192; - G_Conf.buffersize = bufsize; - - const Fl_Menu_Item *i = NULL; - i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked - if (i) - G_Conf.samplerate = atoi(i->label()); - - int _delayComp = atoi(delayComp->value()); - if (_delayComp < 0) _delayComp = 0; - G_Conf.delayComp = _delayComp; -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gTabMidi::gTabMidi(int X, int Y, int W, int H) - : Fl_Group(X, Y, W, H, "MIDI") -{ - begin(); - system = new gChoice(x()+92, y()+9, 253, 20, "System"); - portOut = new gChoice(x()+92, system->y()+system->h()+8, 253, 20, "Output port"); - portIn = new gChoice(x()+92, portOut->y()+portOut->h()+8, 253, 20, "Input port"); - noNoteOff = new gCheck (x()+92, portIn->y()+portIn->h()+8, 253, 20, "Device does not send NoteOff"); - midiMap = new gChoice(x()+92, noNoteOff->y()+noNoteOff->h(), 253, 20, "Output Midi Map"); - sync = new gChoice(x()+92, midiMap->y()+midiMap->h()+8, 253, 20, "Sync"); - new gBox(x(), sync->y()+sync->h()+8, w(), h()-125, "Restart Giada for the changes to take effect."); - end(); - - labelsize(11); - - system->callback(cb_changeSystem, (void*)this); - - fetchSystems(); - fetchOutPorts(); - fetchInPorts(); - fetchMidiMaps(); - - noNoteOff->value(G_Conf.noNoteOff); - - sync->add("(disabled)"); - sync->add("MIDI Clock (master)"); - sync->add("MTC (master)"); - if (G_Conf.midiSync == MIDI_SYNC_NONE) - sync->value(0); - else if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) - sync->value(1); - else if (G_Conf.midiSync == MIDI_SYNC_MTC_M) - sync->value(2); - - systemInitValue = system->value(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabMidi::fetchOutPorts() { - - if (kernelMidi::numOutPorts == 0) { - portOut->add("-- no ports found --"); - portOut->value(0); - portOut->deactivate(); - } - else { - - portOut->add("(disabled)"); - - for (unsigned i=0; iadd(t); - } - - portOut->value(G_Conf.midiPortOut+1); // +1 because midiPortOut=-1 is '(disabled)' - } -} - -/* -------------------------------------------------------------------------- */ - - -void gTabMidi::fetchInPorts() -{ - if (kernelMidi::numInPorts == 0) { - portIn->add("-- no ports found --"); - portIn->value(0); - portIn->deactivate(); - } - else { - - portIn->add("(disabled)"); - - for (unsigned i=0; iadd(t); - } - - portIn->value(G_Conf.midiPortIn+1); // +1 because midiPortIn=-1 is '(disabled)' - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabMidi::fetchMidiMaps() -{ - if (G_MidiMap.maps.size == 0) { - midiMap->add("(no MIDI maps available)"); - midiMap->value(0); - midiMap->deactivate(); - return; - } - for (unsigned i=0; iadd(imap); - if (strcmp(G_Conf.midiMapPath, imap) == 0) - midiMap->value(i); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabMidi::save() -{ - if (!strcmp("ALSA", system->text(system->value()))) - G_Conf.midiSystem = RtMidi::LINUX_ALSA; - else if (!strcmp("Jack", system->text(system->value()))) - G_Conf.midiSystem = RtMidi::UNIX_JACK; - else if (!strcmp("Multimedia MIDI", system->text(system->value()))) - G_Conf.midiSystem = RtMidi::WINDOWS_MM; - else if (!strcmp("OSX Core MIDI", system->text(system->value()))) - G_Conf.midiSystem = RtMidi::MACOSX_CORE; - - G_Conf.midiPortOut = portOut->value()-1; // -1 because midiPortOut=-1 is '(disabled)' - G_Conf.midiPortIn = portIn->value()-1; // -1 because midiPortIn=-1 is '(disabled)' - - G_Conf.noNoteOff = noNoteOff->value(); - - G_Conf.setPath(G_Conf.midiMapPath, midiMap->text(midiMap->value())); - - if (sync->value() == 0) - G_Conf.midiSync = MIDI_SYNC_NONE; - else if (sync->value() == 1) - G_Conf.midiSync = MIDI_SYNC_CLOCK_M; - else if (sync->value() == 2) - G_Conf.midiSync = MIDI_SYNC_MTC_M; -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabMidi::fetchSystems() -{ -#if defined(__linux__) - - if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA)) - system->add("ALSA"); - if (kernelMidi::hasAPI(RtMidi::UNIX_JACK)) - system->add("Jack"); - -#elif defined(_WIN32) - - if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM)) - system->add("Multimedia MIDI"); - -#elif defined (__APPLE__) - - system->add("OSX Core MIDI"); - -#endif - - switch (G_Conf.midiSystem) { - case RtMidi::LINUX_ALSA: system->show("ALSA"); break; - case RtMidi::UNIX_JACK: system->show("Jack"); break; - case RtMidi::WINDOWS_MM: system->show("Multimedia MIDI"); break; - case RtMidi::MACOSX_CORE: system->show("OSX Core MIDI"); break; - default: system->value(0); break; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((gTabMidi*)p)->__cb_changeSystem(); } - - -/* -------------------------------------------------------------------------- */ - - -void gTabMidi::__cb_changeSystem() -{ - /* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates. - * If it returns to the original system, we re-fill the list by - * querying kernelMidi. */ - - if (systemInitValue == system->value()) { - portOut->clear(); - fetchOutPorts(); - portOut->activate(); - } - else { - portOut->deactivate(); - portOut->clear(); - portOut->add("-- restart to fetch device(s) --"); - portOut->value(0); - } - -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gTabBehaviors::gTabBehaviors(int X, int Y, int W, int H) - : Fl_Group(X, Y, W, H, "Behaviors") -{ - begin(); - Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex - new gBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT); - recsStopOnChanHalt_1 = new gRadio(x()+25, y()+35, 280, 20, "stop it immediately"); - recsStopOnChanHalt_0 = new gRadio(x()+25, y()+55, 280, 20, "play it until finished"); - radioGrp_1->end(); - - Fl_Group *radioGrp_2 = new Fl_Group(x(), y()+70, w(), 70); // radio group for the mutex - new gBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT); - chansStopOnSeqHalt_1 = new gRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels"); - chansStopOnSeqHalt_0 = new gRadio(x()+25, y()+125, 280, 20, "play all dynamic channels until finished"); - radioGrp_2->end(); - - treatRecsAsLoops = new gCheck(x(), y()+155, 280, 20, "Treat one shot channels with actions as loops"); - - end(); - labelsize(11); - - G_Conf.recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1); - G_Conf.chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1); - G_Conf.treatRecsAsLoops == 1 ? treatRecsAsLoops->value(1) : treatRecsAsLoops->value(0); - - 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 gTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((gTabBehaviors*)p)->__cb_radio_mutex(w); } - - -/* -------------------------------------------------------------------------- */ - - -void gTabBehaviors::__cb_radio_mutex(Fl_Widget *w) -{ - ((Fl_Button *)w)->type(FL_RADIO_BUTTON); -} - - -/* -------------------------------------------------------------------------- */ - - -void gTabBehaviors::save() -{ - G_Conf.recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0; - G_Conf.chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0; - G_Conf.treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0; -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdConfig::gdConfig(int w, int h) : gWindow(w, h, "Configuration") -{ - set_modal(); - - if (G_Conf.configX) - resize(G_Conf.configX, G_Conf.configY, this->w(), this->h()); - - Fl_Tabs *tabs = new Fl_Tabs(8, 8, w-16, h-44); - tabAudio = new gTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); - tabMidi = new gTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); - tabBehaviors = new gTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); - tabMisc = new gTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); - tabs->end(); - - save = new gClick (w-88, h-28, 80, 20, "Save"); - cancel = new gClick (w-176, h-28, 80, 20, "Cancel"); - - end(); - - tabs->box(FL_FLAT_BOX); // TODO - G_BOX crashes FLTK 1.3.3 - - tabs->labelcolor(COLOR_TEXT_0); - - save->callback(cb_save_config, (void*)this); - cancel->callback(cb_cancel, (void*)this); - - gu_setFavicon(this); - setId(WID_CONFIG); - show(); -} - - -/* -------------------------------------------------------------------------- */ - - -gdConfig::~gdConfig() -{ - G_Conf.configX = x(); - G_Conf.configY = y(); -} - - -/* -------------------------------------------------------------------------- */ - - -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() -{ - tabAudio->save(); - tabBehaviors->save(); - tabMidi->save(); - tabMisc->save(); - do_callback(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdConfig::__cb_cancel() -{ - do_callback(); -} diff --git a/src/gd_config.h b/src/gd_config.h deleted file mode 100644 index fa28b80..0000000 --- a/src/gd_config.h +++ /dev/null @@ -1,171 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_config - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GD_CONFIG_H -#define GD_CONFIG_H - -#include -#include -#include -#include "ge_window.h" - - -class gdConfig : public gWindow -{ -private: - static void cb_save_config (Fl_Widget *w, void *p); - static void cb_cancel (Fl_Widget *w, void *p); - inline void __cb_save_config(); - inline void __cb_cancel(); - -public: - gdConfig(int w, int h); - ~gdConfig(); - - class gTabAudio *tabAudio; - class gTabBehaviors *tabBehaviors; - class gTabMidi *tabMidi; - class gTabMisc *tabMisc; - class gClick *save; - class gClick *cancel; -}; - - -/* ------------------------------------------------------------------ */ - - -class gTabMidi : public Fl_Group -{ -private: - - void fetchSystems(); - void fetchOutPorts(); - void fetchInPorts(); - void fetchMidiMaps(); - - static void cb_changeSystem (Fl_Widget *w, void *p); - inline void __cb_changeSystem(); - - int systemInitValue; - -public: - - class gChoice *system; - class gChoice *portOut; - class gChoice *portIn; - class gCheck *noNoteOff; - class gChoice *midiMap; - class gChoice *sync; - - gTabMidi(int x, int y, int w, int h); - - void save(); -}; - - -/* ------------------------------------------------------------------ */ - - -class gTabAudio : public Fl_Group -{ -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); - inline void __cb_deactivate_sounddev(); - inline void __cb_fetchInChans(); - inline void __cb_fetchOutChans(); - inline void __cb_showInputInfo(); - inline void __cb_showOutputInfo(); - - void fetchSoundDevs(); - void fetchInChans(int menuItem); - void fetchOutChans(int menuItem); - int findMenuDevice(class gChoice *m, int device); - - int soundsysInitValue; - -public: - class gChoice *soundsys; - class gChoice *samplerate; - class gChoice *rsmpQuality; - class gChoice *sounddevIn; - class gClick *devInInfo; - class gChoice *channelsIn; - class gChoice *sounddevOut; - class gClick *devOutInfo; - class gChoice *channelsOut; - class gCheck *limitOutput; - class gChoice *buffersize; - class gInput *delayComp; - - gTabAudio(int x, int y, int w, int h); - - void save(); -}; - - -/* ------------------------------------------------------------------ */ - - -class gTabBehaviors : public Fl_Group -{ -private: - static void cb_radio_mutex (Fl_Widget *w, void *p); - inline void __cb_radio_mutex(Fl_Widget *w); - -public: - class gRadio *recsStopOnChanHalt_1; - class gRadio *recsStopOnChanHalt_0; - class gRadio *chansStopOnSeqHalt_1; - class gRadio *chansStopOnSeqHalt_0; - class gCheck *treatRecsAsLoops; - - gTabBehaviors(int x, int y, int w, int h); - - void save(); -}; - - -/* ------------------------------------------------------------------ */ - - -class gTabMisc : public Fl_Group -{ -public: - class gChoice *debugMsg; - - gTabMisc(int x, int y, int w, int h); - - void save(); -}; - - -#endif diff --git a/src/gd_devInfo.cpp b/src/gd_devInfo.cpp deleted file mode 100644 index b7c5e40..0000000 --- a/src/gd_devInfo.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_devInfo - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "gd_devInfo.h" -#include "ge_mixed.h" -#include "kernelAudio.h" -#include "gui_utils.h" - - -gdDevInfo::gdDevInfo(unsigned dev) -: Fl_Window(340, 300, "Device information") { - set_modal(); - - text = new gBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); - close = new gClick(252, h()-28, 80, 20, "Close"); - end(); - - std::string bufTxt; - char bufNum[128]; - int lines = 0; - - bufTxt = "Device name: "; - bufTxt += +kernelAudio::getDeviceName(dev); - bufTxt += "\n"; - lines++; - - bufTxt += "Total output(s): "; - sprintf(bufNum, "%d\n", kernelAudio::getMaxOutChans(dev)); - bufTxt += bufNum; - lines++; - - bufTxt += "Total intput(s): "; - sprintf(bufNum, "%d\n", kernelAudio::getMaxInChans(dev)); - bufTxt += bufNum; - lines++; - - bufTxt += "Duplex channel(s): "; - sprintf(bufNum, "%d\n", kernelAudio::getDuplexChans(dev)); - bufTxt += bufNum; - lines++; - - bufTxt += "Default output: "; - sprintf(bufNum, "%s\n", kernelAudio::isDefaultOut(dev) ? "yes" : "no"); - bufTxt += bufNum; - lines++; - - bufTxt += "Default input: "; - sprintf(bufNum, "%s\n", kernelAudio::isDefaultIn(dev) ? "yes" : "no"); - bufTxt += bufNum; - lines++; - - int totalFreq = kernelAudio::getTotalFreqs(dev); - bufTxt += "Supported frequencies: "; - sprintf(bufNum, "%d", totalFreq); - bufTxt += bufNum; - lines++; - - for (int i=0; icopy_label(bufTxt.c_str()); - - /* resize the window to fit the content. fl_height() returns the height - * of a line. fl_height() * total lines + margins + button size */ - - resize(x(), y(), w(), lines*fl_height() + 8 + 8 + 8 + 20); - close->position(close->x(), lines*fl_height() + 8 + 8); - - close->callback(__cb_window_closer, (void*)this); - gu_setFavicon(this); - show(); -} - - -gdDevInfo::~gdDevInfo() {} diff --git a/src/gd_devInfo.h b/src/gd_devInfo.h deleted file mode 100644 index c5a06ca..0000000 --- a/src/gd_devInfo.h +++ /dev/null @@ -1,47 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_devInfo - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GD_DEV_INFO_H -#define GD_DEV_INFO_H - -#include -#include - - -class gdDevInfo : public Fl_Window { -private: - class gBox *text; - class gClick *close; - -public: - gdDevInfo(unsigned dev); - ~gdDevInfo(); -}; - -#endif diff --git a/src/gd_editor.cpp b/src/gd_editor.cpp deleted file mode 100644 index 3e96a1b..0000000 --- a/src/gd_editor.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_editor - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "gd_editor.h" -#include "gd_mainWindow.h" -#include "ge_waveform.h" -#include "gd_warnings.h" -#include "gg_waveTools.h" -#include "ge_mixed.h" -#include "gg_keyboard.h" -#include "ge_channel.h" -#include "waveFx.h" -#include "conf.h" -#include "graphics.h" -#include "gui_utils.h" -#include "glue.h" -#include "mixerHandler.h" -#include "channel.h" -#include "sampleChannel.h" -#include "mixer.h" -#include "wave.h" - - -extern Mixer G_Mixer; -extern gdMainWindow *mainWin; -extern Conf G_Conf; - - -gdEditor::gdEditor(SampleChannel *ch) - : gWindow(640, 480), - ch(ch) -{ - set_non_modal(); - - if (G_Conf.sampleEditorX) - resize(G_Conf.sampleEditorX, G_Conf.sampleEditorY, G_Conf.sampleEditorW, G_Conf.sampleEditorH); - - /* top bar: grid and zoom tools */ - - Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20); - bar->begin(); - grid = new gChoice(bar->x(), bar->y(), 50, 20); - snap = new gCheck(grid->x()+grid->w()+4, bar->y()+4, 12, 12); - zoomOut = new gClick(bar->x()+bar->w()-20, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm); - zoomIn = new gClick(zoomOut->x()-24, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm); - bar->end(); - bar->resizable(new gBox(grid->x()+grid->w()+4, bar->y(), 80, bar->h())); - - /* waveform */ - - waveTools = new gWaveTools(8, 36, w()-16, h()-120, ch); - waveTools->end(); - - /* other tools */ - - Fl_Group *tools = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 130); - tools->begin(); - volume = new gDial (tools->x()+42, tools->y(), 20, 20, "Volume"); - volumeNum = new gInput(volume->x()+volume->w()+4, tools->y(), 46, 20, "dB"); - - boost = new gDial (volumeNum->x()+volumeNum->w()+80, tools->y(), 20, 20, "Boost"); - boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 46, 20, "dB"); - - normalize = new gClick(boostNum->x()+boostNum->w()+54, tools->y(), 70, 20, "Normalize"); - pan = new gDial (normalize->x()+normalize->w()+40, tools->y(), 20, 20, "Pan"); - panNum = new gInput(pan->x()+pan->w()+4, tools->y(), 45, 20, "%"); - - pitch = new gDial (tools->x()+42, volume->y()+volume->h()+4, 20, 20, "Pitch"); - pitchNum = new gInput(pitch->x()+pitch->w()+4, volume->y()+volume->h()+4, 46, 20); - pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 46, 20, "To bar"); - pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 46, 20, "To song"); - pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 21, 20, "÷"); - pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 21, 20, "×"); - pitchReset = new gClick(pitchDouble->x()+pitchDouble->w()+4, volume->y()+volume->h()+4, 46, 20, "Reset"); - reload = new gClick(pitchReset->x()+pitchReset->w()+4, volume->y()+volume->h()+4, 70, 20, "Reload"); - - chanStart = new gInput(tools->x()+52, pitch->y()+pitch->h()+4, 60, 20, "Start"); - chanEnd = new gInput(chanStart->x()+chanStart->w()+40, pitch->y()+pitch->h()+4, 60, 20, "End"); - resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 46, 20, "Reset"); - - tools->end(); - tools->resizable(new gBox(panNum->x()+panNum->w()+4, tools->y(), 80, tools->h())); - - /* grid tool setup */ - - grid->add("(off)"); - grid->add("2"); - grid->add("3"); - grid->add("4"); - grid->add("6"); - grid->add("8"); - grid->add("16"); - grid->add("32"); - grid->add("64"); - grid->value(G_Conf.sampleEditorGridVal); - grid->callback(cb_changeGrid, (void*)this); - - snap->value(G_Conf.sampleEditorGridOn); - snap->callback(cb_enableSnap, (void*)this); - - /* TODO - redraw grid if != (off) */ - - char buf[16]; - sprintf(buf, "%d", ch->begin / 2); // divided by 2 because stereo - chanStart->value(buf); - chanStart->type(FL_INT_INPUT); - chanStart->callback(cb_setChanPos, this); - - /* inputs callback: fire when they lose focus or Enter is pressed. */ - - chanStart->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); - chanEnd ->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); - - sprintf(buf, "%d", ch->end / 2); // divided by 2 because stereo - chanEnd->value(buf); - chanEnd->type(FL_INT_INPUT); - chanEnd->callback(cb_setChanPos, this); - - resetStartEnd->callback(cb_resetStartEnd, this); - - volume->callback(cb_setVolume, (void*)this); - volume->value(ch->guiChannel->vol->value()); - - float dB = 20*log10(ch->volume); // dB = 20*log_10(linear value) - if (dB > -INFINITY) sprintf(buf, "%.2f", dB); - else sprintf(buf, "-inf"); - volumeNum->value(buf); - volumeNum->align(FL_ALIGN_RIGHT); - volumeNum->callback(cb_setVolumeNum, (void*)this); - - boost->range(1.0f, 10.0f); - boost->callback(cb_setBoost, (void*)this); - if (ch->boost > 10.f) - boost->value(10.0f); - else - boost->value(ch->boost); - boost->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE); - - float boost = 20*log10(ch->boost); // dB = 20*log_10(linear value) - sprintf(buf, "%.2f", boost); - boostNum->value(buf); - boostNum->align(FL_ALIGN_RIGHT); - boostNum->callback(cb_setBoostNum, (void*)this); - - normalize->callback(cb_normalize, (void*)this); - - pan->range(0.0f, 2.0f); - pan->callback(cb_panning, (void*)this); - - pitch->range(0.01f, 4.0f); - pitch->value(ch->pitch); - pitch->callback(cb_setPitch, (void*)this); - pitch->when(FL_WHEN_RELEASE); - - sprintf(buf, "%.4f", ch->pitch); // 4 digits - pitchNum->value(buf); - pitchNum->align(FL_ALIGN_RIGHT); - pitchNum->callback(cb_setPitchNum, (void*)this); - pitchNum->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); - - reload->callback(cb_reload, (void*)this); - - zoomOut->callback(cb_zoomOut, (void*)this); - zoomIn->callback(cb_zoomIn, (void*)this); - - /* logical samples (aka takes) cannot be reloaded. So far. */ - - if (ch->wave->isLogical) - reload->deactivate(); - - if (ch->panRight < 1.0f) { - char buf[8]; - sprintf(buf, "%d L", abs((ch->panRight * 100.0f) - 100)); - pan->value(ch->panRight); - panNum->value(buf); - } - else if (ch->panRight == 1.0f && ch->panLeft == 1.0f) { - pan->value(1.0f); - panNum->value("C"); - } - else { - char buf[8]; - sprintf(buf, "%d R", abs((ch->panLeft * 100.0f) - 100)); - pan->value(2.0f - ch->panLeft); - panNum->value(buf); - } - - panNum->align(FL_ALIGN_RIGHT); - panNum->readonly(1); - panNum->cursor_color(FL_WHITE); - - gu_setFavicon(this); - size_range(640, 480); - resizable(waveTools); - - label(ch->wave->name.c_str()); - - show(); -} - - -/* ------------------------------------------------------------------ */ - - -gdEditor::~gdEditor() -{ - G_Conf.sampleEditorX = x(); - G_Conf.sampleEditorY = y(); - G_Conf.sampleEditorW = w(); - G_Conf.sampleEditorH = h(); - G_Conf.sampleEditorGridVal = grid->value(); - G_Conf.sampleEditorGridOn = snap->value(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::cb_setChanPos (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setChanPos(); } -void gdEditor::cb_resetStartEnd (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetStartEnd(); } -void gdEditor::cb_setVolume (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolume(); } -void gdEditor::cb_setVolumeNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolumeNum(); } -void gdEditor::cb_setBoost (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoost(); } -void gdEditor::cb_setBoostNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoostNum(); } -void gdEditor::cb_normalize (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_normalize(); } -void gdEditor::cb_panning (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_panning(); } -void gdEditor::cb_reload (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_reload(); } -void gdEditor::cb_setPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitch(); } -void gdEditor::cb_setPitchToBar (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToBar(); } -void gdEditor::cb_setPitchToSong (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToSong(); } -void gdEditor::cb_setPitchHalf (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchHalf(); } -void gdEditor::cb_setPitchDouble (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchDouble(); } -void gdEditor::cb_resetPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetPitch(); } -void gdEditor::cb_setPitchNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchNum(); } -void gdEditor::cb_zoomIn (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomIn(); } -void gdEditor::cb_zoomOut (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomOut(); } -void gdEditor::cb_changeGrid (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_changeGrid(); } -void gdEditor::cb_enableSnap (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_enableSnap(); } - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_enableSnap() -{ - waveTools->waveform->setSnap(!waveTools->waveform->getSnap()); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setPitchToBar() -{ - glue_setPitch(this, ch, ch->end/(float)G_Mixer.framesPerBar, true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setPitchToSong() -{ - glue_setPitch(this, ch, ch->end/(float)G_Mixer.totalFrames, true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_resetPitch() -{ - glue_setPitch(this, ch, 1.0f, true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setChanPos() -{ - glue_setBeginEndChannel( - this, - ch, - atoi(chanStart->value())*2, // glue halves printed values - atoi(chanEnd->value())*2, - true - ); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_resetStartEnd() -{ - glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setVolume() -{ - glue_setVolEditor(this, ch, volume->value(), false); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setVolumeNum() -{ - glue_setVolEditor(this, ch, atof(volumeNum->value()), true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setBoost() -{ - if (Fl::event() == FL_DRAG) - glue_setBoost(this, ch, boost->value(), false); - else if (Fl::event() == FL_RELEASE) { - glue_setBoost(this, ch, boost->value(), false); - waveTools->updateWaveform(); - } -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setBoostNum() -{ - glue_setBoost(this, ch, atof(boostNum->value()), true); - waveTools->updateWaveform(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_normalize() -{ - float val = wfx_normalizeSoft(ch->wave); - glue_setBoost(this, ch, val, false); // we pretend that a fake user turns the dial (numeric=false) - if (val < 0.0f) - boost->value(0.0f); - else - if (val > 20.0f) // a dial > than it's max value goes crazy - boost->value(10.0f); - else - boost->value(val); - waveTools->updateWaveform(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_panning() -{ - glue_setPanning(this, ch, pan->value()); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_reload() -{ - if (!gdConfirmWin("Warning", "Reload sample: are you sure?")) - return; - - /* no need for glue_loadChan, there's no gui to refresh */ - - ch->load(ch->wave->pathfile.c_str()); - - glue_setBoost(this, ch, DEFAULT_BOOST, true); - glue_setPitch(this, ch, gDEFAULT_PITCH, true); - glue_setPanning(this, ch, 1.0f); - pan->value(1.0f); // glue_setPanning doesn't do it - pan->redraw(); // glue_setPanning doesn't do it - - waveTools->waveform->stretchToWindow(); - waveTools->updateWaveform(); - - glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true); - - redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setPitch() -{ - glue_setPitch(this, ch, pitch->value(), false); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setPitchNum() -{ - glue_setPitch(this, ch, atof(pitchNum->value()), true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setPitchHalf() -{ - glue_setPitch(this, ch, pitch->value()/2, true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_setPitchDouble() -{ - glue_setPitch(this, ch, pitch->value()*2, true); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_zoomIn() -{ - waveTools->waveform->setZoom(-1); - waveTools->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_zoomOut() -{ - waveTools->waveform->setZoom(0); - waveTools->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gdEditor::__cb_changeGrid() -{ - waveTools->waveform->setGridLevel(atoi(grid->text())); -} diff --git a/src/gd_editor.h b/src/gd_editor.h deleted file mode 100644 index 9ae3266..0000000 --- a/src/gd_editor.h +++ /dev/null @@ -1,116 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_editor - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GD_EDITOR_H -#define GD_EDITOR_H - -#include -#include -#include -#include "ge_window.h" - - -class gdEditor : public gWindow { - -private: - - static void cb_setChanPos (Fl_Widget *w, void *p); - static void cb_resetStartEnd (Fl_Widget *w, void *p); - static void cb_setVolume (Fl_Widget *w, void *p); - static void cb_setVolumeNum (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); - static void cb_panning (Fl_Widget *w, void *p); - static void cb_reload (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); - 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); - inline void __cb_setChanPos(); - inline void __cb_resetStartEnd(); - inline void __cb_setVolume(); - inline void __cb_setVolumeNum(); - inline void __cb_setBoost(); - inline void __cb_setBoostNum(); - inline void __cb_normalize(); - inline void __cb_panning(); - inline void __cb_reload(); - inline void __cb_setPitch(); - inline void __cb_setPitchToBar(); - inline void __cb_setPitchToSong(); - inline void __cb_setPitchHalf(); - inline void __cb_setPitchDouble(); - inline void __cb_resetPitch(); - inline void __cb_setPitchNum(); - inline void __cb_zoomIn(); - inline void __cb_zoomOut(); - inline void __cb_changeGrid(); - inline void __cb_enableSnap(); - -public: - - gdEditor(class SampleChannel *ch); - ~gdEditor(); - - class gClick *zoomIn; - class gClick *zoomOut; - class gWaveTools *waveTools; - class gInput *chanStart; - class gInput *chanEnd; - class gClick *resetStartEnd; - class gDial *volume; - class gInput *volumeNum; - class gDial *boost; - class gInput *boostNum; - class gClick *normalize; - class gDial *pan; - class gInput *panNum; - class gClick *reload; - class gDial *pitch; - class gInput *pitchNum; - class gClick *pitchToBar; - class gClick *pitchToSong; - class gClick *pitchHalf; - class gClick *pitchDouble; - class gClick *pitchReset; - class gClick *close; - class gChoice *grid; - class gCheck *snap; - - class SampleChannel *ch; -}; - -#endif diff --git a/src/gd_keyGrabber.cpp b/src/gd_keyGrabber.cpp deleted file mode 100644 index 469adf0..0000000 --- a/src/gd_keyGrabber.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_keyGrabber - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "gd_keyGrabber.h" -#include "gg_keyboard.h" -#include "ge_mixed.h" -#include "gd_config.h" -#include "ge_channel.h" -#include "gd_mainWindow.h" -#include "gui_utils.h" -#include "conf.h" -#include "channel.h" -#include "sampleChannel.h" -#include "midiChannel.h" -#include "log.h" - - -extern Conf G_Conf; -extern gdMainWindow *mainWin; - - -gdKeyGrabber::gdKeyGrabber(Channel *ch) - : gWindow(300, 126, "Key configuration"), ch(ch) -{ - set_modal(); - text = new gBox(8, 8, 284, 80, ""); - clear = new gClick(w()-88, text->y()+text->h()+8, 80, 20, "Clear"); - cancel = new gClick(clear->x()-88, clear->y(), 80, 20, "Close"); - end(); - - clear->callback(cb_clear, (void*)this); - cancel->callback(cb_cancel, (void*)this); - - updateText(ch->key); - - gu_setFavicon(this); - show(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdKeyGrabber::cb_clear (Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_clear(); } -void gdKeyGrabber::cb_cancel(Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_cancel(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdKeyGrabber::__cb_cancel() -{ - do_callback(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdKeyGrabber::__cb_clear() -{ - updateText(0); - setButtonLabel(0); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdKeyGrabber::setButtonLabel(int key) -{ - char tmp[2]; sprintf(tmp, "%c", key); - ch->guiChannel->button->copy_label(tmp); - ch->key = key; -} - -/* -------------------------------------------------------------------------- */ - - -void gdKeyGrabber::updateText(int key) -{ - char tmp2[64]; - if (key != 0) - sprintf(tmp2, "Press a key.\n\nCurrent binding: %c", key); - else - sprintf(tmp2, "Press a key.\n\nCurrent binding: [none]"); - text->copy_label(tmp2); -} - - -/* -------------------------------------------------------------------------- */ - - -int gdKeyGrabber::handle(int e) -{ - int ret = Fl_Group::handle(e); - switch(e) { - case FL_KEYUP: { - int x = Fl::event_key(); - if (strlen(Fl::event_text()) != 0 - && x != FL_BackSpace - && x != FL_Enter - && x != FL_Delete - && x != FL_Tab - && x != FL_End - && x != ' ') - { - gLog("set key '%c' (%d) for channel %d\n", x, x, ch->index); - setButtonLabel(x); - updateText(x); - break; - } - else - gLog("invalid key\n"); - } - } - return(ret); -} diff --git a/src/gd_keyGrabber.h b/src/gd_keyGrabber.h deleted file mode 100644 index 4e4b2a3..0000000 --- a/src/gd_keyGrabber.h +++ /dev/null @@ -1,63 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_keyGrabber - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GD_KEYGRABBER_H -#define GD_KEYGRABBER_H - - -#include -#include "ge_window.h" - - -class gdKeyGrabber : public gWindow -{ -private: - - class Channel *ch; - - class gBox *text; - class gClick *clear; - class gClick *cancel; - - static void cb_clear (Fl_Widget *w, void *p); - static void cb_cancel(Fl_Widget *w, void *p); - inline void __cb_clear (); - inline void __cb_cancel(); - - void setButtonLabel(int key); - - void updateText(int key); - -public: - - gdKeyGrabber(class Channel *ch); - int handle(int e); -}; - -#endif diff --git a/src/gd_mainWindow.cpp b/src/gd_mainWindow.cpp deleted file mode 100644 index b8fc2df..0000000 --- a/src/gd_mainWindow.cpp +++ /dev/null @@ -1,672 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_mainWindow - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifdef __linux__ - #include // for mkdir -#endif - -#include "gd_mainWindow.h" -#include "gd_warnings.h" -#include "gd_bpmInput.h" -#include "gd_beatsInput.h" -#include "gd_midiInput.h" -#include "gg_keyboard.h" -#include "gd_about.h" -#include "gd_config.h" -#include "gd_browser.h" -#include "graphics.h" -#include "glue.h" -#include "mixer.h" -#include "recorder.h" -#include "mixerHandler.h" -#include "pluginHost.h" -#include "channel.h" -#include "sampleChannel.h" -#include "init.h" -#include "patch.h" -#include "conf.h" - -#ifdef WITH_VST -#include "gd_pluginList.h" -#endif - - -extern Mixer G_Mixer; -extern Patch G_Patch; -extern Conf G_Conf; -extern gdMainWindow *mainWin; -extern bool G_quit; -extern bool G_audio_status; - -#if defined(WITH_VST) -extern PluginHost G_PluginHost; -#endif - - -gdMainWindow::gdMainWindow(int W, int H, const char *title, int argc, char **argv) - : gWindow(W, H, title) -{ - Fl::visible_focus(0); - Fl::background(25, 25, 25); - Fl::set_boxtype(G_BOX, gDrawBox, 1, 1, 2, 2); // custom box G_BOX - - size_range(GUI_WIDTH, GUI_HEIGHT); - - menu = new gMenu(8, -1); - inOut = new gInOut(408, 8); - controller = new gController(8, 39); - timing = new gTiming(632, 39); - beatMeter = new gBeatMeter(100, 83, 609, 20); - keyboard = new gKeyboard(8, 122, w()-16, 380); - - /* zone 1 - menus, and I/O tools */ - - Fl_Group *zone1 = new Fl_Group(8, 8, W-16, 20); - zone1->add(menu); - zone1->resizable(new Fl_Box(300, 8, 80, 20)); - zone1->add(inOut); - - /* zone 2 - controller and timing tools */ - - Fl_Group *zone2 = new Fl_Group(8, controller->y(), W-16, controller->h()); - zone2->add(controller); - zone2->resizable(new Fl_Box(controller->x()+controller->w()+4, zone2->y(), 80, 20)); - zone2->add(timing); - - /* zone 3 - beat meter */ - - Fl_Group *zone3 = new Fl_Group(8, beatMeter->y(), W-16, beatMeter->h()); - zone3->add(beatMeter); - - /* zone 4 - the keyboard (Fl_Group is unnecessary here, keyboard is - * a group by itself) */ - - resizable(keyboard); - - add(zone1); - add(zone2); - add(zone3); - add(keyboard); - callback(cb_endprogram); - gu_setFavicon(this); - show(argc, argv); -} - - -/* ------------------------------------------------------------------ */ - - -void gdMainWindow::cb_endprogram(Fl_Widget *v, void *p) { mainWin->__cb_endprogram(); } - - -/* ------------------------------------------------------------------ */ - - -void gdMainWindow::__cb_endprogram() -{ - if (!gdConfirmWin("Warning", "Quit Giada: are you sure?")) - return; - init_shutdown(); - hide(); - delete this; -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gInOut::gInOut(int x, int y) - : Fl_Group(x, y, 394, 20) -{ - begin(); - -#if defined(WITH_VST) - masterFxIn = new gFxButton (x, y, 20, 20, fxOff_xpm, fxOn_xpm); - inVol = new gDial (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20); - inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10); - inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+5, 10, 10, ""); - outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+5, 140, 10); - outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20); - masterFxOut = new gFxButton (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm); -#else - inVol = new gDial (x+62, y, 20, 20); - inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10); - outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 10); - outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20); -#endif - - end(); - - resizable(NULL); // don't resize any widget - - outVol->callback(cb_outVol, (void*)this); - outVol->value(G_Mixer.outVol); - inVol->callback(cb_inVol, (void*)this); - inVol->value(G_Mixer.inVol); - -#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); -#endif -} - - -/* ------------------------------------------------------------------ */ - - -void gInOut::cb_outVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_outVol(); } -void gInOut::cb_inVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_inVol(); } -#ifdef WITH_VST -void gInOut::cb_masterFxOut(Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_masterFxOut(); } -void gInOut::cb_masterFxIn (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_masterFxIn(); } -void gInOut::cb_inToOut (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_inToOut(); } -#endif - - -/* ------------------------------------------------------------------ */ - - -void gInOut::__cb_outVol() -{ - glue_setOutVol(outVol->value()); -} - - -/* ------------------------------------------------------------------ */ - - -void gInOut::__cb_inVol() -{ - glue_setInVol(inVol->value()); -} - - -/* ------------------------------------------------------------------ */ - - -#ifdef WITH_VST -void gInOut::__cb_masterFxOut() -{ - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST); -} - -void gInOut::__cb_masterFxIn() -{ - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST); -} - -void gInOut::__cb_inToOut() -{ - G_Mixer.inToOut = inToOut->value(); -} -#endif - - -/* ------------------------------------------------------------------ */ - - -void gInOut::refresh() -{ - outMeter->mixerPeak = G_Mixer.peakOut; - inMeter->mixerPeak = G_Mixer.peakIn; - outMeter->redraw(); - inMeter->redraw(); -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gMenu::gMenu(int x, int y) - : Fl_Group(x, y, 300, 20) -{ - begin(); - - file = new gClick(x, y, 70, 21, "file"); - edit = new gClick(file->x()+file->w()+4, y, 70, 21, "edit"); - config = new gClick(edit->x()+edit->w()+4, y, 70, 21, "config"); - about = new gClick(config->x()+config->w()+4, y, 70, 21, "about"); - - end(); - - resizable(NULL); // don't resize any widget - - about->callback(cb_about, (void*)this); - file->callback(cb_file, (void*)this); - edit->callback(cb_edit, (void*)this); - config->callback(cb_config, (void*)this); -} - - -/* ------------------------------------------------------------------ */ - - -void gMenu::cb_about (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_about(); } -void gMenu::cb_config(Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_config(); } -void gMenu::cb_file (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_file(); } -void gMenu::cb_edit (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_edit(); } - - -/* ------------------------------------------------------------------ */ - - -void gMenu::__cb_about() -{ - gu_openSubWindow(mainWin, new gdAbout(), WID_ABOUT); -} - - -/* ------------------------------------------------------------------ */ - - -void gMenu::__cb_config() -{ - gu_openSubWindow(mainWin, new gdConfig(380, 370), WID_CONFIG); -} - - -/* ------------------------------------------------------------------ */ - - -void gMenu::__cb_file() -{ - /* An Fl_Menu_Button is made of many Fl_Menu_Item */ - - Fl_Menu_Item menu[] = { - {"Open patch or project..."}, - {"Save patch..."}, - {"Save project..."}, - {"Quit Giada"}, - {0} - }; - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(11); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return; - - - if (strcmp(m->label(), "Open patch or project...") == 0) { - gWindow *childWin = new gdBrowser("Load Patch", G_Conf.patchPath, 0, BROWSER_LOAD_PATCH); - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); - return; - } - if (strcmp(m->label(), "Save patch...") == 0) { - if (G_Mixer.hasLogicalSamples() || G_Mixer.hasEditedSamples()) - if (!gdConfirmWin("Warning", "You should save a project in order to store\nyour takes and/or processed samples.")) - return; - gWindow *childWin = new gdBrowser("Save Patch", G_Conf.patchPath, 0, BROWSER_SAVE_PATCH); - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); - return; - } - if (strcmp(m->label(), "Save project...") == 0) { - gWindow *childWin = new gdBrowser("Save Project", G_Conf.patchPath, 0, BROWSER_SAVE_PROJECT); - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); - return; - } - if (strcmp(m->label(), "Quit Giada") == 0) { - mainWin->do_callback(); - return; - } -} - - -/* ------------------------------------------------------------------ */ - - -void gMenu::__cb_edit() -{ - Fl_Menu_Item menu[] = { - {"Clear all samples"}, - {"Clear all actions"}, - {"Remove empty columns"}, - {"Reset to init state"}, - {"Setup global MIDI input..."}, - {0} - }; - - /* clear all actions disabled if no recs, clear all samples disabled - * if no samples. */ - - menu[1].deactivate(); - - for (unsigned i=0; ihasActions) { - menu[1].activate(); - break; - } - for (unsigned i=0; itype == CHANNEL_SAMPLE) - if (((SampleChannel*)G_Mixer.channels.at(i))->wave != NULL) { - menu[0].activate(); - break; - } - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(11); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return; - - if (strcmp(m->label(), "Clear all samples") == 0) { - if (!gdConfirmWin("Warning", "Clear all samples: are you sure?")) - return; - mainWin->delSubWindow(WID_SAMPLE_EDITOR); - glue_clearAllSamples(); - return; - } - if (strcmp(m->label(), "Clear all actions") == 0) { - if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) - return; - mainWin->delSubWindow(WID_ACTION_EDITOR); - glue_clearAllRecs(); - return; - } - if (strcmp(m->label(), "Reset to init state") == 0) { - if (!gdConfirmWin("Warning", "Reset to init state: are you sure?")) - return; - gu_closeAllSubwindows(); - glue_resetToInitState(); - return; - } - if (strcmp(m->label(), "Remove empty columns") == 0) { - mainWin->keyboard->organizeColumns(); - return; - } - if (strcmp(m->label(), "Setup global MIDI input...") == 0) { - gu_openSubWindow(mainWin, new gdMidiInputMaster(), 0); - return; - } -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gController::gController(int x, int y) - : Fl_Group(x, y, 131, 25) -{ - begin(); - - rewind = new gClick(x, y, 25, 25, "", rewindOff_xpm, rewindOn_xpm); - play = new gClick(rewind->x()+rewind->w()+4, y, 25, 25, "", play_xpm, pause_xpm); - recAction = new gClick(play->x()+play->w()+4, y, 25, 25, "", recOff_xpm, recOn_xpm); - recInput = new gClick(recAction->x()+recAction->w()+4, y, 25, 25, "", inputRecOff_xpm, inputRecOn_xpm); - metronome = new gClick(recInput->x()+recInput->w()+4, y+10, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm); - - end(); - - resizable(NULL); // don't resize any widget - - rewind->callback(cb_rewind, (void*)this); - - play->callback(cb_play); - play->type(FL_TOGGLE_BUTTON); - - recAction->callback(cb_recAction, (void*)this); - recAction->type(FL_TOGGLE_BUTTON); - - recInput->callback(cb_recInput, (void*)this); - recInput->type(FL_TOGGLE_BUTTON); - - metronome->callback(cb_metronome); - metronome->type(FL_TOGGLE_BUTTON); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::cb_rewind (Fl_Widget *v, void *p) { ((gController*)p)->__cb_rewind(); } -void gController::cb_play (Fl_Widget *v, void *p) { ((gController*)p)->__cb_play(); } -void gController::cb_recAction(Fl_Widget *v, void *p) { ((gController*)p)->__cb_recAction(); } -void gController::cb_recInput (Fl_Widget *v, void *p) { ((gController*)p)->__cb_recInput(); } -void gController::cb_metronome(Fl_Widget *v, void *p) { ((gController*)p)->__cb_metronome(); } - - -/* ------------------------------------------------------------------ */ - - -void gController::__cb_rewind() -{ - glue_rewindSeq(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::__cb_play() -{ - glue_startStopSeq(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::__cb_recAction() -{ - glue_startStopActionRec(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::__cb_recInput() -{ - glue_startStopInputRec(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::__cb_metronome() -{ - glue_startStopMetronome(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::updatePlay(int v) -{ - play->value(v); - play->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::updateMetronome(int v) -{ - metronome->value(v); - metronome->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::updateRecInput(int v) -{ - recInput->value(v); - recInput->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gController::updateRecAction(int v) -{ - recAction->value(v); - recAction->redraw(); -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gTiming::gTiming(int x, int y) - : Fl_Group(x, y, 170, 15) -{ - begin(); - - quantizer = new gChoice(x, y, 40, 15, "", false); - bpm = new gClick (quantizer->x()+quantizer->w()+4, y, 40, 15); - meter = new gClick (bpm->x()+bpm->w()+8, y, 40, 15, "4/1"); - multiplier = new gClick (meter->x()+meter->w()+4, y, 15, 15, "", beatsMultiplyOff_xpm, beatsMultiplyOn_xpm); - divider = new gClick (multiplier->x()+multiplier->w()+4, y, 15, 15, "÷", beatsDivideOff_xpm, beatsDivideOn_xpm); - - end(); - - resizable(NULL); // don't resize any widget - - char buf[6]; snprintf(buf, 6, "%f", G_Mixer.bpm); - bpm->copy_label(buf); - - 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("1b", 0, cb_quantizer, (void*)this); - quantizer->add("2b", 0, cb_quantizer, (void*)this); - quantizer->add("3b", 0, cb_quantizer, (void*)this); - quantizer->add("4b", 0, cb_quantizer, (void*)this); - quantizer->add("6b", 0, cb_quantizer, (void*)this); - quantizer->add("8b", 0, cb_quantizer, (void*)this); - quantizer->value(0); // "off" by default -} - - -/* ------------------------------------------------------------------ */ - - -void gTiming::cb_bpm (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_bpm(); } -void gTiming::cb_meter (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_meter(); } -void gTiming::cb_quantizer (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_quantizer(); } -void gTiming::cb_multiplier(Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_multiplier(); } -void gTiming::cb_divider (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_divider(); } - - -/* ------------------------------------------------------------------ */ - - -void gTiming::__cb_bpm() -{ - gu_openSubWindow(mainWin, new gdBpmInput(bpm->label()), WID_BPM); -} - - -/* ------------------------------------------------------------------ */ - - -void gTiming::__cb_meter() -{ - gu_openSubWindow(mainWin, new gdBeatsInput(), WID_BEATS); -} - - -/* ------------------------------------------------------------------ */ - - -void gTiming::__cb_quantizer() -{ - glue_quantize(quantizer->value()); -} - - -/* ------------------------------------------------------------------ */ - - -void gTiming::__cb_multiplier() -{ - glue_beatsMultiply(); -} - - -/* ------------------------------------------------------------------ */ - - -void gTiming::__cb_divider() -{ - glue_beatsDivide(); -} - - -/* ------------------------------------------------------------------ */ - - -void gTiming::setBpm(const char *v) -{ - bpm->copy_label(v); -} - - -void gTiming::setBpm(float v) -{ - char buf[6]; - sprintf(buf, "%.01f", v); // only 1 decimal place (e.g. 120.0) - bpm->copy_label(buf); -} - - -/* ------------------------------------------------------------------ */ - - -void gTiming::setMeter(int beats, int bars) -{ - char buf[8]; - sprintf(buf, "%d/%d", beats, bars); - meter->copy_label(buf); -} diff --git a/src/gd_mainWindow.h b/src/gd_mainWindow.h deleted file mode 100644 index 0361d5d..0000000 --- a/src/gd_mainWindow.h +++ /dev/null @@ -1,210 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * gd_mainWindow - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GD_MAINWINDOW_H -#define GD_MAINWINDOW_H - - -#include -#include -#include "ge_mixed.h" -#include "ge_window.h" - - -/* ------------------------------------------------------------------ */ - - -class gdMainWindow : public gWindow -{ -private: - - static void cb_endprogram (Fl_Widget *v, void *p); - inline void __cb_endprogram(); - -public: - - class gKeyboard *keyboard; - class gBeatMeter *beatMeter; - class gMenu *menu; - class gInOut *inOut; - class gController *controller; - class gTiming *timing; - - gdMainWindow(int w, int h, const char *title, int argc, char **argv); -}; - - -/* ------------------------------------------------------------------ */ - - -class gInOut : public Fl_Group -{ -private: - - class gSoundMeter *outMeter; - class gSoundMeter *inMeter; - class gBeatMeter *beatMeter; - class gDial *outVol; - class gDial *inVol; -#ifdef WITH_VST - class gFxButton *masterFxOut; - class gFxButton *masterFxIn; - class gClick *inToOut; -#endif - - 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 - - inline void __cb_outVol (); - inline void __cb_inVol (); -#ifdef WITH_VST - inline void __cb_masterFxOut(); - inline void __cb_masterFxIn (); - inline void __cb_inToOut (); -#endif - -public: - - gInOut(int x, int y); - - void refresh(); - - inline void setOutVol(float v) { outVol->value(v); } - inline void setInVol (float v) { inVol->value(v); } -#ifdef WITH_VST - inline void setMasterFxOutFull(bool v) { masterFxOut->full = v; masterFxOut->redraw(); } - inline void setMasterFxInFull(bool v) { masterFxIn->full = v; masterFxIn->redraw(); } -#endif -}; - - -/* ------------------------------------------------------------------ */ - - -class gMenu : public Fl_Group -{ -private: - - class gClick *file; - class gClick *edit; - class gClick *config; - class gClick *about; - - static void cb_about (Fl_Widget *v, void *p); - static void cb_config(Fl_Widget *v, void *p); - static void cb_file (Fl_Widget *v, void *p); - static void cb_edit (Fl_Widget *v, void *p); - - inline void __cb_about (); - inline void __cb_config(); - inline void __cb_file (); - inline void __cb_edit (); - -public: - - gMenu(int x, int y); -}; - - -/* ------------------------------------------------------------------ */ - - -class gController : public Fl_Group -{ -private: - - class gClick *rewind; - class gClick *play; - class gClick *recAction; - class gClick *recInput; - class gClick *metronome; - - static void cb_rewind (Fl_Widget *v, void *p); - static void cb_play (Fl_Widget *v, void *p); - static void cb_recAction(Fl_Widget *v, void *p); - static void cb_recInput (Fl_Widget *v, void *p); - static void cb_metronome(Fl_Widget *v, void *p); - - inline void __cb_rewind (); - inline void __cb_play (); - inline void __cb_recAction(); - inline void __cb_recInput (); - inline void __cb_metronome(); - -public: - - gController(int x, int y); - - void updatePlay (int v); - void updateMetronome(int v); - void updateRecInput (int v); - void updateRecAction(int v); -}; - - -/* ------------------------------------------------------------------ */ - - -class gTiming : public Fl_Group -{ -private: - - class gClick *bpm; - class gClick *meter; - class gChoice *quantizer; - class gClick *multiplier; - class gClick *divider; - - static void cb_bpm (Fl_Widget *v, void *p); - static void cb_meter (Fl_Widget *v, void *p); - static void cb_quantizer (Fl_Widget *v, void *p); - static void cb_multiplier(Fl_Widget *v, void *p); - static void cb_divider (Fl_Widget *v, void *p); - - inline void __cb_bpm(); - inline void __cb_meter(); - inline void __cb_quantizer(); - inline void __cb_multiplier(); - inline void __cb_divider(); - -public: - - gTiming(int x, int y); - - void setBpm(const char *v); - void setBpm(float v); - void setMeter(int beats, int bars); -}; - - -#endif diff --git a/src/gd_midiInput.cpp b/src/gd_midiInput.cpp deleted file mode 100644 index 5d41ae1..0000000 --- a/src/gd_midiInput.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_midiInput - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "gd_midiInput.h" -#include "ge_mixed.h" -#include "ge_midiIoTools.h" -#include "gui_utils.h" -#include "kernelMidi.h" -#include "conf.h" -#include "sampleChannel.h" -#include "log.h" - - -extern Conf G_Conf; - - -gdMidiInput::gdMidiInput(int w, int h, const char *title) - : gWindow(w, h, title) -{ -} - - -/* -------------------------------------------------------------------------- */ - - -gdMidiInput::~gdMidiInput() { - kernelMidi::stopMidiLearn(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiInput::stopMidiLearn(gLearner *learner) { - kernelMidi::stopMidiLearn(); - learner->updateValue(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiInput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) { - *param = msg; - stopMidiLearn(l); - gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiInput::cb_learn(uint32_t msg, void *d) { - cbData *data = (cbData*) d; - gdMidiInput *window = (gdMidiInput*) data->window; - gLearner *learner = data->learner; - uint32_t *param = learner->param; - window->__cb_learn(param, msg, learner); - free(data); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiInput::cb_close(Fl_Widget *w, void *p) { ((gdMidiInput*)p)->__cb_close(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiInput::__cb_close() { - do_callback(); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdMidiInputChannel::gdMidiInputChannel(Channel *ch) - : gdMidiInput(300, 206, "MIDI Input Setup"), - ch(ch) -{ - char title[64]; - sprintf(title, "MIDI Input Setup (channel %d)", ch->index+1); - label(title); - - set_modal(); - - enable = new gCheck(8, 8, 120, 20, "enable MIDI input"); - new gLearner(8, 30, w()-16, "key press", cb_learn, &ch->midiInKeyPress); - new gLearner(8, 54, w()-16, "key release", cb_learn, &ch->midiInKeyRel); - new gLearner(8, 78, w()-16, "key kill", cb_learn, &ch->midiInKill); - new gLearner(8, 102, w()-16, "mute", cb_learn, &ch->midiInMute); - new gLearner(8, 126, w()-16, "solo", cb_learn, &ch->midiInSolo); - new gLearner(8, 150, w()-16, "volume", cb_learn, &ch->midiInVolume); - int yy = 178; - - if (ch->type == CHANNEL_SAMPLE) { - size(300, 254); - new gLearner(8, 174, w()-16, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch); - new gLearner(8, 198, w()-16, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions); - yy = 226; - } - - ok = new gButton(w()-88, yy, 80, 20, "Close"); - ok->callback(cb_close, (void*)this); - - enable->value(ch->midiIn); - enable->callback(cb_enable, (void*)this); - - gu_setFavicon(this); - show(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiInputChannel::cb_enable(Fl_Widget *w, void *p) { ((gdMidiInputChannel*)p)->__cb_enable(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiInputChannel::__cb_enable() { - ch->midiIn = enable->value(); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdMidiInputMaster::gdMidiInputMaster() - : gdMidiInput(300, 256, "MIDI Input Setup (global)") -{ - set_modal(); - - new gLearner(8, 8, w()-16, "rewind", &cb_learn, &G_Conf.midiInRewind); - new gLearner(8, 32, w()-16, "play/stop", &cb_learn, &G_Conf.midiInStartStop); - new gLearner(8, 56, w()-16, "action recording", &cb_learn, &G_Conf.midiInActionRec); - new gLearner(8, 80, w()-16, "input recording", &cb_learn, &G_Conf.midiInInputRec); - new gLearner(8, 104, w()-16, "metronome", &cb_learn, &G_Conf.midiInMetronome); - new gLearner(8, 128, w()-16, "input volume", &cb_learn, &G_Conf.midiInVolumeIn); - new gLearner(8, 152, w()-16, "output volume", &cb_learn, &G_Conf.midiInVolumeOut); - new gLearner(8, 176, w()-16, "sequencer ×2", &cb_learn, &G_Conf.midiInBeatDouble); - new gLearner(8, 200, w()-16, "sequencer ÷2", &cb_learn, &G_Conf.midiInBeatHalf); - ok = new gButton(w()-88, 228, 80, 20, "Close"); - - ok->callback(cb_close, (void*)this); - - gu_setFavicon(this); - show(); -} diff --git a/src/gd_midiInput.h b/src/gd_midiInput.h deleted file mode 100644 index 91bdea1..0000000 --- a/src/gd_midiInput.h +++ /dev/null @@ -1,98 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_midiInput - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GD_MIDI_INPUT_H -#define GD_MIDI_INPUT_H - - -#include "ge_window.h" -#include "kernelMidi.h" -#include "utils.h" -#include "ge_mixed.h" - - -class gdMidiInput : public gWindow -{ -protected: - - gClick *ok; - - void stopMidiLearn(class gLearner *l); - - /* cb_learn - * callback attached to kernelMidi to learn various actions. */ - - static void cb_learn (uint32_t msg, void *data); - inline void __cb_learn(uint32_t *param, uint32_t msg, gLearner *l); - - static void cb_close (Fl_Widget *w, void *p); - inline void __cb_close(); - -public: - - gdMidiInput(int w, int h, const char *title); - ~gdMidiInput(); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gdMidiInputChannel : public gdMidiInput -{ -private: - - class Channel *ch; - - gCheck *enable; - - - //gVector items; for future use, with vst parameters - - static void cb_enable (Fl_Widget *w, void *p); - inline void __cb_enable(); - -public: - - gdMidiInputChannel(class Channel *ch); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gdMidiInputMaster : public gdMidiInput -{ -public: - - gdMidiInputMaster(); -}; - - -#endif diff --git a/src/gd_midiOutput.cpp b/src/gd_midiOutput.cpp deleted file mode 100644 index b01f6f6..0000000 --- a/src/gd_midiOutput.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_midiOutput - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "gd_midiOutput.h" -#include "ge_mixed.h" -#include "ge_channel.h" -#include "ge_midiIoTools.h" -#include "gg_keyboard.h" -#include "channel.h" -#include "sampleChannel.h" -#include "conf.h" -#include "midiChannel.h" -#include "gui_utils.h" - - -extern Conf G_Conf; - - -gdMidiOutput::gdMidiOutput(int w, int h) - //: gWindow(300, 64, "Midi Output Setup") - : gWindow(w, h, "Midi Output Setup") -{ -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::stopMidiLearn(gLearner *learner) { - kernelMidi::stopMidiLearn(); - learner->updateValue(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) { - *param = msg; - stopMidiLearn(l); - gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::cb_learn(uint32_t msg, void *d) { - cbData *data = (cbData*) d; - gdMidiOutput *window = (gdMidiOutput*) data->window; - gLearner *learner = data->learner; - uint32_t *param = learner->param; - window->__cb_learn(param, msg, learner); - free(data); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::cb_close(Fl_Widget *w, void *p) { ((gdMidiOutput*)p)->__cb_close(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::__cb_close() { - do_callback(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::cb_enableLightning(Fl_Widget *w, void *p) { - ((gdMidiOutput*)p)->__cb_enableLightning(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::__cb_enableLightning() {} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutput::setTitle(int chanNum) -{ - char title[64]; - sprintf(title, "MIDI Output Setup (channel %d)", chanNum); - copy_label(title); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdMidiOutputMidiCh::gdMidiOutputMidiCh(MidiChannel *ch) - : gdMidiOutput(300, 168), ch(ch) -{ - setTitle(ch->index+1); - begin(); - - enableOut = new gCheck(x()+8, y()+8, 150, 20, "Enable MIDI output"); - chanListOut = new gChoice(w()-108, y()+8, 100, 20); - - enableLightning = new gCheck(x()+8, chanListOut->y()+chanListOut->h()+8, 120, 20, "Enable MIDI lightning output"); - new gLearner(x()+8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying); - new gLearner(x()+8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute); - new gLearner(x()+8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo); - - close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close"); - - end(); - - chanListOut->add("Channel 1"); - chanListOut->add("Channel 2"); - chanListOut->add("Channel 3"); - chanListOut->add("Channel 4"); - chanListOut->add("Channel 5"); - chanListOut->add("Channel 6"); - chanListOut->add("Channel 7"); - chanListOut->add("Channel 8"); - chanListOut->add("Channel 9"); - chanListOut->add("Channel 10"); - chanListOut->add("Channel 11"); - chanListOut->add("Channel 12"); - chanListOut->add("Channel 13"); - chanListOut->add("Channel 14"); - chanListOut->add("Channel 15"); - chanListOut->add("Channel 16"); - chanListOut->value(0); - - if (ch->midiOut) - enableOut->value(1); - else - chanListOut->deactivate(); - - if (ch->midiOutL) - enableLightning->value(1); - - chanListOut->value(ch->midiOutChan); - - enableOut->callback(cb_enableChanList, (void*)this); - close->callback(cb_close, (void*)this); - - set_modal(); - gu_setFavicon(this); - show(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutputMidiCh::cb_close (Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->__cb_close(); } -void gdMidiOutputMidiCh::cb_enableChanList(Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->__cb_enableChanList(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutputMidiCh::__cb_enableChanList() { - enableOut->value() ? chanListOut->activate() : chanListOut->deactivate(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutputMidiCh::__cb_close() { - ch->midiOut = enableOut->value(); - ch->midiOutChan = chanListOut->value(); - ch->midiOutL = enableLightning->value(); - ch->guiChannel->update(); - do_callback(); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdMidiOutputSampleCh::gdMidiOutputSampleCh(SampleChannel *ch) - : gdMidiOutput(300, 140), ch(ch) -{ - setTitle(ch->index+1); - - enableLightning = new gCheck(8, 8, 120, 20, "Enable MIDI lightning output"); - new gLearner(8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying); - new gLearner(8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute); - new gLearner(8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo); - - close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close"); - close->callback(cb_close, (void*)this); - - enableLightning->value(ch->midiOutL); - enableLightning->callback(cb_enableLightning, (void*)this); - - set_modal(); - gu_setFavicon(this); - show(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutputSampleCh::cb_close(Fl_Widget *w, void *p) { ((gdMidiOutputSampleCh*)p)->__cb_close(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdMidiOutputSampleCh::__cb_close() { - ch->midiOutL = enableLightning->value(); - do_callback(); -} diff --git a/src/gd_midiOutput.h b/src/gd_midiOutput.h deleted file mode 100644 index 8dd2fd6..0000000 --- a/src/gd_midiOutput.h +++ /dev/null @@ -1,134 +0,0 @@ -/* ---------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_midiOutput - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GD_MIDI_OUTPUT_H -#define GD_MIDI_OUTPUT_H - - -#include -#include -#include "ge_window.h" - - -/* There's no such thing as a gdMidiOutputMaster vs gdMidiOutputChannel. MIDI -output master is managed by the configuration window, hence gdMidiOutput deals -only with channels. - -Both MidiOutputMidiCh and MidiOutputSampleCh have the MIDI lighting widget set. -In addition MidiOutputMidiCh has the MIDI message output box. */ - -/* TODO - gdMidiOutput is almost the same thing of gdMidiInput. Create another -parent class gdMidiIO to inherit from */ - -class gdMidiOutput : public gWindow -{ -protected: - - class gClick *close; - class gCheck *enableLightning; - - void stopMidiLearn(class gLearner *l); - - /* cb_learn - * callback attached to kernelMidi to learn various actions. */ - - static void cb_learn (uint32_t msg, void *data); - inline void __cb_learn(uint32_t *param, uint32_t msg, class gLearner *l); - - /* cb_close - close current window. */ - - static void cb_close (Fl_Widget *w, void *p); - inline void __cb_close(); - - /* cb_enableLightning - enable MIDI lightning output. */ - - static void cb_enableLightning (Fl_Widget *w, void *p); - inline void __cb_enableLightning(); - - /* setTitle - * set window title. */ - - void setTitle(int chanNum); - -public: - - gdMidiOutput(int w, int h); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gdMidiOutputMidiCh : public gdMidiOutput -{ -private: - - static void cb_enableChanList (Fl_Widget *w, void *p); - inline void __cb_enableChanList(); - - /* __cb_close - override parent method, we need to do more stuff on close. */ - - static void cb_close (Fl_Widget *w, void *p); - inline void __cb_close(); - - class gCheck *enableOut; - class gChoice *chanListOut; - - class MidiChannel *ch; - -public: - - gdMidiOutputMidiCh(class MidiChannel *ch); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gdMidiOutputSampleCh : public gdMidiOutput -{ -private: - - class SampleChannel *ch; - - /* __cb_close - override parent method, we need to do more stuff on close. */ - - static void cb_close (Fl_Widget *w, void *p); - inline void __cb_close(); - -public: - - gdMidiOutputSampleCh(class SampleChannel *ch); -}; - -#endif diff --git a/src/gd_pluginList.cpp b/src/gd_pluginList.cpp deleted file mode 100644 index ade532d..0000000 --- a/src/gd_pluginList.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_pluginList - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST - -#include "gd_pluginList.h" -#include "gd_pluginWindow.h" -#include "gd_pluginWindowGUI.h" -#include "gd_browser.h" -#include "gd_mainWindow.h" -#include "ge_mixed.h" -#include "gui_utils.h" -#include "utils.h" -#include "conf.h" -#include "graphics.h" -#include "pluginHost.h" -#include "mixer.h" -#include "channel.h" -#include "ge_channel.h" - - -extern Conf G_Conf; -extern PluginHost G_PluginHost; -extern gdMainWindow *mainWin; - - -gdPluginList::gdPluginList(int stackType, Channel *ch) - : gWindow(468, 204), ch(ch), stackType(stackType) -{ - - if (G_Conf.pluginListX) - resize(G_Conf.pluginListX, G_Conf.pluginListY, w(), h()); - - list = new Fl_Scroll(8, 8, 476, 188); - list->type(Fl_Scroll::VERTICAL); - list->scrollbar.color(COLOR_BG_0); - list->scrollbar.selection_color(COLOR_BG_1); - list->scrollbar.labelcolor(COLOR_BD_1); - list->scrollbar.slider(G_BOX); - - list->begin(); - refreshList(); - list->end(); - - end(); - set_non_modal(); - - /* TODO - awful stuff... we should subclass into gdPluginListChannel and - gdPluginListMaster */ - - if (stackType == PluginHost::MASTER_OUT) - label("Master Out Plugins"); - else - if (stackType == PluginHost::MASTER_IN) - label("Master In Plugins"); - else { - char tmp[32]; - sprintf(tmp, "Channel %d Plugins", ch->index+1); - copy_label(tmp); - } - - gu_setFavicon(this); - show(); -} - - -/* -------------------------------------------------------------------------- */ - - -gdPluginList::~gdPluginList() { - G_Conf.pluginListX = x(); - G_Conf.pluginListY = y(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPluginList::cb_addPlugin(Fl_Widget *v, void *p) { ((gdPluginList*)p)->__cb_addPlugin(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdPluginList::cb_refreshList(Fl_Widget *v, void *p) { - - /* note: this callback is fired by gdBrowser. Close its window first, - * by calling the parent (pluginList) and telling it to delete its - * subwindow (i.e. gdBrowser). */ - - gWindow *child = (gWindow*) v; - if (child->getParent() != NULL) - (child->getParent())->delSubWindow(child); - - /* finally refresh plugin list: void *p is a pointer to gdPluginList. - * This callback works even when you click 'x' to close the window... - * well, who cares */ - - ((gdPluginList*)p)->refreshList(); - ((gdPluginList*)p)->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPluginList::__cb_addPlugin() { - - /* the usual callback that gWindow adds to each subwindow in this case - * is not enough, because when we close the browser the plugin list - * must be redrawn. We have a special callback, cb_refreshList, which - * we add to gdBrowser. It does exactly what we need. */ - - gdBrowser *b = new gdBrowser("Browse Plugin", G_Conf.pluginPath, ch, BROWSER_LOAD_PLUGIN, stackType); - addSubWindow(b); - b->callback(cb_refreshList, (void*)this); // 'this' refers to gdPluginList - -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPluginList::refreshList() { - - /* delete the previous list */ - - list->clear(); - list->scroll_to(0, 0); - - /* add new buttons, as many as the plugin in pluginHost::stack + 1, - * the 'add new' button. Warning: if ch == NULL we are working with - * master in/master out stacks. */ - - int numPlugins = G_PluginHost.countPlugins(stackType, ch); - int i = 0; - - while (ix(), list->y()-list->yposition()+(i*24), 800); - list->add(gdp); - i++; - } - - int addPlugY = numPlugins == 0 ? 90 : list->y()-list->yposition()+(i*24); - addPlugin = new gClick(8, addPlugY, 452, 20, "-- add new plugin --"); - addPlugin->callback(cb_addPlugin, (void*)this); - list->add(addPlugin); - - /* if num(plugins) > 7 make room for the side scrollbar. - * Scrollbar.width = 20 + 4(margin) */ - - if (i>7) - size(492, h()); - else - size(468, h()); - - redraw(); - - /* set 'full' flag to FX button */ - - /* TODO - awful stuff... we should subclass into gdPluginListChannel and - gdPluginListMaster */ - - if (stackType == PluginHost::MASTER_OUT) { - mainWin->inOut->setMasterFxOutFull(G_PluginHost.countPlugins(stackType, ch) > 0); - } - else - if (stackType == PluginHost::MASTER_IN) { - mainWin->inOut->setMasterFxInFull(G_PluginHost.countPlugins(stackType, ch) > 0); - } - else { - ch->guiChannel->fx->full = G_PluginHost.countPlugins(stackType, ch) > 0; - ch->guiChannel->fx->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdPlugin::gdPlugin(gdPluginList *gdp, Plugin *p, int X, int Y, int W) - : Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p) -{ - begin(); - button = new gButton(8, y(), 220, 20); - program = new gChoice(button->x()+button->w()+4, y(), 132, 20); - bypass = new gButton(program->x()+program->w()+4, y(), 20, 20); - shiftUp = new gButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm); - shiftDown = new gButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm); - remove = new gButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm); - end(); - - if (pPlugin->status != 1) { // bad state - char name[256]; - sprintf(name, "* %s *", gBasename(pPlugin->pathfile).c_str()); - button->copy_label(name); - } - else { - char name[256]; - pPlugin->getProduct(name); - if (strcmp(name, " ")==0) - pPlugin->getName(name); - - button->copy_label(name); - button->callback(cb_openPluginWindow, (void*)this); - - program->callback(cb_setProgram, (void*)this); - - /* loading vst programs */ - /* FIXME - max programs = 128 (unknown source) */ - - for (int i=0; i<64; i++) { - char out[kVstMaxProgNameLen]; - pPlugin->getProgramName(i, out); - for (int j=0; j 0) - program->add(out); - } - if (program->size() == 0) { - program->add("-- no programs --\0"); - program->deactivate(); - } - if (pPlugin->getProgram() == -1) - program->value(0); - else - program->value(pPlugin->getProgram()); - - bypass->callback(cb_setBypass, (void*)this); - bypass->type(FL_TOGGLE_BUTTON); - bypass->value(pPlugin->bypass ? 0 : 1); - } - - shiftUp->callback(cb_shiftUp, (void*)this); - shiftDown->callback(cb_shiftDown, (void*)this); - remove->callback(cb_removePlugin, (void*)this); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPlugin::cb_removePlugin (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_removePlugin(); } -void gdPlugin::cb_openPluginWindow(Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_openPluginWindow(); } -void gdPlugin::cb_setBypass (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setBypass(); } -void gdPlugin::cb_shiftUp (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftUp(); } -void gdPlugin::cb_shiftDown (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftDown(); } -void gdPlugin::cb_setProgram (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setProgram(); } - - -/* -------------------------------------------------------------------------- */ - - -void gdPlugin::__cb_shiftUp() { - - /*nothing to do if there's only one plugin */ - - if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1) - return; - - int pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch); - - if (pluginIndex == 0) // first of the stack, do nothing - return; - - G_PluginHost.swapPlugin(pluginIndex, pluginIndex-1, pParent->stackType, pParent->ch); - pParent->refreshList(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPlugin::__cb_shiftDown() { - - /*nothing to do if there's only one plugin */ - - if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1) - return; - - unsigned pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch); - unsigned stackSize = (G_PluginHost.getStack(pParent->stackType, pParent->ch))->size; - - if (pluginIndex == stackSize-1) // last one in the stack, do nothing - return; - - G_PluginHost.swapPlugin(pluginIndex, pluginIndex+1, pParent->stackType, pParent->ch); - pParent->refreshList(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPlugin::__cb_removePlugin() { - - /* os x hack: show window before deleting it */ - -#ifdef __APPLE__ - gdPluginWindowGUImac* w = (gdPluginWindowGUImac*) pParent->getChild(pPlugin->getId()+1); - if (w) - w->show(); -#endif - - /* any subwindow linked to the plugin must be destroyed */ - - pParent->delSubWindow(pPlugin->getId()+1); - G_PluginHost.freePlugin(pPlugin->getId(), pParent->stackType, pParent->ch); - pParent->refreshList(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPlugin::__cb_openPluginWindow() { - - /* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved - * for the window 'add plugin'. */ - - /* TODO - at the moment you can open a window for each plugin in the stack. - * This is not consistent with the rest of the gui. You can avoid this by - * calling - * - * gu_openSubWindow(this, new gdPluginWindow(pPlugin), WID_FX); - * - * instead of the following code. - * - * EDIT 2 - having only 1 plugin window would be very uncomfortable */ - - if (!pParent->hasWindow(pPlugin->getId()+1)) { - gWindow *w; - if (pPlugin->hasGui()) -#ifdef __APPLE__ - w = new gdPluginWindowGUImac(pPlugin); -#else - w = new gdPluginWindowGUI(pPlugin); -#endif - else - w = new gdPluginWindow(pPlugin); - w->setId(pPlugin->getId()+1); - pParent->addSubWindow(w); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPlugin::__cb_setBypass() { - pPlugin->bypass = !pPlugin->bypass; -} - - -/* -------------------------------------------------------------------------- */ - - -void gdPlugin::__cb_setProgram() { - pPlugin->setProgram(program->value()); -} - - -#endif // #ifdef WITH_VST diff --git a/src/gd_pluginList.h b/src/gd_pluginList.h deleted file mode 100644 index 33ac4a1..0000000 --- a/src/gd_pluginList.h +++ /dev/null @@ -1,107 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_pluginList - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST - -#ifndef __GD_PLUGINLIST_H__ -#define __GD_PLUGINLIST_H__ - -#include -#include -#include "ge_window.h" - - -class gdPluginList : public gWindow { - -private: - - class gClick *addPlugin; - Fl_Scroll *list; - - //gVector subWindows; - - static void cb_addPlugin (Fl_Widget *v, void *p); - inline void __cb_addPlugin (); - -public: - - class Channel *ch; // ch == NULL ? masterOut - int stackType; - - gdPluginList(int stackType, class Channel *ch=NULL); - ~gdPluginList(); - - /* special callback, passed to browser. When closed (i.e. plugin - * has been selected) the same browser will refresh this window. */ - - static void cb_refreshList(Fl_Widget*, void*); - - void refreshList(); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gdPlugin : public Fl_Group { - -private: - - class gdPluginList *pParent; - class Plugin *pPlugin; - - static void cb_removePlugin (Fl_Widget *v, void *p); - static void cb_openPluginWindow (Fl_Widget *v, void *p); - static void cb_setBypass (Fl_Widget *v, void *p); - static void cb_shiftUp (Fl_Widget *v, void *p); - static void cb_shiftDown (Fl_Widget *v, void *p); - static void cb_setProgram (Fl_Widget *v, void *p); - inline void __cb_removePlugin (); - inline void __cb_openPluginWindow (); - inline void __cb_setBypass (); - inline void __cb_shiftUp (); - inline void __cb_shiftDown (); - inline void __cb_setProgram (); - -public: - - class gButton *button; - class gChoice *program; - class gButton *bypass; - class gButton *shiftUp; - class gButton *shiftDown; - class gButton *remove; - - gdPlugin(gdPluginList *gdp, class Plugin *p, int x, int y, int w); - -}; - -#endif - -#endif // #ifdef WITH_VST diff --git a/src/gd_pluginWindow.cpp b/src/gd_pluginWindow.cpp deleted file mode 100644 index f9119c0..0000000 --- a/src/gd_pluginWindow.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_pluginWindow - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifdef WITH_VST - -#include -#include "gd_pluginWindow.h" -#include "pluginHost.h" -#include "ge_mixed.h" -#include "gui_utils.h" - - -extern PluginHost G_PluginHost; - - -Parameter::Parameter(int id, Plugin *p, int X, int Y, int W) - : Fl_Group(X,Y,W-24,20), id(id), pPlugin(p) -{ - begin(); - - label = new gBox(x(), y(), 60, 20); - char name[kVstMaxParamStrLen]; - pPlugin->getParamName(id, name); - label->copy_label(name); - label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); - - slider = new gSlider(label->x()+label->w()+8, y(), W-200, 20); - slider->value(pPlugin->getParam(id)); - slider->callback(cb_setValue, (void *)this); - - value = new gBox(slider->x()+slider->w()+8, y(), 100, 20); - char disp[kVstMaxParamStrLen]; - char labl[kVstMaxParamStrLen]; - char str [256]; - pPlugin->getParamDisplay(id, disp); - pPlugin->getParamLabel(id, labl); - sprintf(str, "%s %s", disp, labl); - value->copy_label(str); - value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); - value->box(G_BOX); - - resizable(slider); - - end(); -} - - -/* ------------------------------------------------------------------ */ - - -void Parameter::cb_setValue(Fl_Widget *v, void *p) { ((Parameter*)p)->__cb_setValue(); } - - -/* ------------------------------------------------------------------ */ - - -void Parameter::__cb_setValue() { - - pPlugin->setParam(id, slider->value()); - - char disp[256]; - char labl[256]; - char str [256]; - - pPlugin->getParamDisplay(id, disp); - pPlugin->getParamLabel(id, labl); - sprintf(str, "%s %s", disp, labl); - - value->copy_label(str); - value->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -gdPluginWindow::gdPluginWindow(Plugin *pPlugin) - : gWindow(400, 156), pPlugin(pPlugin) // 350 -{ - set_non_modal(); - - gLiquidScroll *list = new gLiquidScroll(8, 8, w()-16, h()-16); - list->type(Fl_Scroll::VERTICAL_ALWAYS); - list->begin(); - - int numParams = pPlugin->getNumParams(); - for (int i=0; ix(), list->y()+(i*24), list->w()); - list->end(); - - end(); - - char name[256]; - pPlugin->getProduct(name); - if (strcmp(name, " ")==0) - pPlugin->getName(name); - label(name); - - size_range(400, (24*1)+12); - resizable(list); - - gu_setFavicon(this); - show(); - -} - - -gdPluginWindow::~gdPluginWindow() {} - - -#endif // #ifdef WITH_VST diff --git a/src/gd_pluginWindow.h b/src/gd_pluginWindow.h deleted file mode 100644 index 03d49bf..0000000 --- a/src/gd_pluginWindow.h +++ /dev/null @@ -1,76 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_pluginWindow - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifdef WITH_VST - -#ifndef __GD_PLUGINWINDOW_H__ -#define __GD_PLUGINWINDOW_H__ - - -#include -#include -#include "ge_window.h" - - -class gdPluginWindow : public gWindow { - -private: - class Plugin *pPlugin; - -public: - int id; - - gdPluginWindow(Plugin *pPlugin); - ~gdPluginWindow(); -}; - - -/* ------------------------------------------------------------------ */ - - -class Parameter : public Fl_Group { - -private: - int id; - class Plugin *pPlugin; - - static void cb_setValue(Fl_Widget *v, void *p); - inline void __cb_setValue(); - -public: - class gBox *label; - class gSlider *slider; - class gBox *value; - - Parameter(int id, class Plugin *p, int x, int y, int w); -}; - - -#endif - -#endif // #ifdef WITH_VST diff --git a/src/gd_pluginWindowGUI.cpp b/src/gd_pluginWindowGUI.cpp deleted file mode 100644 index 6fd0a07..0000000 --- a/src/gd_pluginWindowGUI.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_pluginWindowGUI - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifdef WITH_VST - - -#include "gd_pluginWindowGUI.h" -#include "pluginHost.h" -#include "ge_mixed.h" -#include "gui_utils.h" -#include "log.h" - - -extern PluginHost G_PluginHost; - - -gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin) - : gWindow(450, 300), pPlugin(pPlugin) -{ - - /* some effects like to have us get their rect before opening them */ - - ERect *rect; - pPlugin->getRect(&rect); - - gu_setFavicon(this); - set_non_modal(); - resize(x(), y(), pPlugin->getGuiWidth(), pPlugin->getGuiWidth()); - show(); - - gLog("[gdPluginWindowGUI] open window, w=%d h=%d\n", - pPlugin->getGuiWidth(), pPlugin->getGuiWidth()); - - /* Fl::check(): Waits until "something happens" and then returns. It's - * mandatory on linux, otherwise X can't find 'this' window. */ - - Fl::check(); - pPlugin->openGui((void*)fl_xid(this)); - - char name[256]; - pPlugin->getProduct(name); - copy_label(name); - - /* add a pointer to this window to plugin */ - - pPlugin->window = this; - - pPlugin->idle(); -} - - -/* ------------------------------------------------------------------ */ - - -gdPluginWindowGUI::~gdPluginWindowGUI() { - pPlugin->closeGui(); -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -#if defined(__APPLE__) - - -pascal OSStatus gdPluginWindowGUImac::windowHandler(EventHandlerCallRef ehc, EventRef e, void *data) { - return ((gdPluginWindowGUImac*)data)->__wh(ehc, e); -} - - -/* ------------------------------------------------------------------ */ - - -pascal OSStatus gdPluginWindowGUImac::__wh(EventHandlerCallRef inHandlerCallRef, EventRef inEvent) { - OSStatus result = eventNotHandledErr; // let the Carbon Event Manager close the window - UInt32 eventClass = GetEventClass(inEvent); - UInt32 eventKind = GetEventKind(inEvent); - - switch (eventClass) { - case kEventClassWindow: { - switch (eventKind) { - case kEventWindowClose: { - gLog("[pluginWindowMac] <<< CALLBACK >>> kEventWindowClose for gWindow=%p, window=%p\n", (void*)this, (void*)carbonWindow); - show(); - break; - } - case kEventWindowClosed: { - gLog("[pluginWindowMac] <<< CALLBACK >>> kEventWindowClosed for gWindow=%p, window=%p\n", (void*)this, (void*)carbonWindow); - open = false; - result = noErr; - break; - } - } - break; - } - } - return result; -} - - -/* ------------------------------------------------------------------ */ - - -gdPluginWindowGUImac::gdPluginWindowGUImac(Plugin *pPlugin) - : gWindow(450, 300), pPlugin(pPlugin), carbonWindow(NULL) -{ - - /* some effects like to have us get their rect before opening them */ - - ERect *rect; - pPlugin->getRect(&rect); - - /* window initialization */ - - Rect wRect; - - wRect.top = rect->top; - wRect.left = rect->left; - wRect.bottom = rect->bottom; - wRect.right = rect->right; - - int winclass = kDocumentWindowClass; - int winattr = kWindowStandardHandlerAttribute | - kWindowCloseBoxAttribute | - kWindowCompositingAttribute | - kWindowAsyncDragAttribute; - - // winattr &= GetAvailableWindowAttributes(winclass); // make sure that the window will open - - OSStatus status = CreateNewWindow(winclass, winattr, &wRect, &carbonWindow); - if (status != noErr) { - gLog("[pluginWindowMac] Unable to create window! Status=%d\n", (int) status); - return; - } - else - gLog("[pluginWindowMac] created window=%p\n", (void*)carbonWindow); - - /* install event handler, called when window is closed */ - - static EventTypeSpec eventTypes[] = { - { kEventClassWindow, kEventWindowClose }, - { kEventClassWindow, kEventWindowClosed } - }; - InstallWindowEventHandler(carbonWindow, windowHandler, GetEventTypeCount(eventTypes), eventTypes, this, NULL); - - /* open window, center it, show it and start the handler */ - - pPlugin->openGui((void*)carbonWindow); - RepositionWindow(carbonWindow, NULL, kWindowCenterOnMainScreen); - ShowWindow(carbonWindow); - open = true; -} - - - -/* ------------------------------------------------------------------ */ - - -gdPluginWindowGUImac::~gdPluginWindowGUImac() { - gLog("[pluginWindowMac] [[[ destructor ]]] gWindow=%p deleted, window=%p deleted\n", (void*)this, (void*)carbonWindow); - pPlugin->closeGui(); - if (open) - DisposeWindow(carbonWindow); -} - -#endif - -#endif // #ifdef WITH_VST diff --git a/src/gd_pluginWindowGUI.h b/src/gd_pluginWindowGUI.h deleted file mode 100644 index 1675e73..0000000 --- a/src/gd_pluginWindowGUI.h +++ /dev/null @@ -1,84 +0,0 @@ - -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_pluginWindowGUI - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifdef WITH_VST - - -#ifndef __GD_PLUGINWINDOW_GUI_H__ -#define __GD_PLUGINWINDOW_GUI_H__ - - -#include -#include -#include "ge_window.h" -#if defined(__APPLE__) - #include -#endif - - -class gdPluginWindowGUI : public gWindow { -private: - - class Plugin *pPlugin; - -public: - - gdPluginWindowGUI(Plugin *pPlugin); - ~gdPluginWindowGUI(); -}; - - -/* ------------------------------------------------------------------ */ - -#if defined(__APPLE__) - -class gdPluginWindowGUImac : public gWindow { - -private: - - static pascal OSStatus windowHandler(EventHandlerCallRef ehc, EventRef e, void *data); - inline pascal OSStatus __wh(EventHandlerCallRef ehc, EventRef e); - - class Plugin *pPlugin; - WindowRef carbonWindow; - bool open; - -public: - - gdPluginWindowGUImac(Plugin *pPlugin); - ~gdPluginWindowGUImac(); -}; - -#endif - - -#endif // include guard - -#endif // #ifdef WITH_VST diff --git a/src/gd_warnings.cpp b/src/gd_warnings.cpp deleted file mode 100644 index 31a5dbc..0000000 --- a/src/gd_warnings.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_warnings - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "gd_warnings.h" - - -void gdAlert(const char *c) { - Fl_Window *modal = new Fl_Window( - (Fl::w() / 2) - 150, - (Fl::h() / 2) - 47, - 300, 90, "Alert"); - modal->set_modal(); - modal->begin(); - gBox *box = new gBox(10, 10, 280, 40, c); - gClick *b = new gClick(210, 60, 80, 20, "Close"); - modal->end(); - box->labelsize(11); - b->callback(__cb_window_closer, (void *)modal); - b->shortcut(FL_Enter); - gu_setFavicon(modal); - modal->show(); -} - - -int gdConfirmWin(const char *title, const char *msg) { - Fl_Window *win = new Fl_Window( - (Fl::w() / 2) - 150, - (Fl::h() / 2) - 47, - 300, 90, title); - win->set_modal(); - win->begin(); - new gBox(10, 10, 280, 40, msg); - gClick *ok = new gClick(212, 62, 80, 20, "Ok"); - gClick *ko = new gClick(124, 62, 80, 20, "Cancel"); - win->end(); - ok->shortcut(FL_Enter); - gu_setFavicon(win); - win->show(); - - /* no callbacks here. readqueue() check the event stack. */ - - int r = 0; - while (true) { - Fl_Widget *o = Fl::readqueue(); - if (!o) Fl::wait(); - else if (o == ok) {r = 1; break;} - else if (o == ko) {r = 0; break;} - } - //delete win; - win->hide(); - return r; -} diff --git a/src/gd_warnings.h b/src/gd_warnings.h deleted file mode 100644 index ca9c633..0000000 --- a/src/gd_warnings.h +++ /dev/null @@ -1,43 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_warnings - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GD_WARNINGS_H -#define GD_WARNINGS_H - -#include -#include -#include -#include "ge_mixed.h" -#include "gui_utils.h" - - -void gdAlert(const char *c); - -int gdConfirmWin(const char *title, const char *msg); - -#endif diff --git a/src/ge_actionChannel.cpp b/src/ge_actionChannel.cpp deleted file mode 100644 index a98d394..0000000 --- a/src/ge_actionChannel.cpp +++ /dev/null @@ -1,676 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionChannel - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "glue.h" -#include "ge_actionChannel.h" -#include "gd_mainWindow.h" -#include "gd_actionEditor.h" -#include "gg_keyboard.h" -#include "conf.h" -#include "channel.h" -#include "sampleChannel.h" -#include "log.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; -extern Conf G_Conf; - - -/* ------------------------------------------------------------------ */ - - -gActionChannel::gActionChannel(int x, int y, gdActionEditor *pParent, SampleChannel *ch) - : gActionWidget(x, y, 200, 40, pParent), ch(ch), selected(NULL) -{ - size(pParent->totalWidth, h()); - - /* add actions when the window opens. Their position is zoom-based; - * each frame is / 2 because we don't care about stereo infos. */ - - for (unsigned i=0; ichan == pParent->chan->index) { - - /* don't show actions > than the grey area */ - - if (recorder::frames.at(i) > G_Mixer.totalFrames) - continue; - - /* skip the killchan actions in a singlepress channel. They cannot be recorded - * in such mode, but they can exist if you change from another mode to singlepress */ - - if (ra->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS) - continue; - - /* also filter out ACTION_KEYREL: it's up to gAction to find the other piece - * (namely frame_b) */ - - if (ra->type & (ACTION_KEYPRESS | ACTION_KILLCHAN)) { - int ax = x+(ra->frame/pParent->zoom); - gAction *a = new gAction( - ax, // x - y+4, // y - h()-8, // h - ra->frame, // frame_a - i, // n. of recordings - pParent, // pointer to the pParent window - ch, // pointer to SampleChannel - false, // record = false: don't record it, we are just displaying it! - ra->type); // type of action - add(a); - } - } - } - } - end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions -} - - -/* ------------------------------------------------------------------ */ - - -gAction *gActionChannel::getSelectedAction() { - for (int i=0; ix(); - int action_w = ((gAction*)child(i))->w(); - if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w) - return (gAction*)child(i); - } - return NULL; -} - - -/* ------------------------------------------------------------------ */ - - -void gActionChannel::updateActions() { - - /* when zooming, don't delete and re-add actions, just MOVE them. This - * function shifts the action by a zoom factor. Those singlepress are - * stretched, as well */ - - gAction *a; - for (int i=0; iframe_a / pParent->zoom); - - if (ch->mode == SINGLE_PRESS) { - int newW = ((a->frame_b - a->frame_a) / pParent->zoom); - if (newW < gAction::MIN_WIDTH) - newW = gAction::MIN_WIDTH; - a->resize(newX, a->y(), newW, a->h()); - } - else - a->resize(newX, a->y(), gAction::MIN_WIDTH, a->h()); - } -} - - -/* ------------------------------------------------------------------ */ - - -void gActionChannel::draw() { - - /* draw basic boundaries (+ beat bars) and hide the unused area. Then - * draw the children (the actions) */ - - baseDraw(); - - /* print label */ - - fl_color(COLOR_BG_1); - fl_font(FL_HELVETICA, 12); - if (active()) - fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much! - else - fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much! - - draw_children(); -} - - -/* ------------------------------------------------------------------ */ - - -int gActionChannel::handle(int e) { - - int ret = Fl_Group::handle(e); - - /* do nothing if the widget is deactivated. It could happen for loopmode - * channels */ - - if (!active()) - return 1; - - switch (e) { - - case FL_DRAG: { - if (selected != NULL) { // if you don't drag an empty area - - /* if onLeftEdge o onRightEdge are true it means that you're resizing - * an action. Otherwise move the widget. */ - - if (selected->onLeftEdge || selected->onRightEdge) { - - /* some checks: a) cannot resize an action < N pixels, b) no beyond zero, - * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */ - - if (selected->onRightEdge) { - - int aw = Fl::event_x()-selected->x(); - int ah = selected->h(); - - if (Fl::event_x() < selected->x()+gAction::MIN_WIDTH) - aw = gAction::MIN_WIDTH; - else - if (Fl::event_x() > pParent->coverX) - aw = pParent->coverX-selected->x(); - - selected->size(aw, ah); - } - else { - - int ax = Fl::event_x(); - int ay = selected->y(); - int aw = selected->x()-Fl::event_x()+selected->w(); - int ah = selected->h(); - - if (Fl::event_x() < x()) { - ax = x(); - aw = selected->w()+selected->x()-x(); - } - else - if (Fl::event_x() > selected->x()+selected->w()-gAction::MIN_WIDTH) { - ax = selected->x()+selected->w()-gAction::MIN_WIDTH; - aw = gAction::MIN_WIDTH; - } - selected->resize(ax, ay, aw, ah); - } - } - - /* move the widget around */ - - else { - int real_x = Fl::event_x() - actionPickPoint; - if (real_x < x()) // don't go beyond the left border - selected->position(x(), selected->y()); - else - if (real_x+selected->w() > pParent->coverX+x()) // don't go beyond the right border - selected->position(pParent->coverX+x()-selected->w(), selected->y()); - else { - if (pParent->gridTool->isOn()) { - int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1; - selected->position(snpx, selected->y()); - } - else - selected->position(real_x, selected->y()); - } - } - redraw(); - } - ret = 1; - break; - } - - case FL_PUSH: { - - if (Fl::event_button1()) { - - /* avoid at all costs two overlapping actions. We use 'selected' because - * the selected action can be reused in FL_DRAG, in case you want to move - * it. */ - - selected = getSelectedAction(); - - if (selected == NULL) { - - /* avoid click on grey area */ - - if (Fl::event_x() >= pParent->coverX) { - ret = 1; - break; - } - - /* snap function, if enabled */ - - int ax = Fl::event_x(); - int fx = (ax - x()) * pParent->zoom; - if (pParent->gridTool->isOn()) { - ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1; - fx = pParent->gridTool->getSnapFrame(ax-x()); - - /* with snap=on an action can fall onto another */ - - if (actionCollides(fx)) { - ret = 1; - break; - } - } - - gAction *a = new gAction( - ax, // x - y()+4, // y - h()-8, // h - fx, // frame_a - recorder::frames.size-1, // n. of actions recorded - pParent, // pParent window pointer - ch, // pointer to SampleChannel - true, // record = true: record it! - pParent->getActionType()); // type of action - add(a); - mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel); // mainWindow update - redraw(); - ret = 1; - } - else { - actionOriginalX = selected->x(); - actionOriginalW = selected->w(); - actionPickPoint = Fl::event_x() - selected->x(); - ret = 1; // for dragging - } - } - else - if (Fl::event_button3()) { - gAction *a = getSelectedAction(); - if (a != NULL) { - a->delAction(); - remove(a); - delete a; - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); - redraw(); - ret = 1; - } - } - break; - } - case FL_RELEASE: { - - if (selected == NULL) { - ret = 1; - break; - } - - /* noChanges = true when you click on an action without doing anything - * (dragging or moving it). */ - - bool noChanges = false; - if (actionOriginalX == selected->x()) - noChanges = true; - if (ch->mode == SINGLE_PRESS && - actionOriginalX+actionOriginalW != selected->x()+selected->w()) - noChanges = false; - - if (noChanges) { - ret = 1; - selected = NULL; - break; - } - - /* step 1: check if the action doesn't overlap with another one. - * In case of overlap the moved action returns to the original X - * value ("actionOriginalX"). */ - - bool overlap = false; - for (int i=0; ix(); - int action_w = ((gAction*)child(i))->w(); - if (ch->mode == SINGLE_PRESS) { - - /* when 2 segments overlap? - * start = the highest value between the two starting points - * end = the lowest value between the two ending points - * if start < end then there's an overlap of end-start pixels. */ - - int start = action_x >= selected->x() ? action_x : selected->x(); - int end = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w(); - if (start < end) { - selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h()); - redraw(); - overlap = true; // one overlap: stop checking - } - } - else { - if (selected->x() == action_x) { - selected->position(actionOriginalX, selected->y()); - redraw(); - overlap = true; // one overlap: stop checking - } - } - } - - /* step 2: no overlap? then update the coordinates of the action, ie - * delete the previous rec and create a new one. - * Anyway the selected action becomes NULL, because when you release - * the mouse button the dragging process ends. */ - - if (!overlap) { - if (pParent->gridTool->isOn()) { - int f = pParent->gridTool->getSnapFrame(selected->absx()); - selected->moveAction(f); - } - else - selected->moveAction(); - } - selected = NULL; - ret = 1; - break; - } - } - - return ret; -} - - -/* ------------------------------------------------------------------ */ - - -bool gActionChannel::actionCollides(int frame) { - - /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't - * overlap the head (frame) of the new one. First the general case, yet. */ - - bool collision = false; - - for (int i=0; iframe_a == frame) - collision = true; - - if (ch->mode == SINGLE_PRESS) { - for (int i=0; iframe_b && frame >= c->frame_a) - collision = true; - } - } - - return collision; -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -const int gAction::MIN_WIDTH = 8; - - -/* ------------------------------------------------------------------ */ - - -/** TODO - index is useless? - * TODO - pass a record::action pointer and let gAction compute values */ - -gAction::gAction(int X, int Y, int H, int frame_a, unsigned index, gdActionEditor *parent, SampleChannel *ch, bool record, char type) -: Fl_Box (X, Y, MIN_WIDTH, H), - selected (false), - index (index), - parent (parent), - ch (ch), - type (type), - frame_a (frame_a), - onRightEdge(false), - onLeftEdge (false) -{ - /* bool 'record' defines how to understand the action. - * record = false: don't record it, just show it. It happens when you - * open the editor with some actions to be shown. - * - * record = true: record it AND show it. It happens when you click on - * an empty area in order to add a new actions. First you record it - * (addAction()) then you show it (FLTK::Draw()) */ - - if (record) - addAction(); - - /* in order to show a singlepress action we must compute the frame_b. We - * do that after the possible recording, otherwise we don't know which - * key_release is associated. */ - - if (ch->mode == SINGLE_PRESS && type == ACTION_KEYPRESS) { - recorder::action *a2 = NULL; - recorder::getNextAction(ch->index, ACTION_KEYREL, frame_a, &a2); - if (a2) { - frame_b = a2->frame; - w((frame_b - frame_a)/parent->zoom); - } - else - gLog("[gActionChannel] frame_b not found! [%d:???]\n", frame_a); - - /* a singlepress action narrower than 8 pixel is useless. So check it. - * Warning: if an action is 8 px narrow, it has no body space to drag - * it. It's up to the user to zoom in and drag it. */ - - if (w() < MIN_WIDTH) - size(MIN_WIDTH, h()); - } -} - - -/* ------------------------------------------------------------------ */ - - -void gAction::draw() { - - int color; - if (selected) /// && gActionChannel !disabled - color = COLOR_BD_1; - else - color = COLOR_BG_2; - - if (ch->mode == SINGLE_PRESS) { - fl_rectf(x(), y(), w(), h(), (Fl_Color) color); - } - else { - if (type == ACTION_KILLCHAN) - fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); - else { - fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); - if (type == ACTION_KEYPRESS) - fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0); - else - if (type == ACTION_KEYREL) - fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0); - } - } - -} - - -/* ------------------------------------------------------------------ */ - - -int gAction::handle(int e) { - - /* ret = 0 sends the event to the parent window. */ - - int ret = 0; - - switch (e) { - - case FL_ENTER: { - selected = true; - ret = 1; - redraw(); - break; - } - case FL_LEAVE: { - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); - selected = false; - ret = 1; - redraw(); - break; - } - case FL_MOVE: { - - /* handling of the two margins, left & right. 4 pixels are good enough */ - - if (ch->mode == SINGLE_PRESS) { - onLeftEdge = false; - onRightEdge = false; - if (Fl::event_x() >= x() && Fl::event_x() < x()+4) { - onLeftEdge = true; - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - } - else - if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) { - onRightEdge = true; - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - } - else - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); - } - } - } - - return ret; -} - - -/* ------------------------------------------------------------------ */ - - -void gAction::addAction() { - - /* always check frame parity */ - - if (frame_a % 2 != 0) - frame_a++; - - /* anatomy of an action - * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is - * a b the right margin, the ACTION_KEYREL. This is the - * theory behind the singleshot.press actions; for any other kind the - * (b) is just a graphical and meaningless point. */ - - if (ch->mode == SINGLE_PRESS) { - recorder::rec(parent->chan->index, ACTION_KEYPRESS, frame_a); - recorder::rec(parent->chan->index, ACTION_KEYREL, frame_a+4096); - //gLog("action added, [%d, %d]\n", frame_a, frame_a+4096); - } - else { - recorder::rec(parent->chan->index, parent->getActionType(), frame_a); - //gLog("action added, [%d]\n", frame_a); - } - - recorder::sortActions(); - - index++; // important! -} - - -/* ------------------------------------------------------------------ */ - - -void gAction::delAction() { - - /* if SINGLE_PRESS you must delete both the keypress and the keyrelease - * actions. */ - - if (ch->mode == SINGLE_PRESS) { - recorder::deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS, false); - recorder::deleteAction(parent->chan->index, frame_b, ACTION_KEYREL, false); - } - else - recorder::deleteAction(parent->chan->index, frame_a, type, false); - - /* restore the initial cursor shape, in case you delete an action and - * the double arrow (for resizing) is displayed */ - - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); -} - - -/* ------------------------------------------------------------------ */ - - -void gAction::moveAction(int frame_a) { - - /* easy one: delete previous action and record the new ones. As usual, - * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame - * value. */ - - delAction(); - - if (frame_a != -1) - this->frame_a = frame_a; - else - this->frame_a = xToFrame_a(); - - - /* always check frame parity */ - - if (this->frame_a % 2 != 0) - this->frame_a++; - - recorder::rec(parent->chan->index, type, this->frame_a); - - if (ch->mode == SINGLE_PRESS) { - frame_b = xToFrame_b(); - recorder::rec(parent->chan->index, ACTION_KEYREL, frame_b); - } - - recorder::sortActions(); -} - - -/* ------------------------------------------------------------------ */ - - -int gAction::absx() { - return x() - parent->ac->x(); -} - - -/* ------------------------------------------------------------------ */ - - -int gAction::xToFrame_a() { - return (absx()) * parent->zoom; -} - - -/* ------------------------------------------------------------------ */ - - -int gAction::xToFrame_b() { - return (absx() + w()) * parent->zoom; -} diff --git a/src/ge_actionChannel.h b/src/ge_actionChannel.h deleted file mode 100644 index bd0fe24..0000000 --- a/src/ge_actionChannel.h +++ /dev/null @@ -1,135 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionChannel - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GE_ACTIONCHANNEL_H -#define GE_ACTIONCHANNEL_H - -#include -#include -#include "ge_actionWidget.h" -#include "gui_utils.h" -#include "mixer.h" -#include "recorder.h" - - -class gAction : public Fl_Box { - -private: - - bool selected; - unsigned index; - class gdActionEditor *parent; // pointer to parent (gActionEditor) - class SampleChannel *ch; - char type; // type of action - -public: - gAction(int x, int y, int h, int frame_a, unsigned index, - gdActionEditor *parent, class SampleChannel *ch, bool record, - char type); - void draw(); - int handle(int e); - void addAction(); - void delAction(); - - /* moveAction - * shift the action on the x-axis and update Recorder. If frame_a != -1 - * use the new frame in input (used while snapping) */ - - void moveAction(int frame_a=-1); - - /* absx - * x() is relative to scrolling position. absx() returns the absolute - * x value of the action, from the leftmost edge. */ - - int absx(); - - /* xToFrame_a,b - * return the real frames of x() position */ - - int xToFrame_a(); - int xToFrame_b(); - - int frame_a; // initial frame (KEYPRESS for singlemode.press) - int frame_b; // terminal frame (KEYREL for singlemode.press, null for others) - - bool onRightEdge; - bool onLeftEdge; - - static const int MIN_WIDTH; -}; - - -/* ------------------------------------------------------------------ */ - - -class gActionChannel : public gActionWidget { - -private: - - class SampleChannel *ch; - - /* getSelectedAction - * get the action under the mouse. NULL if nothing found. */ - - gAction *getSelectedAction(); - - /* selected - * pointer to the selected action. Useful when dragging around. */ - - gAction *selected; - - /* actionOriginalX, actionOriginalW - * x and w of the action, when moved. Useful for checking if the action - * overlaps another one: in that case the moved action returns to - * actionOriginalX (and to actionOriginalW if resized). */ - - int actionOriginalX; - int actionOriginalW; - - /* actionPickPoint - * the precise x point in which the action has been picked with the mouse, - * before a dragging action. */ - - int actionPickPoint; - - - /* actionCollides - * true if an action collides with another. Used while adding new points - * with snap active.*/ - - bool actionCollides(int frame); - -public: - gActionChannel(int x, int y, gdActionEditor *pParent, class SampleChannel *ch); - void draw(); - int handle(int e); - void updateActions(); -}; - - -#endif diff --git a/src/ge_actionWidget.cpp b/src/ge_actionWidget.cpp deleted file mode 100644 index a06eab9..0000000 --- a/src/ge_actionWidget.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionWidget - * - * pParent class of any widget inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "ge_actionWidget.h" -#include "gd_actionEditor.h" -#include "mixer.h" -#include "ge_mixed.h" - - -extern Mixer G_Mixer; - - -gActionWidget::gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent) - : Fl_Group(x, y, w, h), pParent(pParent) {} - - -/* ------------------------------------------------------------------ */ - - -gActionWidget::~gActionWidget() {} - - -/* ------------------------------------------------------------------ */ - - -void gActionWidget::baseDraw(bool clear) { - - /* clear the screen */ - - if (clear) - fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); - - /* draw the container */ - - fl_color(COLOR_BD_0); - fl_rect(x(), y(), w(), h()); - - /* grid drawing, if > 1 */ - - if (pParent->gridTool->getValue() > 1) { - - fl_color(fl_rgb_color(54, 54, 54)); - fl_line_style(FL_DASH, 0, NULL); - - for (int i=0; i<(int) pParent->gridTool->points.size; i++) { - int px = pParent->gridTool->points.at(i)+x()-1; - fl_line(px, y()+1, px, y()+h()-2); - } - fl_line_style(0); - } - - /* bars and beats drawing */ - - fl_color(COLOR_BD_0); - for (int i=0; i<(int) pParent->gridTool->beats.size; i++) { - int px = pParent->gridTool->beats.at(i)+x()-1; - fl_line(px, y()+1, px, y()+h()-2); - } - - fl_color(COLOR_BG_2); - for (int i=0; i<(int) pParent->gridTool->bars.size; i++) { - int px = pParent->gridTool->bars.at(i)+x()-1; - fl_line(px, y()+1, px, y()+h()-2); - } - - /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats - * are 32) */ - - int coverWidth = pParent->totalWidth-pParent->coverX; - if (coverWidth != 0) - fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1); -} diff --git a/src/ge_actionWidget.h b/src/ge_actionWidget.h deleted file mode 100644 index 554f569..0000000 --- a/src/ge_actionWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionWidget - * - * parent class of any widget inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef __GE_ACTIONWIDGET_H__ -#define __GE_ACTIONWIDGET_H__ - -#include -#include -#include "const.h" - - -class gActionWidget : public Fl_Group { - -protected: - class gdActionEditor *pParent; - void baseDraw(bool clear=true); - -public: - virtual void updateActions() = 0; - - gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent); - ~gActionWidget(); -}; - -#endif diff --git a/src/ge_browser.cpp b/src/ge_browser.cpp deleted file mode 100644 index 15f6836..0000000 --- a/src/ge_browser.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gd_browser - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "ge_browser.h" -#include "const.h" -#include "utils.h" -#include "log.h" - - -gBrowser::gBrowser(int x, int y, int w, int h, const char *L) - : Fl_Hold_Browser(x, y, w, h, L) -{ - box(G_BOX); - textsize(11); - textcolor(COLOR_TEXT_0); - selection_color(COLOR_BG_1); - color(COLOR_BG_0); - - this->scrollbar.color(COLOR_BG_0); - this->scrollbar.selection_color(COLOR_BG_1); - this->scrollbar.labelcolor(COLOR_BD_1); - this->scrollbar.slider(G_BOX); - - this->hscrollbar.color(COLOR_BG_0); - this->hscrollbar.selection_color(COLOR_BG_1); - this->hscrollbar.labelcolor(COLOR_BD_1); - this->hscrollbar.slider(G_BOX); -} - - -/* ------------------------------------------------------------------ */ - - -gBrowser::~gBrowser() {} - - -/* ------------------------------------------------------------------ */ - - -void gBrowser::init(const char *init_path) { - - gLog("[gBrowser] init path = '%s'\n", init_path); - - if (init_path == NULL || !gIsDir(init_path)) { -#if defined(__linux__) || defined(__APPLE__) - path_obj->value("/home"); -#elif defined(_WIN32) - - /* SHGetFolderPath is deprecated. We should use SHGetKnownFolderPath - * but that would break compatibility with XP. On Vista, GetFolderPath - * is a wrapper of GetKnownFolderPath, so no problem. */ - - char winRoot[1024]; - SHGetFolderPath(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, 0, winRoot); // si parte dal Desktop - path_obj->value(winRoot); -#endif - gLog("[gBrowser] init_path null or invalid, using default\n"); - } - else - path_obj->value(init_path); - - refresh(); - sort(); -} - - -/* ------------------------------------------------------------------ */ - - -void gBrowser::refresh() { - DIR *dp; - struct dirent *ep; - dp = opendir(path_obj->value()); - if (dp != NULL) { - while ((ep = readdir(dp))) { - - /* skip: - * - "." e ".." - * - hidden files */ - - if (strcmp(ep->d_name, ".") != 0 && strcmp(ep->d_name, "..") != 0) { - if (ep->d_name[0] != '.') { - - /* is it a folder? add square brackets. Is it a file? Append - * a '/' (on Windows seems useless, though) */ - - std::string file = path_obj->value(); - file.insert(file.size(), gGetSlash()); - file += ep->d_name; - - if (gIsDir(file.c_str())) { - char name[PATH_MAX]; - sprintf(name, "@b[%s]", ep->d_name); - add(name); - } - else - if (gIsProject(file.c_str())) { - char name[PATH_MAX]; - sprintf(name, "@i@b%s", ep->d_name); - add(name); - } - else - add(ep->d_name); - } - } - } - closedir(dp); - } - else - gLog("[gBrowser] Couldn't open the directory '%s'\n", path_obj->value()); -} - - -/* ------------------------------------------------------------------ */ - - -void gBrowser::sort() { - for (int t=1; t<=size(); t++) - for (int r=t+1; r<=size(); r++) - if (strcmp(text(t), text(r)) > 0) - swap(t,r); -} - - -/* ------------------------------------------------------------------ */ - - -void gBrowser::up_dir() { - - /* updir = remove last folder from the path. Start from strlen(-1) to - * skip the trailing slash */ - - int i = strlen(path_obj->value())-1; - - /* on Windows an updir from the path "X:\" (3 chars long) must redirect - * to the list of available devices. */ - -#if defined(_WIN32) - if (i <= 3 || !strcmp(path_obj->value(), "All drives")) { - path_obj->value("All drives"); - showDrives(); - return; - } - else { - while (i >= 0) { - if (path_obj->value()[i] == '\\') - break; - i--; - } - - /* delete the last part of the string, from i to len-i, ie everything - * after the "/" */ - - std::string tmp = path_obj->value(); - tmp.erase(i, tmp.size()-i); - - /* if tmp.size == 2 we have something like 'C:'. Add a trailing - * slash */ - - if (tmp.size() == 2) - tmp += "\\"; - - path_obj->value(tmp.c_str()); - refresh(); - } -#elif defined(__linux__) || defined (__APPLE__) - while (i >= 0) { - if (path_obj->value()[i] == '/') - break; - i--; - } - - /* i == 0 means '/', the root dir. It's meaningless to go updir */ - - if (i==0) - path_obj->value("/"); - else { - - /* delete the last part of the string, from i to len-i, ie everything - * after the "/" */ - - std::string tmp = path_obj->value(); - tmp.erase(i, tmp.size()-i); - path_obj->value(tmp.c_str()); - } - refresh(); -#endif -} - - -/* ------------------------------------------------------------------ */ - - -void gBrowser::down_dir(const char *path) { - path_obj->value(path); - refresh(); -} - - -/* ------------------------------------------------------------------ */ - - -const char *gBrowser::get_selected_item() { - - /* click on an empty line */ - - if (text(value()) == NULL) - return NULL; - - selected_item = text(value()); - - /* @ = formatting marks. - * @b = bold, i.e. a directory. Erease '@b[' and ']' */ - - if (selected_item[0] == '@') { - if (selected_item[1] == 'b') { - selected_item.erase(0, 3); - selected_item.erase(selected_item.size()-1, 1); - } - else - if (selected_item[1] == 'i') - selected_item.erase(0, 4); - } - -#if defined(__linux__) || defined(__APPLE__) - - /* add path to file name, to get an absolute path. Avoid double - * slashes like '//' */ - - if (strcmp("/", path_obj->value())) - selected_item.insert(0, "/"); - - selected_item.insert(0, path_obj->value()); - return selected_item.c_str(); -#elif defined(_WIN32) - - /* if path is 'All drives' we are in the devices list and the user - * has clicked on a device such as 'X:\' */ - - if (strcmp(path_obj->value(), "All drives") == 0) - return selected_item.c_str(); - else { - - /* add '\' if the path is like 'X:\' */ - - if (strlen(path_obj->value()) > 3) /// shouln't it be == 3? - selected_item.insert(0, "\\"); - - selected_item.insert(0, path_obj->value()); - return selected_item.c_str(); - } -#endif -} - - -/* ------------------------------------------------------------------ */ - - -#ifdef _WIN32 -void gBrowser::showDrives() { - - /* GetLogicalDriveStrings fills drives like that: - * - * a:\[null]b:\[null]c:\[null]...[null][null] - * - * where [null] stands for \0. */ - - char drives[64]; - char *i = drives; // pointer to 0th element in drives - GetLogicalDriveStrings(64, drives); - - /* code stolen from the web, still unknown. (Jan 09, 2012). */ - - while (*i) { - add(i); - i = &i[strlen(i) + 1]; - } -} - -#endif diff --git a/src/ge_browser.h b/src/ge_browser.h deleted file mode 100644 index 8445319..0000000 --- a/src/ge_browser.h +++ /dev/null @@ -1,68 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_browser - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GE_BROWSER_H -#define GE_BROWSER_H - -#include -#include -#include -#include "ge_mixed.h" - -class gBrowser : public Fl_Hold_Browser { -public: - gBrowser(int x, int y, int w, int h, const char *L=0); - ~gBrowser(); - void init(const char *init_path=NULL); - void refresh(); - void sort(); - void up_dir(); - void down_dir(const char *path); - const char *get_selected_item(); - - /* path_obj - * the actual path*/ - - class gInput *path_obj; - - /* selected_item - * choosen item */ - - std::string selected_item; - -#ifdef _WIN32 -private: - - /* showDrives [WIN32 only] - * lists all the available drivers */ - - void showDrives(); -#endif -}; - -#endif diff --git a/src/ge_channel.cpp b/src/ge_channel.cpp deleted file mode 100644 index 71cfb38..0000000 --- a/src/ge_channel.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_channel - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "ge_channel.h" -#include "ge_sampleChannel.h" -#include "gd_mainWindow.h" -#include "gd_keyGrabber.h" -#include "gd_midiInput.h" -#include "gd_editor.h" -#include "gd_actionEditor.h" -#include "gd_warnings.h" -#include "gd_browser.h" -#include "gd_midiOutput.h" -#include "gg_keyboard.h" -#include "pluginHost.h" -#include "mixer.h" -#include "conf.h" -#include "patch.h" -#include "graphics.h" -#include "channel.h" -#include "wave.h" -#include "sampleChannel.h" -#include "midiChannel.h" -#include "glue.h" -#include "gui_utils.h" - -#ifdef WITH_VST -#include "gd_pluginList.h" -#endif - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch G_Patch; -extern gdMainWindow *mainWin; - - -gChannel::gChannel(int X, int Y, int W, int H, int type) - : Fl_Group(X, Y, W, H, NULL), type(type) {} - - -/* -------------------------------------------------------------------------- */ - - -int gChannel::getColumnIndex() -{ - return ((gColumn*)parent())->getIndex(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannel::blink() -{ - if (gu_getBlinker() > 6) { - mainButton->bgColor0 = COLOR_BG_2; - mainButton->bdColor = COLOR_BD_1; - mainButton->txtColor = COLOR_TEXT_1; - } - else { - mainButton->bgColor0 = COLOR_BG_0; - mainButton->bdColor = COLOR_BD_0; - mainButton->txtColor = COLOR_TEXT_0; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannel::setColorsByStatus(int playStatus, int recStatus) -{ - switch (playStatus) { - case STATUS_OFF: - mainButton->bgColor0 = COLOR_BG_0; - mainButton->bdColor = COLOR_BD_0; - mainButton->txtColor = COLOR_TEXT_0; - break; - case STATUS_PLAY: - mainButton->bgColor0 = COLOR_BG_2; - mainButton->bdColor = COLOR_BD_1; - mainButton->txtColor = COLOR_TEXT_1; - break; - case STATUS_WAIT: - blink(); - break; - case STATUS_ENDING: - mainButton->bgColor0 = COLOR_BD_0; - break; - } - - switch (recStatus) { - case REC_WAITING: - blink(); - break; - case REC_ENDING: - mainButton->bgColor0 = COLOR_BD_0; - break; - } -} - - -/* -------------------------------------------------------------------------- */ - - -int gChannel::handleKey(int e, int key) -{ - int ret; - if (e == FL_KEYDOWN && button->value()) // key already pressed! skip it - ret = 1; - else - if (Fl::event_key() == key && !button->value()) { - button->take_focus(); // move focus to this button - button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state - button->do_callback(); // invoke the button's callback - ret = 1; - } - else - ret = 0; - - if (Fl::event_key() == key) - button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state - - return ret; -} - - - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gStatus::gStatus(int x, int y, int w, int h, SampleChannel *ch, const char *L) -: Fl_Box(x, y, w, h, L), ch(ch) {} - -void gStatus::draw() -{ - fl_rect(x(), y(), w(), h(), COLOR_BD_0); // reset border - fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // reset background - - if (ch != NULL) { - if (ch->status & (STATUS_WAIT | STATUS_ENDING | REC_ENDING | REC_WAITING) || - ch->recStatus & (REC_WAITING | REC_ENDING)) - { - fl_rect(x(), y(), w(), h(), COLOR_BD_1); - } - else - if (ch->status == STATUS_PLAY) - fl_rect(x(), y(), w(), h(), COLOR_BD_1); - else - fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // status empty - - - if (G_Mixer.chanInput == ch) - fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_3); // take in progress - else - if (recorder::active && recorder::canRec(ch)) - fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_4); // action record - - /* equation for the progress bar: - * ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */ - - int pos = ch->getPosition(); - if (pos == -1) - pos = 0; - else - pos = (pos * (w()-1)) / (ch->end - ch->begin); - fl_rectf(x()+1, y()+1, pos, h()-2, COLOR_BG_2); - } -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gModeBox::gModeBox(int x, int y, int w, int h, SampleChannel *ch, const char *L) - : Fl_Menu_Button(x, y, w, h, L), ch(ch) -{ - box(G_BOX); - textsize(11); - textcolor(COLOR_TEXT_0); - color(COLOR_BG_0); - - add("Loop . basic", 0, cb_change_chanmode, (void *)LOOP_BASIC); - add("Loop . once", 0, cb_change_chanmode, (void *)LOOP_ONCE); - add("Loop . once . bar", 0, cb_change_chanmode, (void *)LOOP_ONCE_BAR); - add("Loop . repeat", 0, cb_change_chanmode, (void *)LOOP_REPEAT); - add("Oneshot . basic", 0, cb_change_chanmode, (void *)SINGLE_BASIC); - add("Oneshot . press", 0, cb_change_chanmode, (void *)SINGLE_PRESS); - add("Oneshot . retrig", 0, cb_change_chanmode, (void *)SINGLE_RETRIG); - add("Oneshot . endless", 0, cb_change_chanmode, (void *)SINGLE_ENDLESS); -} - - -/* -------------------------------------------------------------------------- */ - - -void gModeBox::draw() { - fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border - switch (ch->mode) { - case LOOP_BASIC: - fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1); - break; - case LOOP_ONCE: - fl_draw_pixmap(loopOnce_xpm, x()+1, y()+1); - break; - case LOOP_ONCE_BAR: - fl_draw_pixmap(loopOnceBar_xpm, x()+1, y()+1); - break; - case LOOP_REPEAT: - fl_draw_pixmap(loopRepeat_xpm, x()+1, y()+1); - break; - case SINGLE_BASIC: - fl_draw_pixmap(oneshotBasic_xpm, x()+1, y()+1); - break; - case SINGLE_PRESS: - fl_draw_pixmap(oneshotPress_xpm, x()+1, y()+1); - break; - case SINGLE_RETRIG: - fl_draw_pixmap(oneshotRetrig_xpm, x()+1, y()+1); - break; - case SINGLE_ENDLESS: - fl_draw_pixmap(oneshotEndless_xpm, x()+1, y()+1); - break; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gModeBox::cb_change_chanmode(Fl_Widget *v, void *p) { ((gModeBox*)v)->__cb_change_chanmode((intptr_t)p); } - - -/* -------------------------------------------------------------------------- */ - - -void gModeBox::__cb_change_chanmode(int mode) -{ - ch->mode = mode; - - /* what to do when the channel is playing and you change the mode? - * Nothing, since v0.5.3. Just refresh the action editor window, in - * case it's open */ - - gu_refreshActionEditor(); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gMainButton::gMainButton(int x, int y, int w, int h, const char *l) - : gClick(x, y, w, h, l) {} diff --git a/src/ge_channel.h b/src/ge_channel.h deleted file mode 100644 index 113f537..0000000 --- a/src/ge_channel.h +++ /dev/null @@ -1,162 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_channel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_CHANNEL_H -#define GE_CHANNEL_H - - -#include -#include -#include -#include "ge_mixed.h" - - -class gChannel : public Fl_Group -{ -protected: - - /* define some breakpoints for dynamic resize */ - -#ifdef WITH_VST - static const int BREAK_READ_ACTIONS = 212; - static const int BREAK_MODE_BOX = 188; - static const int BREAK_FX = 164; - static const int BREAK_DELTA = 120; -#else - static const int BREAK_READ_ACTIONS = 188; - static const int BREAK_MODE_BOX = 164; - static const int BREAK_FX = 140; - static const int BREAK_DELTA = 96; -#endif - static const int BREAK_UNIT = 24; - - /* blink - * blink button when channel is in wait/ending status. */ - - void blink(); - - /* setColorByStatus - * update colors depending on channel status. */ - - void setColorsByStatus(int playStatus, int recStatus); - - /* handleKey - * method wrapped by virtual handle(int e). */ - - int handleKey(int e, int key); - -public: - - gChannel(int x, int y, int w, int h, int type); - - /* reset - * reset channel to initial status. */ - - virtual void reset() = 0; - - /* update - * update the label of sample button and everything else such as 'R' - * button, key box and so on, according to global values. */ - - virtual void update() = 0; - - /* refresh - * update graphics. */ - - virtual void refresh() = 0; - - /* keypress - * what to do when the corresponding key is pressed. */ - - virtual int keyPress(int event) = 0; - - /* getColumnIndex - * return the numeric index of the column in which this channel is - * located. */ - - int getColumnIndex(); - - class gButton *button; - class gStatus *status; - class gMainButton *mainButton; - class gDial *vol; - class gClick *mute; - class gClick *solo; -#ifdef WITH_VST - class gFxButton *fx; -#endif - - int type; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gStatus : public Fl_Box -{ -public: - gStatus(int X, int Y, int W, int H, class SampleChannel *ch, const char *L=0); - void draw(); - class SampleChannel *ch; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gModeBox : public Fl_Menu_Button -{ -private: - static void cb_change_chanmode(Fl_Widget *v, void *p); - inline void __cb_change_chanmode(int mode); - - class SampleChannel *ch; - -public: - gModeBox(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0); - void draw(); -}; - - -/* -------------------------------------------------------------------------- */ - - -/* gMainButton - * base main button for MIDI and Sample Channels. */ - -class gMainButton : public gClick -{ -public: - gMainButton(int x, int y, int w, int h, const char *l=0); - virtual int handle(int e) = 0; -}; - - -#endif diff --git a/src/ge_column.cpp b/src/ge_column.cpp deleted file mode 100644 index 9acabd6..0000000 --- a/src/ge_column.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_column - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "ge_column.h" -#include "gd_mainWindow.h" -#include "gd_warnings.h" -#include "gg_keyboard.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" -#include "ge_midiChannel.h" -#include "mixer.h" -#include "conf.h" -#include "log.h" -#include "patch.h" -#include "glue.h" -#include "channel.h" -#include "sampleChannel.h" -#include "midiChannel.h" - -#ifdef WITH_VST - #include "gd_pluginList.h" -#endif - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch G_Patch; -extern gdMainWindow *mainWin; - - -gColumn::gColumn(int X, int Y, int W, int H, int index, gKeyboard *parent) - : Fl_Group(X, Y, W, H), parent(parent), index(index) -{ - /* gColumn does a bit of a mess: we pass a pointer to its parent (gKeyboard) and - the gColumn itself deals with the creation of another widget, outside gColumn - and inside gKeyboard, which handles the vertical resize bar (gResizerBar). - The resizer cannot stay inside gColumn: it needs a broader view on the other - side widgets. The view can be obtained from gKeyboard only (the upper level). - Unfortunately, parent() can be NULL: at this point (i.e the constructor) - gColumn is still detached from any parent. We use a custom gKeyboard *parent - instead. */ - - begin(); - addChannelBtn = new gClick(x(), y(), w(), 20, "Add new channel"); - end(); - - resizer = new gResizerBar(x()+w(), y(), 16, h(), false); - resizer->setMinSize(140); - parent->add(resizer); - - addChannelBtn->callback(cb_addChannel, (void*)this); -} - - -/* -------------------------------------------------------------------------- */ - - -gColumn::~gColumn() -{ - /* FIXME - this could actually cause a memory leak. resizer is - just removed, not deleted. But we cannot delete it right now. */ - - parent->remove(resizer); -} - - -/* -------------------------------------------------------------------------- */ - - -int gColumn::handle(int e) -{ - switch (e) { - case FL_DND_ENTER: // return(1) for these events to 'accept' dnd - case FL_DND_DRAG: - case FL_DND_RELEASE: { - return 1; - } - case FL_PASTE: { // handle actual drop (paste) operation - gVector paths; - gSplit(Fl::event_text(), "\n", &paths); - bool fails = false; - int result = 0; - for (unsigned i=0; iguiChannel); - fails = true; - } - } - if (fails) { - if (paths.size > 1) - gdAlert("Some files were not loaded successfully."); - else - parent->printChannelMessage(result); - } - return 1; - } - } - - /* we return fl_Group::handle only if none of the cases above are fired. That - is because we don't want to propagate a dnd drop to all the sub widgets. */ - - return Fl_Group::handle(e); -} - - -/* -------------------------------------------------------------------------- */ - - -void gColumn::resize(int X, int Y, int W, int H) -{ - /* resize all children */ - - int ch = children(); - for (int i=0; iresize(X, Y + (i * (c->h() + 4)), W, c->h()); - } - - /* resize group itself */ - - x(X); y(Y); w(W); h(H); - - /* resize resizerBar */ - - resizer->size(16, H); -} - - -/* -------------------------------------------------------------------------- */ - - -void gColumn::refreshChannels() -{ - for (int i=1; irefresh(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gColumn::draw() -{ - fl_color(fl_rgb_color(27, 27, 27)); - fl_rectf(x(), y(), w(), h()); - - /* call draw and then redraw in order to avoid channel corruption when - scrolling horizontally */ - - for (int i=0; idraw(); - child(i)->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gColumn::cb_addChannel(Fl_Widget *v, void *p) { ((gColumn*)p)->__cb_addChannel(); } - - -/* -------------------------------------------------------------------------- */ - - -gChannel *gColumn::addChannel(class Channel *ch) -{ - gChannel *gch = NULL; - - if (ch->type == CHANNEL_SAMPLE) - gch = (gSampleChannel*) new gSampleChannel( - x(), - y() + children() * 24, - 600, // (1) see notes below - 20, - (SampleChannel*) ch); - else - gch = (gMidiChannel*) new gMidiChannel( - x(), - y() + children() * 24, - w(), - 20, - (MidiChannel*) ch); - - /* (1) we create a new sample channel with a fake width, instead of w() (i.e. - the column width), in case the column is too narrow to display all widgets. - This workaround prevents the widgets to disappear if they have an initial - negative width. MidiChannel does not need such hack because it already fits - nicely in a collapsed column. */ - - add(gch); - resize(x(), y(), w(), (children() * 24) + 66); // evil space for drag n drop - gch->redraw(); // avoid corruption - parent->redraw(); // redraw Keyboard - return gch; -} - - -/* -------------------------------------------------------------------------- */ - - -void gColumn::deleteChannel(gChannel *gch) -{ - gch->hide(); - remove(gch); - delete gch; - - /* reposition all other channels and resize this group */ - /** TODO - * reposition is useless when called by gColumn::clear(). Add a new - * parameter to skip the operation */ - - for (int i=0; iposition(gch->x(), y()+(i*24)); - } - size(w(), children() * 24 + 66); // evil space for drag n drop - redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gColumn::__cb_addChannel() -{ - gLog("[gColumn::__cb_addChannel] index = %d\n", index); - int type = openTypeMenu(); - if (type) - glue_addChannel(index, type); -} - - -/* -------------------------------------------------------------------------- */ - - -int gColumn::openTypeMenu() -{ - Fl_Menu_Item rclick_menu[] = { - {"Sample channel"}, - {"MIDI channel"}, - {0} - }; - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(11); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return 0; - - if (strcmp(m->label(), "Sample channel") == 0) - return CHANNEL_SAMPLE; - if (strcmp(m->label(), "MIDI channel") == 0) - return CHANNEL_MIDI; - return 0; -} - - -/* -------------------------------------------------------------------------- */ - - -void gColumn::clear(bool full) -{ - if (full) - Fl_Group::clear(); - else { - while (children() >= 2) { // skip "add new channel" btn - int i = children()-1; - deleteChannel((gChannel*)child(i)); - } - } -} diff --git a/src/ge_column.h b/src/ge_column.h deleted file mode 100644 index 84ebc8a..0000000 --- a/src/ge_column.h +++ /dev/null @@ -1,98 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_column - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_COLUMN_H -#define GE_COLUMN_H - - -#include -#include - - -class gColumn : public Fl_Group -{ -private: - - static void cb_addChannel (Fl_Widget *v, void *p); - inline void __cb_addChannel(); - - int openTypeMenu(); - - class gClick *addChannelBtn; - class gResizerBar *resizer; - class gKeyboard *parent; - - int index; - -public: - - gColumn(int x, int y, int w, int h, int index, class gKeyboard *parent); - ~gColumn(); - - /* addChannel - * add a new channel in this column and set the internal pointer - * to channel to 'ch'. */ - - class gChannel *addChannel(class Channel *ch); - - /* handle */ - - int handle(int e); - - /* resize - * custom resize behavior. */ - - void resize(int x, int y, int w, int h); - - /* deleteChannel - * remove the channel 'gch' from this column. */ - - void deleteChannel(gChannel *gch); - - /* refreshChannels - * update channels' graphical statues. Called on each GUI cycle. */ - - void refreshChannels(); - - /* clear - * remove all channels from the column. If full==true, delete also the - * "add new channel" button. This method ovverrides the inherited one - * from Fl_Group. */ - - void clear(bool full=false); - - void draw(); - - inline int getIndex() { return index; } - inline void setIndex(int i) { index = i; } - inline bool isEmpty() { return children() == 1; } -}; - - -#endif diff --git a/src/ge_envelopeChannel.cpp b/src/ge_envelopeChannel.cpp deleted file mode 100644 index 442f6ba..0000000 --- a/src/ge_envelopeChannel.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_envelopeWidget - * - * Parent class of any envelope controller, from volume to VST parameter - * automations. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "ge_envelopeChannel.h" -#include "gd_actionEditor.h" -#include "gd_mainWindow.h" -#include "gg_keyboard.h" -#include "channel.h" -#include "recorder.h" -#include "mixer.h" - - -extern Mixer G_Mixer; -extern gdMainWindow *mainWin; - - -gEnvelopeChannel::gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l) - : gActionWidget(x, y, 200, 80, pParent), l(l), type(type), range(range), - selectedPoint(-1), draggedPoint(-1) -{ - size(pParent->totalWidth, h()); -} - - -/* ------------------------------------------------------------------ */ - - -gEnvelopeChannel::~gEnvelopeChannel() { - clearPoints(); -} - - -/* ------------------------------------------------------------------ */ - - -void gEnvelopeChannel::addPoint(int frame, int iValue, float fValue, int px, int py) { - point p; - p.frame = frame; - p.iValue = iValue; - p.fValue = fValue; - p.x = px; - p.y = py; - points.add(p); -} - - -/* ------------------------------------------------------------------ */ - - -void gEnvelopeChannel::updateActions() { - for (unsigned i=0; izoom; -} - - -/* ------------------------------------------------------------------ */ - - -void gEnvelopeChannel::draw() { - - baseDraw(); - - /* print label */ - - fl_color(COLOR_BG_1); - fl_font(FL_HELVETICA, 12); - fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT)); - - int pxOld = x()-3; - int pyOld = y()+1; - int pxNew = 0; - int pyNew = 0; - - fl_color(COLOR_BG_2); - - for (unsigned i=0; i 0) - fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3); - - pxOld = pxNew; - pyOld = pyNew; - } -} - - -/* ------------------------------------------------------------------ */ - - -int gEnvelopeChannel::handle(int e) { - - /* Adding an action: no further checks required, just record it on frame - * mx*pParent->zoom. Deleting action is trickier: find the active - * point and derive from it the corresponding frame. */ - - int ret = 0; - int mx = Fl::event_x()-x(); // mouse x - int my = Fl::event_y()-y(); // mouse y - - switch (e) { - - case FL_ENTER: { - ret = 1; - break; - } - - case FL_MOVE: { - selectedPoint = getSelectedPoint(); - redraw(); - ret = 1; - break; - } - - case FL_LEAVE: { - draggedPoint = -1; - selectedPoint = -1; - redraw(); - ret = 1; - break; - } - - case FL_PUSH: { - - /* left click on point: drag - * right click on point: delete - * left click on void: add */ - - if (Fl::event_button1()) { - - if (selectedPoint != -1) { - draggedPoint = selectedPoint; - } - else { - - /* top & border fix */ - - if (my > h()-8) my = h()-8; - if (mx > pParent->coverX-x()) mx = pParent->coverX-x(); - - if (range == RANGE_FLOAT) { - - /* if this is the first point ever, add other two points at the beginning - * and the end of the range */ - - if (points.size == 0) { - addPoint(0, 0, 1.0f, 0, 1); - recorder::rec(pParent->chan->index, type, 0, 0, 1.0f); - addPoint(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1); - recorder::rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f); - } - - /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */ - - int frame = mx * pParent->zoom; - float value = (my - h() + 8) / (float) (1 - h() + 8); - addPoint(frame, 0, value, mx, my); - recorder::rec(pParent->chan->index, type, frame, 0, value); - recorder::sortActions(); - sortPoints(); - } - else { - /// TODO - } - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow - redraw(); - } - } - else { - - /* right click on point 0 or point size-1 deletes the entire envelope */ - - if (selectedPoint != -1) { - if (selectedPoint == 0 || (unsigned) selectedPoint == points.size-1) { - recorder::clearAction(pParent->chan->index, type); - points.clear(); - } - else { - recorder::deleteAction(pParent->chan->index, points.at(selectedPoint).frame, type, false); - recorder::sortActions(); - points.del(selectedPoint); - } - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow - redraw(); - } - } - - ret = 1; - break; - } - - case FL_RELEASE: { - if (draggedPoint != -1) { - - if (points.at(draggedPoint).x == previousXPoint) { - //gLog("nothing to do\n"); - } - else { - int newFrame = points.at(draggedPoint).x * pParent->zoom; - - /* x edge correction */ - - if (newFrame < 0) - newFrame = 0; - else if (newFrame > G_Mixer.totalFrames) - newFrame = G_Mixer.totalFrames; - - /* vertical line check */ - - int vp = verticalPoint(points.at(draggedPoint)); - if (vp == 1) newFrame -= 256; - else if (vp == -1) newFrame += 256; - - /* delete previous point and record a new one */ - - recorder::deleteAction(pParent->chan->index, points.at(draggedPoint).frame, type, false); - - if (range == RANGE_FLOAT) { - float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8); - recorder::rec(pParent->chan->index, type, newFrame, 0, value); - } - else { - /// TODO - } - - recorder::sortActions(); - points.at(draggedPoint).frame = newFrame; - draggedPoint = -1; - selectedPoint = -1; - } - } - ret = 1; - break; - } - - case FL_DRAG: { - - if (draggedPoint != -1) { - - /* y constraint */ - - if (my > h()-8) - points.at(draggedPoint).y = h()-8; - else - if (my < 1) - points.at(draggedPoint).y = 1; - else - points.at(draggedPoint).y = my; - - /* x constraint - * constrain the point between two ends (leftBorder-point, point-point, - * point-rightBorder). First & last points cannot be shifted on x */ - - if (draggedPoint == 0) - points.at(draggedPoint).x = x()-8; - else - if ((unsigned) draggedPoint == points.size-1) - points.at(draggedPoint).x = pParent->coverX; - else { - int prevPoint = points.at(draggedPoint-1).x; - int nextPoint = points.at(draggedPoint+1).x; - if (mx <= prevPoint) - points.at(draggedPoint).x = prevPoint; - else - if (mx >= nextPoint) - points.at(draggedPoint).x = nextPoint; - //else - // points.at(draggedPoint).x = mx; - else { - if (pParent->gridTool->isOn()) - points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1; - else - points.at(draggedPoint).x = mx; - } - } - redraw(); - } - - ret = 1; - break; - } - } - - return ret; -} - - -/* ------------------------------------------------------------------ */ - - -int gEnvelopeChannel::verticalPoint(const point &p) { - for (unsigned i=0; i points.at(i).x) - points.swap(j, i); -} - - -/* ------------------------------------------------------------------ */ - - -int gEnvelopeChannel::getSelectedPoint() { - - /* point is a 7x7 dot */ - - for (unsigned i=0; i= points.at(i).x+x()-4 && - Fl::event_x() <= points.at(i).x+x()+4 && - Fl::event_y() >= points.at(i).y+y() && - Fl::event_y() <= points.at(i).y+y()+7) - return i; - } - return -1; -} - - -/* ------------------------------------------------------------------ */ - - -void gEnvelopeChannel::fill() { - points.clear(); - for (unsigned i=0; itype == type && a->chan == pParent->chan->index) { - if (range == RANGE_FLOAT) - addPoint( - a->frame, // frame - 0, // int value (unused) - a->fValue, // float value - a->frame / pParent->zoom, // x - ((1-h()+8)*a->fValue)+h()-8); // y = (b-a)x + a (line between two points) - // else: TODO - } - } - -} diff --git a/src/ge_envelopeChannel.h b/src/ge_envelopeChannel.h deleted file mode 100644 index 7a1aa24..0000000 --- a/src/ge_envelopeChannel.h +++ /dev/null @@ -1,115 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_envelopeWidget - * - * parent class of any envelope controller, from volume to VST parameter - * automations. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef __GE_ENVELOPECHANNEL_H__ -#define __GE_ENVELOPECHANNEL_H__ - -#include -#include -#include "ge_actionWidget.h" -#include "utils.h" - - -class gEnvelopeChannel : public gActionWidget { - - const char *l; // internal label - int type; // type of action - int range; - - /* point - * a single dot in the graph. x = relative frame, y = relative value */ - - struct point { - int frame; - int iValue; - float fValue; - int x; - int y; - }; - - /* points - * array of points, filled by fillPoints() */ - - gVector points; - - /* selectedPoint - * which point we are selecting? */ - - int selectedPoint; - - /* draggedPoint - * which point we are dragging? */ - - int draggedPoint; - - /* previousXPoint - * x coordinate of point at time t-1. Used to check effective shifts */ - - int previousXPoint; - - void draw(); - - int handle(int e); - - int getSelectedPoint(); - - void sortPoints(); - - /* verticalPoint - * check if two points form a vertical line. In that case the frame value - * would be the same and recorder would go crazy, so shift by a small value - * of frames to create a minimal fadein/fadeout level. Return 0: no - * vertical points; return 1: vertical with the next one, return -1: vertical - * with the previous one. */ - - int verticalPoint(const point &p); - -public: - gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l); - ~gEnvelopeChannel(); - - /* addPoint - * add a point made of frame+value to internal points[]. */ - - void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1); - - void updateActions(); - - /* fill - * parse recorder's stack and fill the widget with points. It's up to - * the caller to call this method as initialization. */ - - void fill(); - - inline void clearPoints() { points.clear(); } -}; - -#endif diff --git a/src/ge_midiChannel.cpp b/src/ge_midiChannel.cpp deleted file mode 100644 index 33cd991..0000000 --- a/src/ge_midiChannel.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_midiChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "ge_midiChannel.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" -#include "gd_mainWindow.h" -#include "gd_keyGrabber.h" -#include "gd_midiInput.h" -#include "gd_editor.h" -#include "gd_actionEditor.h" -#include "gd_warnings.h" -#include "gd_browser.h" -#include "gd_keyGrabber.h" -#include "gd_midiOutput.h" -#include "gg_keyboard.h" -#include "pluginHost.h" -#include "mixer.h" -#include "conf.h" -#include "patch.h" -#include "graphics.h" -#include "channel.h" -#include "wave.h" -#include "sampleChannel.h" -#include "midiChannel.h" -#include "glue.h" -#include "gui_utils.h" - -#ifdef WITH_VST -#include "gd_pluginList.h" -#endif - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch G_Patch; -extern gdMainWindow *mainWin; - - -gMidiChannel::gMidiChannel(int X, int Y, int W, int H, class MidiChannel *ch) - : gChannel(X, Y, W, H, CHANNEL_MIDI), ch(ch) -{ - begin(); - -#if defined(WITH_VST) - int delta = 120; // (5 widgets * 20) + (5 paddings * 4) -#else - int delta = 96; // (4 widgets * 20) + (4 paddings * 4) -#endif - - button = new gButton(x(), y(), 20, 20); - mainButton = new gMidiMainButton(button->x()+button->w()+4, y(), w() - delta, 20, "-- MIDI --"); - mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); - solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); -#if defined(WITH_VST) - fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); - vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); -#else - vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); -#endif - - end(); - - resizable(mainButton); - - update(); - - button->callback(cb_button, (void*)this); - button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease - -#ifdef WITH_VST - fx->callback(cb_openFxWindow, (void*)this); -#endif - - mute->type(FL_TOGGLE_BUTTON); - mute->callback(cb_mute, (void*)this); - - solo->type(FL_TOGGLE_BUTTON); - solo->callback(cb_solo, (void*)this); - - mainButton->callback(cb_openMenu, (void*)this); - vol->callback(cb_changeVol, (void*)this); - - ch->guiChannel = this; -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::cb_button (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_button(); } -void gMidiChannel::cb_mute (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_mute(); } -void gMidiChannel::cb_solo (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_solo(); } -void gMidiChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openMenu(); } -void gMidiChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_changeVol(); } -#ifdef WITH_VST -void gMidiChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openFxWindow(); } -#endif - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_mute() -{ - glue_setMute(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_solo() -{ - solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_changeVol() -{ - glue_setChanVol(ch, vol->value()); -} - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST -void gMidiChannel::__cb_openFxWindow() -{ - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST); -} -#endif - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_button() -{ - if (button->value()) - glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_openMenu() -{ - Fl_Menu_Item rclick_menu[] = { - {"Edit actions..."}, // 0 - {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1 - {"All"}, // 2 - {0}, // 3 - {"Setup keyboard input..."}, // 5 - {"Setup MIDI input..."}, // 6 - {"Setup MIDI output..."}, // 7 - {"Delete channel"}, // 8 - {0} - }; - - /* no 'clear actions' if there are no actions */ - - if (!ch->hasActions) - rclick_menu[1].deactivate(); - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(11); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return; - - if (strcmp(m->label(), "Delete channel") == 0) { - if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) - return; - glue_deleteChannel(ch); - return; - } - - if (strcmp(m->label(), "Setup keyboard input...") == 0) { - gu_openSubWindow(mainWin, new gdKeyGrabber(ch), 0); - //new gdKeyGrabber(ch); - return; - } - - if (strcmp(m->label(), "All") == 0) { - if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) - return; - recorder::clearChan(ch->index); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Edit actions...") == 0) { - gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); - return; - } - - if (strcmp(m->label(), "Setup MIDI input...") == 0) { - gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0); - return; - } - - if (strcmp(m->label(), "Setup MIDI output...") == 0) { - //gu_openSubWindow(mainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0); - gu_openSubWindow(mainWin, new gdMidiOutputMidiCh(ch), 0); - return; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::refresh() -{ - setColorsByStatus(ch->status, ch->recStatus); - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::reset() -{ - mainButton->bgColor0 = COLOR_BG_0; - mainButton->bdColor = COLOR_BD_0; - mainButton->txtColor = COLOR_TEXT_0; - mainButton->label("-- MIDI --"); - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::update() -{ - if (ch->midiOut) { - char tmp[32]; - sprintf(tmp, "-- MIDI (channel %d) --", ch->midiOutChan+1); - mainButton->copy_label(tmp); - } - else - mainButton->label("-- MIDI --"); - - vol->value(ch->volume); - mute->value(ch->mute); - solo->value(ch->solo); - -#ifdef WITH_VST - fx->full = ch->plugins.size > 0; -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::resize(int X, int Y, int W, int H) -{ - gChannel::resize(X, Y, W, H); - - /* this stuff makes sense only with FX button available. Do nothing - * otherwise */ - -#ifdef WITH_VST - if (w() < BREAK_FX) { - fx->hide(); - - mainButton->size(w() - (BREAK_DELTA - BREAK_UNIT), mainButton->h()); - } - else { - fx->show(); - mainButton->size(w() - BREAK_DELTA, mainButton->h()); - } - mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - solo->resize(mute->x()+mute->w()+4, y(), 20, 20); - - gChannel::init_sizes(); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -int gMidiChannel::keyPress(int e) -{ - return handleKey(e, ch->key); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gMidiMainButton::gMidiMainButton(int x, int y, int w, int h, const char *l) - : gMainButton(x, y, w, h, l) {} - - -/* -------------------------------------------------------------------------- */ - - -int gMidiMainButton::handle(int e) -{ - // MIDI drag-n-drop does nothing so far. - return gClick::handle(e); -} diff --git a/src/ge_midiChannel.h b/src/ge_midiChannel.h deleted file mode 100644 index af07fb3..0000000 --- a/src/ge_midiChannel.h +++ /dev/null @@ -1,89 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_midiChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_MIDI_CHANNEL_H -#define GE_MIDI_CHANNEL_H - - -#include -#include -#include -#include "ge_channel.h" -#include "ge_mixed.h" - - -class gMidiChannel : public gChannel -{ -private: - - static void cb_button (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_openMenu (Fl_Widget *v, void *p); - static void cb_changeVol (Fl_Widget *v, void *p); -#ifdef WITH_VST - static void cb_openFxWindow (Fl_Widget *v, void *p); -#endif - - inline void __cb_mute (); - inline void __cb_solo (); - inline void __cb_changeVol (); - inline void __cb_button (); - inline void __cb_openMenu (); - inline void __cb_readActions (); -#ifdef WITH_VST - inline void __cb_openFxWindow(); -#endif - -public: - - gMidiChannel(int x, int y, int w, int h, class MidiChannel *ch); - - void reset (); - void update (); - void refresh (); - int keyPress(int event); - void resize (int x, int y, int w, int h); - - class MidiChannel *ch; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gMidiMainButton : public gMainButton -{ -public: - gMidiMainButton(int x, int y, int w, int h, const char *l=0); - int handle(int e); -}; - - -#endif diff --git a/src/ge_midiIoTools.cpp b/src/ge_midiIoTools.cpp deleted file mode 100644 index da9b873..0000000 --- a/src/ge_midiIoTools.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_midiIoTools - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "ge_midiIoTools.h" -#include "ge_mixed.h" - - -gLearner::gLearner(int X, int Y, int W, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param) - : Fl_Group(X, Y, W, 20), - callback(cb), - param (param) -{ - begin(); - text = new gBox(x(), y(), 156, 20, l); - value = new gClick(text->x()+text->w()+4, y(), 80, 20, "(not set)"); - button = new gButton(value->x()+value->w()+4, y(), 40, 20, "learn"); - end(); - - text->box(G_BOX); - text->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); - - value->box(G_BOX); - value->callback(cb_value, (void*)this); - value->when(FL_WHEN_RELEASE); - updateValue(); - - button->type(FL_TOGGLE_BUTTON); - button->callback(cb_button, (void*)this); -} - - -/* -------------------------------------------------------------------------- */ - - -void gLearner::updateValue() { - char buf[16]; - if (*param != 0x0) - snprintf(buf, 9, "0x%X", *param); - else - snprintf(buf, 16, "(not set)"); - value->copy_label(buf); - button->value(0); -} - - -/* -------------------------------------------------------------------------- */ - - -void gLearner::cb_button(Fl_Widget *v, void *p) { ((gLearner*)p)->__cb_button(); } -void gLearner::cb_value(Fl_Widget *v, void *p) { ((gLearner*)p)->__cb_value(); } - - -/* -------------------------------------------------------------------------- */ - - -void gLearner::__cb_value() { - if (Fl::event_button() == FL_RIGHT_MOUSE) { - *param = 0x0; - updateValue(); - } - /// TODO - elif (LEFT_MOUSE) : insert values by hand -} - - -/* -------------------------------------------------------------------------- */ - - -/* FIXME - do not malloc on each callback! do it on the constructor! */ - -void gLearner::__cb_button() { - if (button->value() == 1) { - cbData *data = (cbData*) malloc(sizeof(cbData)); - data->window = (gdMidiInput*) parent(); // parent = gdMidiGrabberChannel - data->learner = this; - kernelMidi::startMidiLearn(callback, (void*)data); - } - else - kernelMidi::stopMidiLearn(); -} diff --git a/src/ge_midiIoTools.h b/src/ge_midiIoTools.h deleted file mode 100644 index bd52756..0000000 --- a/src/ge_midiIoTools.h +++ /dev/null @@ -1,92 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_midiIoTools - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_LEARNER_H -#define GE_LEARNER_H - - -#include -#include -#include "gd_midiInput.h" -#include "kernelMidi.h" - - -class gLearner : public Fl_Group -{ -private: - - /* callback - * cb to pass to kernelMidi. Requires two parameters: - * uint32_t msg - MIDI message - * void *data - extra data */ - - kernelMidi::cb_midiLearn *callback; - - class gBox *text; - class gClick *value; - class gButton *button; - - static void cb_button(Fl_Widget *v, void *p); - static void cb_value (Fl_Widget *v, void *p); - inline void __cb_button(); - inline void __cb_value(); - -public: - - /* param - * pointer to ch->midiIn[value] */ - - uint32_t *param; - - gLearner(int x, int y, int w, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param); - - void updateValue(); -}; - - -/* -------------------------------------------------------------------------- */ - - -/* cbData - * struct we pass to kernelMidi as extra parameter. Local scope made - * with unnamed namespace. Infos: - * http://stackoverflow.com/questions/4422507/superiority-of-unnamed-namespace-over-static */ - -/* TODO - instead of the unnamed namespace, why don't we make the struct as a -(static) member of gLearner? */ - -namespace { - struct cbData { - gdMidiInput *window; - gLearner *learner; - }; -} - - -#endif diff --git a/src/ge_mixed.cpp b/src/ge_mixed.cpp deleted file mode 100644 index 81200c8..0000000 --- a/src/ge_mixed.cpp +++ /dev/null @@ -1,666 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_mixed - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include "ge_mixed.h" -#include "gd_mainWindow.h" -#include "const.h" -#include "mixer.h" -#include "graphics.h" -#include "recorder.h" -#include "gui_utils.h" -#include "channel.h" -#include "sampleChannel.h" - - -extern Mixer G_Mixer; -extern unsigned G_beats; -extern bool G_audio_status; -extern gdMainWindow *mainWin; - - -void __cb_window_closer(Fl_Widget *v, void *p) -{ - delete (Fl_Window*)p; -} - - -/* -------------------------------------------------------------------------- */ - - -gButton::gButton(int X, int Y, int W, int H, const char *L, const char **imgOff, const char **imgOn) - : gClick(X, Y, W, H, L, imgOff, imgOn) {} - - -/* -------------------------------------------------------------------------- */ - - -gClick::gClick(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn) -: gBaseButton(x, y, w, h, L), - imgOff(imgOff), - imgOn(imgOn), - bgColor0(COLOR_BG_0), - bgColor1(COLOR_BG_1), - bdColor(COLOR_BD_0), - txtColor(COLOR_TEXT_0) {} - -void gClick::draw() -{ - if (!active()) txtColor = bdColor; - else txtColor = COLOR_TEXT_0; - - fl_rect(x(), y(), w(), h(), bdColor); // borders - if (value()) { // -- clicked - if (imgOn != NULL) - fl_draw_pixmap(imgOn, x()+1, y()+1); - else - fl_rectf(x(), y(), w(), h(), bgColor1); // covers the border - } - else { // -- not clicked - fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor0); // bg inside the border - if (imgOff != NULL) - fl_draw_pixmap(imgOff, x()+1, y()+1); - } - if (!active()) - fl_color(FL_INACTIVE_COLOR); - - fl_color(txtColor); - fl_font(FL_HELVETICA, 11); - fl_draw(label(), x(), y(), w(), h(), FL_ALIGN_CENTER); -} - - -/* -------------------------------------------------------------------------- */ - - -gClickRepeat::gClickRepeat(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn) -: Fl_Repeat_Button(x, y, w, h, L), imgOff(imgOff), imgOn(imgOn) {} - -void gClickRepeat::draw() -{ - if (value()) { // -- clicked - fl_rectf(x(), y(), w(), h(), COLOR_BG_1); // bg - if (imgOn != NULL) - fl_draw_pixmap(imgOn, x()+1, y()+1); - } - else { // -- not clicked - fl_rectf(x(), y(), w(), h(), COLOR_BG_0); // bg - fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border - if (imgOff != NULL) - fl_draw_pixmap(imgOff, x()+1, y()+1); - } - if (!active()) - fl_color(FL_INACTIVE_COLOR); - - fl_color(COLOR_TEXT_0); - fl_font(FL_HELVETICA, 11); - fl_draw(label(), x(), y(), w(), h(), FL_ALIGN_CENTER); -} - - -/* -------------------------------------------------------------------------- */ - - -gInput::gInput(int x, int y, int w, int h, const char *L) -: Fl_Input(x, y, w, h, L) -{ - //Fl::set_boxtype(G_BOX, gDrawBox, 1, 1, 2, 2); - box(G_BOX); - labelsize(11); - labelcolor(COLOR_TEXT_0); - color(COLOR_BG_DARK); - textcolor(COLOR_TEXT_0); - cursor_color(COLOR_TEXT_0); - selection_color(COLOR_BD_0); - textsize(11); - -} - - -/* -------------------------------------------------------------------------- */ - - -gDial::gDial(int x, int y, int w, int h, const char *L) -: Fl_Dial(x, y, w, h, L) -{ - labelsize(11); - labelcolor(COLOR_TEXT_0); - align(FL_ALIGN_LEFT); - type(FL_FILL_DIAL); - angles(0, 360); - color(COLOR_BG_0); // background - selection_color(COLOR_BG_1); // selection -} - -void gDial::draw() -{ - double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1(); - - fl_color(COLOR_BG_0); - fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle); - - fl_color(COLOR_BD_0); - fl_arc(x(), y(), w(), h(), 0, 360); - fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1()); -} - -/* -------------------------------------------------------------------------- */ - - -gBox::gBox(int x, int y, int w, int h, const char *L, Fl_Align al) -: Fl_Box(x, y, w, h, L) -{ - labelsize(11); - box(FL_NO_BOX); - labelcolor(COLOR_TEXT_0); - if (al != 0) - align(al | FL_ALIGN_INSIDE); -} - - -/* -------------------------------------------------------------------------- */ - - -gCheck::gCheck(int x, int y, int w, int h, const char *L) -: Fl_Check_Button(x, y, w, h, L) {} - -void gCheck::draw() -{ - int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0; - - if (value()) { - fl_rect(x(), y(), 12, 12, (Fl_Color) color); - fl_rectf(x(), y(), 12, 12, (Fl_Color) color); - } - else { - fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR); - fl_rect(x(), y(), 12, 12, (Fl_Color) color); - } - - fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer - fl_font(FL_HELVETICA, 11); - fl_color(COLOR_TEXT_0); - fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); -} - - -/* -------------------------------------------------------------------------- */ - - -gRadio::gRadio(int x, int y, int w, int h, const char *L) -: Fl_Radio_Button(x, y, w, h, L) {} - -void gRadio::draw() -{ - int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0; - - if (value()) { - fl_rect(x(), y(), 12, 12, (Fl_Color) color); - fl_rectf(x(), y(), 12, 12, (Fl_Color) color); - } - else { - fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR); - fl_rect(x(), y(), 12, 12, (Fl_Color) color); - } - - fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer - fl_font(FL_HELVETICA, 11); - fl_color(COLOR_TEXT_0); - fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); -} - - -/* -------------------------------------------------------------------------- */ - - -gProgress::gProgress(int x, int y, int w, int h, const char *L) -: Fl_Progress(x, y, w, h, L) { - color(COLOR_BG_0, COLOR_BD_0); - box(G_BOX); - -} - - -/* -------------------------------------------------------------------------- */ - - -gSoundMeter::gSoundMeter(int x, int y, int w, int h, const char *L) - : Fl_Box(x, y, w, h, L), - clip(false), - mixerPeak(0.0f), - peak(0.0f), - peak_old(0.0f), - db_level(0.0f), - db_level_old(0.0f) {} - -void gSoundMeter::draw() -{ - fl_rect(x(), y(), w(), h(), COLOR_BD_0); - - /* peak = the highest value inside the frame */ - - peak = 0.0f; - float tmp_peak = 0.0f; - - tmp_peak = fabs(mixerPeak); - if (tmp_peak > peak) - peak = tmp_peak; - - clip = peak >= 1.0f ? true : false; // 1.0f is considered clip - - - /* dBFS (full scale) calculation, plus decay of -2dB per frame */ - - db_level = 20 * log10(peak); - if (db_level < db_level_old) - if (db_level_old > -DB_MIN_SCALE) - db_level = db_level_old - 2.0f; - - db_level_old = db_level; - - /* graphical part */ - - float px_level = 0.0f; - if (db_level < 0.0f) - px_level = ((w()/DB_MIN_SCALE) * db_level) + w(); - else - px_level = w(); - - fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); - fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !G_audio_status ? COLOR_ALERT : COLOR_BD_0); -} - -/* -------------------------------------------------------------------------- */ - -gBeatMeter::gBeatMeter(int x, int y, int w, int h, const char *L) - : Fl_Box(x, y, w, h, L) {} - -void gBeatMeter::draw() -{ - int cursorW = w() / MAX_BEATS; - int greyX = G_Mixer.beats * cursorW; - - fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border - fl_rectf(x()+1, y()+1, w()-2, h()-2, FL_BACKGROUND_COLOR); // bg - fl_rectf(x()+(G_Mixer.actualBeat*cursorW)+3, y()+3, cursorW-5, h()-6, COLOR_BG_2); // cursor - - /* beat cells */ - - fl_color(COLOR_BD_0); - for (int i=1; i<=G_Mixer.beats; i++) - fl_line(x()+cursorW*i, y()+1, x()+cursorW*i, y()+h()-2); - - /* bar line */ - - fl_color(COLOR_BG_2); - int delta = G_Mixer.beats / G_Mixer.bars; - for (int i=1; i= w()-16) { - tmp.resize(size); - size--; - } - tmp += "..."; - fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER); - } - - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gDrawBox(int x, int y, int w, int h, Fl_Color c) -{ - fl_color(c); - fl_rectf(x, y, w, h); - fl_color(COLOR_BD_0); - fl_rect(x, y, w, h); -} - - -/* -------------------------------------------------------------------------- */ - - -gLiquidScroll::gLiquidScroll(int x, int y, int w, int h, const char *l) - : Fl_Scroll(x, y, w, h, l) -{ - type(Fl_Scroll::VERTICAL); - scrollbar.color(COLOR_BG_0); - scrollbar.selection_color(COLOR_BG_1); - scrollbar.labelcolor(COLOR_BD_1); - scrollbar.slider(G_BOX); -} - - -void gLiquidScroll::resize(int X, int Y, int W, int H) -{ - int nc = children()-2; // skip hscrollbar and vscrollbar - for ( int t=0; tresize(c->x(), c->y(), W-24, c->h()); // W-24: leave room for scrollbar - } - init_sizes(); // tell scroll children changed in size - Fl_Scroll::resize(X,Y,W,H); -} - - -/* -------------------------------------------------------------------------- */ - - -gSlider::gSlider(int x, int y, int w, int h, const char *l) - : Fl_Slider(x, y, w, h, l) -{ - type(FL_HOR_FILL_SLIDER); - - labelsize(11); - align(FL_ALIGN_LEFT); - labelcolor(COLOR_TEXT_0); - - box(G_BOX); - color(COLOR_BG_0); - selection_color(COLOR_BD_0); -} - - -/* -------------------------------------------------------------------------- */ - - -gResizerBar::gResizerBar(int X,int Y,int W,int H, bool vertical) - : Fl_Box(X,Y,W,H), vertical(vertical) -{ - last_y = 0; - min_h = 30; - if (vertical) { - orig_h = H; - labelsize(H); - } - else { - orig_h = W; - labelsize(W); - } - align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); - labelfont(FL_COURIER); - visible_focus(0); -} - -/* -gResizerBar::~gResizerBar() -{ - gLog("------ resizerbar %p destroyed\n", (void*)this); -} -*/ - -void gResizerBar::HandleDrag(int diff) -{ - Fl_Scroll *grp = (Fl_Scroll*)parent(); - int top; - int bot; - if (vertical) { - top = y(); - bot = y()+h(); - } - else { - top = x(); - bot = x()+w(); - } - - // First pass: find widget directly above us with common edge - // Possibly clamp 'diff' if widget would get too small.. - - for (int t=0; tchildren(); t++) { - Fl_Widget *wd = grp->child(t); - if (vertical) { - if ((wd->y()+wd->h()) == top) { // found widget directly above? - if ((wd->h()+diff) < min_h) - diff = wd->h() - min_h; // clamp - wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height - break; // done with first pass - } - } - else { - if ((wd->x()+wd->w()) == top) { // found widget directly above? - if ((wd->w()+diff) < min_h) - diff = wd->w() - min_h; // clamp - wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height - break; // done with first pass - } - } - } - - // Second pass: find widgets below us, move based on clamped diff - - for (int t=0; tchildren(); t++) { - Fl_Widget *wd = grp->child(t); - if (vertical) { - if (wd->y() >= bot) // found widget below us? - wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position - } - else { - if (wd->x() >= bot) - wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h()); - } - } - - // Change our position last - - if (vertical) - resize(x(), y()+diff, w(), h()); - else - resize(x()+diff, y(), w(), h()); - - grp->init_sizes(); - grp->redraw(); -} - - -int gResizerBar::handle(int e) -{ - int ret = 0; - int this_y; - if (vertical) - this_y = Fl::event_y_root(); - else - this_y = Fl::event_x_root(); - switch (e) { - case FL_FOCUS: - ret = 1; - break; - case FL_ENTER: - ret = 1; - fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE); - break; - case FL_LEAVE: - ret = 1; - fl_cursor(FL_CURSOR_DEFAULT); - break; - case FL_PUSH: - ret = 1; - last_y = this_y; - break; - case FL_DRAG: - HandleDrag(this_y-last_y); - last_y = this_y; - ret = 1; - break; - default: break; - } - return(Fl_Box::handle(e) | ret); -} - - -void gResizerBar::resize(int X,int Y,int W,int H) -{ - if (vertical) - Fl_Box::resize(X,Y,W,orig_h); // height of resizer stays constant size - else - Fl_Box::resize(X,Y,orig_h,H); -} - - -/* -------------------------------------------------------------------------- */ - - -gScroll::gScroll(int x, int y, int w, int h, int t) - : Fl_Scroll(x, y, w, h) -{ - type(t); - - scrollbar.color(COLOR_BG_0); - scrollbar.selection_color(COLOR_BG_1); - scrollbar.labelcolor(COLOR_BD_1); - scrollbar.slider(G_BOX); - - hscrollbar.color(COLOR_BG_0); - hscrollbar.selection_color(COLOR_BG_1); - hscrollbar.labelcolor(COLOR_BD_1); - hscrollbar.slider(G_BOX); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gBaseButton::gBaseButton(int x, int y, int w, int h, const char *l) - : Fl_Button(x, y, w, h, l) -{ - initLabel = l ? l : ""; -} - - -/* -------------------------------------------------------------------------- */ - - -void gBaseButton::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 gBaseButton::label(const char *l) -{ - Fl_Button::label(l); - initLabel = l; - trimLabel(); -} - -const char *gBaseButton::label() -{ - return Fl_Button::label(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gBaseButton::resize(int X, int Y, int W, int H) -{ - trimLabel(); - Fl_Button::resize(X, Y, W, H); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gFxButton::gFxButton(int x, int y, int w, int h, const char **imgOff, const char **imgOn) - : gClick(x, y, w, h, NULL, imgOff, imgOn), full(false) {} - - -/* -------------------------------------------------------------------------- */ - - -void gFxButton::draw() -{ - gClick::draw(); - if (full) - fl_draw_pixmap(imgOn, x()+1, y()+1, COLOR_BD_0); -} diff --git a/src/ge_mixed.h b/src/ge_mixed.h deleted file mode 100644 index 26942d5..0000000 --- a/src/ge_mixed.h +++ /dev/null @@ -1,355 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_mixed - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_MIXED_H -#define GE_MIXED_H - -#include -#include -#include // for intptr_t -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 - #include // for SHGetFolderPath -#endif - - -/* cb_window_closer - * callback for when closing windows. Deletes the widget (delete). */ - -void __cb_window_closer(Fl_Widget *v, void *p); - - -/* -------------------------------------------------------------------------- */ - - -class gBaseButton : public Fl_Button -{ -private: - std::string initLabel; - - void trimLabel(); - -public: - gBaseButton(int x, int y, int w, int h, const char *l=0); - void resize(int x, int y, int w, int h); - void label(const char *l); - const char *label(); -}; - - -/* -------------------------------------------------------------------------- */ - - -/* gClick - * a normal button. */ - -class gClick : public gBaseButton -{ -public: - gClick(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL); - void draw(); - const char **imgOff; - const char **imgOn; - Fl_Color bgColor0; // background not clicked - Fl_Color bgColor1; // background clicked - Fl_Color bdColor; // border - Fl_Color txtColor; // testo -}; - - -/* -------------------------------------------------------------------------- */ - - -class gClickRepeat : public Fl_Repeat_Button -{ -public: - gClickRepeat(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL); - void draw(); - const char **imgOff; - const char **imgOn; -}; - - -/* -------------------------------------------------------------------------- */ - - -/* gButton - * exactly as gClick but with a unique id inside of it. Used for the buttons in - * channels and for FXs. */ - -class gButton : public gClick -{ -public: - gButton(int X,int Y,int W,int H,const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL); - int key; - int id; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gInput : public Fl_Input -{ -public: - gInput(int x, int y, int w, int h, const char *L=0); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gDial : public Fl_Dial -{ -public: - gDial(int x, int y, int w, int h, const char *L=0); - void draw(); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gBox : public Fl_Box -{ -public: - gBox(int x, int y, int w, int h, const char *L=0, Fl_Align al=FL_ALIGN_CENTER); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gCheck : public Fl_Check_Button -{ -public: - gCheck(int x, int y, int w, int h, const char *L=0); - void draw(); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gRadio : public Fl_Radio_Button -{ -public: - gRadio(int x, int y, int w, int h, const char *L=0); - void draw(); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gProgress : public Fl_Progress -{ -public: - gProgress(int x, int y, int w, int h, const char *L=0); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gSoundMeter : public Fl_Box -{ -public: - gSoundMeter(int X,int Y,int W,int H,const char *L=0); - void draw(); - bool clip; - float mixerPeak; // peak from mixer -private: - float peak; - float peak_old; - float db_level; - float db_level_old; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gBeatMeter : public Fl_Box -{ -public: - gBeatMeter(int X,int Y,int W,int H,const char *L=0); - void draw(); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gChoice : public Fl_Choice -{ -public: - - gChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true); - void draw(); - - inline void show(const char *c) {value(find_index(c)); } - - bool angle; - int id; -}; - - -/* -------------------------------------------------------------------------- */ - - -/* gDrawBox - * custom boxes in FLTK. */ - -#define G_BOX FL_FREE_BOXTYPE -void gDrawBox(int x, int y, int w, int h, Fl_Color c); - - -/* -------------------------------------------------------------------------- */ - - -/* gLiquidScroll - * custom scroll that tells children to follow scroll's width when - * resized. Thanks to Greg Ercolano from FLTK dev team. - * http://seriss.com/people/erco/fltk/ */ - -class gLiquidScroll : public Fl_Scroll -{ -public: - gLiquidScroll(int x, int y, int w, int h, const char *l=0); - void resize(int x, int y, int w, int h); -}; - - -/* -------------------------------------------------------------------------- */ - - -/* gScroll - * custom scroll with nice scrollbars and something else. */ - -class gScroll : public Fl_Scroll -{ -public: - gScroll(int x, int y, int w, int h, int type=Fl_Scroll::BOTH); -}; - - -/* -------------------------------------------------------------------------- */ - -/* gResizerBar - * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from - * FLTK dev team. http://seriss.com/people/erco/fltk/ - * - * Shows a resize cursor when hovered over. - * Assumes: - * - Parent is an Fl_Scroll - * - All children of Fl_Scroll are vertically arranged - * - The widget above us has a bottom edge touching our top edge - * ie. (w->y()+w->h() == this->y()) - * - * When this widget is dragged: - * - The widget above us (with a common edge) will be /resized/ - * vertically - * - All children below us will be /moved/ vertically */ - -/* TODO - use more general variable names - * (last_y -> last_?, min_h -> min_?, ...) */ - -class gResizerBar : public Fl_Box -{ -private: - bool vertical; - int orig_h; - int last_y; - int min_h; // min height for widget above us - - void HandleDrag(int diff); - -public: - - /* 'vertical' defines the bar movement. Vertical=true: the bar moves - * vertically (up and down). */ - - gResizerBar(int x, int y, int w, int h, bool vertical=true); - //~gResizerBar(); - - inline void setMinSize(int val) { min_h = val; } - inline int getMinSize() { return min_h; } - - int handle(int e); - void resize(int x, int y, int w, int h); -}; - - -/* -------------------------------------------------------------------------- */ - - -class gSlider : public Fl_Slider -{ -public: - gSlider(int x, int y, int w, int h, const char *l=0); - int id; -}; - - -/* -------------------------------------------------------------------------- */ - - -/* gFxButton - * a simple gClick with 'full' parameter (i.e. has plugins). If 'full' is true, - * draw something somewhere. */ - -class gFxButton : public gClick -{ -public: - gFxButton(int x, int y, int w, int h, const char **imgOff=NULL, const char **imgOn=NULL); - void draw(); - bool full; -}; - - -#endif diff --git a/src/ge_muteChannel.cpp b/src/ge_muteChannel.cpp deleted file mode 100644 index 01d1391..0000000 --- a/src/ge_muteChannel.cpp +++ /dev/null @@ -1,411 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_muteChannel - * a widget that represents mute actions inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "ge_muteChannel.h" -#include "gd_actionEditor.h" -#include "ge_actionWidget.h" -#include "gd_mainWindow.h" -#include "gg_keyboard.h" -#include "recorder.h" -#include "mixer.h" -#include "glue.h" -#include "channel.h" -#include "log.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; - - -gMuteChannel::gMuteChannel(int x, int y, gdActionEditor *pParent) - : gActionWidget(x, y, 200, 80, pParent), draggedPoint(-1), selectedPoint(-1) -{ - size(pParent->totalWidth, h()); - extractPoints(); -} - - -/* ------------------------------------------------------------------ */ - - -void gMuteChannel::draw() { - - baseDraw(); - - /* print label */ - - fl_color(COLOR_BG_1); - fl_font(FL_HELVETICA, 12); - fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); - - /* draw "on" and "off" labels. Must stay in background */ - - fl_color(COLOR_BG_1); - fl_font(FL_HELVETICA, 9); - fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); - fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); - - /* draw on-off points. On = higher rect, off = lower rect. It always - * starts with a note_off */ - - fl_color(COLOR_BG_2); - - int pxOld = x()+1; - int pxNew = 0; - int py = y()+h()-5; - int pyDot = py-6; - - for (unsigned i=0; icoverX+x()-1, py); -} - - -/* ------------------------------------------------------------------ */ - - -void gMuteChannel::extractPoints() { - - points.clear(); - - /* actions are already sorted by recorder::sortActions() */ - - for (unsigned i=0; ichan == pParent->chan->index) { - if (recorder::global.at(i).at(j)->type & (ACTION_MUTEON | ACTION_MUTEOFF)) { - point p; - p.frame = recorder::frames.at(i); - p.type = recorder::global.at(i).at(j)->type; - p.x = p.frame / pParent->zoom; - points.add(p); - //gLog("[gMuteChannel::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame); - } - } - } - } -} - - -/* ------------------------------------------------------------------ */ - - -void gMuteChannel::updateActions() { - for (unsigned i=0; izoom; -} - - -/* ------------------------------------------------------------------ */ - - -int gMuteChannel::handle(int e) { - - int ret = 0; - int mouseX = Fl::event_x()-x(); - - switch (e) { - - case FL_ENTER: { - ret = 1; - break; - } - - case FL_MOVE: { - selectedPoint = getSelectedPoint(); - redraw(); - ret = 1; - break; - } - - case FL_LEAVE: { - draggedPoint = -1; - selectedPoint = -1; - redraw(); - ret = 1; - break; - } - - case FL_PUSH: { - - /* left click on point: drag - * right click on point: delete - * left click on void: add */ - - if (Fl::event_button1()) { - - if (selectedPoint != -1) { - draggedPoint = selectedPoint; - previousXPoint = points.at(selectedPoint).x; - } - else { - - /* click on the grey area leads to nowhere */ - - if (mouseX > pParent->coverX) { - ret = 1; - break; - } - - /* click in the middle of a long mute_on (between two points): new actions - * must be added in reverse: first mute_off then mute_on. Let's find the - * next point from here. */ - - unsigned nextPoint = points.size; - for (unsigned i=0; izoom; - int frame_b = frame_a+2048; - - if (pParent->gridTool->isOn()) { - frame_a = pParent->gridTool->getSnapFrame(mouseX); - frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize()); - - /* with snap=on a point can fall onto another */ - - if (pointCollides(frame_a) || pointCollides(frame_b)) { - ret = 1; - break; - } - } - - /* ensure frame parity */ - - if (frame_a % 2 != 0) frame_a++; - if (frame_b % 2 != 0) frame_b++; - - /* avoid overflow: frame_b must be within the sequencer range. In that - * case shift the ON-OFF block */ - - if (frame_b >= G_Mixer.totalFrames) { - frame_b = G_Mixer.totalFrames; - frame_a = frame_b-2048; - } - - if (nextPoint % 2 != 0) { - recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_a); - recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_b); - } - else { - recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_a); - recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_b); - } - recorder::sortActions(); - - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow - extractPoints(); - redraw(); - } - } - else { - - /* delete points pair */ - - if (selectedPoint != -1) { - - unsigned a; - unsigned b; - - if (points.at(selectedPoint).type == ACTION_MUTEOFF) { - a = selectedPoint-1; - b = selectedPoint; - } - else { - a = selectedPoint; - b = selectedPoint+1; - } - - //gLog("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n", - // a, b, points.at(a).frame, points.at(b).frame); - - recorder::deleteAction(pParent->chan->index, points.at(a).frame, points.at(a).type, false); // false = don't check vals - recorder::deleteAction(pParent->chan->index, points.at(b).frame, points.at(b).type, false); // false = don't check vals - recorder::sortActions(); - - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow - extractPoints(); - redraw(); - } - } - ret = 1; - break; - } - - case FL_RELEASE: { - - if (draggedPoint != -1) { - - if (points.at(draggedPoint).x == previousXPoint) { - //gLog("nothing to do\n"); - } - else { - - int newFrame = points.at(draggedPoint).x * pParent->zoom; - - recorder::deleteAction( - pParent->chan->index, - points.at(draggedPoint).frame, - points.at(draggedPoint).type, - false); // don't check values - - recorder::rec( - pParent->chan->index, - points.at(draggedPoint).type, - newFrame); - - recorder::sortActions(); - - points.at(draggedPoint).frame = newFrame; - } - } - draggedPoint = -1; - selectedPoint = -1; - - ret = 1; - break; - } - - case FL_DRAG: { - - if (draggedPoint != -1) { - - /* constrain the point between two ends (leftBorder-point, - * point-point, point-rightBorder) */ - - int prevPoint; - int nextPoint; - - if (draggedPoint == 0) { - prevPoint = 0; - nextPoint = points.at(draggedPoint+1).x - 1; - if (pParent->gridTool->isOn()) - nextPoint -= pParent->gridTool->getCellSize(); - } - else - if ((unsigned) draggedPoint == points.size-1) { - prevPoint = points.at(draggedPoint-1).x + 1; - nextPoint = pParent->coverX-x(); - if (pParent->gridTool->isOn()) - prevPoint += pParent->gridTool->getCellSize(); - } - else { - prevPoint = points.at(draggedPoint-1).x + 1; - nextPoint = points.at(draggedPoint+1).x - 1; - if (pParent->gridTool->isOn()) { - prevPoint += pParent->gridTool->getCellSize(); - nextPoint -= pParent->gridTool->getCellSize(); - } - } - - if (mouseX <= prevPoint) - points.at(draggedPoint).x = prevPoint; - else - if (mouseX >= nextPoint) - points.at(draggedPoint).x = nextPoint; - else - if (pParent->gridTool->isOn()) - points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1; - else - points.at(draggedPoint).x = mouseX; - - redraw(); - } - ret = 1; - break; - } - } - - - return ret; -} - - -/* ------------------------------------------------------------------ */ - - -bool gMuteChannel::pointCollides(int frame) { - for (unsigned i=0; i= points.at(i).x+x()-3 && - Fl::event_x() <= points.at(i).x+x()+3) - return i; - } - return -1; -} diff --git a/src/ge_muteChannel.h b/src/ge_muteChannel.h deleted file mode 100644 index 5cb0ca1..0000000 --- a/src/ge_muteChannel.h +++ /dev/null @@ -1,103 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_muteChannel - * a widget representing mute actions inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GE_MUTECHANNEL_H -#define GE_MUTECHANNEL_H - -#include -#include -#include -#include "ge_actionWidget.h" -#include "utils.h" - - -class gMuteChannel : public gActionWidget { - -private: - - /* point - * a single dot in the graph. */ - - struct point { - int frame; - char type; - int x; - }; - - /* points - * array of on/off points, in frames */ - - gVector points; - - /* draggedPoint - * which point we are dragging? */ - - int draggedPoint; - - /* selectedPoint - * which point we are selecting? */ - - int selectedPoint; - - /* previousXPoint - * x coordinate of point at time t-1. Used to check effective shifts */ - - int previousXPoint; - - /* extractPoints - * va a leggere l'array di azioni di Recorder ed estrae tutti i punti - * interessanti mute_on o mute_off. Li mette poi nel gVector points. */ - void extractPoints(); - - /* getSelectedPoint - * ritorna l'indice di points[] in base al punto selezionato (quello - * con il mouse hover). Ritorna -1 se non trova niente. */ - int getSelectedPoint(); - - /* pointCollides - * true if a point collides with another. Used while adding new points - * with snap active.*/ - - bool pointCollides(int frame); - -public: - - gMuteChannel(int x, int y, class gdActionEditor *pParent); - void draw(); - int handle(int e); - - /* updateActions - * calculates new points affected by the zoom. Call this one after - * each zoom update. */ - - void updateActions(); -}; - -#endif diff --git a/src/ge_pianoRoll.cpp b/src/ge_pianoRoll.cpp deleted file mode 100644 index 51e8b95..0000000 --- a/src/ge_pianoRoll.cpp +++ /dev/null @@ -1,730 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_pianoRoll - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "ge_pianoRoll.h" -#include "gd_mainWindow.h" -#include "gd_actionEditor.h" -#include "channel.h" -#include "midiChannel.h" -#include "const.h" -#include "kernelMidi.h" -#include "log.h" -#include "conf.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; -extern Conf G_Conf; - - -gPianoRollContainer::gPianoRollContainer(int x, int y, class gdActionEditor *pParent) - : Fl_Scroll(x, y, 200, 422), pParent(pParent) -{ - size(pParent->totalWidth, G_Conf.pianoRollH); - pianoRoll = new gPianoRoll(x, y, pParent->totalWidth, pParent); -} - - -/* ------------------------------------------------------------------ */ - - -gPianoRollContainer::~gPianoRollContainer() { - clear(); - G_Conf.pianoRollH = h(); - G_Conf.pianoRollY = pianoRoll->y(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRollContainer::updateActions() { - pianoRoll->updateActions(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRollContainer::draw() { - - pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal - - /* clear background */ - - fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); - - /* clip pianoRoll to pianoRollContainer size */ - - fl_push_clip(x(), y(), w(), h()); - draw_child(*pianoRoll); - fl_pop_clip(); - - fl_color(COLOR_BD_0); - fl_line_style(0); - fl_rect(x(), y(), pParent->totalWidth, h()); -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gPianoRoll::gPianoRoll(int X, int Y, int W, class gdActionEditor *pParent) - : gActionWidget(X, Y, W, 40, pParent) -{ - resizable(NULL); // don't resize children (i.e. pianoItem) - size(W, (MAX_NOTES+1) * CELL_H); // 128 MIDI channels * 15 px height - - if (G_Conf.pianoRollY == -1) - position(x(), y()-(h()/2)); // center - else - position(x(), G_Conf.pianoRollY); - - drawSurface1(); - drawSurface2(); - - /* add actions when the window is opened. Position is zoom-based. MIDI - * actions come always in pair: start + end. */ - - recorder::sortActions(); - - recorder::action *a2 = NULL; - recorder::action *prev = NULL; - - for (unsigned i=0; i than the grey area */ - /** FIXME - can we move this to the outer cycle? */ - - if (recorder::frames.at(i) > G_Mixer.totalFrames) - continue; - - recorder::action *a1 = recorder::global.at(i).at(j); - - if (a1->chan != pParent->chan->index) - continue; - - if (a1->type == ACTION_MIDI) { - - /* if this action is == to previous one: skip it, we have already - * checked it */ - - if (a1 == prev) { - //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was previous\n"); - continue; - } - - /* extract MIDI infos from a1: if is note off skip it, we are looking - * for note on only */ - - int a1_type = kernelMidi::getB1(a1->iValue); - int a1_note = kernelMidi::getB2(a1->iValue); - int a1_velo = kernelMidi::getB3(a1->iValue); - - if (a1_type == 0x80) { - //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was note off\n"); - continue; - } - - /* search for the next action. Must have: same channel, ACTION_MIDI, greater - * than a1->frame and with MIDI properties of note_off (0x80), same note - * of a1, same velocity of a1 */ - - recorder::getNextAction( - a1->chan, - ACTION_MIDI, - a1->frame, - &a2, - kernelMidi::getIValue(0x80, a1_note, a1_velo)); - - /* next action note off found: add a new gPianoItem to piano roll */ - - if (a2) { - //gLog("[gPianoRoll] ACTION_MIDI pair found, frame_a=%d frame_b=%d, note_a=%d, note_b=%d, type_a=%d, type_b=%d\n", - // a1->frame, a2->frame, kernelMidi::getNoteValue(a1->iValue), kernelMidi::getNoteValue(a2->iValue), - // kernelMidi::getNoteOnOff(a1->iValue), kernelMidi::getNoteOnOff(a2->iValue)); - new gPianoItem(0, 0, x(), y()+3, a1, a2, pParent); - prev = a2; - a2 = NULL; - } - else - gLog("[gPianoRoll] recorder didn't find action!\n"); - - } - } - } - - end(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::drawSurface1() { - - surface1 = fl_create_offscreen(40, h()); - fl_begin_offscreen(surface1); - - /* warning: only w() and h() come from this widget, x and y coordinates - * are absolute, since we are writing in a memory chunk */ - - fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN); - - fl_line_style(FL_DASH, 0, NULL); - fl_font(FL_HELVETICA, 11); - - int octave = 9; - - for (int i=1; i<=MAX_NOTES+1; i++) { - - /* print key note label. C C# D D# E F F# G G# A A# B */ - - char note[6]; - int step = i % 12; - - switch (step) { - case 1: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dG", octave); - break; - case 2: - sprintf(note, "%dF#", octave); - break; - case 3: - sprintf(note, "%dF", octave); - break; - case 4: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dE", octave); - break; - case 5: - sprintf(note, "%dD#", octave); - break; - case 6: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dD", octave); - break; - case 7: - sprintf(note, "%dC#", octave); - break; - case 8: - sprintf(note, "%dC", octave); - break; - case 9: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dB", octave); - break; - case 10: - sprintf(note, "%dA#", octave); - break; - case 11: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dA", octave); - break; - case 0: - sprintf(note, "%dG#", octave); - octave--; - break; - } - - fl_color(fl_rgb_color(54, 54, 54)); - fl_draw(note, 4, ((i-1)*CELL_H)+1, 30, CELL_H, (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); - - /* print horizontal line */ - - if (i < 128) - fl_line(0, i*CELL_H, 40, +i*CELL_H); - } - - fl_line_style(0); - fl_end_offscreen(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::drawSurface2() { - surface2 = fl_create_offscreen(40, h()); - fl_begin_offscreen(surface2); - fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN); - fl_color(fl_rgb_color(54, 54, 54)); - fl_line_style(FL_DASH, 0, NULL); - for (int i=1; i<=MAX_NOTES+1; i++) { - int step = i % 12; - switch (step) { - case 1: - case 4: - case 6: - case 9: - case 11: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - break; - } - if (i < 128) { - fl_color(fl_rgb_color(54, 54, 54)); - fl_line(0, i*CELL_H, 40, +i*CELL_H); - } - } - fl_line_style(0); - fl_end_offscreen(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::draw() { - - fl_copy_offscreen(x(), y(), 40, h(), surface1, 0, 0); - -#if defined(__APPLE__) - for (int i=36; itotalWidth; i+=36) /// TODO: i < pParent->coverX is faster - fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 1, 0); -#else - for (int i=40; itotalWidth; i+=40) /// TODO: i < pParent->coverX is faster - fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 0, 0); -#endif - - baseDraw(false); - draw_children(); -} - - -/* ------------------------------------------------------------------ */ - - -int gPianoRoll::handle(int e) { - - int ret = Fl_Group::handle(e); - - switch (e) { - case FL_PUSH: { - - /* avoid click on grey area */ - - if (Fl::event_x() >= pParent->coverX) { - ret = 1; - break; - } - - - push_y = Fl::event_y() - y(); - - if (Fl::event_button1()) { - - /* ax is driven by grid, ay by the height in px of each note */ - - int ax = Fl::event_x(); - int ay = Fl::event_y(); - - /* vertical snap */ - - int edge = (ay-y()-3) % 15; - if (edge != 0) ay -= edge; - - /* if no overlap, add new piano item. Also check that it doesn't - * overflow on the grey area, by shifting it to the left if - * necessary. */ - - if (!onItem(ax, ay-y()-3)) { - int greyover = ax+20 - pParent->coverX-x(); - if (greyover > 0) - ax -= greyover; - add(new gPianoItem(ax, ay, ax-x(), ay-y()-3, NULL, NULL, pParent)); - redraw(); - } - } - ret = 1; - break; - } - case FL_DRAG: { - - if (Fl::event_button3()) { - - gPianoRollContainer *prc = (gPianoRollContainer*) parent(); - position(x(), Fl::event_y() - push_y); - - if (y() > prc->y()) - position(x(), prc->y()); - else - if (y() < prc->y()+prc->h()-h()) - position(x(), prc->y()+prc->h()-h()); - - prc->redraw(); - } - ret = 1; - break; - } - case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll - ret = 1; - break; - } - } - return ret; -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::updateActions() { - - /* when zooming, don't delete and re-add actions, just MOVE them. This - * function shifts the action by a zoom factor. Those singlepress are - * stretched, as well */ - - gPianoItem *i; - for (int k=0; kgetFrame_a(), i->getFrame_b(), i->x()); - - int newX = x() + (i->getFrame_a() / pParent->zoom); - int newW = ((i->getFrame_b() - i->getFrame_a()) / pParent->zoom); - if (newW < 8) - newW = 8; - i->resize(newX, i->y(), newW, i->h()); - i->redraw(); - - //gLog("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x()); - } -} - - -/* ------------------------------------------------------------------ */ - - -bool gPianoRoll::onItem(int rel_x, int rel_y) { - - if (!pParent->chan->hasActions) - return false; - - int note = MAX_NOTES - (rel_y / CELL_H); - - int n = children(); - for (int i=0; igetNote() != note) - continue; - - /* when 2 segments overlap? - * start = the highest value between the two starting points - * end = the lowest value between the two ending points - * if start < end then there's an overlap of end-start pixels. We - * also add 1 px to the edges in order to gain some space: - * [ ][ ] ---> no - * [ ] [ ] ---> yes! */ - - int start = p->x() > rel_x ? p->x() : rel_x-1; - int end = p->x()+p->w() < rel_x + 20 ? p->x()+p->w() : rel_x + 21; - if (start < end) - return true; - } - return false; -} - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gPianoItem::gPianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, gdActionEditor *pParent) - : Fl_Box (X, Y, 20, gPianoRoll::CELL_H-5), - a (a), - b (b), - pParent (pParent), - selected(false), - event_a (0x00), - event_b (0x00), - changed (false) -{ - - /* a is a pointer: action exists, needs to be displayed */ - - if (a) { - note = kernelMidi::getB2(a->iValue); - frame_a = a->frame; - frame_b = b->frame; - event_a = a->iValue; - event_b = b->iValue; - int newX = rel_x + (frame_a / pParent->zoom); - int newY = rel_y + getY(note); - int newW = (frame_b - frame_a) / pParent->zoom; - resize(newX, newY, newW, h()); - } - - /* a is null: action needs to be recorded from scratch */ - - else { - note = getNote(rel_y); - frame_a = rel_x * pParent->zoom; - frame_b = (rel_x + 20) * pParent->zoom; - record(); - size((frame_b - frame_a) / pParent->zoom, h()); - } -} - - -/* ------------------------------------------------------------------ */ - - -bool gPianoItem::overlap() { - - /* when 2 segments overlap? - * start = the highest value between the two starting points - * end = the lowest value between the two ending points - * if start < end then there's an overlap of end-start pixels. */ - - gPianoRoll *pPiano = (gPianoRoll*) parent(); - - for (int i=0; ichildren(); i++) { - - gPianoItem *pItem = (gPianoItem*) pPiano->child(i); - - /* don't check against itself and with different y positions */ - - if (pItem == this || pItem->y() != y()) - continue; - - int start = pItem->x() >= x() ? pItem->x() : x(); - int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w(); - if (start < end) - return true; - } - - return false; -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoItem::draw() { - int _w = w() > 4 ? w() : 4; - //gLog("[gPianoItem] draw me (%p) at x=%d\n", (void*)this, x()); - fl_rectf(x(), y(), _w, h(), (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoItem::record() { - - /* avoid frame overflow */ - - int overflow = frame_b - G_Mixer.totalFrames; - if (overflow > 0) { - frame_b -= overflow; - frame_a -= overflow; - } - - /* note off */ - /** FIXME - use constants */ - event_a |= (0x90 << 24); // note on - event_a |= (note << 16); // note value - event_a |= (0x3F << 8); // velocity - event_a |= (0x00); - - event_b |= (0x80 << 24); // note off - event_b |= (note << 16); // note value - event_b |= (0x3F << 8); // velocity - event_b |= (0x00); - - recorder::rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a); - recorder::rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoItem::remove() { - recorder::deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true, event_a, 0.0); - recorder::deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true, event_b, 0.0); - - /* send a note-off in case we are deleting it in a middle of a key_on - * key_off sequence. */ - - ((MidiChannel*) pParent->chan)->sendMidi(event_b); -} - - -/* ------------------------------------------------------------------ */ - - -int gPianoItem::handle(int e) { - - int ret = 0; - - switch (e) { - - case FL_ENTER: { - selected = true; - ret = 1; - redraw(); - break; - } - - case FL_LEAVE: { - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); - selected = false; - ret = 1; - redraw(); - break; - } - - case FL_MOVE: { - onLeftEdge = false; - onRightEdge = false; - - if (Fl::event_x() >= x() && Fl::event_x() < x()+4) { - onLeftEdge = true; - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - } - else - if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) { - onRightEdge = true; - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - } - else - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); - - ret = 1; - break; - } - - case FL_PUSH: { - - push_x = Fl::event_x() - x(); - old_x = x(); - old_w = w(); - - if (Fl::event_button3()) { - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); - remove(); - hide(); // for Windows - Fl::delete_widget(this); - ((gPianoRoll*)parent())->redraw(); - } - ret = 1; - break; - } - - case FL_DRAG: { - - changed = true; - - gPianoRoll *pr = (gPianoRoll*) parent(); - int coverX = pParent->coverX + pr->x(); // relative coverX - int nx, ny, nw; - - if (onLeftEdge) { - nx = Fl::event_x(); - ny = y(); - nw = x()-Fl::event_x()+w(); - if (nx < pr->x()) { - nx = pr->x(); - nw = w()+x()-pr->x(); - } - else - if (nx > x()+w()-8) { - nx = x()+w()-8; - nw = 8; - } - resize(nx, ny, nw, h()); - } - else - if (onRightEdge) { - nw = Fl::event_x()-x(); - if (Fl::event_x() < x()+8) - nw = 8; - else - if (Fl::event_x() > coverX) - nw = coverX-x(); - size(nw, h()); - } - else { - nx = Fl::event_x() - push_x; - if (nx < pr->x()+1) - nx = pr->x()+1; - else - if (nx+w() > coverX) - nx = coverX-w(); - - /* snapping */ - - if (pParent->gridTool->isOn()) - nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1; - - position(nx, y()); - } - - /* update screen */ - - redraw(); - ((gPianoRoll*)parent())->redraw(); - ret = 1; - break; - } - - case FL_RELEASE: { - - /* delete & record the action, only if it doesn't overlap with - * another one */ - - if (overlap()) { - resize(old_x, y(), old_w, h()); - redraw(); - } - else - if (changed) { - remove(); - note = getNote(getRelY()); - frame_a = getRelX() * pParent->zoom; - frame_b = (getRelX()+w()) * pParent->zoom; - record(); - changed = false; - } - - ((gPianoRoll*)parent())->redraw(); - - ret = 1; - break; - } - } - return ret; -} diff --git a/src/ge_pianoRoll.h b/src/ge_pianoRoll.h deleted file mode 100644 index 30c04d2..0000000 --- a/src/ge_pianoRoll.h +++ /dev/null @@ -1,183 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_pianoRoll - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GE_PIANOROLL_H -#define GE_PIANOROLL_H - -#include -#include -#include -#include "ge_actionWidget.h" -#include "recorder.h" - - -class gPianoRollContainer : public Fl_Scroll { - -private: - class gdActionEditor *pParent; - class gPianoRoll *pianoRoll; - -public: - gPianoRollContainer(int x, int y, class gdActionEditor *parent); - ~gPianoRollContainer(); - void draw(); - void updateActions(); -}; - - -/* ------------------------------------------------------------------ */ - - -class gPianoRoll : public gActionWidget { - -private: - - /* onItem - * is curson on a gPianoItem? */ - - bool onItem(int rel_x, int rel_y); - - /* drawSurface* - * generate a complex drawing in memory first and copy it to the - * screen at a later point in time. Fl_Offscreen surface holds the - * necessary data. */ - - /* drawSurface1 - * draw first tile of note values. */ - - void drawSurface1(); - - /* drawSurface2 - * draw the rest of the piano roll. */ - - void drawSurface2(); - - int push_y; - Fl_Offscreen surface1; // notes, no repeat - Fl_Offscreen surface2; // lines, x-repeat - - -public: - gPianoRoll(int x, int y, int w, class gdActionEditor *pParent); - - void draw(); - int handle(int e); - void updateActions(); - - enum { MAX_NOTES = 127, CELL_H = 15 }; -}; - - -/* ------------------------------------------------------------------ */ - - -class gPianoItem : public Fl_Box { - -private: - - /* getRelX/Y - * return x/y point of this item, relative to piano roll (and not to - * entire screen) */ - - inline int getRelY() { return y() - parent()->y() - 3; }; - inline int getRelX() { return x() - parent()->x(); }; - - /* getNote - * from a relative_y return the real MIDI note, range 0-127. 15 is - * the hardcoded value for note height in pixels */ - - inline int getNote(int rel_y) { - return gPianoRoll::MAX_NOTES - (rel_y / gPianoRoll::CELL_H); - }; - - /* getY - * from a note, return the y position on piano roll */ - - inline int getY(int note) { - return (gPianoRoll::MAX_NOTES * gPianoRoll::CELL_H) - (note * gPianoRoll::CELL_H); - }; - - /* overlap - * check if this item don't overlap with another one. */ - - bool overlap(); - - recorder::action *a; - recorder::action *b; - class gdActionEditor *pParent; - - bool selected; - int push_x; - - /* MIDI note, start frame, end frame - Used only if it's a newly added - * action */ /** FIXME - is it true? */ - - int note; - int frame_a; - int frame_b; - - /* event - bitmasked MIDI events, generated by record() or by ctor if - * not newly added action */ - - int event_a; - int event_b; - - /* changed - if Item has been moved or resized: re-recording needed */ - - bool changed; - - /* onLeft,RightEdge - if cursor is on a widget's edge */ - - bool onLeftEdge; - bool onRightEdge; - - /* old_x, old_w - store previous width and position while dragging - * and moving, in order to restore it if overlap */ - - int old_x, old_w; - -public: - - /* pianoItem ctor - * if action *a == NULL, record a new action */ - - gPianoItem(int x, int y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, class gdActionEditor *pParent); - - void draw(); - int handle(int e); - void record(); - void remove(); - - inline int getFrame_a() { return frame_a; } - inline int getFrame_b() { return frame_b; } - inline int getNote() { return note; } - -}; - -#endif diff --git a/src/ge_sampleChannel.cpp b/src/ge_sampleChannel.cpp deleted file mode 100644 index 7d0f846..0000000 --- a/src/ge_sampleChannel.cpp +++ /dev/null @@ -1,612 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_sampleChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "ge_sampleChannel.h" -#include "gd_mainWindow.h" -#include "gd_keyGrabber.h" -#include "gd_midiInput.h" -#include "gd_editor.h" -#include "gd_actionEditor.h" -#include "gd_warnings.h" -#include "gd_browser.h" -#include "gd_midiOutput.h" -#include "gg_keyboard.h" -#include "pluginHost.h" -#include "mixer.h" -#include "conf.h" -#include "patch.h" -#include "graphics.h" -#include "channel.h" -#include "wave.h" -#include "sampleChannel.h" -#include "midiChannel.h" -#include "glue.h" -#include "gui_utils.h" - -#ifdef WITH_VST -#include "gd_pluginList.h" -#endif - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch G_Patch; -extern gdMainWindow *mainWin; - - -gSampleChannel::gSampleChannel(int X, int Y, int W, int H, class SampleChannel *ch) - : gChannel(X, Y, W, H, CHANNEL_SAMPLE), ch(ch) -{ - begin(); - -#if defined(WITH_VST) - int delta = 168; // (7 widgets * 20) + (7 paddings * 4) -#else - int delta = 144; // (6 widgets * 20) + (6 paddings * 4) -#endif - - button = new gButton(x(), y(), 20, 20); - status = new gStatus(button->x()+button->w()+4, y(), 20, 20, ch); - mainButton = new gSampleMainButton(status->x()+status->w()+4, y(), w() - delta, 20, "-- no sample --"); - modeBox = new gModeBox(mainButton->x()+mainButton->w()+4, y(), 20, 20, ch); - mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); - solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); - readActions = NULL; // no 'R' button - -#if defined(WITH_VST) - fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); - vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); -#else - vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); -#endif - - end(); - - resizable(mainButton); - - update(); - - button->callback(cb_button, (void*)this); - button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease - -#ifdef WITH_VST - fx->callback(cb_openFxWindow, (void*)this); -#endif - - mute->type(FL_TOGGLE_BUTTON); - mute->callback(cb_mute, (void*)this); - - solo->type(FL_TOGGLE_BUTTON); - solo->callback(cb_solo, (void*)this); - - mainButton->callback(cb_openMenu, (void*)this); - vol->callback(cb_changeVol, (void*)this); - - ch->guiChannel = this; -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::cb_button (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_button(); } -void gSampleChannel::cb_mute (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_mute(); } -void gSampleChannel::cb_solo (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_solo(); } -void gSampleChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openMenu(); } -void gSampleChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_changeVol(); } -void gSampleChannel::cb_readActions (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_readActions(); } -#ifdef WITH_VST -void gSampleChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openFxWindow(); } -#endif - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_mute() -{ - glue_setMute(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_solo() -{ - solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_changeVol() -{ - glue_setChanVol(ch, vol->value()); -} - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST -void gSampleChannel::__cb_openFxWindow() -{ - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST); -} -#endif - - -/* -------------------------------------------------------------------------- */ - - - -void gSampleChannel::__cb_button() -{ - if (button->value()) // pushed - glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); - else // released - glue_keyRelease(ch, Fl::event_ctrl(), Fl::event_shift()); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_openMenu() -{ - /* if you're recording (actions or input) no menu is allowed; you can't - * do anything, especially deallocate the channel */ - - if (G_Mixer.chanInput == ch || recorder::active) - return; - - /* the following is a trash workaround for a FLTK menu. We need a gMenu - * widget asap */ - - Fl_Menu_Item rclick_menu[] = { - {"Load new sample..."}, // 0 - {"Export sample to file..."}, // 1 - {"Setup keyboard input..."}, // 2 - {"Setup MIDI input..."}, // 3 - {"Setup MIDI output..."}, // 4 - {"Edit sample..."}, // 5 - {"Edit actions..."}, // 6 - {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 7 - {"All"}, // 8 - {"Mute"}, // 9 - {"Volume"}, // 10 - {"Start/Stop"}, // 11 - {0}, // 12 - {"Free channel"}, // 13 - {"Delete channel"}, // 14 - {0} - }; - - if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) { - rclick_menu[1].deactivate(); - rclick_menu[5].deactivate(); - rclick_menu[13].deactivate(); - } - - /* no 'clear actions' if there are no actions */ - - if (!ch->hasActions) - rclick_menu[6].deactivate(); - - /* no 'clear start/stop actions' for those channels in loop mode: - * they cannot have start/stop actions. */ - - if (ch->mode & LOOP_ANY) - rclick_menu[10].deactivate(); - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(11); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return; - - if (strcmp(m->label(), "Load new sample...") == 0) { - openBrowser(BROWSER_LOAD_SAMPLE); - return; - } - - if (strcmp(m->label(), "Setup keyboard input...") == 0) { - new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow - return; - } - - if (strcmp(m->label(), "Setup MIDI input...") == 0) { - gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0); - return; - } - - if (strcmp(m->label(), "Setup MIDI output...") == 0) { - gu_openSubWindow(mainWin, new gdMidiOutputSampleCh(ch), 0); - return; - } - - if (strcmp(m->label(), "Edit sample...") == 0) { - gu_openSubWindow(mainWin, new gdEditor(ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor - return; - } - - if (strcmp(m->label(), "Export sample to file...") == 0) { - openBrowser(BROWSER_SAVE_SAMPLE); - return; - } - - if (strcmp(m->label(), "Delete channel") == 0) { - if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) - return; - glue_deleteChannel(ch); - return; - } - - if (strcmp(m->label(), "Free channel") == 0) { - if (ch->status == STATUS_PLAY) { - if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) - return; - } - else if (!gdConfirmWin("Warning", "Free channel: are you sure?")) - return; - - glue_freeChannel(ch); - - /* delete any related subwindow */ - - /** FIXME - use gu_closeAllSubwindows() */ - - mainWin->delSubWindow(WID_FILE_BROWSER); - mainWin->delSubWindow(WID_ACTION_EDITOR); - mainWin->delSubWindow(WID_SAMPLE_EDITOR); - mainWin->delSubWindow(WID_FX_LIST); - - return; - } - - if (strcmp(m->label(), "Mute") == 0) { - if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?")) - return; - recorder::clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF); - if (!ch->hasActions) - delActionButton(); - - /* TODO - set mute=false */ - - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Start/Stop") == 0) { - if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?")) - return; - recorder::clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN); - if (!ch->hasActions) - delActionButton(); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Volume") == 0) { - if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?")) - return; - recorder::clearAction(ch->index, ACTION_VOLUME); - if (!ch->hasActions) - delActionButton(); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "All") == 0) { - if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) - return; - recorder::clearChan(ch->index); - delActionButton(); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Edit actions...") == 0) { - gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); - return; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_readActions() -{ - ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::openBrowser(int type) -{ - const char *title = ""; - switch (type) { - case BROWSER_LOAD_SAMPLE: - title = "Browse Sample"; - break; - case BROWSER_SAVE_SAMPLE: - title = "Save Sample"; - break; - case -1: - title = "Edit Sample"; - break; - } - gWindow *childWin = new gdBrowser(title, G_Conf.samplePath, ch, type); - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::refresh() -{ - if (!mainButton->visible()) // mainButton invisible? status too (see below) - return; - - setColorsByStatus(ch->status, ch->recStatus); - - if (ch->wave != NULL) { - - if (G_Mixer.chanInput == ch) - mainButton->bgColor0 = COLOR_BG_3; - - if (recorder::active) { - if (recorder::canRec(ch)) { - mainButton->bgColor0 = COLOR_BG_4; - mainButton->txtColor = COLOR_TEXT_0; - } - } - status->redraw(); // status invisible? sampleButton too (see below) - } - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::reset() -{ - mainButton->bgColor0 = COLOR_BG_0; - mainButton->bdColor = COLOR_BD_0; - mainButton->txtColor = COLOR_TEXT_0; - mainButton->label("-- no sample --"); - delActionButton(true); // force==true, don't check, just remove it - mainButton->redraw(); - status->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::update() -{ - /* update sample button's label */ - - switch (ch->status) { - case STATUS_EMPTY: - mainButton->label("-- no sample --"); - break; - case STATUS_MISSING: - case STATUS_WRONG: - mainButton->label("* file not found! *"); - break; - default: - mainButton->label(ch->wave->name.c_str()); - break; - } - - /* update channels. If you load a patch with recorded actions, the 'R' - * button must be shown. Moreover if the actions are active, the 'R' - * button must be activated accordingly. */ - - if (ch->hasActions) - addActionButton(); - else - delActionButton(); - - /* update key box */ - - char k[4]; - sprintf(k, "%c", ch->key); - button->copy_label(k); - button->redraw(); - - /* updates modebox */ - - modeBox->value(ch->mode); - modeBox->redraw(); - - /* update volumes+mute+solo */ - - vol->value(ch->volume); - mute->value(ch->mute); - solo->value(ch->solo); - -#ifdef WITH_VST - fx->full = ch->plugins.size > 0; -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -int gSampleChannel::keyPress(int e) -{ - return handleKey(e, ch->key); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::addActionButton() -{ - /* quit if 'R' exists yet. */ - - if (readActions != NULL) - return; - - mainButton->size(mainButton->w()-24, mainButton->h()); - - redraw(); - - readActions = new gClick(mainButton->x() + mainButton->w() + 4, - mainButton->y(), 20, 20, "", readActionOff_xpm, - readActionOn_xpm); - readActions->type(FL_TOGGLE_BUTTON); - readActions->value(ch->readActions); - readActions->callback(cb_readActions, (void*)this); - add(readActions); - - /* hard redraw: there's no other way to avoid glitches when moving - * the 'R' button */ - - mainWin->keyboard->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::delActionButton(bool force) -{ - if (readActions == NULL) - return; - - /* TODO - readActions check is useless here */ - - if (!force && (readActions == NULL || ch->hasActions)) - return; - - remove(readActions); // delete from Keyboard group (FLTK) - delete readActions; // delete (C++) - readActions = NULL; - - mainButton->size(mainButton->w()+24, mainButton->h()); - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::resize(int X, int Y, int W, int H) -{ - gChannel::resize(X, Y, W, H); - - if (w() < BREAK_FX) { -#ifdef WITH_VST - fx->hide(); -#endif - mainButton->size(w() - BREAK_DELTA, mainButton->h()); - mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - solo->resize(mute->x()+mute->w()+4, y(), 20, 20); - } - else - if (w() < BREAK_MODE_BOX) { -#ifdef WITH_VST - fx->show(); -#endif - mainButton->size(w() - (BREAK_DELTA + BREAK_UNIT), mainButton->h()); - mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - solo->resize(mute->x()+mute->w()+4, y(), 20, 20); - modeBox->hide(); - } - else - if (w() < BREAK_READ_ACTIONS) { - modeBox->show(); - mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 2)), mainButton->h()); - modeBox->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - if (readActions) { - readActions->hide(); - } - } - else { - if (readActions) { - mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 3)), mainButton->h()); - readActions->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - readActions->show(); - } - } - - gChannel::init_sizes(); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gSampleMainButton::gSampleMainButton(int x, int y, int w, int h, const char *l) - : gMainButton(x, y, w, h, l) {} - - -/* -------------------------------------------------------------------------- */ - - -int gSampleMainButton::handle(int e) -{ - int ret = gClick::handle(e); - switch (e) { - case FL_DND_ENTER: - case FL_DND_DRAG: - case FL_DND_RELEASE: { - ret = 1; - break; - } - case FL_PASTE: { - gSampleChannel *gch = (gSampleChannel*) parent(); // parent is gSampleChannel - SampleChannel *ch = gch->ch; - int result = glue_loadChannel(ch, gTrim(gStripFileUrl(Fl::event_text())).c_str()); - if (result != SAMPLE_LOADED_OK) - mainWin->keyboard->printChannelMessage(result); - ret = 1; - break; - } - } - return ret; -} diff --git a/src/ge_sampleChannel.h b/src/ge_sampleChannel.h deleted file mode 100644 index 09238d7..0000000 --- a/src/ge_sampleChannel.h +++ /dev/null @@ -1,103 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_sampleChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_SAMPLE_CHANNEL_H -#define GE_SAMPLE_CHANNEL_H - - -#include -#include -#include -#include "ge_channel.h" -#include "ge_mixed.h" - - -class gSampleChannel : public gChannel -{ -private: - - static void cb_button (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_openMenu (Fl_Widget *v, void *p); - static void cb_changeVol (Fl_Widget *v, void *p); - static void cb_readActions (Fl_Widget *v, void *p); -#ifdef WITH_VST - static void cb_openFxWindow (Fl_Widget *v, void *p); -#endif - - inline void __cb_mute (); - inline void __cb_solo (); - inline void __cb_changeVol (); - inline void __cb_button (); - inline void __cb_openMenu (); - inline void __cb_readActions (); -#ifdef WITH_VST - inline void __cb_openFxWindow(); -#endif - - void openBrowser(int type); - -public: - - gSampleChannel(int x, int y, int w, int h, class SampleChannel *ch); - - void reset (); - void update (); - void refresh (); - int keyPress(int event); - void resize (int x, int y, int w, int h); - - /* add/delActionButton - * add or remove 'R' button when actions are available. 'Status' is - * the initial status of the button: on or off. - * If force==true remove the button with no further checks. */ - - void addActionButton(); - void delActionButton(bool force=false); - - class gModeBox *modeBox; - class gClick *readActions; - - class SampleChannel *ch; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gSampleMainButton : public gMainButton -{ -public: - gSampleMainButton(int x, int y, int w, int h, const char *l=0); - int handle(int e); -}; - - -#endif diff --git a/src/ge_waveform.cpp b/src/ge_waveform.cpp deleted file mode 100644 index 5dcbdad..0000000 --- a/src/ge_waveform.cpp +++ /dev/null @@ -1,838 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_waveform - * an element which represents a waveform. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include -#include -#include "ge_waveform.h" -#include "gd_editor.h" -#include "wave.h" -#include "conf.h" -#include "glue.h" -#include "mixer.h" -#include "waveFx.h" -#include "ge_mixed.h" -#include "gg_waveTools.h" -#include "channel.h" -#include "sampleChannel.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; - - -gWaveform::gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l) -: Fl_Widget(x, y, w, h, l), - chan(ch), - menuOpen(false), - chanStart(0), - chanStartLit(false), - chanEnd(0), - chanEndLit(false), - ratio(0.0f), - selectionA(0), - selectionB(0), - selectionA_abs(0), - selectionB_abs(0) -{ - data.sup = NULL; - data.inf = NULL; - data.size = 0; - - grid.snap = G_Conf.sampleEditorGridOn; - grid.level = G_Conf.sampleEditorGridVal; - - stretchToWindow(); -} - - -/* ------------------------------------------------------------------ */ - - -gWaveform::~gWaveform() -{ - freeData(); -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::freeData() -{ - if (data.sup != NULL) { - free(data.sup); - free(data.inf); - data.sup = NULL; - data.inf = NULL; - data.size = 0; - } - grid.points.clear(); -} - - -/* ------------------------------------------------------------------ */ - - -int gWaveform::alloc(int datasize) -{ - ratio = chan->wave->size / (float) datasize; - - if (ratio < 2) - return 0; - - freeData(); - - data.size = datasize; - data.sup = (int*) malloc(data.size * sizeof(int)); - data.inf = (int*) malloc(data.size * sizeof(int)); - - int offset = h() / 2; - int zero = y() + offset; // center, zero amplitude (-inf dB) - - /* grid frequency: store a grid point every 'gridFreq' pixel. Must be - * even, as always */ - - int gridFreq = 0; - if (grid.level != 0) { - gridFreq = chan->wave->size / grid.level; - if (gridFreq % 2 != 0) - gridFreq--; - } - - for (int i=0; iwave->size - 1) / (float) datasize); - pn = (i+1) * ((chan->wave->size - 1) / (float) datasize); - - if (pp % 2 != 0) pp -= 1; - if (pn % 2 != 0) pn -= 1; - - float peaksup = 0.0f; - float peakinf = 0.0f; - - /* scan the original data in chunks */ - - int k = pp; - while (k < pn) { - - if (chan->wave->data[k] > peaksup) - peaksup = chan->wave->data[k]; // FIXME - Left data only - else - if (chan->wave->data[k] <= peakinf) - peakinf = chan->wave->data[k]; // FIXME - Left data only - - /* print grid */ - - if (gridFreq != 0) - if (k % gridFreq == 0 && k != 0) - grid.points.add(i); - - k += 2; - } - - data.sup[i] = zero - (peaksup * chan->boost * offset); - data.inf[i] = zero - (peakinf * chan->boost * offset); - - // avoid window overflow - - if (data.sup[i] < y()) data.sup[i] = y(); - if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1; - } - - recalcPoints(); - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::recalcPoints() -{ - selectionA = relativePoint(selectionA_abs); - selectionB = relativePoint(selectionB_abs); - chanStart = relativePoint(chan->begin / 2); - - /* fix the rounding error when chanEnd is set on the very end of the - * sample */ - - if (chan->end == chan->wave->size) - chanEnd = data.size - 2; // 2 px border - else - chanEnd = relativePoint(chan->end / 2); -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::draw() -{ - /* blank canvas */ - - fl_rectf(x(), y(), w(), h(), COLOR_BG_0); - - /* draw selection (if any) */ - - if (selectionA != selectionB) { - - int a_x = selectionA + x() - BORDER; // - start; - int b_x = selectionB + x() - BORDER; // - start; - - if (a_x < 0) - a_x = 0; - if (b_x >= w()-1) - b_x = w()-1; - - if (selectionA < selectionB) - fl_rectf(a_x+BORDER, y(), b_x-a_x, h(), COLOR_BD_0); - else - fl_rectf(b_x+BORDER, y(), a_x-b_x, h(), COLOR_BD_0); - } - - /* draw waveform from x1 (offset driven by the scrollbar) to x2 - * (width of parent window). We don't draw the entire waveform, - * only the visibile part. */ - - int offset = h() / 2; - int zero = y() + offset; // sample zero (-inf dB) - - int wx1 = abs(x() - ((gWaveTools*)parent())->x()); - int wx2 = wx1 + ((gWaveTools*)parent())->w(); - if (x()+w() < ((gWaveTools*)parent())->w()) - wx2 = x() + w() - BORDER; - - fl_color(0, 0, 0); - for (int i=wx1; i w()+x()-2) - fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT); - else { - fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT); - fl_color(255, 255, 255); - fl_draw("s", lineX+4, y()+h()-3); - } - - /* print chanEnd */ - - lineX = x()+chanEnd; - if (chanEndLit) fl_color(COLOR_BD_1); - else fl_color(COLOR_BD_0); - - fl_line(lineX, y()+1, lineX, y()+h()-2); - - if (lineX-FLAG_WIDTH < x()) - fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT); - else { - fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT); - fl_color(255, 255, 255); - fl_draw("e", lineX-10, y()+10); - } -} - - -/* ------------------------------------------------------------------ */ - - -int gWaveform::handle(int e) -{ - int ret = 0; - - switch (e) { - - case FL_PUSH: { - - mouseX = Fl::event_x(); - pushed = true; - - if (!mouseOnEnd() && !mouseOnStart()) { - - /* right button? show the menu. Don't set selectionA,B,etc */ - - if (Fl::event_button3()) { - openEditMenu(); - } - else - if (mouseOnSelectionA() || mouseOnSelectionB()) { - resized = true; - } - else { - dragged = true; - selectionA = Fl::event_x() - x(); - - if (selectionA >= data.size) selectionA = data.size; - - selectionB = selectionA; - selectionA_abs = absolutePoint(selectionA); - selectionB_abs = selectionA_abs; - } - } - - ret = 1; - break; - } - - case FL_RELEASE: { - - /* don't recompute points if something is selected */ - - if (selectionA != selectionB) { - pushed = false; - dragged = false; - ret = 1; - break; - } - - int realChanStart = chan->begin; - int realChanEnd = chan->end; - - if (chanStartLit) - realChanStart = absolutePoint(chanStart)*2; - else - if (chanEndLit) - realChanEnd = absolutePoint(chanEnd)*2; - - glue_setBeginEndChannel((gdEditor *) window(), chan, realChanStart, realChanEnd, false); - - pushed = false; - dragged = false; - - redraw(); - ret = 1; - break; - } - - case FL_ENTER: { // enables FL_DRAG - ret = 1; - break; - } - - case FL_LEAVE: { - if (chanStartLit || chanEndLit) { - chanStartLit = false; - chanEndLit = false; - redraw(); - } - ret = 1; - break; - } - - case FL_MOVE: { - mouseX = Fl::event_x(); - mouseY = Fl::event_y(); - - if (mouseOnStart()) { - chanStartLit = true; - redraw(); - } - else - if (chanStartLit) { - chanStartLit = false; - redraw(); - } - - if (mouseOnEnd()) { - chanEndLit = true; - redraw(); - } - else - if (chanEndLit) { - chanEndLit = false; - redraw(); - } - - if (mouseOnSelectionA()) - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - else - if (mouseOnSelectionB()) - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - else - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); - - ret = 1; - break; - } - - case FL_DRAG: { - - /* here the mouse is on the chanStart tool */ - - if (chanStartLit && pushed) { - - chanStart = Fl::event_x() - x(); - - if (grid.snap) - chanStart = applySnap(chanStart); - - if (chanStart < 0) - chanStart = 0; - else - if (chanStart >= chanEnd) - chanStart = chanEnd-2; - - redraw(); - } - else - if (chanEndLit && pushed) { - - chanEnd = Fl::event_x() - x(); - - if (grid.snap) - chanEnd = applySnap(chanEnd); - - if (chanEnd >= data.size - 2) - chanEnd = data.size - 2; - else - if (chanEnd <= chanStart) - chanEnd = chanStart + 2; - - redraw(); - } - - /* here the mouse is on the waveform, i.e. a selection */ - - else - if (dragged) { - - selectionB = Fl::event_x() - x(); - - if (selectionB >= data.size) - selectionB = data.size; - - if (selectionB <= 0) - selectionB = 0; - - if (grid.snap) - selectionB = applySnap(selectionB); - - selectionB_abs = absolutePoint(selectionB); - redraw(); - } - - /* here the mouse is on a selection boundary i.e. resize */ - - else - if (resized) { - int pos = Fl::event_x() - x(); - if (mouseOnSelectionA()) { - selectionA = grid.snap ? applySnap(pos) : pos; - selectionA_abs = absolutePoint(selectionA); - } - else - if (mouseOnSelectionB()) { - selectionB = grid.snap ? applySnap(pos) : pos; - selectionB_abs = absolutePoint(selectionB); - } - redraw(); - } - mouseX = Fl::event_x(); - ret = 1; - break; - } - } - return ret; -} - - -/* ------------------------------------------------------------------ */ - -/* pixel snap disances (10px) must be equal to those defined in - * gWaveform::mouseOnSelectionA() and gWaverfrom::mouseOnSelectionB() */ -/* TODO - use constant for 10px */ - -int gWaveform::applySnap(int pos) -{ - for (unsigned i=0; i= grid.points.at(i) - 10 && - pos <= grid.points.at(i) + 10) - { - return grid.points.at(i); - } - } - return pos; -} - - -/* ------------------------------------------------------------------ */ - - -bool gWaveform::mouseOnStart() -{ - return mouseX-10 > chanStart + x() - BORDER && - mouseX-10 <= chanStart + x() - BORDER + FLAG_WIDTH && - mouseY > h() + y() - FLAG_HEIGHT; -} - - -/* ------------------------------------------------------------------ */ - - -bool gWaveform::mouseOnEnd() -{ - return mouseX-10 >= chanEnd + x() - BORDER - FLAG_WIDTH && - mouseX-10 <= chanEnd + x() - BORDER && - mouseY <= y() + FLAG_HEIGHT + 1; -} - - -/* ------------------------------------------------------------------ */ - -/* pixel boundaries (10px) must be equal to the snap factor distance - * defined in gWaveform::applySnap() */ - -bool gWaveform::mouseOnSelectionA() -{ - if (selectionA == selectionB) - return false; - return mouseX >= selectionA-10+x() && mouseX <= selectionA+10+x(); -} - - -bool gWaveform::mouseOnSelectionB() -{ - if (selectionA == selectionB) - return false; - return mouseX >= selectionB-10+x() && mouseX <= selectionB+10+x(); -} - - -/* ------------------------------------------------------------------ */ - - -int gWaveform::absolutePoint(int p) -{ - if (p <= 0) - return 0; - - if (p > data.size) - return chan->wave->size / 2; - - return (p * ratio) / 2; -} - - -/* ------------------------------------------------------------------ */ - - -int gWaveform::relativePoint(int p) -{ - return (ceilf(p / ratio)) * 2; -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::openEditMenu() -{ - if (selectionA == selectionB) - return; - - menuOpen = true; - - Fl_Menu_Item menu[] = { - {"Cut"}, - {"Trim"}, - {"Silence"}, - {"Fade in"}, - {"Fade out"}, - {"Smooth edges"}, - {"Set start/end here"}, - {0} - }; - - if (chan->status == STATUS_PLAY) { - menu[0].deactivate(); - menu[1].deactivate(); - } - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(11); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) { - menuOpen = false; - return; - } - - /* straightSel() to ensure that point A is always lower than B */ - - straightSel(); - - if (strcmp(m->label(), "Silence") == 0) { - wfx_silence(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); - - selectionA = 0; - selectionB = 0; - - stretchToWindow(); - redraw(); - menuOpen = false; - return; - } - - if (strcmp(m->label(), "Set start/end here") == 0) { - - glue_setBeginEndChannel( - (gdEditor *) window(), // parent - chan, - absolutePoint(selectionA) * 2, // stereo! - absolutePoint(selectionB) * 2, // stereo! - false, // no recalc (we do it here) - false // don't check - ); - - selectionA = 0; - selectionB = 0; - selectionA_abs = 0; - selectionB_abs = 0; - - recalcPoints(); - redraw(); - menuOpen = false; - return; - } - - if (strcmp(m->label(), "Cut") == 0) { - wfx_cut(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); - - /* for convenience reset start/end points */ - - glue_setBeginEndChannel( - (gdEditor *) window(), - chan, - 0, - chan->wave->size, - false); - - selectionA = 0; - selectionB = 0; - selectionA_abs = 0; - selectionB_abs = 0; - - setZoom(0); - - menuOpen = false; - return; - } - - if (strcmp(m->label(), "Trim") == 0) { - wfx_trim(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); - - glue_setBeginEndChannel( - (gdEditor *) window(), - chan, - 0, - chan->wave->size, - false); - - selectionA = 0; - selectionB = 0; - selectionA_abs = 0; - selectionB_abs = 0; - - stretchToWindow(); - menuOpen = false; - redraw(); - return; - } - - if (!strcmp(m->label(), "Fade in") || !strcmp(m->label(), "Fade out")) { - - int type = !strcmp(m->label(), "Fade in") ? 0 : 1; - wfx_fade(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB), type); - - selectionA = 0; - selectionB = 0; - - stretchToWindow(); - redraw(); - menuOpen = false; - return; - } - - if (!strcmp(m->label(), "Smooth edges")) { - - wfx_smooth(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); - - selectionA = 0; - selectionB = 0; - - stretchToWindow(); - redraw(); - menuOpen = false; - return; - } -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::straightSel() -{ - if (selectionA > selectionB) { - unsigned tmp = selectionB; - selectionB = selectionA; - selectionA = tmp; - } -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::setZoom(int type) -{ - int newSize; - if (type == -1) newSize = data.size*2; // zoom in - else newSize = data.size/2; // zoom out - - if (alloc(newSize)) { - size(data.size, h()); - - /* zoom to pointer */ - - int shift; - if (x() > 0) - shift = Fl::event_x() - x(); - else - if (type == -1) - shift = Fl::event_x() + abs(x()); - else - shift = (Fl::event_x() + abs(x())) / -2; - - if (x() - shift > BORDER) - shift = 0; - - position(x() - shift, y()); - - - /* avoid overflow when zooming out with scrollbar like that: - * |----------[scrollbar]| - * - * offset vs smaller: - * |[wave------------| offset > 0 smaller = false - * |[wave----] | offset < 0, smaller = true - * |-------------] | offset < 0, smaller = false */ - - int parentW = ((gWaveTools*)parent())->w(); - int thisW = x() + w() - BORDER; // visible width, not full width - - if (thisW < parentW) - position(x() + parentW - thisW, y()); - if (smaller()) - stretchToWindow(); - - redraw(); - } -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::stretchToWindow() -{ - int s = ((gWaveTools*)parent())->w(); - alloc(s); - position(BORDER, y()); - size(s, h()); -} - - -/* ------------------------------------------------------------------ */ - - -bool gWaveform::smaller() -{ - return w() < ((gWaveTools*)parent())->w(); -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveform::setGridLevel(int l) -{ - grid.points.clear(); - grid.level = l; - alloc(data.size); - redraw(); -} diff --git a/src/ge_waveform.h b/src/ge_waveform.h deleted file mode 100644 index 65c1e40..0000000 --- a/src/ge_waveform.h +++ /dev/null @@ -1,193 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_waveform - * an element which represents a waveform. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GE_WAVEFORM_H -#define GE_WAVEFORM_H - -#include -#include -#include -#include -#include "utils.h" - -#define FLAG_WIDTH 14 -#define FLAG_HEIGHT 12 -#define BORDER 8 // window border <-> widget border - - -class gWaveform : public Fl_Widget { - -private: - - /* data - * real graphic stuff from the underlying waveform */ - - struct data { - int *sup; - int *inf; - int size; - } data; - - /* grid */ - - struct grid { - bool snap; - int level; - gVector points; - } grid; - - /* chan - * chan in use. */ - - class SampleChannel *chan; - - /* menuOpen - * is the menu open? */ - - bool menuOpen; - - /* mouseOnStart/end - * is mouse on start or end flag? */ - - bool mouseOnStart(); - bool mouseOnEnd(); - - /* mouseOnSelectionA/B - * as above, for the selection */ - - bool mouseOnSelectionA(); - bool mouseOnSelectionB(); - - /* absolutePoint - * from a relative 'p' point (zoom affected) returns the same point - * zoom 1:1 based */ - - int absolutePoint(int p); - - /* relativePoint - * from an absolute 'p' point (1:1 zoom), returns the same point zoom - * affected */ - - int relativePoint(int p); - - /* straightSel - * helper function which flattens the selection if it was made from - * right to left (inverse selection) */ - - void straightSel(); - - /* freeData - * destroy any graphical buffer */ - - void freeData(); - - /* smaller - * is the waveform smaller than the parent window? */ - - bool smaller(); - - /* applySnap - * snap a point at 'pos' pixel */ - - int applySnap(int pos); - -public: - - gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0); - ~gWaveform(); - void draw(); - int handle(int e); - - /* alloc - * allocate memory for the picture */ - - int alloc(int datasize=0); - - /* recalcPoints - * re-calc chanStart, chanEnd, ... */ - - void recalcPoints(); - - /* openEditMenu - * show edit menu on right-click */ - - void openEditMenu(); - - /* displayRatio - * how much of the waveform is being displayed on screen */ - - inline float displayRatio() { return 1.0f / (data.size / (float) w()); }; - - /* zoom - * type == 1 : zoom out, type == -1: zoom in */ - - void setZoom(int type); - - /* strecthToWindow - * shrink or enlarge the waveform to match parent's width (gWaveTools) */ - - void stretchToWindow(); - - /* setGridLevel - * set a new frequency level for the grid. 0 means disabled. */ - - void setGridLevel(int l); - - inline void setSnap(bool v) { grid.snap = v; } - inline bool getSnap() { return grid.snap; } - - inline int getSize() { return data.size; } - - int chanStart; - bool chanStartLit; - int chanEnd; - bool chanEndLit; - bool pushed; - bool dragged; - bool resized; - - float ratio; - - /* TODO - useless! use Fl::mouse_x() and Fl::mouse_y() instead */ - int mouseX; // mouse pos for drag.n.drop - int mouseY; - - /* selectionA/B = portion of the selected wave - * " " "" " _abs = selectionA/B not affected by zoom */ - /** TODO - change selectionA to selectionA_rel - TODO - change selectionB to selectionB_rel */ - int selectionA; - int selectionB; - int selectionA_abs; - int selectionB_abs; -}; - - -#endif diff --git a/src/ge_window.cpp b/src/ge_window.cpp deleted file mode 100644 index aba6fc0..0000000 --- a/src/ge_window.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_window - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "ge_window.h" -#include "log.h" - - -gWindow::gWindow(int x, int y, int w, int h, const char *title, int id) - : Fl_Double_Window(x, y, w, h, title), id(id), parent(NULL) { } - - -/* ------------------------------------------------------------------ */ - - -gWindow::gWindow(int w, int h, const char *title, int id) - : Fl_Double_Window(w, h, title), id(id), parent(NULL) { } - - -/* ------------------------------------------------------------------ */ - - -gWindow::~gWindow() { - - /* delete all subwindows in order to empty the stack */ - - for (unsigned i=0; igetParent() != NULL) - (child->getParent())->delSubWindow(child); -} - - -/* ------------------------------------------------------------------ */ - - -void gWindow::addSubWindow(gWindow *w) { - - /** TODO - useless: delete ---------------------------------------- */ - for (unsigned i=0; igetId() == subWindows.at(i)->getId()) { - //gLog("[gWindow] 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.add(w); - //debug(); -} - - -/* ------------------------------------------------------------------ */ - - -void gWindow::delSubWindow(gWindow *w) { - for (unsigned i=0; igetId() == subWindows.at(i)->getId()) { - delete subWindows.at(i); - subWindows.del(i); - //debug(); - return; - } - //debug(); -} - - -/* ------------------------------------------------------------------ */ - - -void gWindow::delSubWindow(int id) { - for (unsigned i=0; igetId() == id) { - delete subWindows.at(i); - subWindows.del(i); - //debug(); - return; - } - //debug(); -} - - -/* ------------------------------------------------------------------ */ - - -int gWindow::getId() { - return id; -} - - -/* ------------------------------------------------------------------ */ - - -void gWindow::setId(int id) { - this->id = id; -} - - -/* ------------------------------------------------------------------ */ - - -void gWindow::debug() { - gLog("---- window stack (id=%d): ----\n", getId()); - for (unsigned i=0; igetId()); - gLog("----\n"); -} - - -/* ------------------------------------------------------------------ */ - - -gWindow *gWindow::getParent() { - return parent; -} - - -/* ------------------------------------------------------------------ */ - - -void gWindow::setParent(gWindow *w) { - parent = w; -} - - -/* ------------------------------------------------------------------ */ - - -bool gWindow::hasWindow(int id) { - for (unsigned i=0; igetId()) - return true; - return false; -} - - -/* ------------------------------------------------------------------ */ - - -gWindow *gWindow::getChild(int id) { - for (unsigned i=0; igetId()) - return subWindows.at(i); - return NULL; -} diff --git a/src/ge_window.h b/src/ge_window.h deleted file mode 100644 index 79d772e..0000000 --- a/src/ge_window.h +++ /dev/null @@ -1,73 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_window - * A custom window. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef __GE_WINDOW_H__ -#define __GE_WINDOW_H__ - - -#include -#include "utils.h" - - -class gWindow : public Fl_Double_Window { - -protected: - gVector subWindows; - int id; - gWindow *parent; - -public: - gWindow(int x, int y, int w, int h, const char *title=0, int id=0); - gWindow(int w, int h, const char *title=0, int id=0); - ~gWindow(); - - static void cb_closeChild(Fl_Widget *v, void *p); - - void addSubWindow(gWindow *w); - void delSubWindow(gWindow *w); - void delSubWindow(int id); - - int getId(); - void setId(int id); - void debug(); - - void setParent(gWindow *); - gWindow *getParent(); - gWindow *getChild(int id); - - /* hasWindow - * true if the window with id 'id' exists in the stack. */ - - bool hasWindow(int id); - -}; - - -#endif diff --git a/src/gg_keyboard.cpp b/src/gg_keyboard.cpp deleted file mode 100644 index 8ea9be8..0000000 --- a/src/gg_keyboard.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gg_keyboard - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "gg_keyboard.h" -#include "gd_browser.h" -#include "gd_mainWindow.h" -#include "gd_editor.h" -#include "gd_warnings.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" -#include "mixer.h" -#include "conf.h" -#include "const.h" -#include "glue.h" -#include "patch.h" -#include "channel.h" -#include "sampleChannel.h" -#include "log.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch G_Patch; -extern gdMainWindow *mainWin; - - -int gKeyboard::indexColumn = 0; - - -/* -------------------------------------------------------------------------- */ - - -gKeyboard::gKeyboard(int X, int Y, int W, int H) -: Fl_Scroll (X, Y, W, H), - bckspcPressed(false), - endPressed (false), - spacePressed (false), - addColumnBtn (NULL) -{ - color(COLOR_BG_MAIN); - type(Fl_Scroll::BOTH_ALWAYS); - scrollbar.color(COLOR_BG_0); - scrollbar.selection_color(COLOR_BG_1); - scrollbar.labelcolor(COLOR_BD_1); - scrollbar.slider(G_BOX); - hscrollbar.color(COLOR_BG_0); - hscrollbar.selection_color(COLOR_BG_1); - hscrollbar.labelcolor(COLOR_BD_1); - hscrollbar.slider(G_BOX); - - addColumnBtn = new gClick(8, y(), 200, 20, "Add new column"); - addColumnBtn->callback(cb_addColumn, (void*) this); - add(addColumnBtn); - - init(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::init() -{ - /* add 6 empty columns as init layout */ - - __cb_addColumn(); - __cb_addColumn(); - __cb_addColumn(); - __cb_addColumn(); - __cb_addColumn(); - __cb_addColumn(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::freeChannel(gChannel *gch) -{ - gch->reset(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::deleteChannel(gChannel *gch) -{ - for (unsigned i=0; ifind(gch); - if (k != columns.at(i)->children()) { - columns.at(i)->deleteChannel(gch); - return; - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::updateChannel(gChannel *gch) -{ - gch->update(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::organizeColumns() -{ - /* if only one column exists don't cleanup: the initial column must - * stay here. */ - - if (columns.size == 1) - return; - - /* otherwise delete all empty columns */ - /** FIXME - this for loop might not work correctly! */ - - for (unsigned i=columns.size-1; i>=1; i--) { - if (columns.at(i)->isEmpty()) { - //Fl::delete_widget(columns.at(i)); - delete columns.at(i); - columns.del(i); - } - } - - /* compact column, avoid empty spaces */ - - for (unsigned i=1; iposition(columns.at(i-1)->x() + columns.at(i-1)->w() + 16, y()); - - addColumnBtn->position(columns.last()->x() + columns.last()->w() + 16, y()); - - redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::cb_addColumn(Fl_Widget *v, void *p) { ((gKeyboard*)p)->__cb_addColumn(); } - - -/* -------------------------------------------------------------------------- */ - - -gChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build) -{ - gColumn *col = getColumn(colIndex); - - /* no column with index 'colIndex' found? Just create it and set its index - to 'colIndex'. */ - - if (!col) { - __cb_addColumn(); - col = columns.last(); - col->setIndex(colIndex); - gLog("[gKeyboard::addChannel] created new column with index=%d\n", colIndex); - } - - gLog("[gKeyboard::addChannel] add to column with index = %d\n", col->getIndex()); - return col->addChannel(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::refreshColumns() -{ - for (unsigned i=0; irefreshChannels(); -} - - -/* -------------------------------------------------------------------------- */ - - -gColumn *gKeyboard::getColumn(int index) -{ - for (unsigned i=0; igetIndex() == index) - return columns.at(i); - return NULL; -} - - -/* -------------------------------------------------------------------------- */ - - -int gKeyboard::handle(int e) -{ - int ret = Fl_Group::handle(e); // assume the buttons won't handle the Keyboard events - switch (e) { - case FL_FOCUS: - case FL_UNFOCUS: { - ret = 1; // enables receiving Keyboard events - break; - } - case FL_SHORTCUT: // in case widget that isn't ours has focus - case FL_KEYDOWN: // Keyboard key pushed - case FL_KEYUP: { // Keyboard key released - - /* rewind session. Avoid retrigs */ - - if (e == FL_KEYDOWN) { - if (Fl::event_key() == FL_BackSpace && !bckspcPressed) { - bckspcPressed = true; - glue_rewindSeq(); - ret = 1; - break; - } - else if (Fl::event_key() == FL_End && !endPressed) { - endPressed = true; - glue_startStopInputRec(false); // update gui - ret = 1; - break; - } - else if (Fl::event_key() == FL_Enter && !enterPressed) { - enterPressed = true; - glue_startStopActionRec(); - ret = 1; - break; - } - else if (Fl::event_key() == ' ' && !spacePressed) { - spacePressed = true; - G_Mixer.running ? glue_stopSeq() : glue_startSeq(); // TODO - glue_startStopSeq, no core logic here - ret = 1; - break; - } - } - else if (e == FL_KEYUP) { - if (Fl::event_key() == FL_BackSpace) - bckspcPressed = false; - else if (Fl::event_key() == FL_End) - endPressed = false; - else if (Fl::event_key() == ' ') - spacePressed = false; - else if (Fl::event_key() == FL_Enter) - enterPressed = false; - } - - /* Walk button arrays, trying to match button's label with the Keyboard event. - * If found, set that button's value() based on up/down event, - * and invoke that button's callback() */ - - for (unsigned i=0; ichildren(); k++) - ret &= ((gChannel*)columns.at(i)->child(k))->keyPress(e); - break; - } - } - return ret; -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::clear() -{ - for (unsigned i=0; iposition(8, y()); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::setChannelWithActions(gSampleChannel *gch) -{ - if (gch->ch->hasActions) - gch->addActionButton(); - else - gch->delActionButton(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::printChannelMessage(int res) -{ - if (res == SAMPLE_NOT_VALID) - gdAlert("This is not a valid WAVE file."); - else if (res == SAMPLE_MULTICHANNEL) - gdAlert("Multichannel samples not supported."); - else if (res == SAMPLE_WRONG_BIT) - gdAlert("This sample has an\nunsupported bit-depth (> 32 bit)."); - else if (res == SAMPLE_WRONG_ENDIAN) - gdAlert("This sample has a wrong\nbyte order (not little-endian)."); - else if (res == SAMPLE_WRONG_FORMAT) - gdAlert("This sample is encoded in\nan unsupported audio format."); - else if (res == SAMPLE_READ_ERROR) - gdAlert("Unable to read this sample."); - else if (res == SAMPLE_PATH_TOO_LONG) - gdAlert("File path too long."); - else - gdAlert("Unknown error."); -} - -/* -------------------------------------------------------------------------- */ - - -void gKeyboard::__cb_addColumn() -{ - int colx; - int colxw; - int colw = 380; - if (columns.size == 0) { - colx = x() - xposition(); // mind the offset with xposition() - colxw = colx + colw; - } - else { - gColumn *prev = columns.last(); - colx = prev->x()+prev->w() + 16; - colxw = colx + colw; - } - - /* add gColumn to gKeyboard and to columns vector */ - - gColumn *gc = new gColumn(colx, y(), colw-20, 2000, indexColumn, this); - add(gc); - columns.add(gc); - indexColumn++; - - /* move addColumn button */ - - addColumnBtn->position(colxw-4, y()); - redraw(); - - gLog("[gKeyboard::__cb_addColumn] new column added (index = %d), total count=%d, addColumn(x)=%d\n", - gc->getIndex(), columns.size, addColumnBtn->x()); -} diff --git a/src/gg_keyboard.h b/src/gg_keyboard.h deleted file mode 100644 index 6059824..0000000 --- a/src/gg_keyboard.h +++ /dev/null @@ -1,145 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gg_keyboard - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GG_KEYBOARD_H -#define GG_KEYBOARD_H - - -#include -#include -#include -#include -#include -#include "ge_column.h" -#include "utils.h" - - -class gKeyboard : public Fl_Scroll -{ -private: - - static void cb_addColumn (Fl_Widget *v, void *p); - inline void __cb_addColumn(); - - bool bckspcPressed; - bool endPressed; - bool spacePressed; - bool enterPressed; - - /* indexColumn - * the last index used for column. */ - - static int indexColumn; - - class gClick *addColumnBtn; - - /* columns - * a vector of columns which in turn contain channels. */ - - gVector columns; - -public: - - gKeyboard(int X, int Y, int W, int H); - - int handle(int e); - - /* init - * build the initial setup of empty channels. */ - - void init(); - - /* addChannel - * add a new channel to gChannels. Used by callbacks and during - * patch loading. Requires Channel (and not gChannel). If build is - * set to true, also generate the corresponding column.*/ - - gChannel *addChannel(int column, class Channel *ch, bool build=false); - - /* deleteChannel - * delete a channel from gChannels<> where gChannel->ch == ch and remove - * it from the stack. */ - - void deleteChannel(gChannel *gch); - - /* freeChannel - * free a channel from gChannels<> where gChannel->ch == ch. No channels - * are deleted */ - - void freeChannel(gChannel *gch); - - /* updateChannel - * wrapper function to call gch->update(). */ - - void updateChannel(gChannel *gch); - - /* organizeColumns - * reorganize columns layout by removing empty gaps. */ - - void organizeColumns(); - - /* refreshColumns - * refresh each column's channel, called on each GUI cycle. */ - - void refreshColumns(); - - /* getColumn - * return the column with index 'index', or NULL if not found. */ - - gColumn *getColumn(int index); - - /* clear - * delete all channels and groups. */ - - void clear(); - - /* setChannelWithActions - * add 'R' button if channel has actions, and set recorder to active. */ - - void setChannelWithActions(class gSampleChannel *gch); - - /* printChannelMessage - * given any output by glue_loadChannel, print the message on screen - * on a gdAlert subwindow. */ - - void printChannelMessage(int res); - - /* getTotalColumns */ - - inline unsigned getTotalColumns() { return columns.size; } - - /* getColumnWidth - * return the width in pixel of i-th column. Warning: 'i' is the i-th column - * in the column array, NOT the index. */ - - inline int getColumnWidth(int i) { return getColumn(i)->w(); } -}; - - -#endif diff --git a/src/gg_waveTools.cpp b/src/gg_waveTools.cpp deleted file mode 100644 index d8462dd..0000000 --- a/src/gg_waveTools.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gg_waveTools - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include "gg_waveTools.h" -#include "graphics.h" -#include "ge_mixed.h" -#include "ge_waveform.h" -#include "mixer.h" - - -gWaveTools::gWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l) - : Fl_Scroll(x, y, w, h, l) -{ - type(Fl_Scroll::HORIZONTAL_ALWAYS); - hscrollbar.color(COLOR_BG_0); - hscrollbar.selection_color(COLOR_BG_1); - hscrollbar.labelcolor(COLOR_BD_1); - hscrollbar.slider(G_BOX); - - waveform = new gWaveform(x, y, w, h-24, ch); - - - //resizable(waveform); -} - - - -/* ------------------------------------------------------------------ */ - - -void gWaveTools::updateWaveform() -{ - waveform->alloc(w()); - waveform->redraw(); -} - - -/* ------------------------------------------------------------------ */ - - -void gWaveTools::resize(int x, int y, int w, int h) -{ - if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize - Fl_Widget::resize(x, y, w, h); - waveform->resize(x, y, waveform->w(), h-24); - updateWaveform(); - } - else { // horizontal resize - Fl_Widget::resize(x, y, w, h); - } - - if (this->w() > waveform->w()) - waveform->stretchToWindow(); - - int offset = waveform->x() + waveform->w() - this->w() - this->x(); - if (offset < 0) - waveform->position(waveform->x()-offset, this->y()); -} - - -/* ------------------------------------------------------------------ */ - - -int gWaveTools::handle(int e) -{ - int ret = Fl_Group::handle(e); - switch (e) { - case FL_MOUSEWHEEL: { - waveform->setZoom(Fl::event_dy()); - redraw(); - ret = 1; - break; - } - } - return ret; -} - diff --git a/src/gg_waveTools.h b/src/gg_waveTools.h deleted file mode 100644 index 8f81ba1..0000000 --- a/src/gg_waveTools.h +++ /dev/null @@ -1,49 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gg_waveTools - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GG_WAVETOOLS_H -#define GG_WAVETOOLS_H - -#include -#include -#include - - -class gWaveTools : public Fl_Scroll { -public: - class gWaveform *waveform; - - gWaveTools(int X,int Y,int W, int H, class SampleChannel *ch, const char *L=0); - void resize(int x, int y, int w, int h); - int handle(int e); - - void updateWaveform(); -}; - -#endif diff --git a/src/giada.ico b/src/giada.ico deleted file mode 100644 index e0a90bd..0000000 Binary files a/src/giada.ico and /dev/null differ diff --git a/src/glue.cpp b/src/glue.cpp deleted file mode 100644 index 60d0a17..0000000 --- a/src/glue.cpp +++ /dev/null @@ -1,1189 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * glue - * Intermediate layer GUI <-> CORE. - * - * How to know if you need another glue_ function? Ask yourself if the - * new action will ever be called via MIDI or keyboard/mouse. If yes, - * put it here. - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "glue.h" -#include "ge_waveform.h" -#include "gd_mainWindow.h" -#include "gd_editor.h" -#include "gd_warnings.h" -#include "gg_waveTools.h" -#include "ge_mixed.h" -#include "gg_keyboard.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" -#include "gui_utils.h" -#include "mixerHandler.h" -#include "mixer.h" -#include "recorder.h" -#include "wave.h" -#include "pluginHost.h" -#include "channel.h" -#include "sampleChannel.h" -#include "midiChannel.h" -#include "utils.h" -#include "kernelMidi.h" -#include "log.h" -#include "patch.h" -#include "conf.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; -extern Patch G_Patch; -extern Conf G_Conf; -extern bool G_audio_status; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -static bool __soloSession__ = false; - - -/* -------------------------------------------------------------------------- */ - - -int glue_loadChannel(SampleChannel *ch, const char *fname) -{ - /* save the patch and take the last browser's dir in order to re-use it - * the next time */ - - G_Conf.setPath(G_Conf.samplePath, gDirname(fname).c_str()); - - int result = ch->load(fname); - - if (result == SAMPLE_LOADED_OK) - mainWin->keyboard->updateChannel(ch->guiChannel); - - return result; -} - - -/* -------------------------------------------------------------------------- */ - - -Channel *glue_addChannel(int column, int type) -{ - Channel *ch = G_Mixer.addChannel(type); - gChannel *gch = mainWin->keyboard->addChannel(column, ch); - ch->guiChannel = gch; - glue_setChanVol(ch, 1.0, false); // false = not from gui click - return ch; -} - - -/* -------------------------------------------------------------------------- */ - - -int glue_loadPatch(const char *fname, const char *fpath, gProgress *status, bool isProject) -{ - /* update browser's status bar with % 0.1 */ - - status->show(); - status->value(0.1f); - //Fl::check(); - Fl::wait(0); - - /* is it a valid patch? */ - - int res = G_Patch.open(fname); - if (res != PATCH_OPEN_OK) - return res; - - /* close all other windows. This prevents segfault if plugin windows - * GUI are on. */ - - if (res) - gu_closeAllSubwindows(); - - /* reset the system. False(1): don't update the gui right now. False(2): do - * not create empty columns. */ - - glue_resetToInitState(false, false); - - status->value(0.2f); // progress status: % 0.2 - //Fl::check(); - Fl::wait(0); - - /* mixerHandler will update the samples inside Mixer */ - - mh_loadPatch(isProject, fname); - - /* take the patch name and update the main window's title */ - - G_Patch.getName(); - gu_update_win_label(G_Patch.name); - - status->value(0.4f); // progress status: 0.4 - //Fl::check(); - Fl::wait(0); - - G_Patch.readRecs(); - status->value(0.6f); // progress status: 0.6 - //Fl::check(); - Fl::wait(0); - -#ifdef WITH_VST - int resPlugins = G_Patch.readPlugins(); - status->value(0.8f); // progress status: 0.8 - //Fl::check(); - Fl::wait(0); -#endif - - /* this one is vital: let recorder recompute the actions' positions if - * the current samplerate != patch samplerate */ - - recorder::updateSamplerate(G_Conf.samplerate, G_Patch.samplerate); - - /* update gui */ - - gu_updateControls(); - - status->value(1.0f); // progress status: 1.0 (done) - //Fl::check(); - Fl::wait(0); - - /* save patchPath by taking the last dir of the broswer, in order to - * reuse it the next time */ - - G_Conf.setPath(G_Conf.patchPath, fpath); - - gLog("[glue] patch %s loaded\n", fname); - -#ifdef WITH_VST - if (resPlugins != 1) - gdAlert("Some VST plugins were not loaded successfully."); -#endif - - return res; -} - - -/* -------------------------------------------------------------------------- */ - - -int glue_savePatch(const char *fullpath, const char *name, bool isProject) -{ - if (G_Patch.write(fullpath, name, isProject) == 1) { - strcpy(G_Patch.name, name); - G_Patch.name[strlen(name)] = '\0'; - gu_update_win_label(name); - gLog("[glue] patch saved as %s\n", fullpath); - return 1; - } - else - return 0; -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_deleteChannel(Channel *ch) -{ - int index = ch->index; - recorder::clearChan(index); - Fl::lock(); - mainWin->keyboard->deleteChannel(ch->guiChannel); - Fl::unlock(); - G_Mixer.deleteChannel(ch); - gu_closeAllSubwindows(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_freeChannel(Channel *ch) -{ - mainWin->keyboard->freeChannel(ch->guiChannel); - recorder::clearChan(ch->index); - ch->empty(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setBpm(const char *v1, const char *v2) -{ - char buf[6]; - float value = atof(v1) + (atof(v2)/10); - if (value < 20.0f) { - value = 20.0f; - sprintf(buf, "20.0"); - } - else - sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2); - - /* a value such as atof("120.1") will never be 120.1 but 120.0999999, - * because of the rounding error. So we pass the real "wrong" value to - * G_Mixer and we show the nice looking (but fake) one to the GUI. */ - - float old_bpm = G_Mixer.bpm; - G_Mixer.bpm = value; - G_Mixer.updateFrameBars(); - - /* inform recorder and actionEditor of the change */ - - recorder::updateBpm(old_bpm, value, G_Mixer.quanto); - gu_refreshActionEditor(); - - mainWin->timing->setBpm(buf); - gLog("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setBeats(int beats, int bars, bool expand) -{ - /* temp vars to store old data (they are necessary) */ - - int oldvalue = G_Mixer.beats; - unsigned oldfpb = G_Mixer.totalFrames; - - if (beats > MAX_BEATS) - G_Mixer.beats = MAX_BEATS; - else if (beats < 1) - G_Mixer.beats = 1; - else - G_Mixer.beats = beats; - - /* update bars - bars cannot be greate than beats and must be a sub - * multiple of beats. If not, approximation to the nearest (and greater) - * value available. */ - - if (bars > G_Mixer.beats) - G_Mixer.bars = G_Mixer.beats; - else if (bars <= 0) - G_Mixer.bars = 1; - else if (beats % bars != 0) { - G_Mixer.bars = bars + (beats % bars); - if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it) - G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars); - } - else - G_Mixer.bars = bars; - - G_Mixer.updateFrameBars(); - - /* update recorded actions */ - - if (expand) { - if (G_Mixer.beats > oldvalue) - recorder::expand(oldfpb, G_Mixer.totalFrames); - //else if (G_Mixer.beats < oldvalue) - // recorder::shrink(G_Mixer.totalFrames); - } - - mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); - gu_refreshActionEditor(); // in case the action editor is open -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopSeq(bool gui) -{ - G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startSeq(bool gui) -{ - G_Mixer.running = true; - - if (gui) { -#ifdef __linux__ - kernelAudio::jackStart(); -#endif - } - - if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) { - kernelMidi::send(MIDI_START, -1, -1); - kernelMidi::send(MIDI_POSITION_PTR, 0, 0); - } - - if (gui) Fl::lock(); - mainWin->controller->updatePlay(1); - if (gui) Fl::unlock(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_stopSeq(bool gui) { - - mh_stopSequencer(); - - if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) - kernelMidi::send(MIDI_STOP, -1, -1); - -#ifdef __linux__ - if (gui) - 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::active) { - recorder::active = false; - if (gui) Fl::lock(); - mainWin->controller->updateRecAction(0); - if (gui) 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 (G_Mixer.chanInput != NULL) { - mh_stopInputRec(); - if (gui) Fl::lock(); - mainWin->controller->updateRecInput(0); - if (gui) Fl::unlock(); - } - - if (gui) Fl::lock(); - mainWin->controller->updatePlay(0); - if (gui) Fl::unlock(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_rewindSeq() { - mh_rewindSequencer(); - if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) - kernelMidi::send(MIDI_POSITION_PTR, 0, 0); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopActionRec() { - recorder::active ? glue_stopActionRec() : glue_startActionRec(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startActionRec() { - if (G_audio_status == false) - return; - if (!G_Mixer.running) - glue_startSeq(); // start the sequencer for convenience - recorder::active = true; - - Fl::lock(); - mainWin->controller->updateRecAction(1); - Fl::unlock(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_stopActionRec() { - - /* stop the recorder and sort new actions */ - - recorder::active = false; - recorder::sortActions(); - - for (unsigned i=0; itype == CHANNEL_SAMPLE) { - SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); - if (ch->hasActions) - ch->readActions = true; - else - ch->readActions = false; - mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel); - } - - Fl::lock(); - mainWin->controller->updateRecAction(0); - Fl::unlock(); - - /* in case acton editor is on, refresh it */ - - gu_refreshActionEditor(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopReadingRecs(SampleChannel *ch, bool gui) { - if (ch->readActions) - glue_stopReadingRecs(ch, gui); - else - glue_startReadingRecs(ch, gui); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startReadingRecs(SampleChannel *ch, bool gui) { - if (G_Conf.treatRecsAsLoops) - ch->recStatus = REC_WAITING; - else - ch->setReadActions(true); - if (!gui) { - gSampleChannel *gch = (gSampleChannel*)ch->guiChannel; - if (gch->readActions) { // if button exists - Fl::lock(); - gch->readActions->value(1); - Fl::unlock(); - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_stopReadingRecs(SampleChannel *ch, bool gui) { - - /* if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put - * the channel in REC_ENDING status */ - - if (G_Conf.treatRecsAsLoops) - ch->recStatus = REC_ENDING; - else - ch->setReadActions(false); - if (!gui) { - gSampleChannel *gch = (gSampleChannel*)ch->guiChannel; - if (gch->readActions) { // if button exists - Fl::lock(); - gch->readActions->value(0); - Fl::unlock(); - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_quantize(int val) { - G_Mixer.quantize = val; - G_Mixer.updateQuanto(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setChanVol(Channel *ch, float v, bool gui) { - - ch->volume = v; - - /* also update wave editor if it's shown */ - - gdEditor *editor = (gdEditor*) gu_getSubwindow(mainWin, WID_SAMPLE_EDITOR); - if (editor) { - glue_setVolEditor(editor, (SampleChannel*) ch, v, false); - Fl::lock(); - editor->volume->value(v); - Fl::unlock(); - } - - if (!gui) { - Fl::lock(); - ch->guiChannel->vol->value(v); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setOutVol(float v, bool gui) { - G_Mixer.outVol = v; - if (!gui) { - Fl::lock(); - mainWin->inOut->setOutVol(v); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setInVol(float v, bool gui) -{ - G_Mixer.inVol = v; - if (!gui) { - Fl::lock(); - mainWin->inOut->setInVol(v); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_clearAllSamples() -{ - G_Mixer.running = false; - for (unsigned i=0; iempty(); - G_Mixer.channels.at(i)->guiChannel->reset(); - } - recorder::init(); - return; -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_clearAllRecs() -{ - recorder::init(); - gu_updateControls(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_resetToInitState(bool resetGui, bool createColumns) -{ - G_Mixer.ready = false; - - mh_clear(); - mainWin->keyboard->clear(); - if (createColumns) - mainWin->keyboard->init(); - recorder::init(); - G_Patch.setDefault(); - G_Mixer.init(); -#ifdef WITH_VST - G_PluginHost.freeAllStacks(); -#endif - - if (resetGui) - gu_updateControls(); - - G_Mixer.ready = true; -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopMetronome(bool gui) -{ - G_Mixer.metronome = !G_Mixer.metronome; - if (!gui) { - Fl::lock(); - mainWin->controller->updateMetronome(G_Mixer.metronome); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e, bool recalc, bool check) -{ - if (check) { - if (e > ch->wave->size) - e = ch->wave->size; - if (b < 0) - b = 0; - if (b > ch->wave->size) - b = ch->wave->size-2; - if (b >= ch->end) - b = ch->begin; - if (e <= ch->begin) - e = ch->end; - } - - /* continue only if new values != old values */ - - if (b == ch->begin && e == ch->end) - return; - - /* print mono values */ - - char tmp[16]; - sprintf(tmp, "%d", b/2); - win->chanStart->value(tmp); - - tmp[0] = '\0'; - sprintf(tmp, "%d", e/2); - win->chanEnd->value(tmp); - - ch->setBegin(b); - ch->setEnd(e); - - /* recalc is not needed when the user drags the bars directly over the waveform */ - - if (recalc) { - win->waveTools->waveform->recalcPoints(); // importante, altrimenti non si vedono - win->waveTools->waveform->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric) -{ - if (numeric) { - if (val > 20.0f) - val = 20.0f; - else if (val < 0.0f) - val = 0.0f; - - float linear = pow(10, (val / 20)); // linear = 10^(dB/20) - - ch->boost = linear; - - char buf[10]; - sprintf(buf, "%.2f", val); - win->boostNum->value(buf); - win->boostNum->redraw(); - - win->boost->value(linear); - win->boost->redraw(); /// inutile - } - else { - ch->boost = val; - char buf[10]; - sprintf(buf, "%.2f", 20*log10(val)); - win->boostNum->value(buf); - win->boostNum->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setVolEditor(class gdEditor *win, SampleChannel *ch, float val, bool numeric) -{ - if (numeric) { - if (val > 0.0f) - val = 0.0f; - else if (val < -60.0f) - val = -INFINITY; - - float linear = pow(10, (val / 20)); // linear = 10^(dB/20) - - ch->volume = linear; - - win->volume->value(linear); - win->volume->redraw(); - - char buf[10]; - if (val > -INFINITY) - sprintf(buf, "%.2f", val); - else - sprintf(buf, "-inf"); - win->volumeNum->value(buf); - win->volumeNum->redraw(); - - ch->guiChannel->vol->value(linear); - ch->guiChannel->vol->redraw(); - } - else { - ch->volume = val; - - float dbVal = 20 * log10(val); - char buf[10]; - if (dbVal > -INFINITY) - sprintf(buf, "%.2f", dbVal); - else - sprintf(buf, "-inf"); - - win->volumeNum->value(buf); - win->volumeNum->redraw(); - - ch->guiChannel->vol->value(val); - ch->guiChannel->vol->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setMute(Channel *ch, bool gui) -{ - if (recorder::active && recorder::canRec(ch)) { - if (!ch->mute) - recorder::startOverdub(ch->index, ACTION_MUTES, G_Mixer.actualFrame); - else - recorder::stopOverdub(G_Mixer.actualFrame); - } - - ch->mute ? ch->unsetMute(false) : ch->setMute(false); - - if (!gui) { - Fl::lock(); - ch->guiChannel->mute->value(ch->mute); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setSoloOn(Channel *ch, bool gui) -{ - /* if there's no solo session, store mute configuration of all chans - * and start the session */ - - if (!__soloSession__) { - for (unsigned i=0; imute_s = och->mute; - } - __soloSession__ = true; - } - - ch->solo = !ch->solo; - ch->sendMidiLsolo(); - - /* mute all other channels and unmute this (if muted) */ - - for (unsigned i=0; isolo && !och->mute) { - och->setMute(false); - Fl::lock(); - och->guiChannel->mute->value(true); - Fl::unlock(); - } - } - - if (ch->mute) { - ch->unsetMute(false); - Fl::lock(); - ch->guiChannel->mute->value(false); - Fl::unlock(); - } - - if (!gui) { - Fl::lock(); - ch->guiChannel->solo->value(1); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setSoloOff(Channel *ch, bool gui) -{ - /* if this is uniqueSolo, stop solo session and restore mute status, - * else mute this */ - - if (mh_uniqueSolo(ch)) { - __soloSession__ = false; - for (unsigned i=0; imute_s) { - och->setMute(false); - Fl::lock(); - och->guiChannel->mute->value(true); - Fl::unlock(); - } - else { - och->unsetMute(false); - Fl::lock(); - och->guiChannel->mute->value(false); - Fl::unlock(); - } - och->mute_s = false; - } - } - else { - ch->setMute(false); - Fl::lock(); - ch->guiChannel->mute->value(true); - Fl::unlock(); - } - - ch->solo = !ch->solo; - ch->sendMidiLsolo(); - - if (!gui) { - Fl::lock(); - ch->guiChannel->solo->value(0); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setPanning(class gdEditor *win, SampleChannel *ch, float val) -{ - if (val < 1.0f) { - ch->panLeft = 1.0f; - ch->panRight= 0.0f + val; - - char buf[8]; - sprintf(buf, "%d L", abs((ch->panRight * 100.0f) - 100)); - win->panNum->value(buf); - } - else if (val == 1.0f) { - ch->panLeft = 1.0f; - ch->panRight= 1.0f; - win->panNum->value("C"); - } - else { - ch->panLeft = 2.0f - val; - ch->panRight= 1.0f; - - char buf[8]; - sprintf(buf, "%d R", abs((ch->panLeft * 100.0f) - 100)); - win->panNum->value(buf); - } - win->panNum->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopInputRec(bool gui, bool alert) -{ - if (G_Mixer.chanInput == NULL) { - if (!glue_startInputRec(gui)) { - if (alert) gdAlert("No channels available for recording."); - else gLog("[glue] no channels available for recording\n"); - } - } - else - glue_stopInputRec(gui); -} - - -/* -------------------------------------------------------------------------- */ - - -int glue_startInputRec(bool gui) -{ - if (G_audio_status == false) - return -1; - - SampleChannel *ch = mh_startInputRec(); - if (ch == NULL) { // no chans available - Fl::lock(); - mainWin->controller->updateRecInput(0); - Fl::unlock(); - return 0; - } - - if (!G_Mixer.running) { - glue_startSeq(); - Fl::lock(); - mainWin->controller->updatePlay(1); - Fl::unlock(); - } - - glue_setChanVol(ch, 1.0f, false); // false = not from gui click - - ch->guiChannel->mainButton->label(ch->wave->name.c_str()); - - if (!gui) { - Fl::lock(); - mainWin->controller->updateRecInput(1); - Fl::unlock(); - } - - return 1; - -} - - -/* -------------------------------------------------------------------------- */ - - -int glue_stopInputRec(bool gui) -{ - SampleChannel *ch = mh_stopInputRec(); - - if (ch->mode & (LOOP_BASIC | LOOP_ONCE | LOOP_REPEAT)) - ch->start(0, true); // on frame 0: user-generated event - - if (!gui) { - Fl::lock(); - mainWin->controller->updateRecInput(0); - Fl::unlock(); - } - - return 1; -} - - -/* -------------------------------------------------------------------------- */ - - -int glue_saveProject(const char *folderPath, const char *projName) -{ - if (gIsProject(folderPath)) { - gLog("[glue] the project folder already exists\n"); - // don't exit - } - else if (!gMkdir(folderPath)) { - gLog("[glue] unable to make project directory!\n"); - return 0; - } - - /* copy all samples inside the folder. Takes and logical ones are saved - * via glue_saveSample() */ - - for (unsigned i=0; itype == CHANNEL_SAMPLE) { - - SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); - - if (ch->wave == NULL) - continue; - - /* update the new samplePath: everything now comes from the - * project folder (folderPath) */ - - char samplePath[PATH_MAX]; - sprintf(samplePath, "%s%s%s.%s", folderPath, gGetSlash().c_str(), ch->wave->basename().c_str(), ch->wave->extension().c_str()); - - /* remove any existing file */ - - if (gFileExists(samplePath)) - remove(samplePath); - if (ch->save(samplePath)) - ch->wave->pathfile = samplePath; - } - } - - char gptcPath[PATH_MAX]; - sprintf(gptcPath, "%s%s%s.gptc", folderPath, gGetSlash().c_str(), gStripExt(projName).c_str()); - glue_savePatch(gptcPath, projName, true); // true == it's a project - - return 1; -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyPress(Channel *ch, bool ctrl, bool shift) -{ - if (ch->type == CHANNEL_SAMPLE) - glue_keyPress((SampleChannel*)ch, ctrl, shift); - else - glue_keyPress((MidiChannel*)ch, ctrl, shift); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyRelease(Channel *ch, bool ctrl, bool shift) -{ - if (ch->type == CHANNEL_SAMPLE) - glue_keyRelease((SampleChannel*)ch, ctrl, shift); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift) -{ - if (ctrl) - glue_setMute(ch); - else - if (shift) - ch->kill(0); // on frame 0: user-generated event - else - ch->start(0, true); // on frame 0: user-generated event -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift) -{ - /* case CTRL */ - - if (ctrl) - glue_setMute(ch); - - /* case SHIFT - * - * action recording on: - * if seq is playing, rec a killchan - * action recording off: - * if chan has recorded events: - * | if seq is playing OR channel 'c' is stopped, de/activate recs - * | else kill chan - * else kill chan */ - - else - if (shift) { - if (recorder::active) { - if (G_Mixer.running) { - ch->kill(0); // on frame 0: user-generated event - if (recorder::canRec(ch) && !(ch->mode & LOOP_ANY)) // don't record killChan actions for LOOP channels - recorder::rec(ch->index, ACTION_KILLCHAN, G_Mixer.actualFrame); - } - } - else { - if (ch->hasActions) { - if (G_Mixer.running || ch->status == STATUS_OFF) - ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch); - else - ch->kill(0); // on frame 0: user-generated event - } - else - ch->kill(0); // on frame 0: user-generated event - } - } - - /* case no modifier */ - - else { - - /* record now if the quantizer is off, otherwise let mixer to handle it - * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are - * meaningless for loop modes */ - - if (G_Mixer.quantize == 0 && - recorder::canRec(ch) && - !(ch->mode & LOOP_ANY)) - { - if (ch->mode == SINGLE_PRESS) - recorder::startOverdub(ch->index, ACTION_KEYS, G_Mixer.actualFrame); - else - recorder::rec(ch->index, ACTION_KEYPRESS, G_Mixer.actualFrame); - } - - ch->start(0, true); // on frame 0: user-generated event - } - - /* the GUI update is done by gui_refresh() */ -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift) -{ - if (!ctrl && !shift) { - ch->stop(); - - /* record a key release only if channel is single_press. For any - * other mode the KEY REL is meaningless. */ - - if (ch->mode == SINGLE_PRESS && recorder::canRec(ch)) - recorder::stopOverdub(G_Mixer.actualFrame); - } - - /* the GUI update is done by gui_refresh() */ - -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setPitch(class gdEditor *win, SampleChannel *ch, float val, bool numeric) -{ - if (numeric) { - if (val <= 0.0f) - val = 0.1000f; - if (val > 4.0f) - val = 4.0000f; - if (win) - win->pitch->value(val); - } - - ch->setPitch(val); - - if (win) { - char buf[16]; - sprintf(buf, "%.4f", val); - Fl::lock(); - win->pitchNum->value(buf); - win->pitchNum->redraw(); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -/* never expand or shrink recordings (last param of setBeats = false): - * this is live manipulation */ - -void glue_beatsMultiply() -{ - glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false); -} - -void glue_beatsDivide() -{ - glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false); -} diff --git a/src/glue.h b/src/glue.h deleted file mode 100644 index d018a6c..0000000 --- a/src/glue.h +++ /dev/null @@ -1,169 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * glue - * Intermediate layer GUI <-> CORE. - * - * How to know if you need another glue_ function? Ask yourself if the - * new action will ever be called via MIDI or keyboard/mouse. If yes, - * put it here. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef GLUE_H -#define GLUE_H - -/* addChannel - * add an empty new channel to the stack. Returns the new channel. */ - -class Channel *glue_addChannel(int column, int type); - -/* loadChannel - * fill an existing channel with a wave. */ - -int glue_loadChannel(class SampleChannel *ch, const char *fname); - -void glue_deleteChannel(class Channel *ch); - -void glue_freeChannel(class Channel *ch); - -/** FIXME - nobody will call these via MIDI/keyb/mouse! */ -int glue_loadPatch(const char *fname, const char *fpath, class gProgress *status, bool isProject); -int glue_savePatch(const char *fullpath, const char *name, bool isProject); - -/* keyPress / keyRelease - * handle the key pressure, either via mouse/keyboard or MIDI. If gui - * is true it means that the event comes from the main window (mouse, - * keyb or MIDI), otherwise the event comes from the action recorder. */ - -void glue_keyPress (class Channel *ch, bool ctrl=0, bool shift=0); -void glue_keyPress (class SampleChannel *ch, bool ctrl=0, bool shift=0); -void glue_keyPress (class MidiChannel *ch, bool ctrl=0, bool shift=0); -void glue_keyRelease(class Channel *ch, bool ctrl=0, bool shift=0); -void glue_keyRelease(class SampleChannel *ch, bool ctrl=0, bool shift=0); - -void glue_setBpm(const char *v1, const char *v2); -void glue_setBeats(int beats, int bars, bool expand); - -/* start, stop, rewind sequencer - * if gui == true the signal comes from an internal interaction on the - * GUI, otherwise it's a MIDI/Jack/external signal. */ - -void glue_startStopSeq(bool gui=true); -void glue_startSeq (bool gui=true); -void glue_stopSeq (bool gui=true); -void glue_rewindSeq (); - -/* start/stopActionRec - * handle the action recording. */ - -void glue_startStopActionRec(); -void glue_startActionRec(); -void glue_stopActionRec(); - -/* start/stopInputRec - * handle the input recording (take). If gui == true the signal comes - * from an internal interaction on the GUI, otherwise it's a - * MIDI/Jack/external signal. Alert displays or not the popup message - * if there are no available channels. */ - -void glue_startStopInputRec(bool gui=true, bool alert=true); -int glue_startInputRec (bool gui=true); -int glue_stopInputRec (bool gui=true); - -/* start/stopReadingRecs - * handle the 'R' button. If gui == true the signal comes from an - * internal interaction on the GUI, otherwise it's a MIDI/Jack/external - * signal. */ - -void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true); -void glue_startReadingRecs (class SampleChannel *ch, bool gui=true); -void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true); - -void glue_quantize(int val); - -void glue_setChanVol(class Channel *ch, float v, bool gui=true); -void glue_setOutVol (float v, bool gui=true); -void glue_setInVol (float v, bool gui=true); - -void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val); - -void glue_clearAllSamples(); -void glue_clearAllRecs(); - -/* resetToInitState - * reset Giada to init state. If resetGui also refresh all widgets. If - * createColumns also build initial empty columns. */ - -void glue_resetToInitState(bool resetGui=true, bool createColumns=true); - -void glue_startStopMetronome(bool gui=true); - -/* setBeginEndChannel - * sets start/end points in the sample editor. - * Recalc=false: don't recalc internal position - * check=true: check the points' consistency */ - -/** FIXME - nobody will call this via MIDI/keyb/mouse! */ -void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch, int b, int e, - bool recalc=false, bool check=true); - -/** FIXME - nobody will call this via MIDI/keyb/mouse! */ -void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); - -void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); - -/* setVolEditor - * handles the volume inside the SAMPLE EDITOR (not the main gui). The - * numeric flag tells if we want to handle the dial or the numeric input - * field. */ - - /** FIXME - nobody will call this via MIDI/keyb/mouse! */ -void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); - -/* mute - * set mute on or off. If gui == true the signal comes from an internal - * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ - -void glue_setMute(class Channel *ch, bool gui=true); - -/* solo on/off - * set solo on and off. If gui == true the signal comes from an internal - * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ - -void glue_setSoloOn (class Channel *ch, bool gui=true); -void glue_setSoloOff(class Channel *ch, bool gui=true); - -/** FIXME - nobody will call this via MIDI/keyb/mouse! */ -int glue_saveProject(const char *folderPath, const char *projName); - - -/* beatsDivide/Multiply - * shrinks or enlarges the number of beats by 2. */ - -void glue_beatsMultiply(); -void glue_beatsDivide(); - -#endif diff --git a/src/glue/glue.cpp b/src/glue/glue.cpp new file mode 100644 index 0000000..c2ec2c5 --- /dev/null +++ b/src/glue/glue.cpp @@ -0,0 +1,1189 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * glue + * Intermediate layer GUI <-> CORE. + * + * How to know if you need another glue_ function? Ask yourself if the + * new action will ever be called via MIDI or keyboard/mouse. If yes, + * put it here. + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../gui/elems/ge_waveform.h" +#include "../gui/elems/ge_mixed.h" +#include "../gui/elems/ge_channel.h" +#include "../gui/elems/ge_sampleChannel.h" +#include "../gui/elems/ge_waveTools.h" +#include "../gui/elems/ge_keyboard.h" +#include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/gd_editor.h" +#include "../gui/dialogs/gd_warnings.h" +#include "../utils/gui_utils.h" +#include "../utils/utils.h" +#include "../utils/log.h" +#include "../core/mixerHandler.h" +#include "../core/mixer.h" +#include "../core/recorder.h" +#include "../core/wave.h" +#include "../core/pluginHost.h" +#include "../core/channel.h" +#include "../core/sampleChannel.h" +#include "../core/midiChannel.h" +#include "../core/kernelMidi.h" +#include "../core/patch.h" +#include "../core/conf.h" +#include "glue.h" + + +extern gdMainWindow *mainWin; +extern Mixer G_Mixer; +extern Patch G_Patch; +extern Conf G_Conf; +extern bool G_audio_status; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +static bool __soloSession__ = false; + + +/* -------------------------------------------------------------------------- */ + + +int glue_loadChannel(SampleChannel *ch, const char *fname) +{ + /* save the patch and take the last browser's dir in order to re-use it + * the next time */ + + G_Conf.setPath(G_Conf.samplePath, gDirname(fname).c_str()); + + int result = ch->load(fname); + + if (result == SAMPLE_LOADED_OK) + mainWin->keyboard->updateChannel(ch->guiChannel); + + return result; +} + + +/* -------------------------------------------------------------------------- */ + + +Channel *glue_addChannel(int column, int type) +{ + Channel *ch = G_Mixer.addChannel(type); + gChannel *gch = mainWin->keyboard->addChannel(column, ch); + ch->guiChannel = gch; + glue_setChanVol(ch, 1.0, false); // false = not from gui click + return ch; +} + + +/* -------------------------------------------------------------------------- */ + + +int glue_loadPatch(const char *fname, const char *fpath, gProgress *status, bool isProject) +{ + /* update browser's status bar with % 0.1 */ + + status->show(); + status->value(0.1f); + //Fl::check(); + Fl::wait(0); + + /* is it a valid patch? */ + + int res = G_Patch.open(fname); + if (res != PATCH_OPEN_OK) + return res; + + /* close all other windows. This prevents segfault if plugin windows + * GUI are on. */ + + if (res) + gu_closeAllSubwindows(); + + /* reset the system. False(1): don't update the gui right now. False(2): do + * not create empty columns. */ + + glue_resetToInitState(false, false); + + status->value(0.2f); // progress status: % 0.2 + //Fl::check(); + Fl::wait(0); + + /* mixerHandler will update the samples inside Mixer */ + + mh_loadPatch(isProject, fname); + + /* take the patch name and update the main window's title */ + + G_Patch.getName(); + gu_update_win_label(G_Patch.name); + + status->value(0.4f); // progress status: 0.4 + //Fl::check(); + Fl::wait(0); + + G_Patch.readRecs(); + status->value(0.6f); // progress status: 0.6 + //Fl::check(); + Fl::wait(0); + +#ifdef WITH_VST + int resPlugins = G_Patch.readPlugins(); + status->value(0.8f); // progress status: 0.8 + //Fl::check(); + Fl::wait(0); +#endif + + /* this one is vital: let recorder recompute the actions' positions if + * the current samplerate != patch samplerate */ + + recorder::updateSamplerate(G_Conf.samplerate, G_Patch.samplerate); + + /* update gui */ + + gu_updateControls(); + + status->value(1.0f); // progress status: 1.0 (done) + //Fl::check(); + Fl::wait(0); + + /* save patchPath by taking the last dir of the broswer, in order to + * reuse it the next time */ + + G_Conf.setPath(G_Conf.patchPath, fpath); + + gLog("[glue] patch %s loaded\n", fname); + +#ifdef WITH_VST + if (resPlugins != 1) + gdAlert("Some VST plugins were not loaded successfully."); +#endif + + return res; +} + + +/* -------------------------------------------------------------------------- */ + + +int glue_savePatch(const char *fullpath, const char *name, bool isProject) +{ + if (G_Patch.write(fullpath, name, isProject) == 1) { + strcpy(G_Patch.name, name); + G_Patch.name[strlen(name)] = '\0'; + gu_update_win_label(name); + gLog("[glue] patch saved as %s\n", fullpath); + return 1; + } + else + return 0; +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_deleteChannel(Channel *ch) +{ + int index = ch->index; + recorder::clearChan(index); + Fl::lock(); + mainWin->keyboard->deleteChannel(ch->guiChannel); + Fl::unlock(); + G_Mixer.deleteChannel(ch); + gu_closeAllSubwindows(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_freeChannel(Channel *ch) +{ + mainWin->keyboard->freeChannel(ch->guiChannel); + recorder::clearChan(ch->index); + ch->empty(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setBpm(const char *v1, const char *v2) +{ + char buf[6]; + float value = atof(v1) + (atof(v2)/10); + if (value < 20.0f) { + value = 20.0f; + sprintf(buf, "20.0"); + } + else + sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2); + + /* a value such as atof("120.1") will never be 120.1 but 120.0999999, + * because of the rounding error. So we pass the real "wrong" value to + * G_Mixer and we show the nice looking (but fake) one to the GUI. */ + + float old_bpm = G_Mixer.bpm; + G_Mixer.bpm = value; + G_Mixer.updateFrameBars(); + + /* inform recorder and actionEditor of the change */ + + recorder::updateBpm(old_bpm, value, G_Mixer.quanto); + gu_refreshActionEditor(); + + mainWin->timing->setBpm(buf); + gLog("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setBeats(int beats, int bars, bool expand) +{ + /* temp vars to store old data (they are necessary) */ + + int oldvalue = G_Mixer.beats; + unsigned oldfpb = G_Mixer.totalFrames; + + if (beats > MAX_BEATS) + G_Mixer.beats = MAX_BEATS; + else if (beats < 1) + G_Mixer.beats = 1; + else + G_Mixer.beats = beats; + + /* update bars - bars cannot be greate than beats and must be a sub + * multiple of beats. If not, approximation to the nearest (and greater) + * value available. */ + + if (bars > G_Mixer.beats) + G_Mixer.bars = G_Mixer.beats; + else if (bars <= 0) + G_Mixer.bars = 1; + else if (beats % bars != 0) { + G_Mixer.bars = bars + (beats % bars); + if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it) + G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars); + } + else + G_Mixer.bars = bars; + + G_Mixer.updateFrameBars(); + + /* update recorded actions */ + + if (expand) { + if (G_Mixer.beats > oldvalue) + recorder::expand(oldfpb, G_Mixer.totalFrames); + //else if (G_Mixer.beats < oldvalue) + // recorder::shrink(G_Mixer.totalFrames); + } + + mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); + gu_refreshActionEditor(); // in case the action editor is open +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopSeq(bool gui) +{ + G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startSeq(bool gui) +{ + G_Mixer.running = true; + + if (gui) { +#ifdef __linux__ + kernelAudio::jackStart(); +#endif + } + + if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) { + kernelMidi::send(MIDI_START, -1, -1); + kernelMidi::send(MIDI_POSITION_PTR, 0, 0); + } + + if (gui) Fl::lock(); + mainWin->controller->updatePlay(1); + if (gui) Fl::unlock(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_stopSeq(bool gui) { + + mh_stopSequencer(); + + if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) + kernelMidi::send(MIDI_STOP, -1, -1); + +#ifdef __linux__ + if (gui) + 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::active) { + recorder::active = false; + if (gui) Fl::lock(); + mainWin->controller->updateRecAction(0); + if (gui) 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 (G_Mixer.chanInput != NULL) { + mh_stopInputRec(); + if (gui) Fl::lock(); + mainWin->controller->updateRecInput(0); + if (gui) Fl::unlock(); + } + + if (gui) Fl::lock(); + mainWin->controller->updatePlay(0); + if (gui) Fl::unlock(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_rewindSeq() { + mh_rewindSequencer(); + if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) + kernelMidi::send(MIDI_POSITION_PTR, 0, 0); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopActionRec() { + recorder::active ? glue_stopActionRec() : glue_startActionRec(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startActionRec() { + if (G_audio_status == false) + return; + if (!G_Mixer.running) + glue_startSeq(); // start the sequencer for convenience + recorder::active = true; + + Fl::lock(); + mainWin->controller->updateRecAction(1); + Fl::unlock(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_stopActionRec() { + + /* stop the recorder and sort new actions */ + + recorder::active = false; + recorder::sortActions(); + + for (unsigned i=0; itype == CHANNEL_SAMPLE) { + SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); + if (ch->hasActions) + ch->readActions = true; + else + ch->readActions = false; + mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel); + } + + Fl::lock(); + mainWin->controller->updateRecAction(0); + Fl::unlock(); + + /* in case acton editor is on, refresh it */ + + gu_refreshActionEditor(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopReadingRecs(SampleChannel *ch, bool gui) { + if (ch->readActions) + glue_stopReadingRecs(ch, gui); + else + glue_startReadingRecs(ch, gui); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startReadingRecs(SampleChannel *ch, bool gui) { + if (G_Conf.treatRecsAsLoops) + ch->recStatus = REC_WAITING; + else + ch->setReadActions(true); + if (!gui) { + gSampleChannel *gch = (gSampleChannel*)ch->guiChannel; + if (gch->readActions) { // if button exists + Fl::lock(); + gch->readActions->value(1); + Fl::unlock(); + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_stopReadingRecs(SampleChannel *ch, bool gui) { + + /* if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put + * the channel in REC_ENDING status */ + + if (G_Conf.treatRecsAsLoops) + ch->recStatus = REC_ENDING; + else + ch->setReadActions(false); + if (!gui) { + gSampleChannel *gch = (gSampleChannel*)ch->guiChannel; + if (gch->readActions) { // if button exists + Fl::lock(); + gch->readActions->value(0); + Fl::unlock(); + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_quantize(int val) { + G_Mixer.quantize = val; + G_Mixer.updateQuanto(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setChanVol(Channel *ch, float v, bool gui) { + + ch->volume = v; + + /* also update wave editor if it's shown */ + + gdEditor *editor = (gdEditor*) gu_getSubwindow(mainWin, WID_SAMPLE_EDITOR); + if (editor) { + glue_setVolEditor(editor, (SampleChannel*) ch, v, false); + Fl::lock(); + editor->volume->value(v); + Fl::unlock(); + } + + if (!gui) { + Fl::lock(); + ch->guiChannel->vol->value(v); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setOutVol(float v, bool gui) { + G_Mixer.outVol = v; + if (!gui) { + Fl::lock(); + mainWin->inOut->setOutVol(v); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setInVol(float v, bool gui) +{ + G_Mixer.inVol = v; + if (!gui) { + Fl::lock(); + mainWin->inOut->setInVol(v); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_clearAllSamples() +{ + G_Mixer.running = false; + for (unsigned i=0; iempty(); + G_Mixer.channels.at(i)->guiChannel->reset(); + } + recorder::init(); + return; +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_clearAllRecs() +{ + recorder::init(); + gu_updateControls(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_resetToInitState(bool resetGui, bool createColumns) +{ + G_Mixer.ready = false; + + mh_clear(); + mainWin->keyboard->clear(); + if (createColumns) + mainWin->keyboard->init(); + recorder::init(); + G_Patch.setDefault(); + G_Mixer.init(); +#ifdef WITH_VST + G_PluginHost.freeAllStacks(); +#endif + + if (resetGui) + gu_updateControls(); + + G_Mixer.ready = true; +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopMetronome(bool gui) +{ + G_Mixer.metronome = !G_Mixer.metronome; + if (!gui) { + Fl::lock(); + mainWin->controller->updateMetronome(G_Mixer.metronome); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e, bool recalc, bool check) +{ + if (check) { + if (e > ch->wave->size) + e = ch->wave->size; + if (b < 0) + b = 0; + if (b > ch->wave->size) + b = ch->wave->size-2; + if (b >= ch->end) + b = ch->begin; + if (e <= ch->begin) + e = ch->end; + } + + /* continue only if new values != old values */ + + if (b == ch->begin && e == ch->end) + return; + + /* print mono values */ + + char tmp[16]; + sprintf(tmp, "%d", b/2); + win->chanStart->value(tmp); + + tmp[0] = '\0'; + sprintf(tmp, "%d", e/2); + win->chanEnd->value(tmp); + + ch->setBegin(b); + ch->setEnd(e); + + /* recalc is not needed when the user drags the bars directly over the waveform */ + + if (recalc) { + win->waveTools->waveform->recalcPoints(); // importante, altrimenti non si vedono + win->waveTools->waveform->redraw(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric) +{ + if (numeric) { + if (val > 20.0f) + val = 20.0f; + else if (val < 0.0f) + val = 0.0f; + + float linear = pow(10, (val / 20)); // linear = 10^(dB/20) + + ch->boost = linear; + + char buf[10]; + sprintf(buf, "%.2f", val); + win->boostNum->value(buf); + win->boostNum->redraw(); + + win->boost->value(linear); + win->boost->redraw(); /// inutile + } + else { + ch->boost = val; + char buf[10]; + sprintf(buf, "%.2f", 20*log10(val)); + win->boostNum->value(buf); + win->boostNum->redraw(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setVolEditor(class gdEditor *win, SampleChannel *ch, float val, bool numeric) +{ + if (numeric) { + if (val > 0.0f) + val = 0.0f; + else if (val < -60.0f) + val = -INFINITY; + + float linear = pow(10, (val / 20)); // linear = 10^(dB/20) + + ch->volume = linear; + + win->volume->value(linear); + win->volume->redraw(); + + char buf[10]; + if (val > -INFINITY) + sprintf(buf, "%.2f", val); + else + sprintf(buf, "-inf"); + win->volumeNum->value(buf); + win->volumeNum->redraw(); + + ch->guiChannel->vol->value(linear); + ch->guiChannel->vol->redraw(); + } + else { + ch->volume = val; + + float dbVal = 20 * log10(val); + char buf[10]; + if (dbVal > -INFINITY) + sprintf(buf, "%.2f", dbVal); + else + sprintf(buf, "-inf"); + + win->volumeNum->value(buf); + win->volumeNum->redraw(); + + ch->guiChannel->vol->value(val); + ch->guiChannel->vol->redraw(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setMute(Channel *ch, bool gui) +{ + if (recorder::active && recorder::canRec(ch)) { + if (!ch->mute) + recorder::startOverdub(ch->index, ACTION_MUTES, G_Mixer.actualFrame); + else + recorder::stopOverdub(G_Mixer.actualFrame); + } + + ch->mute ? ch->unsetMute(false) : ch->setMute(false); + + if (!gui) { + Fl::lock(); + ch->guiChannel->mute->value(ch->mute); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setSoloOn(Channel *ch, bool gui) +{ + /* if there's no solo session, store mute configuration of all chans + * and start the session */ + + if (!__soloSession__) { + for (unsigned i=0; imute_s = och->mute; + } + __soloSession__ = true; + } + + ch->solo = !ch->solo; + ch->sendMidiLsolo(); + + /* mute all other channels and unmute this (if muted) */ + + for (unsigned i=0; isolo && !och->mute) { + och->setMute(false); + Fl::lock(); + och->guiChannel->mute->value(true); + Fl::unlock(); + } + } + + if (ch->mute) { + ch->unsetMute(false); + Fl::lock(); + ch->guiChannel->mute->value(false); + Fl::unlock(); + } + + if (!gui) { + Fl::lock(); + ch->guiChannel->solo->value(1); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setSoloOff(Channel *ch, bool gui) +{ + /* if this is uniqueSolo, stop solo session and restore mute status, + * else mute this */ + + if (mh_uniqueSolo(ch)) { + __soloSession__ = false; + for (unsigned i=0; imute_s) { + och->setMute(false); + Fl::lock(); + och->guiChannel->mute->value(true); + Fl::unlock(); + } + else { + och->unsetMute(false); + Fl::lock(); + och->guiChannel->mute->value(false); + Fl::unlock(); + } + och->mute_s = false; + } + } + else { + ch->setMute(false); + Fl::lock(); + ch->guiChannel->mute->value(true); + Fl::unlock(); + } + + ch->solo = !ch->solo; + ch->sendMidiLsolo(); + + if (!gui) { + Fl::lock(); + ch->guiChannel->solo->value(0); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setPanning(class gdEditor *win, SampleChannel *ch, float val) +{ + if (val < 1.0f) { + ch->panLeft = 1.0f; + ch->panRight= 0.0f + val; + + char buf[8]; + sprintf(buf, "%d L", abs((ch->panRight * 100.0f) - 100)); + win->panNum->value(buf); + } + else if (val == 1.0f) { + ch->panLeft = 1.0f; + ch->panRight= 1.0f; + win->panNum->value("C"); + } + else { + ch->panLeft = 2.0f - val; + ch->panRight= 1.0f; + + char buf[8]; + sprintf(buf, "%d R", abs((ch->panLeft * 100.0f) - 100)); + win->panNum->value(buf); + } + win->panNum->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopInputRec(bool gui, bool alert) +{ + if (G_Mixer.chanInput == NULL) { + if (!glue_startInputRec(gui)) { + if (alert) gdAlert("No channels available for recording."); + else gLog("[glue] no channels available for recording\n"); + } + } + else + glue_stopInputRec(gui); +} + + +/* -------------------------------------------------------------------------- */ + + +int glue_startInputRec(bool gui) +{ + if (G_audio_status == false) + return -1; + + SampleChannel *ch = mh_startInputRec(); + if (ch == NULL) { // no chans available + Fl::lock(); + mainWin->controller->updateRecInput(0); + Fl::unlock(); + return 0; + } + + if (!G_Mixer.running) { + glue_startSeq(); + Fl::lock(); + mainWin->controller->updatePlay(1); + Fl::unlock(); + } + + glue_setChanVol(ch, 1.0f, false); // false = not from gui click + + ch->guiChannel->mainButton->label(ch->wave->name.c_str()); + + if (!gui) { + Fl::lock(); + mainWin->controller->updateRecInput(1); + Fl::unlock(); + } + + return 1; + +} + + +/* -------------------------------------------------------------------------- */ + + +int glue_stopInputRec(bool gui) +{ + SampleChannel *ch = mh_stopInputRec(); + + if (ch->mode & (LOOP_BASIC | LOOP_ONCE | LOOP_REPEAT)) + ch->start(0, true); // on frame 0: user-generated event + + if (!gui) { + Fl::lock(); + mainWin->controller->updateRecInput(0); + Fl::unlock(); + } + + return 1; +} + + +/* -------------------------------------------------------------------------- */ + + +int glue_saveProject(const char *folderPath, const char *projName) +{ + if (gIsProject(folderPath)) { + gLog("[glue] the project folder already exists\n"); + // don't exit + } + else if (!gMkdir(folderPath)) { + gLog("[glue] unable to make project directory!\n"); + return 0; + } + + /* copy all samples inside the folder. Takes and logical ones are saved + * via glue_saveSample() */ + + for (unsigned i=0; itype == CHANNEL_SAMPLE) { + + SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); + + if (ch->wave == NULL) + continue; + + /* update the new samplePath: everything now comes from the + * project folder (folderPath) */ + + char samplePath[PATH_MAX]; + sprintf(samplePath, "%s%s%s.%s", folderPath, gGetSlash().c_str(), ch->wave->basename().c_str(), ch->wave->extension().c_str()); + + /* remove any existing file */ + + if (gFileExists(samplePath)) + remove(samplePath); + if (ch->save(samplePath)) + ch->wave->pathfile = samplePath; + } + } + + char gptcPath[PATH_MAX]; + sprintf(gptcPath, "%s%s%s.gptc", folderPath, gGetSlash().c_str(), gStripExt(projName).c_str()); + glue_savePatch(gptcPath, projName, true); // true == it's a project + + return 1; +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyPress(Channel *ch, bool ctrl, bool shift) +{ + if (ch->type == CHANNEL_SAMPLE) + glue_keyPress((SampleChannel*)ch, ctrl, shift); + else + glue_keyPress((MidiChannel*)ch, ctrl, shift); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyRelease(Channel *ch, bool ctrl, bool shift) +{ + if (ch->type == CHANNEL_SAMPLE) + glue_keyRelease((SampleChannel*)ch, ctrl, shift); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift) +{ + if (ctrl) + glue_setMute(ch); + else + if (shift) + ch->kill(0); // on frame 0: user-generated event + else + ch->start(0, true); // on frame 0: user-generated event +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift) +{ + /* case CTRL */ + + if (ctrl) + glue_setMute(ch); + + /* case SHIFT + * + * action recording on: + * if seq is playing, rec a killchan + * action recording off: + * if chan has recorded events: + * | if seq is playing OR channel 'c' is stopped, de/activate recs + * | else kill chan + * else kill chan */ + + else + if (shift) { + if (recorder::active) { + if (G_Mixer.running) { + ch->kill(0); // on frame 0: user-generated event + if (recorder::canRec(ch) && !(ch->mode & LOOP_ANY)) // don't record killChan actions for LOOP channels + recorder::rec(ch->index, ACTION_KILLCHAN, G_Mixer.actualFrame); + } + } + else { + if (ch->hasActions) { + if (G_Mixer.running || ch->status == STATUS_OFF) + ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch); + else + ch->kill(0); // on frame 0: user-generated event + } + else + ch->kill(0); // on frame 0: user-generated event + } + } + + /* case no modifier */ + + else { + + /* record now if the quantizer is off, otherwise let mixer to handle it + * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are + * meaningless for loop modes */ + + if (G_Mixer.quantize == 0 && + recorder::canRec(ch) && + !(ch->mode & LOOP_ANY)) + { + if (ch->mode == SINGLE_PRESS) + recorder::startOverdub(ch->index, ACTION_KEYS, G_Mixer.actualFrame); + else + recorder::rec(ch->index, ACTION_KEYPRESS, G_Mixer.actualFrame); + } + + ch->start(0, true); // on frame 0: user-generated event + } + + /* the GUI update is done by gui_refresh() */ +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift) +{ + if (!ctrl && !shift) { + ch->stop(); + + /* record a key release only if channel is single_press. For any + * other mode the KEY REL is meaningless. */ + + if (ch->mode == SINGLE_PRESS && recorder::canRec(ch)) + recorder::stopOverdub(G_Mixer.actualFrame); + } + + /* the GUI update is done by gui_refresh() */ + +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setPitch(class gdEditor *win, SampleChannel *ch, float val, bool numeric) +{ + if (numeric) { + if (val <= 0.0f) + val = 0.1000f; + if (val > 4.0f) + val = 4.0000f; + if (win) + win->pitch->value(val); + } + + ch->setPitch(val); + + if (win) { + char buf[16]; + sprintf(buf, "%.4f", val); + Fl::lock(); + win->pitchNum->value(buf); + win->pitchNum->redraw(); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +/* never expand or shrink recordings (last param of setBeats = false): + * this is live manipulation */ + +void glue_beatsMultiply() +{ + glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false); +} + +void glue_beatsDivide() +{ + glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false); +} diff --git a/src/glue/glue.h b/src/glue/glue.h new file mode 100644 index 0000000..d018a6c --- /dev/null +++ b/src/glue/glue.h @@ -0,0 +1,169 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * glue + * Intermediate layer GUI <-> CORE. + * + * How to know if you need another glue_ function? Ask yourself if the + * new action will ever be called via MIDI or keyboard/mouse. If yes, + * put it here. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GLUE_H +#define GLUE_H + +/* addChannel + * add an empty new channel to the stack. Returns the new channel. */ + +class Channel *glue_addChannel(int column, int type); + +/* loadChannel + * fill an existing channel with a wave. */ + +int glue_loadChannel(class SampleChannel *ch, const char *fname); + +void glue_deleteChannel(class Channel *ch); + +void glue_freeChannel(class Channel *ch); + +/** FIXME - nobody will call these via MIDI/keyb/mouse! */ +int glue_loadPatch(const char *fname, const char *fpath, class gProgress *status, bool isProject); +int glue_savePatch(const char *fullpath, const char *name, bool isProject); + +/* keyPress / keyRelease + * handle the key pressure, either via mouse/keyboard or MIDI. If gui + * is true it means that the event comes from the main window (mouse, + * keyb or MIDI), otherwise the event comes from the action recorder. */ + +void glue_keyPress (class Channel *ch, bool ctrl=0, bool shift=0); +void glue_keyPress (class SampleChannel *ch, bool ctrl=0, bool shift=0); +void glue_keyPress (class MidiChannel *ch, bool ctrl=0, bool shift=0); +void glue_keyRelease(class Channel *ch, bool ctrl=0, bool shift=0); +void glue_keyRelease(class SampleChannel *ch, bool ctrl=0, bool shift=0); + +void glue_setBpm(const char *v1, const char *v2); +void glue_setBeats(int beats, int bars, bool expand); + +/* start, stop, rewind sequencer + * if gui == true the signal comes from an internal interaction on the + * GUI, otherwise it's a MIDI/Jack/external signal. */ + +void glue_startStopSeq(bool gui=true); +void glue_startSeq (bool gui=true); +void glue_stopSeq (bool gui=true); +void glue_rewindSeq (); + +/* start/stopActionRec + * handle the action recording. */ + +void glue_startStopActionRec(); +void glue_startActionRec(); +void glue_stopActionRec(); + +/* start/stopInputRec + * handle the input recording (take). If gui == true the signal comes + * from an internal interaction on the GUI, otherwise it's a + * MIDI/Jack/external signal. Alert displays or not the popup message + * if there are no available channels. */ + +void glue_startStopInputRec(bool gui=true, bool alert=true); +int glue_startInputRec (bool gui=true); +int glue_stopInputRec (bool gui=true); + +/* start/stopReadingRecs + * handle the 'R' button. If gui == true the signal comes from an + * internal interaction on the GUI, otherwise it's a MIDI/Jack/external + * signal. */ + +void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true); +void glue_startReadingRecs (class SampleChannel *ch, bool gui=true); +void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true); + +void glue_quantize(int val); + +void glue_setChanVol(class Channel *ch, float v, bool gui=true); +void glue_setOutVol (float v, bool gui=true); +void glue_setInVol (float v, bool gui=true); + +void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val); + +void glue_clearAllSamples(); +void glue_clearAllRecs(); + +/* resetToInitState + * reset Giada to init state. If resetGui also refresh all widgets. If + * createColumns also build initial empty columns. */ + +void glue_resetToInitState(bool resetGui=true, bool createColumns=true); + +void glue_startStopMetronome(bool gui=true); + +/* setBeginEndChannel + * sets start/end points in the sample editor. + * Recalc=false: don't recalc internal position + * check=true: check the points' consistency */ + +/** FIXME - nobody will call this via MIDI/keyb/mouse! */ +void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch, int b, int e, + bool recalc=false, bool check=true); + +/** FIXME - nobody will call this via MIDI/keyb/mouse! */ +void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); + +void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); + +/* setVolEditor + * handles the volume inside the SAMPLE EDITOR (not the main gui). The + * numeric flag tells if we want to handle the dial or the numeric input + * field. */ + + /** FIXME - nobody will call this via MIDI/keyb/mouse! */ +void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); + +/* mute + * set mute on or off. If gui == true the signal comes from an internal + * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ + +void glue_setMute(class Channel *ch, bool gui=true); + +/* solo on/off + * set solo on and off. If gui == true the signal comes from an internal + * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ + +void glue_setSoloOn (class Channel *ch, bool gui=true); +void glue_setSoloOff(class Channel *ch, bool gui=true); + +/** FIXME - nobody will call this via MIDI/keyb/mouse! */ +int glue_saveProject(const char *folderPath, const char *projName); + + +/* beatsDivide/Multiply + * shrinks or enlarges the number of beats by 2. */ + +void glue_beatsMultiply(); +void glue_beatsDivide(); + +#endif diff --git a/src/graphics.cpp b/src/graphics.cpp deleted file mode 100644 index 82ca2bc..0000000 --- a/src/graphics.cpp +++ /dev/null @@ -1,1631 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * graphics - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#include "graphics.h" - -const char *giada_logo_xpm[] = { -"300 82 8 1", -" c #181917", -". c #333432", -"+ c #484A47", -"@ c #5F615E", -"# c #767875", -"$ c #8D8F8C", -"% c #A5A7A4", -"& c #C5C7C4", -" .#%%&$ ", -" ..#%&&&&&# ", -" +@#$%&&&&&&&&&@ ", -" .. +&&&&&&&&&&&&&&. ", -" +$%%#+ +%&&&&&&&&&&&&&. ", -" #&&&&&%+ .+@@$&&&&&&&&&%. ", -" #&&&&&&&$ +&&&&&&&&# ", -" .&&&&&&&&%. #&&&&&&&@ ", -" @&&&&&&&&$ +&&&&&&&+ ", -" $&&&&&&&&# .&&&&&&&. ", -" #&&&&&&&&. +&&&&&&%. ", -" .&&&&&&&@ @&&&&&&$. ", -" @&&&&&@ $&&&&&&# ", -" .##@. %&&&&&&@ ", -" &&&&&&&+ ", -" .&&&&&&%. ", -" @&&&&&&$. ", -" .+@@###@+. ...... ... ++@@###@@. ....... .+@@###@@+. #&&&&&&# .+@@###@@+ ....... ", -" .@$%%&&&&&&&%$+ +%%%%%%. .+@#$%%$. .@$%&&&&&&&&%$@. $%%%%%%+ .@#%%&&&&&&&&%$@ %&&&&&&@ .@$%%&&&&&&&%$#. @%%%%%%@ ", -" #%&&&&&&&&&&&&&&$. #&&&&&& +@#$$%%%&&&&&%. .$%&&&&&&&&&&&&&&%@ .&&&&&&&+ #%&&&&&&&&&&&&&&&$+ &&&&&&&@ #%&&&&&&&&&&&&&&%#. %&&&&&&@ ", -" +%&&&&&&&&&&&&&&&&&%@ .&&&&&&% .%&&&&&&&&&&&&$ #%&&&&&&&&&&&&&&&&&&$ +&&&&&&%. @%&&&&&&&&&&&&&&&&&&&# .&&&&&&%+ @%&&&&&&&&&&&&&&&&&&%. &&&&&&&+ ", -" #&&&&&&&&&#+....@$&&&&@@&&&&&&$ .&&&&&&&&&&&&&# +%&&&&&&&&%#+....+#%&&&$#&&&&&&$. .$&&&&&&&&&$+.....@%&&&&$@&&&&&&%. .$&&&&&&&&&#@.....#%&&&%+&&&&&&%. ", -" $&&&&&&&&@ .%&&&&&&&&&&@ .@$&&&&&&&&&&@ +%&&&&&&&%@ .#&&&&&&&&&&$ .%&&&&&&&&@ .$&&&&&&&&&&$ .%&&&&&&&&#. @&&&&&&&&&&%. ", -" $&&&&&&&%. @&&&&&&&&&+ .%&&&&&&&%+ @&&&&&&&&# +%&&&&&&&&# +%&&&&&&&$. @&&&&&&&&&# .%&&&&&&&$. .%&&&&&&&&$ ", -" %&&&&&&&# @&&&&&&&&. +%&&&&&&%. @&&&&&&&&# .%&&&&&&&@ +%&&&&&&&# @&&&&&&&&@ +%&&&&&&&# %&&&&&&&@ ", -" $&&&&&&&$ #&&&&&&%. %&&&&&&% +&&&&&&&&@ +&&&&&&%+ .%&&&&&&&# @&&&&&&&+ .%&&&&&&&# &&&&&&&+ ", -" @&&&&&&&$. #&&&&&&$ %&&&&&&$ .%&&&&&&&@ @&&&&&&%. .$&&&&&&&$ +&&&&&&%+ $&&&&&&&$ .&&&&&&&+ ", -" +&&&&&&&%+ %&&&&&&# %&&&&&&# $&&&&&&&$ $&&&&&&% @&&&&&&&% #&&&&&&%. #&&&&&&&%. @&&&&&&%. ", -" %&&&&&&&# &&&&&&&+ .%&&&&&&@ @&&&&&&&$ %&&&&&&$ +&&&&&&&&+ $&&&&&&$ +&&&&&&&%. $&&&&&&$. ", -" @&&&&&&&%. .&&&&&&&. +%&&&&&%. .&&&&&&&&. .%&&&&&&# $&&&&&&&# %&&&&&&# .$&&&&&&&@ %&&&&&&# ", -" .%&&&&&&&@ @&&&&&&&. @&&&&&&% @&&&&&&&# @&&&&&&&+ +&&&&&&&&. +&&&&&&&@ +&&&&&&&% .&&&&&&&@ ", -" @&&&&&&&% $&&&&&&%. #&&&&&&% &&&&&&&&. #&&&&&&%. %&&&&&&&# @&&&&&&%+ .$&&&&&&&@ +&&&&&&&+ ", -" .$&&&&&&&# %&&&&&&# $&&&&&&# #&&&&&&&# $&&&&&&% +&&&&&&&&. $&&&&&&%. +&&&&&&&% #&&&&&&%+ ", -" +%&&&&&&&+ .&&&&&&&@ .%&&&&&&@ %&&&&&&&+ .%&&&&&&$ $&&&&&&&$ %&&&&&&% #&&&&&&&# %&&&&&&%. ", -" @&&&&&&&% +&&&&&&&+ +%&&&&&&+ .&&&&&&&%. +&&&&&&&# &&&&&&&&+ +%&&&&&&$ .%&&&&&&&. &&&&&&&$ ", -" $&&&&&&&@ #&&&&&&&. @&&&&&&&. #&&&&&&&# #&&&&&&&@ +&&&&&&&%. @&&&&&&&# .&&&&&&&% +&&&&&&&# ", -" .%&&&&&&&. %&&&&&&%. #&&&&&&% %&&&&&&&+ $&&&&&&&+ #&&&&&&&$. $&&&&&&&+ @&&&&&&&@ #&&&&&&&@ ", -" +&&&&&&&& &&&&&&&$. #&&&&&&$ &&&&&&&%. .%&&&&&&& %&&&&&&&# .%&&&&&&%. $&&&&&&&+ $&&&&&&%+ ", -" +&&&&&&&$ +&&&&&&&# .$&&&&&&# .&&&&&&&$. +&&&&&&&% %&&&&&&&+ +%&&&&&&% %&&&&&&&. .%&&&&&&%. ", -" @&&&&&&&@ $&&&&&&&@ .%&&&&&&+ +&&&&&&&# #&&&&&&&$ &&&&&&&&+ #&&&&&&&% &&&&&&&% @&&&&&&&$ ", -" @&&&&&&&+ &&&&&&&&+ +%&&&&&&. @&&&&&&&@ .%&&&&&&&@ .&&&&&&&%. .%&&&&&&&# &&&&&&&# $&&&&&&&$ ", -" #&&&&&&&. @&&&&&&&%. @&&&&&&& @&&&&&&&@ @&&&&&&&&+ .&&&&&&&%. @&&&&&&&&@ .&&&&&&&# +%&&&&&&&# ", -" #&&&&&&&. %&&&&&&&$. #&&&&&&% #&&&&&&&+ .$&&&&&&&&. .&&&&&&&$. .$&&&&&&&&. .&&&&&&&@ $&&&&&&&&@ ", -" #&&&&&&& #&&&&&&&&$ $&&&&&&# @&&&&&&&+ @&&&&&&&&& .&&&&&&&$. @&&&&&&&&& .&&&&&&&+ +%&&&&&&&%+ ", -" @&&&&&&& .%&&&&&&&&# .%&&&&&&@ @&&&&&&%+ .%&&&&&&&&% &&&&&&&$. .%&&&&&&&&% &&&&&&&+ $&&&&&&&&%. ", -" @&&&&&&&. $&&&&&&&&&@ +%&&&&&&. +&&&&&&&@ @&&&&&&&&&$ &&&&&&&%. #&&&&&&&&&$ &&&&&&&@ +&&&&&&&&&% ", -" +&&&&&&&+ @&&&&&&&&&%+ +&&&&&&& &&&&&&&@ +&&&&&&&&&&# $&&&&&&%+ @&&&&&&&&&&# $&&&&&&# .%&&&&&&&&&% ", -" .%&&&&&&@ +%&&$&&&&&&%. +&&&&&&& %&&&&&&# .&&&&&&&&&&&@ @&&&&&&&+ +&&&$%&&&&&&@ @&&&&&&$ .$&&&&&&&&&&$ ", -" #&&&&&&$ .$&&##&&&&&&$ @&&&&&&& @&&&&&&%. .%&&%@%&&&&&&@ .&&&&&&&# .&&&$+%&&&&&&. .&&&&&&&. .#&&%@%&&&&&&$ ", -" +&&&&&&&. .%&&% $&&&&&&# +&&&&&&&. +.&&&&&&&@ .%&&%+.%&&&&&&$ @+$&&&&&&&+ +&&&% +&&&&&&&+ .+ $&&&&&&$ .$&&&@ %&&&&&&% .# ", -" $&&&&&&$ +%&&&+ %&&&&&&@ +&&&&&&&@ #&$#&&&&&&%+ @&&&&@ .$&&&&&&&+ #&&@&&&&&&&$. .#&&&%. +%&&&&&&# .$&$ +&&&&&&&+ +%&&&# $&&&&&&&# +&&$ ", -" +%&&&&&&#. +#&&&%@ .%&&&&&&+ .%&&&&&&&+ .$&&%.%&&&&&&%+ +#&&&&@ #&&&&&&&%@..+@$&&&+@&&&&&&&%+ .@%&&&%+ .%&&&&&&&+ +%&&$. #&&&&&&%@ +#&&&&# @&&&&&&&&@...+#&&&# ", -" @&&&&&&&$@+..++#%&&&%+ +&&&&&&%. $&&&&&&&%#@@#%&&%. .%&&&&&&%@+...+@$&&&&%+ +%&&&&&&&&%$%&&&&+ $&&&&&&&&$#@+@@#%&&&&$. #&&&&&&&&#@+@$&&&$. .$&&&&&&&#+...++#%&&&&@ .%&&&&&&&&%$%&&&&# ", -" @&&&&&&&&%%%%&&&&&$. @&&&&&&% +%&&&&&&&&&&&&&$. +%&&&&&&&&%$%%&&&&&$. #&&&&&&&&&&&&&%+ .$&&&&&&&&&&&&&&&&&&# .%&&&&&&&&&&&&&&# .$&&&&&&&&%$%%&&&&&%+ @&&&&&&&&&&&&&%@ ", -" @%&&&&&&&&&&&&&%@. #&&&&&&$ #&&&&&&&&&&&%@. .$&&&&&&&&&&&&&&%@. .%&&&&&&&&&&&#. @&&&&&&&&&&&&&&&$+ +&&&&&&&&&&&&%+ .#&&&&&&&&&&&&&&&#. $&&&&&&&&&&&$+ ", -" .#%&&&&&&&&&%+. %&&&&&&# +%&&&&&&&%#. +$&&&&&&&&&&%@. .$&&&&&&&&#+ .#&&&&&&&&&&%#. .$&&&&&&&&%@. +$&&&&&&&&&&%@. .@&&&&&&&&$+. ", -" .+#$%%$#+.. .%&&&&&&+ .@#$%$#+. .@#$%%$#@+ +@$%$#+. .+#$%%$#@+. +@$%$$@. .+#$%%$#@+. .@$%$#@. ", -" +&&&&&&%. ", -" #&&&&&&% ", -" .%&&&&&&@ ", -" @&&&&&&%. ", -" $&&&&&&$ ", -" @&&&&&&&+ ", -" @#$#+ .$&&&&&&$ ", -" $&&&&&# #&&&&&&% ", -"#&&&&&&&@ @&&&&&&&+ ", -"%&&&&&&&%. @&&&&&&%+ ", -"%&&&&&&&&# #&&&&&&%. ", -"@&&&&&&&&&@ .$&&&&&&#. ", -" $&&&&&&&&&$+ +$&&&&&&%+ ", -" +&&&&&&&&&&%#@@#$%&&&&&&&#. +. .+ +@. ++ .+ +++++ +. .+ ++ +++++. .++++. +@. .@+ .++++. .+++++++ +. +@. .+@. .++++. ++. ++. .+. .+@. .+ +. .+ .+. .+ .+++++++ ", -" .$&&&&&&&&&&&&&&&&&&&%@ $%. %@ +&&%&%. .$$ +& .&&&&&&# .%@ +&. %&+ $&&&&&%. @&&&&&%. +&&%&%. .%&%&&+ #&&&&&&@ +&&&&&&%. &# +&&%&%. @&&%&%. +&&&&&&@ $&% +%&+ .$&@ @&&%&$. $% .%$ $% +&%. +%. +&&&&&&$. ", -" +$%%&&&&&&&&&&%$@. +&# #% +&# %% $$ +& .&+ @&@ .%@ +&. +$&$ $$ .%% @% .%&. .&$ .%$. %%. #&+ #& .$&+ +&. &# +&# $%. @&# .%%. +&. $&+ $%&+ #%&+ .&%$ @&# +%$ $% .%# $% +&%$ +%. +&. ", -" .++@###@@+. #&. .&+ $%. .&@ $$ +& .&+ %$ .%@ +&. ##$% $$ @&. @% %$ $%. @&. @&+ %$. #& .%@ +&. &# %% .&# %% .&@ +&. &# $#%$ $#&+ @%@%. %% @%+ $% .%# $% +&+%+ +%. +&. ", -" .%$ $$ .&# %% $$ +& .&+ %$ .%@ +&. .%@+&+ $$ @&. @% #& &# +. %% #&. #& .%@ +&. &# .&@ $$ +&+ .$$ +&. %# $#@& +$@&+ $#.%@ +&@ .+. $% .%# $% +& $$ +%. +&. ", -" @&#&. .&@ $& $$ +& .&#+++#&+ .%%####$&. +%. %$ $%.++@&$ @% +&..&@ &$ @&+ #&++++$%. +&$###@ &# +&+ #%.@&. #% +&+ ..#&+ $#.&@ ##+&+ .&..$$ @&. $&$###$&# $% +& +%@ +%. +&####+ ", -" %&@ +&+ #& $$ +& .&%$$%%@ .%%####$&. #$ #& $&$$%&#. @% +&++&@ &# +&+ #&$$%&%+ +&$$$$# &# @&. #%.@&. #%.+%%%%%%@ $# $% .$+.&+ @% @%. @&. $&$###$&# $% +& #%.+%. +&$$$$@ ", -" #&. .&@ $% $$ +& .&#+.$%. .%@ +&. .$%##$&+ $%.+@&@ @% +&..&@ &$ @&. #&+++$$ +&+ &# +&. #% @&. $$ +&#@@++ $# +&.+$.+&+ $%@#$&+ @&. $% .%# $% +& .%@+%. +&. ", -" @& .%# &$ $$ +% .&+ .%@ .%@ +&. +%$###&$ $$ $% @% $& &$ .$+ $% #%. #& +&+ +&. &# .&@ .%$ +&@ .%# +&. $# .%### +&+ .&####&# .&@ .$+ $% .%# $% +& @%@%. +&. ", -" @& #%. @&. #%. $% .&+ $% .%@ +&. @$. #& $$ +&+ @% +&@ #&. #&. +&+ .%@ #& .%# +&. &# $% +&+ %% @&+ +&. $# #&%+ +&+ @% #%. %% #%. $% .%# $% +& .$%%. +&. ", -" @& .%%+.@&# .%%++#&@ .&+ +&+ .%@ +&. $# .&+ $$ %$ @&+++#&%. $%+.#&# #&@.+%%. #& @%. +&@+++++. &$++++. .%%+.@%# .%%..@&# +&. $# +&$ +&+ %# +&+ .%%+.#&# $% .%# $% +& +&&. +&@++++. %&", -" @& .$&&%@ +%&&&@ .&+ %$ .%@ +&..%+ %$ $$ @&. @&&&&%@ .$%&&# @%&&$. #& .%@ +&&&&&&%+ &&&&&&$. .$&&%# .$&&%@ +&. $# .%# +&+.&. .%# .$&&&@ $% .%# $% +& $&. +&&&&&&%. &&"}; - - -const char *loopRepeat_xpm[] = { -"18 18 8 1", -" c #181917", -". c #242523", -"+ c #323331", -"@ c #4D4F4C", -"# c #646663", -"$ c #787A77", -"% c #919390", -"& c #BFC1BD", -"..................", -"..................", -"..................", -"...&%#......#%&...", -"...&&&%+..+%&&&...", -"...$%&&%..%&&%$...", -".....$&&##&&$.....", -"......%&%%&%......", -"......$&&&&$......", -"......$&&&&$......", -"......%&%%&%......", -".....$&&##&&$.....", -"...$%&&%..%&&%$...", -"...&&&%+..+%&&&...", -"...&%#......#%&...", -"..................", -"..................", -".................."}; - - -const char *loopBasic_xpm[] = { -"18 18 8 1", -" c #181917", -". c #242523", -"+ c #313230", -"@ c #4D4F4C", -"# c #666765", -"$ c #787A77", -"% c #919390", -"& c #BEC0BD", -"..................", -"..................", -"..................", -"......#%&&%#......", -"....+%&&&&&&%+....", -"....%&&&%%&&&%....", -"...#&&%+..+%&&#...", -"...%&&+....+&&%...", -"...&&%......%&&...", -"...&&%......%&&...", -"...%&&+....+&&%...", -"...#&&%+..+%&&#...", -"....%&&&%%&&&%....", -"....+%&&&&&&%+....", -"......#%&&%#......", -"..................", -"..................", -".................."}; - - -const char *loopOnce_xpm[] = { -"18 18 8 1", -" c #181917", -". c #242523", -"+ c #323331", -"@ c #4D4F4C", -"# c #646663", -"$ c #787A77", -"% c #919390", -"& c #BFC1BD", -"..................", -"..................", -"..................", -"......$%&&%#......", -"....+&&&&&&&&+....", -"...+&&&&$$&&&&+...", -"...$&&$....$&&$...", -"...%&&......%&%...", -"..................", -"..................", -"...%&&+.....&&&...", -"...#&&$....$&&#...", -"....%&&&%$&&&%....", -"....+%&&&&&&%+....", -"......#%&&%#......", -"..................", -"..................", -".................."}; - - -const char *loopOnceBar_xpm[] = { -"18 18 8 1", -" c #242523", -". c #393A38", -"+ c #545553", -"@ c #747673", -"# c #A3A5A2", -"$ c #ADAFAC", -"% c #B5B7B4", -"& c #C7C9C6", -" ", -" ", -" ", -" @$&%#@ ", -" .$&&&&&&$. ", -" %&&#@@#&&$ ", -" @&&@ @&&@ ", -" %&# +%$+ #&$ ", -" %&&% ", -" %&&% ", -" $&# +%%+ #&$ ", -" @&&@ @&&@ ", -" $&&#@@#&&$ ", -" .$&&&&&&$. ", -" @#&&#@ ", -" ", -" ", -" "}; - - -const char *oneshotBasic_xpm[] = { -"18 18 8 1", -" c #181917", -". c #242523", -"+ c #313230", -"@ c #4D4F4C", -"# c #666765", -"$ c #787A77", -"% c #919390", -"& c #BEC0BD", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"...$$$$$$$$$$$$...", -"...&&&&&&&&&&&&...", -"...&&&&&&&&&&&&...", -"..................", -"..................", -".................."}; - - -const char *oneshotRetrig_xpm[] = { -"18 18 8 1", -" c #181917", -". c #242523", -"+ c #313230", -"@ c #4D4F4C", -"# c #666765", -"$ c #787A77", -"% c #919390", -"& c #BEC0BD", -"..................", -"..................", -"..................", -"..................", -"..................", -"...$$$$$$$#@......", -"...&&&&&&&&&&@....", -"...&&&&&&&&&&&+...", -"..........+$&&%...", -"............%&&...", -"............%&&...", -"...........+&&%...", -"...$$$$$$$%&&&#...", -"...&&&&&&&&&&%....", -"...&&&&&&&&%#.....", -"..................", -"..................", -".................."}; - - -const char *oneshotPress_xpm[] = { -"18 18 8 1", -" c #181917", -". c #242523", -"+ c #313230", -"@ c #4D4F4C", -"# c #666765", -"$ c #787A77", -"% c #919390", -"& c #BEC0BD", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"...+%&%+..........", -"...%&&&%..........", -"...&&&&&..........", -"...$&&&$..........", -"...+$&$+..........", -"..................", -"...$$$$$$$$$$$$...", -"...&&&&&&&&&&&&...", -"...&&&&&&&&&&&&...", -"..................", -"..................", -".................."}; - - -const char *oneshotEndless_xpm[] = { -"18 18 6 1", -" c #242523", -". c #464745", -"+ c #6D6F6C", -"@ c #888A87", -"# c #ADAFAC", -"$ c #C6C8C5", -" ", -" ", -" ", -" ", -" ", -" .++. ", -" @$$$$#. ", -" @$$$$$$$. ", -" .$$#. +$$@ ", -" +$$. @$# ", -" +$$ @$$ ", -" .$$+ #$# ", -" @@@$$$@@#$$+ ", -" $$$$$$$$$$@ ", -" $$$$$$$$#+ ", -" ", -" ", -" "}; - - -const char *updirOff_xpm[] = { -"18 18 8 1", -" c #242523", -". c #332F2E", -"+ c #54494A", -"@ c #6B5A5C", -"# c #866C6B", -"$ c #967B7A", -"% c #987D7C", -"& c #B18E8F", -" ", -" ", -" ", -" ", -" @@ ", -" #&&# ", -" .#&&&&#. ", -" .$&&&&&&$. ", -" +@%&&&&%@+ ", -" #&&&&# ", -" #&&&&# ", -" #&&&&# ", -" #&&&&# ", -" #&&&&# ", -" .... ", -" ", -" ", -" "}; - - -const char *updirOn_xpm[] = { -"18 18 8 1", -" c #4D4F4C", -". c #555150", -"+ c #706465", -"@ c #7D6B6E", -"# c #877373", -"$ c #957978", -"% c #9F8382", -"& c #B18E8F", -" ", -" ", -" ", -" ", -" ## ", -" #&&# ", -" .$&&&&$. ", -" .%&&&&&&%. ", -" +@%&&&&%@+ ", -" $&&&&$ ", -" $&&&&$ ", -" $&&&&$ ", -" $&&&&$ ", -" $&&&&$ ", -" .... ", -" ", -" ", -" "}; - - -const char *pause_xpm[] = { -"23 23 8 1", -" c #4D4F4C", -". c #514E53", -"+ c #5C4F61", -"@ c #6F507E", -"# c #855098", -"$ c #9551AE", -"% c #A652C5", -"& c #AE52D1", -" ", -" ", -" ", -" ", -" ", -" #+ ", -" &%#. ", -" &&&%@ ", -" &&&&&$+ ", -" &&&&&&&#. ", -" &&&&&&&&%@ ", -" &&&&&&&&&&# ", -" &&&&&&&&%@. ", -" &&&&&&&#. ", -" &&&&&$+ ", -" &&&%@ ", -" &&#. ", -" $+ ", -" ", -" ", -" ", -" ", -" "}; - - -const char *play_xpm[] = { -"23 23 8 1", -" c #242523", -". c #393534", -"+ c #574B4C", -"@ c #6E5B5A", -"# c #7C6663", -"$ c #8C7170", -"% c #A48384", -"& c #B18E8F", -" ", -" ", -" ", -" ", -" ", -" $. ", -" &&@ ", -" &&&%+ ", -" &&&&&$. ", -" &&&&&&&@ ", -" &&&&&&&&%+ ", -" &&&&&&&&&&# ", -" &&&&&&&&%+ ", -" &&&&&&&#. ", -" &&&&&$. ", -" &&&%+ ", -" &&@ ", -" $. ", -" ", -" ", -" ", -" ", -" "}; - - -const char *rewindOff_xpm[] = { -"23 23 8 1", -" c #242523", -". c #393534", -"+ c #574B4C", -"@ c #6E5B5A", -"# c #7C6663", -"$ c #8C7170", -"% c #A48384", -"& c #B18E8F", -" ", -" ", -" ", -" ", -" ", -" .$ ", -" @&& ", -" +%&&& ", -" .$&&&&& ", -" @&&&&&&& ", -" +%&&&&&&&& ", -" #&&&&&&&&&& ", -" +%&&&&&&&& ", -" .#&&&&&&& ", -" .$&&&&& ", -" +%&&& ", -" @&& ", -" .$ ", -" ", -" ", -" ", -" ", -" "}; - - -const char *rewindOn_xpm[] = { -"23 23 8 1", -" c #4D4F4C", -". c #514E53", -"+ c #5C4F61", -"@ c #6F507E", -"# c #855098", -"$ c #9551AE", -"% c #A652C5", -"& c #AE52D1", -" ", -" ", -" ", -" ", -" ", -" +# ", -" .#%& ", -" @%&&& ", -" +$&&&&& ", -" .#&&&&&&& ", -" @%&&&&&&&& ", -" #&&&&&&&&&& ", -" .@%&&&&&&&& ", -" .#&&&&&&& ", -" +$&&&&& ", -" @%&&& ", -" .#&& ", -" +$ ", -" ", -" ", -" ", -" ", -" "}; - - -// 18x18 -/* -const unsigned char giada_icon[] = { - 0x00, 0x00, 0x00, 0xfc, 0xff, 0x00, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01, - 0x3e, 0xf0, 0x01, 0x1e, 0xe0, 0x01, 0x0e, 0xc3, 0x01, 0x8e, 0xff, 0x01, - 0x8e, 0xc1, 0x01, 0x8e, 0xc1, 0x01, 0x8e, 0xc7, 0x01, 0x0e, 0xc7, 0x01, - 0x1e, 0xc0, 0x01, 0x3e, 0xf0, 0x01, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01, - 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00 }; -*/ - -const char *giada_icon[] = { -"65 65 11 1", -" c None", -". c #000000", -"+ c #000100", -"@ c #FFFFFF", -"# c #FDFFFC", -"$ c #CBCDC9", -"% c #292B28", -"& c #626461", -"* c #484A47", -"= c #888A87", -"- c #A7A9A6", -"....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++....", -".@@@#####################$%+++++++++++++%&&*%+++++++&##%+++++....", -".@@#####################&++++++++++++=$#######-*+++++&$+++++++...", -".@@####################&++++++++++%-############$*+++++++++++++..", -"+#####################*++++++++++&################-++++++++++++%+", -"+####################*++++++++++=##################$+++++++++++*+", -"+###################*++++++++++$####################$++++++++++=+", -"+##################=++++++++++-######################-+++++++++-+", -"+#################$++++++++++=#######################=+++++++++$+", -"+#################%+++++++++*########################*+++++++++#+", -"+################*++++++++++#########################%++++++++*#+", -"+###############-++++++++++=########################$+++++++++&#+", -"+###############%+++++++++%#########################-+++++++++-#+", -"+##############-++++++++++-#########################&+++++++++$#+", -"+##############%+++++++++%##########################*+++++++++##+", -"+#############-++++++++++-##########################+++++++++%##+", -"+#############%++++++++++##########################$+++++++++*##+", -"+############$++++++++++&##########################=+++++++++=##+", -"+############=++++++++++$##########################&+++++++++-##+", -"+############*+++++++++%###########################%+++++++++$##+", -"+############++++++++++=###########################++++++++++###+", -"+###########$++++++++++$##########################-+++++++++*###+", -"+###########=++++++++++###########################=+++++++++&###+", -"+###########*+++++++++%###########################*+++++++++-###+", -"+###########%+++++++++&###########################++++++++++$###+", -"+###########%+++++++++-##########################=++++++++++####+", -"+###########++++++++++$##########################%+++++++++%####+", -"+###########++++++++++##########################$++++++++++&####+", -"+##########$++++++++++##########################&++++++++++=####+", -"+##########$+++++++++%#########################$+++++++++++-####+", -"+##########$+++++++++%#########################&+++++++++++$####+", -"+###########+++++++++*########################$++++++++++++#####+", -"+###########+++++++++*########################*+++++++++++*#####+", -"+###########%++++++++%#######################=++++++++++++&#####+", -"+###########&+++++++++######################$+++++++++++++-#####+", -"+###########=+++++++++$#####################%+++%+++++++++$#####+", -"+###########$+++++++++$####################&+++%=+++++++++######+", -"+############%++++++++&###################=++++$&++++++++%######+", -"+############-+++++++++$#################&++++=#*++++++++&######+", -"+#############+++++++++&################*++++*##+++++++++=######+", -"+#############=+++++++++-#############$%++++%###+++++++++-######+", -"+##############*+++++++++=##########$*+++++%###$+++++++++#######+", -"+###############++++++++++&$######$&++++++&####=+++++++++#######+", -"+###############$++++++++++++%&*%++++++++=#####&++++++++*#######+", -"+################$%+++++++++++++++++++++-######%++++++++=#######+", -"+##################&++++++++++++++++++=########+++++++++-#######+", -"+###################$%+++++++++++++%=#########$+++++++++########+", -"+#####################$=%++++++%*=-###########=++++++++%########+", -"+##########################$$$################*++++++++&########+", -"+#############################################+++++++++=########+", -"+############################################$+++++++++$########+", -"+############################################&++++++++%#########+", -"+############################################+++++++++=#########+", -"+###########################################=+++++++++##########+", -"+###########################################%++++++++*##########+", -"+##########################################=+++++++++-##########+", -"+#########-==$############################$+++++++++&###########+", -"+#######=++++++-##########################*+++++++++############+", -"+######$++++++++$########################=+++++++++-############+", -"+######=+++++++++$######################-+++++++++&#############+", -"+######=+++++++++*#####################$+++++++++&##############.", -".@#####=++++++++++-###################-+++++++++=##############@.", -".@@####=++++++++++%##################=+++++++++=#############@@@.", -".@@@###=+++++++++++*###############$*++++++++%$##############@@@.", -"....++++++++++++++++++++++++++++++++++++++++++++++++++++++++....."}; - -const char *recOff_xpm[] = { -"23 23 8 1", -" c #242523", -". c #342F2E", -"+ c #3F3B3A", -"@ c #594F4F", -"# c #7A6663", -"$ c #8C7170", -"% c #A68384", -"& c #B18E8F", -" ", -" ", -" ", -" ", -" ", -" @$%%$@ ", -" .$&&&&&&$. ", -" $&&&&&&&&$ ", -" @&&&#++#&&&@ ", -" $&&# #&&$ ", -" %&&+ +&&% ", -" %&&+ +&&% ", -" $&&# #&&$ ", -" @&&&#++#&&&@ ", -" $&&&&&&&&$ ", -" .$&&&&&&$. ", -" @$%%$@ ", -" ", -" ", -" ", -" ", -" ", -" "}; - -const char *recOn_xpm[] = { -"23 23 8 1", -" c #4D4F4C", -". c #5F4E50", -"+ c #6E4F50", -"@ c #8C5050", -"# c #AE5454", -"$ c #BB5253", -"% c #C55352", -"& c #E85557", -" ", -" ", -" ", -" ", -" ", -" @$&&$@ ", -" .%&&&&&&%. ", -" %&&&&&&&&% ", -" @&&&#++#&&&@ ", -" $&&# #&&$ ", -" &&&+ +&&& ", -" &&&+ +&&& ", -" $&&# #&&$ ", -" @&&&#++#&&&@ ", -" %&&&&&&&&% ", -" .%&&&&&&%. ", -" @$&&$@ ", -" ", -" ", -" ", -" ", -" ", -" "}; - -const char *inputRecOn_xpm[] = { -"23 23 8 1", -" c #524D4C", -". c #4D4F4C", -"+ c #5D4F50", -"@ c #8C5050", -"# c #BB5253", -"$ c #C45251", -"% c #DD5256", -"& c #EA5657", -".......................", -".......................", -".......................", -".......................", -".......................", -"........ @#%%#@ .......", -".......+$&&&&&&$+......", -"...... $&&&&&&&&$ .....", -"......@&&&&&&&&&&@.....", -"......#&&&&&&&&&&#.....", -"......%&&&&&&&&&&%.....", -"......%&&&&&&&&&&%.....", -"......#&&&&&&&&&&#.....", -"......@&&&&&&&&&&@.....", -".......$&&&&&&&&$......", -".......+$&&&&&&$+......", -"........ @#%%#@ .......", -".......................", -".......................", -".......................", -".......................", -".......................", -"......................."}; - -const char *inputRecOff_xpm[] = { -"23 23 8 1", -" c #242523", -". c #252724", -"+ c #332F2E", -"@ c #594E4F", -"# c #896E6D", -"$ c #8D7271", -"% c #A68384", -"& c #B18E8F", -" ", -" ", -" ", -" ", -" ", -" .@#%%#@. ", -" +$&&&&&&$+ ", -" .$&&&&&&&&$. ", -" @&&&&&&&&&&@ ", -" #&&&&&&&&&&# ", -" %&&&&&&&&&&% ", -" %&&&&&&&&&&% ", -" #&&&&&&&&&&# ", -" @&&&&&&&&&&@ ", -" $&&&&&&&&$ ", -" +$&&&&&&$+ ", -" .@#%%#@. ", -" ", -" ", -" ", -" ", -" ", -" "}; - -const char *muteOff_xpm[] = { -"18 18 8 1", -" c #242523", -". c #2E2F2D", -"+ c #3B3C3A", -"@ c #525451", -"# c #6F716E", -"$ c #878986", -"% c #ADAFAC", -"& c #C6C8C5", -" ", -" ", -" ", -" ", -" ++. .++ ", -" +&&$ $&&+ ", -" +&&% %&&+ ", -" +&%&++&%&+ ", -" +&$&##&$&+ ", -" +&#%$$%#&+ ", -" +&#$%%$#&+ ", -" +&#@&&@#&+ ", -" +&#+&&+#&+ ", -" .#@ ## @#. ", -" ", -" ", -" ", -" "}; - -const char *muteOn_xpm[] = { -"18 18 8 1", -" c #4D4F4C", -". c #585A57", -"+ c #616260", -"@ c #7A7C79", -"# c #888A87", -"$ c #989A97", -"% c #B2B4B1", -"& c #C6C8C5", -" ", -" ", -" ", -" ", -" .. .. ", -" +&&$ $&&+ ", -" +&&% %&&+ ", -" +&%&++&%&+ ", -" +&$&@@&$&+ ", -" +&#%$$%#&+ ", -" +&#$&&$#&+ ", -" +&#@&&@#&+ ", -" +&#.&&.#&+ ", -" .#+ ## +#. ", -" ", -" ", -" ", -" "}; - - -const char *readActionOff_xpm[] = { -"18 18 8 1", -" c #242523", -". c #393B38", -"+ c #555754", -"@ c #6B6D6A", -"# c #7F807E", -"$ c #9C9E9B", -"% c #B1B3B0", -"& c #C3C5C2", -" ", -" ", -" ", -" ", -" .... ", -" %&&&&%+ ", -" %&@@@&& ", -" %% $&. ", -" %&@@#&$ ", -" %&&&&@ ", -" %% +&$ ", -" %% #&# ", -" %% %&+ ", -" @@ .#+ ", -" ", -" ", -" ", -" "}; - - -const char *readActionOn_xpm[] = { -"18 18 8 1", -" c #4D4F4C", -". c #696B68", -"+ c #7A7C79", -"@ c #888A87", -"# c #939592", -"$ c #A7A9A6", -"% c #B7B9B6", -"& c #C4C6C3", -" ", -" ", -" ", -" ", -" ", -" %&&&&%. ", -" %&++@&& ", -" %% $& ", -" %&@@#&$ ", -" %&&&&@ ", -" %% +&$ ", -" %% #&# ", -" %% %&. ", -" +@ .@+ ", -" ", -" ", -" ", -" "}; - - -const char *metronomeOff_xpm[] = { -"13 13 8 1", -" c #242523", -". c #2D2928", -"+ c #34302F", -"@ c #443D3C", -"# c #4F4445", -"$ c #685659", -"% c #826A68", -"& c #A18282", -" ", -" ", -" . . ", -" #% %# ", -" .&+ +&. ", -" %$ $% ", -" @& &@ ", -" &@ @& ", -" $% %$ ", -" +&. .&+ ", -" %# #% ", -" . . ", -" "}; - - -const char *metronomeOn_xpm[] = { -"13 13 8 1", -" c #4D4F4C", -". c #565150", -"+ c #645C5C", -"@ c #716465", -"# c #837070", -"$ c #8F7775", -"% c #977C7B", -"& c #A68787", -" ", -" ", -" . . ", -" @% %@ ", -" .&. .&. ", -" $# #$ ", -" +& &+ ", -" &+ +& ", -" #$ $# ", -" .&. .&. ", -" %@ @% ", -" . . ", -" "}; - - -const char *zoomInOff_xpm[] = { -"18 18 8 1", -" c None", -". c #252525", -"+ c #262626", -"@ c #535353", -"# c #ACACAC", -"$ c #AEAEAE", -"% c #B1B1B1", -"& c #C4C4C4", -"++++++++++++++++++", -"+................+", -"+................+", -"+................+", -"+................+", -"+.......@@.......+", -"+.......#$.......+", -"+.......#$.......+", -"+....@%%&&%%@....+", -"+....@%%&&%%@....+", -"+.......#$.......+", -"+.......#$.......+", -"+.......@@.......+", -"+................+", -"+................+", -"+................+", -"+................+", -"++++++++++++++++++"}; - - -const char *zoomInOn_xpm[] = { -"18 18 8 1", -" c None", -". c #4E4E4E", -"+ c #707070", -"@ c #717171", -"# c #B3B3B3", -"$ c #B5B5B5", -"% c #B7B7B7", -"& c #C5C5C5", -"..................", -"..................", -"..................", -"..................", -"..................", -"........++........", -"........#$........", -"........#$........", -".....@%%&&%%@.....", -".....@%%&&%%@.....", -"........#$........", -"........#$........", -"........++........", -"..................", -"..................", -"..................", -"..................", -".................."}; - - -const char *zoomOutOff_xpm[] = { -"18 18 5 1", -" c None", -". c #252525", -"+ c #262626", -"@ c #9C9C9C", -"# c #BBBBBB", -"++++++++++++++++++", -"+................+", -"+................+", -"+................+", -"+................+", -"+................+", -"+................+", -"+................+", -"+......@##@......+", -"+......@##@......+", -"+................+", -"+................+", -"+................+", -"+................+", -"+................+", -"+................+", -"+................+", -"++++++++++++++++++"}; - - -const char *zoomOutOn_xpm[] = { -"18 18 4 1", -" c None", -". c #4E4E4E", -"+ c #A7A7A7", -"@ c #BEBEBE", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -".......+@@+.......", -".......+@@+.......", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -"..................", -".................."}; - - - -const char *scrollRightOff_xpm[] = { -"12 12 8 1", -" c #181917", -". c #242523", -"+ c #2E2F2D", -"@ c #4D4F4C", -"# c #5D5F5C", -"$ c #828481", -"% c #9B9D9A", -"& c #BCBEBB", -"............", -"............", -"...+........", -"...&$@......", -"...$&&%@....", -"....+#%&%...", -"....+#%&%...", -"...$&&%#....", -"...&$@......", -"...+........", -"............", -"............"}; - - -const char *scrollLeftOff_xpm[] = { -"12 12 8 1", -" c #181917", -". c #242523", -"+ c #2E2F2D", -"@ c #4D4F4C", -"# c #5D5F5C", -"$ c #828481", -"% c #9B9D9A", -"& c #BCBEBB", -"............", -"............", -"........+...", -"......@$&...", -"....@%&&$...", -"...%&%#+....", -"...%&%#+....", -"....#%&&$...", -"......@$&...", -"........+...", -"............", -"............"}; - - -const char *scrollLeftOn_xpm[] = { -"12 12 8 1", -" c #4D4F4C", -". c #6B6D6A", -"+ c #7B7D7A", -"@ c #969895", -"# c #A6A8A5", -"$ c #B4B6B3", -"% c #C0C2BF", -"& c #FEFFFC", -" ", -" ", -" ", -" .@$ ", -" +#%%@ ", -" $%#+ ", -" %%#+ ", -" +$%%@ ", -" .#$ ", -" ", -" ", -" "}; - - -const char *scrollRightOn_xpm[] = { -"12 12 8 1", -" c #4D4F4C", -". c #6B6D6A", -"+ c #7B7D7A", -"@ c #969895", -"# c #A6A8A5", -"$ c #B4B6B3", -"% c #C0C2BF", -"& c #FEFFFC", -" ", -" ", -" ", -" %@. ", -" @%%#. ", -" +#%# ", -" +#%# ", -" @%%#+ ", -" %@. ", -" ", -" ", -" "}; - - -const char *soloOn_xpm[] = { -"18 18 8 1", -" c #4D4F4C", -". c #616360", -"+ c #737572", -"@ c #838582", -"# c #929491", -"$ c #A5A7A4", -"% c #B1B3B0", -"& c #C6C8C5", -" ", -" ", -" ", -" ", -" .@+. ", -" #&&&&# ", -" .&$ %&. ", -" &%+ .. ", -" #&&&$. ", -" .@$&&. ", -" .#. @&@ ", -" .&$. #&+ ", -" #&&&&$ ", -" .+@+ ", -" ", -" ", -" ", -" "}; - - -const char *soloOff_xpm[] = { -"18 18 8 1", -" c #242523", -". c #3D3F3D", -"+ c #525451", -"@ c #666865", -"# c #80827F", -"$ c #979996", -"% c #A7A9A6", -"& c #C6C8C5", -" ", -" ", -" ", -" ", -" .@@. ", -" #&&&&# ", -" .&$ %&. ", -" &%+ .. ", -" #&&&$+ ", -" .@%&&. ", -" +#. @&@ ", -" .&$..#&+ ", -" #&&&&$ ", -" .@@+ ", -" ", -" ", -" ", -" "}; - - -#ifdef WITH_VST - - -const char *fxOff_xpm[] = { -"18 18 8 1", -" c #242523", -". c #40423F", -"+ c #4D4E4C", -"@ c #686A67", -"# c #7B7D7A", -"$ c #919390", -"% c #AEB0AD", -"& c #C1C3C0", -" ", -" ", -" ", -" ", -" ", -" ..... . . ", -" $&&&$ $% @&. ", -" $$ .&#&@ ", -" $%##. @&$ ", -" $%##. #&% ", -" $$ .&@&# ", -" $$ %$ @&. ", -" .. + +. ", -" ", -" ", -" ", -" ", -" "}; - - -const char *fxOn_xpm[] = { -"18 18 8 1", -" c #4D4F4C", -". c #565855", -"+ c #636562", -"@ c #80827F", -"# c #8E908D", -"$ c #9FA19E", -"% c #B1B3B0", -"& c #C1C3C0", -" ", -" ", -" ", -" ", -" ", -" .++++ +. +. ", -" $&&&$ $% @&. ", -" $$ .&#&@ ", -" $%##+ @&$ ", -" $%##+ #&% ", -" $$ +&@&# ", -" $$ %$ @&+ ", -" ++ .+. ++ ", -" ", -" ", -" ", -" ", -" "}; - - -const char *fxShiftUpOff_xpm[] = { -"18 18 7 1", -" c #242523", -". c #4D4F4C", -"+ c #A3A5A2", -"@ c #868885", -"# c #C1C3C0", -"$ c #313330", -"% c #626361", -" ", -" ", -" ", -" ", -" ", -" ", -" .+@ ", -" @+#. ", -" $#%+@ ", -" %# %#$ ", -" +@ $#% ", -" $#. @+ ", -" $. $. ", -" ", -" ", -" ", -" ", -" "}; - - -const char *fxShiftUpOn_xpm[] = { -"18 18 5 1", -" c #4D4F4C", -". c #70726F", -"+ c #A5A7A4", -"@ c #C1C3BF", -"# c #8E908D", -" ", -" ", -" ", -" ", -" ", -" ", -" .++ ", -" +@@. ", -" @.+# ", -" .@ .@ ", -" +# @. ", -" .@. #+ ", -" . . ", -" ", -" ", -" ", -" ", -" "}; - - -const char *fxShiftDownOff_xpm[] = { -"18 18 7 1", -" c #242523", -". c #4D4F4C", -"+ c #A3A5A2", -"@ c #313330", -"# c #626361", -"$ c #868885", -"% c #C1C3C0", -" ", -" ", -" ", -" ", -" ", -" ", -" .+@ #$ ", -" @%# +$ ", -" $+ .%@ ", -" .%@$+ ", -" +$%# ", -" #%%@ ", -" @.. ", -" ", -" ", -" ", -" ", -" "}; - - -const char *fxShiftDownOn_xpm[] = { -"18 18 5 1", -" c #4D4F4C", -". c #70726F", -"+ c #A5A7A4", -"@ c #C1C3BF", -"# c #8E908D", -" ", -" ", -" ", -" ", -" ", -" ", -" .+ .+ ", -" @. +# ", -" #+ .@. ", -" .@.#+ ", -" +#@. ", -" #@@ ", -" .. ", -" ", -" ", -" ", -" ", -" "}; - - -const char *vstLogo_xpm[] = { -"65 38 8 1", -" c #161715", -". c #2B2D2A", -"+ c #474846", -"@ c #6A6C69", -"# c #8C8E8B", -"$ c #A8AAA7", -"% c #C7C9C6", -"& c #EEF0ED", -" @#############################################################+ ", -"@#.............................................................$+", -"#. .#", -"#. .#", -"#. ...... .. .#", -"#. .@$$$####$%$#@.+&$ .#", -"#. .#$$#+. +#$%%%%$ .#", -"#. .$$#$ .#$$%$ .#", -"#. ............. ....$$$$$ ++$$%$+@@@@@@@@@@@@@@@ .#", -"#. ##$$$$$$%%%%@ %%&&&&%%$@ %&@#$$@@$%%&&&%%%&&&&& .#", -"#. +$$$$$%@ .&%####%$@ $&$.$# #$%%%& @&%& .#", -"#. +$$$$$% +&$###$%%&&$@. $&. #$%%%&. .%& .#", -"#. @$$$$%$ %##$##$%&&&&&&%#.%# #$$%%&. @& .#", -"#. $$$$$%+ #& #$$%%&&&&&&&%%$$@ #$$%%&. + .#", -"#. .$$$$$% +&+ .#%&&&&&&&&%$$#$$# #$$%%&. .#", -"#. @$$$$%$ %$ @%&&&&&&%$$###$$ #$$%%&. .#", -"#. #$$$%%@ #& . +$&&&%$####$%$ #$$%%&. .#", -"#. $$$%%% .&@ +%# .@$$$###$$% #$$$%&. .#", -"#. +%$%%%$$% +$$+ #$#$$$% @$$$%&. .#", -"#. #%%%%%&. +%$$ ##$$%$ @$$$%%. .#", -"#. $$%%%@ +%$$$. #$$$%. @$$$$%. .#", -"#. +%%%$ +%$$#$@ +$$%$ @#$$$%+ .#", -"#. @%%. +%%%$$$$#@++.++@#$$$@ @@##$$$%%%$$@ .#", -"#. #@ +&# .@@###$$$###@. @+++@@@@###$@ .#", -"#. .#", -"#. .#", -"#. .#", -"#. .#", -"#. .#", -"#. .@$$$$$$$$ .$%%%%%%# .#", -"#. ....... .@@@@@@@@@. .#", -"#. ........ @@@+@@@@@@@@@@+ .#", -"@# ......... .####@@@@@@@@@@@+ #@", -" @$$$$$$$$$$$$$$$.......... .@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ ", -" ......... .@@@@@@@@@@@@@@@. ", -" ........ @@@@@@@@@@. ", -" ........... .@@@@@@@@@ ", -" .......... .@@@@@@@@ "}; - - -const char *fxRemoveOff_xpm[] = { -"18 18 9 1", -" c None", -". c #242623", -"+ c #2F312E", -"@ c #393A38", -"# c #484A47", -"$ c #5D5F5C", -"% c #8E908D", -"& c #9B9D9A", -"* c #BDBFBC", -"..................", -"..................", -"..................", -"..................", -"..................", -".....+#@..@#+.....", -"......&*++*&......", -"......@*%%*@......", -".......$**$.......", -".......#**#.......", -"......+*&&*+......", -"......%*@@*%......", -"......@@..@@......", -"..................", -"..................", -"..................", -"..................", -".................."}; - - -const char *fxRemoveOn_xpm[] = { -"18 18 9 1", -" c None", -". c #4D4F4C", -"+ c #575956", -"@ c #5C5D5B", -"# c #666865", -"$ c #787977", -"% c #9C9E9B", -"& c #A6A8A5", -"* c #BFC1BE", -"..................", -"..................", -"..................", -"..................", -"..................", -"......#@..@#......", -"......&*++*&......", -"......@*%%*@......", -".......$**$.......", -".......#**#.......", -"......+*&&*+......", -"......%*@+*%......", -"......@+..+@......", -"..................", -"..................", -"..................", -"..................", -".................."}; -#endif // #ifdef WITH_VST - - -const char *beatsDivideOn_xpm[] = { -"13 13 13 1", -" c None", -". c #595B58", -"+ c #5B5D5A", -"@ c #5F615E", -"# c #686967", -"$ c #737572", -"% c #787A77", -"& c #80827F", -"* c #8F918E", -"= c #959794", -"- c #9A9C99", -"; c #C4C6C3", -"> c #C7C9C6", -".............", -".............", -".............", -".............", -".....#>#.....", -"....+@%@+....", -"...*>>>>>*...", -"....+@%@+....", -".....#>#.....", -".............", -".............", -".............", -"............."}; - - -const char *beatsDivideOff_xpm[] = { -"13 13 13 1", -" c None", -". c #242523", -"+ c #262825", -"@ c #2D2E2C", -"# c #3A3B39", -"$ c #494B48", -"% c #525451", -"& c #595B58", -"* c #5F615E", -"= c #787A77", -"- c #858784", -"; c #C3C5C1", -"> c #C7C9C6", -".............", -".............", -".............", -"......+......", -".....#>#.....", -"...++@%@++...", -"...=>>>>>=...", -"...++@%@++...", -".....#>#.....", -"......+......", -".............", -".............", -"............."}; - - -const char *beatsMultiplyOn_xpm[] = { -"13 13 13 1", -" c None", -". c #595B58", -"+ c #5B5D5A", -"@ c #5F615E", -"# c #686967", -"$ c #737572", -"% c #787A77", -"& c #80827F", -"* c #8F918E", -"= c #959794", -"- c #9A9C99", -"; c #C4C6C3", -"> c #C7C9C6", -".............", -".............", -".............", -"....$...$....", -"...$;&.&;$...", -"....&;-;&....", -".....->-.....", -"....&;=;&....", -"...$;&.&;$...", -"...+$...$....", -".............", -".............", -"............."}; - - -const char *beatsMultiplyOff_xpm[] = { -"13 13 12 1", -" c #242523", -". c #262825", -"+ c #2D2E2C", -"@ c #3A3B39", -"# c #494B48", -"$ c #525451", -"% c #595B58", -"& c #5F615E", -"* c #787A77", -"= c #858784", -"- c #C3C5C1", -"; c #C7C9C6", -" ", -" ", -" ", -" .# #. ", -" #-& &-# ", -" &-=-& ", -" =;= ", -" %-*-& ", -" #-% %-# ", -" .# #. ", -" ", -" ", -" "}; diff --git a/src/graphics.h b/src/graphics.h deleted file mode 100644 index c76da0f..0000000 --- a/src/graphics.h +++ /dev/null @@ -1,102 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * graphics - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GRAPHICS_H -#define GRAPHICS_H - -extern const char *giada_logo_xpm[]; - -extern const char *loopRepeat_xpm[]; -extern const char *loopBasic_xpm[]; -extern const char *loopOnce_xpm[]; -extern const char *loopOnceBar_xpm[]; -extern const char *oneshotBasic_xpm[]; -extern const char *oneshotRetrig_xpm[]; -extern const char *oneshotPress_xpm[]; -extern const char *oneshotEndless_xpm[]; - -extern const char *updirOff_xpm[]; -extern const char *updirOn_xpm[]; - -extern const char *pause_xpm[]; -extern const char *play_xpm[]; - -extern const char *zoomInOff_xpm[]; -extern const char *zoomInOn_xpm[]; -extern const char *zoomOutOff_xpm[]; -extern const char *zoomOutOn_xpm[]; - -extern const char *scrollLeftOff_xpm[]; -extern const char *scrollLeftOn_xpm[]; -extern const char *scrollRightOff_xpm[]; -extern const char *scrollRightOn_xpm[]; - -extern const char *rewindOff_xpm[]; -extern const char *rewindOn_xpm[]; - -extern const char *recOff_xpm[]; -extern const char *recOn_xpm[]; - -extern const char *metronomeOff_xpm[]; -extern const char *metronomeOn_xpm[]; - -extern const char *inputRecOn_xpm[]; -extern const char *inputRecOff_xpm[]; - -extern const char *beatsDivideOn_xpm[]; -extern const char *beatsDivideOff_xpm[]; -extern const char *beatsMultiplyOn_xpm[]; -extern const char *beatsMultiplyOff_xpm[]; - -extern const char *muteOff_xpm[]; -extern const char *muteOn_xpm[]; - -extern const char *soloOff_xpm[]; -extern const char *soloOn_xpm[]; - -extern const char *readActionOn_xpm[]; -extern const char *readActionOff_xpm[]; - -#ifdef WITH_VST -extern const char *fxOff_xpm[]; -extern const char *fxOn_xpm[]; - -extern const char *fxShiftUpOn_xpm[]; -extern const char *fxShiftUpOff_xpm[]; -extern const char *fxShiftDownOn_xpm[]; -extern const char *fxShiftDownOff_xpm[]; - -extern const char *fxRemoveOff_xpm[]; -extern const char *fxRemoveOn_xpm[]; - -extern const char *vstLogo_xpm[]; -#endif - -extern const char *giada_icon[]; - -#endif diff --git a/src/gui/dialogs/gd_about.cpp b/src/gui/dialogs/gd_about.cpp new file mode 100644 index 0000000..e790253 --- /dev/null +++ b/src/gui/dialogs/gd_about.cpp @@ -0,0 +1,126 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_about + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "../../core/conf.h" +#include "../../core/const.h" +#include "../../core/kernelAudio.h" +#include "../../core/kernelMidi.h" +#include "../../core/graphics.h" +#include "../../utils/gui_utils.h" +#include "../elems/ge_mixed.h" +#include "gd_about.h" + + +extern Conf G_Conf; + + +gdAbout::gdAbout() +#ifdef WITH_VST +: gWindow(340, 405, "About Giada") { +#else +: gWindow(340, 320, "About Giada") { +#endif + + if (G_Conf.aboutX) + resize(G_Conf.aboutX, G_Conf.aboutY, w(), h()); + + set_modal(); + + logo = new gBox(8, 10, 324, 86); + text = new gBox(8, 120, 324, 145); + close = new gClick(252, h()-28, 80, 20, "Close"); +#ifdef WITH_VST + vstLogo = new gBox(8, 265, 324, 50); + vstText = new gBox(8, 315, 324, 46); +#endif + end(); + + logo->image(new Fl_Pixmap(giada_logo_xpm)); + text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP); + + char message[512]; + sprintf( + message, + "Version " VERSIONE " (" __DATE__ ")\n\n" + "Developed by Monocasual\n" + "Based on FLTK (%d.%d.%d), RtAudio (%s),\n" + "RtMidi (%s), libsamplerate and libsndfile\n\n" + "Released under the terms of the GNU General\n" + "Public License (GPL v3)\n\n" + "News, infos, contacts and documentation:\n" + "www.giadamusic.com", + FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION, + kernelAudio::getRtAudioVersion().c_str(), + kernelMidi::getRtMidiVersion().c_str()); + + int tw = 0; + int th = 0; + fl_measure(message, tw, th); + text->copy_label(message); + text->size(text->w(), th); + +#ifdef WITH_VST + vstLogo->image(new Fl_Pixmap(vstLogo_xpm)); + vstLogo->position(vstLogo->x(), text->y()+text->h()+8); + vstText->label( + "VST Plug-In Technology by Steinberg\n" + "VST is a trademark of Steinberg\nMedia Technologies GmbH" + ); + vstText->position(vstText->x(), vstLogo->y()+vstLogo->h()); + +#endif + + close->callback(cb_close, (void*)this); + gu_setFavicon(this); + setId(WID_ABOUT); + show(); +} + + +/* ------------------------------------------------------------------ */ + + +gdAbout::~gdAbout() { + G_Conf.aboutX = x(); + G_Conf.aboutY = y(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdAbout::cb_close(Fl_Widget *w, void *p) { ((gdAbout*)p)->__cb_close(); } + + +/* ------------------------------------------------------------------ */ + + +void gdAbout::__cb_close() { + do_callback(); +} diff --git a/src/gui/dialogs/gd_about.h b/src/gui/dialogs/gd_about.h new file mode 100644 index 0000000..91f5065 --- /dev/null +++ b/src/gui/dialogs/gd_about.h @@ -0,0 +1,58 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_about + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GD_ABOUT_H +#define GD_ABOUT_H + +#include +#include +#include "../elems/ge_window.h" + + +class gdAbout : public gWindow { +private: + class gBox *logo; + class gBox *text; + class gClick *close; + +#ifdef WITH_VST + class gBox *vstText; + class gBox *vstLogo; +#endif + +public: + gdAbout(); + ~gdAbout(); + + static void cb_close(Fl_Widget *w, void *p); + inline void __cb_close(); + +}; + +#endif diff --git a/src/gui/dialogs/gd_actionEditor.cpp b/src/gui/dialogs/gd_actionEditor.cpp new file mode 100644 index 0000000..5d5edc2 --- /dev/null +++ b/src/gui/dialogs/gd_actionEditor.cpp @@ -0,0 +1,472 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_actionEditor + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../../utils/gui_utils.h" +#include "../../core/graphics.h" +#include "../../core/mixer.h" +#include "../../core/recorder.h" +#include "../../core/conf.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../elems/ge_actionChannel.h" +#include "../elems/ge_muteChannel.h" +#include "../elems/ge_envelopeChannel.h" +#include "../elems/ge_pianoRoll.h" +#include "../elems/ge_mixed.h" +#include "gd_actionEditor.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; + + +gdActionEditor::gdActionEditor(Channel *chan) + : gWindow(640, 284), + chan (chan), + zoom (100), + coverX (0) +{ + if (G_Conf.actionEditorW) { + resize(G_Conf.actionEditorX, G_Conf.actionEditorY, G_Conf.actionEditorW, G_Conf.actionEditorH); + zoom = G_Conf.actionEditorZoom; + } + + totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom); + + /* 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(); + + if (chan->type == CHANNEL_SAMPLE) { + actionType = new gChoice(8, 8, 80, 20); + gridTool = new gGridTool(actionType->x()+actionType->w()+4, 8, this); + actionType->add("key press"); + actionType->add("key release"); + actionType->add("kill chan"); + actionType->value(0); + + SampleChannel *ch = (SampleChannel*) chan; + if (ch->mode == SINGLE_PRESS || ch->mode & LOOP_ANY) + actionType->deactivate(); + } + else { + gridTool = new gGridTool(8, 8, this); + } + + gBox *b1 = new gBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons + zoomIn = new gClick(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm); + zoomOut = new gClick(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm); + upperArea->end(); + upperArea->resizable(b1); + + zoomIn->callback(cb_zoomIn, (void*)this); + zoomOut->callback(cb_zoomOut, (void*)this); + + /* main scroller: contains all widgets */ + + scroller = new gScroll(8, 36, w()-16, h()-44); + + if (chan->type == CHANNEL_SAMPLE) { + + SampleChannel *ch = (SampleChannel*) chan; + + ac = new gActionChannel (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch); + mc = new gMuteChannel (scroller->x(), ac->y()+ac->h()+8, this); + vc = new gEnvelopeChannel (scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume"); + scroller->add(ac); + //scroller->add(new gResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8)); + scroller->add(mc); + //scroller->add(new gResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8)); + scroller->add(vc); + //scroller->add(new gResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8)); + + /* fill volume envelope with actions from recorder */ + + vc->fill(); + + /* if channel is LOOP_ANY, deactivate it: a loop mode channel cannot + * hold keypress/keyrelease actions */ + + if (ch->mode & LOOP_ANY) + ac->deactivate(); + } + else { + pr = new gPianoRollContainer(scroller->x(), upperArea->y()+upperArea->h()+8, this); + scroller->add(pr); + scroller->add(new gResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8)); + } + + end(); + + /* compute values */ + + update(); + gridTool->calc(); + + gu_setFavicon(this); + + char buf[256]; + sprintf(buf, "Edit Actions in Channel %d", chan->index+1); + label(buf); + + set_non_modal(); + size_range(640, 284); + resizable(scroller); + + show(); +} + + +/* ------------------------------------------------------------------ */ + + +gdActionEditor::~gdActionEditor() { + G_Conf.actionEditorX = x(); + G_Conf.actionEditorY = y(); + G_Conf.actionEditorW = w(); + G_Conf.actionEditorH = h(); + G_Conf.actionEditorZoom = zoom; + + /** CHECKME - missing clear() ? */ + +} + + +/* ------------------------------------------------------------------ */ + + +void gdActionEditor::cb_zoomIn(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomIn(); } +void gdActionEditor::cb_zoomOut(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomOut(); } + + +/* ------------------------------------------------------------------ */ + + +void gdActionEditor::__cb_zoomIn() { + + /* zoom 50: empirical value, to avoid a totalWidth > 16 bit signed + * (32767 max), unsupported by FLTK 1.3.x */ + + if (zoom <= 50) + return; + + zoom /= 2; + + update(); + + if (chan->type == CHANNEL_SAMPLE) { + ac->size(totalWidth, ac->h()); + mc->size(totalWidth, mc->h()); + vc->size(totalWidth, vc->h()); + ac->updateActions(); + mc->updateActions(); + vc->updateActions(); + } + else { + pr->size(totalWidth, pr->h()); + pr->updateActions(); + } + + /* scroll to pointer */ + + int shift = Fl::event_x() + scroller->xposition(); + scroller->scroll_to(scroller->xposition() + shift, scroller->yposition()); + + /* update all underlying widgets */ + + gridTool->calc(); + scroller->redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdActionEditor::__cb_zoomOut() { + + zoom *= 2; + + update(); + + if (chan->type == CHANNEL_SAMPLE) { + ac->size(totalWidth, ac->h()); + mc->size(totalWidth, mc->h()); + vc->size(totalWidth, vc->h()); + ac->updateActions(); + mc->updateActions(); + vc->updateActions(); + } + else { + pr->size(totalWidth, pr->h()); + pr->updateActions(); + } + + /* scroll to pointer */ + + int shift = (Fl::event_x() + scroller->xposition()) / -2; + if (scroller->xposition() + shift < 0) + shift = 0; + scroller->scroll_to(scroller->xposition() + shift, scroller->yposition()); + + /* update all underlying widgets */ + + gridTool->calc(); + scroller->redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdActionEditor::update() { + totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom); + if (totalWidth < scroller->w()) { + totalWidth = scroller->w(); + zoom = (int) ceilf(G_Mixer.framesInSequencer / (float) totalWidth); + scroller->scroll_to(0, scroller->yposition()); + } +} + + +/* ------------------------------------------------------------------ */ + + +int gdActionEditor::handle(int e) { + int ret = Fl_Group::handle(e); + switch (e) { + case FL_MOUSEWHEEL: { + Fl::event_dy() == -1 ? __cb_zoomIn() : __cb_zoomOut(); + ret = 1; + break; + } + } + return ret; +} + + +/* ------------------------------------------------------------------ */ + + +int gdActionEditor::getActionType() { + if (actionType->value() == 0) + return ACTION_KEYPRESS; + else + if (actionType->value() == 1) + return ACTION_KEYREL; + else + if (actionType->value() == 2) + return ACTION_KILLCHAN; + else + return -1; +} + + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +gGridTool::gGridTool(int x, int y, gdActionEditor *parent) + : Fl_Group(x, y, 80, 20), parent(parent) +{ + gridType = new gChoice(x, y, 40, 20); + gridType->add("1"); + gridType->add("2"); + gridType->add("3"); + gridType->add("4"); + gridType->add("6"); + gridType->add("8"); + gridType->add("16"); + gridType->add("32"); + gridType->value(0); + gridType->callback(cb_changeType, (void*)this); + + active = new gCheck (x+44, y+4, 12, 12); + + gridType->value(G_Conf.actionEditorGridVal); + active->value(G_Conf.actionEditorGridOn); + + end(); +} + + +/* ------------------------------------------------------------------ */ + + +gGridTool::~gGridTool() { + G_Conf.actionEditorGridVal = gridType->value(); + G_Conf.actionEditorGridOn = active->value(); +} + + +/* ------------------------------------------------------------------ */ + + +void gGridTool::cb_changeType(Fl_Widget *w, void *p) { ((gGridTool*)p)->__cb_changeType(); } + + +/* ------------------------------------------------------------------ */ + + +void gGridTool::__cb_changeType() { + calc(); + parent->redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +bool gGridTool::isOn() { + return active->value(); +} + + +/* ------------------------------------------------------------------ */ + + +int gGridTool::getValue() { + switch (gridType->value()) { + case 0: return 1; + case 1: return 2; + case 2: return 3; + case 3: return 4; + case 4: return 6; + case 5: return 8; + case 6: return 16; + case 7: return 32; + } + return 0; +} + + +/* ------------------------------------------------------------------ */ + + +void gGridTool::calc() { + + points.clear(); + frames.clear(); + bars.clear(); + beats.clear(); + + /* find beats, bars and grid. The method is the same of the waveform in sample + * editor. Take totalwidth (the width in pixel of the area to draw), knowing + * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth, + * put a concentrate of each block (which is totalFrames / zoom) */ + + int j = 0; + int fpgc = floor(G_Mixer.framesPerBeat / getValue()); // frames per grid cell + + for (int i=1; itotalWidth; i++) { // if i=0, step=0 -> useless cycle + int step = parent->zoom*i; + while (j < step && j < G_Mixer.totalFrames) { + if (j % fpgc == 0) { + points.add(i); + frames.add(j); + } + if (j % G_Mixer.framesPerBeat == 0) + beats.add(i); + if (j % G_Mixer.framesPerBar == 0 && i != 1) + bars.add(i); + if (j == G_Mixer.totalFrames-1) + parent->coverX = i; + j++; + } + j = step; + } + + /* fix coverX if == 0, which means G_Mixer.beats == 32 */ + + if (G_Mixer.beats == 32) + parent->coverX = parent->totalWidth; +} + + +/* ------------------------------------------------------------------ */ + + +int gGridTool::getSnapPoint(int v) { + + if (v == 0) return 0; + + for (int i=0; i<(int)points.size; i++) { + + if (i == (int) points.size-1) + return points.at(i); + + int gp = points.at(i); + int gpn = points.at(i+1); + + if (v >= gp && v < gpn) + return gp; + } + return v; // default value +} + + +/* ------------------------------------------------------------------ */ + + +int gGridTool::getSnapFrame(int v) { + + v *= parent->zoom; // transformation pixel -> frame + + for (int i=0; i<(int)frames.size; i++) { + + if (i == (int) frames.size-1) + return frames.at(i); + + int gf = frames.at(i); // grid frame + int gfn = frames.at(i+1); // grid frame next + + if (v >= gf && v < gfn) { + + /* which one is the closest? gf < v < gfn */ + + if ((gfn - v) < (v - gf)) + return gfn; + else + return gf; + } + } + return v; // default value +} + + +/* ------------------------------------------------------------------ */ + + +int gGridTool::getCellSize() { + return (parent->coverX - parent->ac->x()) / G_Mixer.beats / getValue(); +} diff --git a/src/gui/dialogs/gd_actionEditor.h b/src/gui/dialogs/gd_actionEditor.h new file mode 100644 index 0000000..1c38e7e --- /dev/null +++ b/src/gui/dialogs/gd_actionEditor.h @@ -0,0 +1,131 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_actionEditor + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GD_ACTIONEDITOR_H +#define GD_ACTIONEDITOR_H + +#include +#include +#include +#include "../elems/ge_window.h" + + +/* gActionEditor + * main window which contains the tools for dealing with actions. + * This class calculates chan, zoom, frames per beat, and so on. Each + * sub-widget contains a pointer to this window to query those data. */ + +class gdActionEditor : public gWindow { + +private: + + /* update + * compute total width, in pixel. */ + + void update(); + +public: + + gdActionEditor(class Channel *chan); + ~gdActionEditor(); + + int handle(int e); + + int getActionType(); + + static void cb_zoomIn(Fl_Widget *w, void *p); + static void cb_zoomOut(Fl_Widget *w, void *p); + inline void __cb_zoomIn(); + inline void __cb_zoomOut(); + + class gChoice *actionType; + class gGridTool *gridTool; + class gClick *zoomIn; + class gClick *zoomOut; + class gScroll *scroller; // widget container + + class gActionChannel *ac; + class gMuteChannel *mc; + class gEnvelopeChannel *vc; + class gPianoRollContainer *pr; + + gVector widgets; + + class Channel *chan; + + int zoom; + int totalWidth; // total width of the widget, in pixel (zoom affected) + int coverX; // x1 of the unused area (x2 = totalWidth) +}; + + +/* ------------------------------------------------------------------ */ + + +class gGridTool : public Fl_Group { + +private: + class gChoice *gridType; + class gCheck *active; + + class gdActionEditor *parent; + + static void cb_changeType(Fl_Widget *w, void *p); + inline void __cb_changeType(); + +public: + + gGridTool(int x, int y, gdActionEditor *parent); + ~gGridTool(); + + int getValue(); + bool isOn(); + void calc(); + + /* getSnapPoint + * given a cursor position in input, return the x coordinates of the + * nearest snap point (in pixel, clean, ie. not x()-shifted) */ + + int getSnapPoint(int v); + int getSnapFrame(int v); + + /* getCellSize + * return the size in pixel of a single cell of the grid. */ + + int getCellSize(); + + gVector points; // points of the grid + gVector frames; // frames of the grid + + gVector bars; + gVector beats; +}; + + +#endif diff --git a/src/gui/dialogs/gd_beatsInput.cpp b/src/gui/dialogs/gd_beatsInput.cpp new file mode 100644 index 0000000..bf7455f --- /dev/null +++ b/src/gui/dialogs/gd_beatsInput.cpp @@ -0,0 +1,102 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_beatsInput + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "../../utils/gui_utils.h" +#include "../../core/patch.h" +#include "../../core/mixer.h" +#include "../../core/conf.h" +#include "../../glue/glue.h" +#include "gd_beatsInput.h" +#include "gd_mainWindow.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern gdMainWindow *mainWin; + + +gdBeatsInput::gdBeatsInput() + : gWindow(164, 60, "Beats") +{ + if (G_Conf.beatsX) + resize(G_Conf.beatsX, G_Conf.beatsY, w(), h()); + + set_modal(); + + beats = new gInput(8, 8, 35, 20); + bars = new gInput(47, 8, 35, 20); + ok = new gClick(86, 8, 70, 20, "Ok"); + resizeRec = new gCheck(8, 40, 12, 12, "resize recorded actions"); + end(); + + char buf_bars[3]; sprintf(buf_bars, "%d", G_Mixer.bars); + char buf_beats[3]; sprintf(buf_beats, "%d", G_Mixer.beats); + beats->maximum_size(2); + beats->value(buf_beats); + beats->type(FL_INT_INPUT); + bars->maximum_size(2); + bars->value(buf_bars); + bars->type(FL_INT_INPUT); + ok->shortcut(FL_Enter); + ok->callback(cb_update_batt, (void*)this); + resizeRec->value(G_Conf.resizeRecordings); + + gu_setFavicon(this); + setId(WID_BEATS); + show(); +} + + +/* ------------------------------------------------------------------ */ + + +gdBeatsInput::~gdBeatsInput() +{ + G_Conf.beatsX = x(); + G_Conf.beatsY = y(); + G_Conf.resizeRecordings = resizeRec->value(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdBeatsInput::cb_update_batt(Fl_Widget *w, void *p) { ((gdBeatsInput*)p)->__cb_update_batt(); } + + +/* ------------------------------------------------------------------ */ + + +void gdBeatsInput::__cb_update_batt() +{ + if (!strcmp(beats->value(), "") || !strcmp(bars->value(), "")) + return; + glue_setBeats(atoi(beats->value()), atoi(bars->value()), resizeRec->value()); + do_callback(); +} diff --git a/src/gui/dialogs/gd_beatsInput.h b/src/gui/dialogs/gd_beatsInput.h new file mode 100644 index 0000000..2818c72 --- /dev/null +++ b/src/gui/dialogs/gd_beatsInput.h @@ -0,0 +1,56 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_beatsInput + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GD_BEATSINPUT_H +#define GD_BEATSINPUT_H + +#include +#include +#include "../elems/ge_window.h" + + +class gdBeatsInput : public gWindow +{ +private: + + static void cb_update_batt(Fl_Widget *w, void *p); + inline void __cb_update_batt(); + + class gInput *beats; + class gInput *bars; + class gClick *ok; + class gCheck *resizeRec; + +public: + + gdBeatsInput(); + ~gdBeatsInput(); +}; + + +#endif diff --git a/src/gui/dialogs/gd_bpmInput.cpp b/src/gui/dialogs/gd_bpmInput.cpp new file mode 100644 index 0000000..a10e35c --- /dev/null +++ b/src/gui/dialogs/gd_bpmInput.cpp @@ -0,0 +1,105 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_bpmInput + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "../../core/conf.h" +#include "../../core/mixer.h" +#include "../../glue/glue.h" +#include "../../utils/gui_utils.h" +#include "../elems/ge_mixed.h" +#include "gd_bpmInput.h" +#include "gd_mainWindow.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern gdMainWindow *mainWin; + + +gdBpmInput::gdBpmInput(const char *label) +: gWindow(144, 36, "Bpm") { + + if (G_Conf.bpmX) + resize(G_Conf.bpmX, G_Conf.bpmY, w(), h()); + + set_modal(); + + input_a = new gInput(8, 8, 30, 20); + input_b = new gInput(42, 8, 20, 20); + ok = new gClick(66, 8, 70, 20, "Ok"); + end(); + + char a[4]; + snprintf(a, 4, "%d", (int) G_Mixer.bpm); + char b[2]; + for (unsigned i=0; imaximum_size(3); + input_a->type(FL_INT_INPUT); + input_a->value(a); + input_b->maximum_size(1); + input_b->type(FL_INT_INPUT); + input_b->value(b); + + ok->shortcut(FL_Enter); + ok->callback(cb_update_bpm, (void*)this); + + gu_setFavicon(this); + setId(WID_BPM); + show(); +} + + +/* ------------------------------------------------------------------ */ + + +gdBpmInput::~gdBpmInput() { + G_Conf.bpmX = x(); + G_Conf.bpmY = y(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdBpmInput::cb_update_bpm(Fl_Widget *w, void *p) { ((gdBpmInput*)p)->__cb_update_bpm(); } + + +/* ------------------------------------------------------------------ */ + + +void gdBpmInput::__cb_update_bpm() { + if (strcmp(input_a->value(), "") == 0) + return; + glue_setBpm(input_a->value(), input_b->value()); + do_callback(); +} diff --git a/src/gui/dialogs/gd_bpmInput.h b/src/gui/dialogs/gd_bpmInput.h new file mode 100644 index 0000000..8a6a945 --- /dev/null +++ b/src/gui/dialogs/gd_bpmInput.h @@ -0,0 +1,51 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_bpmInput + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GD_BPMINPUT_H +#define GD_BPMINPUT_H + +#include +#include +#include "../elems/ge_window.h" + + +class gdBpmInput : public gWindow { +private: + static void cb_update_bpm(Fl_Widget *w, void *p); + inline void __cb_update_bpm(); + + class gInput *input_a; + class gInput *input_b; + class gClick *ok; + +public: + gdBpmInput(const char *label); // pointer to mainWin->timing->bpm->label() + ~gdBpmInput(); +}; + +#endif diff --git a/src/gui/dialogs/gd_browser.cpp b/src/gui/dialogs/gd_browser.cpp new file mode 100644 index 0000000..d415ae0 --- /dev/null +++ b/src/gui/dialogs/gd_browser.cpp @@ -0,0 +1,395 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_browser + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/mixer.h" +#include "../../core/graphics.h" +#include "../../core/wave.h" +#include "../../core/pluginHost.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../core/patch.h" +#include "../../core/conf.h" +#include "../../glue/glue.h" +#include "../elems/ge_browser.h" +#include "../elems/ge_channel.h" +#include "../elems/ge_keyboard.h" +#include "gd_browser.h" +#include "gd_pluginList.h" +#include "gd_mainWindow.h" +#include "gd_warnings.h" + + +extern Patch G_Patch; +extern Conf G_Conf; +extern Mixer G_Mixer; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif +extern gdMainWindow *mainWin; + + +gdBrowser::gdBrowser(const char *title, const char *initPath, Channel *ch, int type, int stackType) + : gWindow (396, 302, title), + ch (ch), + type (type), + stackType(stackType) +{ + set_non_modal(); + + browser = new gBrowser(8, 36, 380, 230); + Fl_Group *group_btn = new Fl_Group(8, 274, 380, 20); + gBox *b = new gBox(8, 274, 204, 20); // spacer window border <-> buttons + ok = new gClick(308, 274, 80, 20); + cancel = new gClick(220, 274, 80, 20, "Cancel"); + status = new gProgress(8, 274, 204, 20); + status->minimum(0); + status->maximum(1); + status->hide(); // show the bar only if necessary + group_btn->resizable(b); + group_btn->end(); + + Fl_Group *group_upd = new Fl_Group(8, 8, 380, 25); + if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please! + name = new gInput(208, 8, 152, 20); + if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please! + where = new gInput(8, 8, 192, 20); + else + where = new gInput(8, 8, 352, 20); + updir = new gClick(368, 8, 20, 20, "", updirOff_xpm, updirOn_xpm); + group_upd->resizable(where); + group_upd->end(); + + end(); + + resizable(browser); + size_range(w(), h(), 0, 0); + + where->readonly(true); + where->cursor_color(COLOR_BG_DARK); + + if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please! + ok->label("Save"); + else + ok->label("Load"); + + if (type == BROWSER_LOAD_PATCH) + ok->callback(cb_load_patch, (void*)this); + else + if (type == BROWSER_LOAD_SAMPLE) + ok->callback(cb_load_sample, (void*)this); + else + if (type == BROWSER_SAVE_PATCH) { + ok->callback(cb_save_patch, (void*)this); + name->value(G_Patch.name[0] == '\0' ? "my_patch.gptc" : G_Patch.name); + name->maximum_size(MAX_PATCHNAME_LEN+5); // +5 for ".gptc" + } + else + if (type == BROWSER_SAVE_SAMPLE) { + ok->callback(cb_save_sample, (void*)this); + name->value(((SampleChannel*)ch)->wave->name.c_str()); + } + else + if (type == BROWSER_SAVE_PROJECT) { + ok->callback(cb_save_project, (void*)this); + name->value(gStripExt(G_Patch.name).c_str()); + } +#ifdef WITH_VST + else + if (type == BROWSER_LOAD_PLUGIN) { + ok->callback(cb_loadPlugin, (void*)this); + } +#endif + + ok->shortcut(FL_Enter); + + updir->callback(cb_up, (void*)this); + cancel->callback(cb_close, (void*)this); + browser->callback(cb_down, this); + browser->path_obj = where; + browser->init(initPath); + + if (G_Conf.browserW) + resize(G_Conf.browserX, G_Conf.browserY, G_Conf.browserW, G_Conf.browserH); + + gu_setFavicon(this); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +gdBrowser::~gdBrowser() { + G_Conf.browserX = x(); + G_Conf.browserY = y(); + G_Conf.browserW = w(); + G_Conf.browserH = h(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::cb_load_patch (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_load_patch(); } +void gdBrowser::cb_load_sample (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_load_sample(); } +void gdBrowser::cb_save_sample (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_sample(); } +void gdBrowser::cb_save_patch (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_patch(); } +void gdBrowser::cb_save_project(Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_project(); } +void gdBrowser::cb_down (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_down(); } +void gdBrowser::cb_up (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_up(); } +void gdBrowser::cb_close (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_close(); } +#ifdef WITH_VST +void gdBrowser::cb_loadPlugin (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_loadPlugin(); } +#endif + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_load_patch() { + + if (browser->text(browser->value()) == NULL) + return; + + /* patchFile is the file to open. + * For patches: browser->get_selected_item() + * for projects: browser->get_selected_item() without extention + + * patch name appended */ + + std::string patchFile = browser->get_selected_item();; + bool isProject; + + if (gIsProject(browser->get_selected_item())) { + std::string patchName = gGetProjectName(browser->get_selected_item()); +#if defined(__linux__) || defined(__APPLE__) + patchFile = patchFile+"/"+patchName+".gptc"; +#elif defined(_WIN32) + patchFile = patchFile+"\\"+patchName+".gptc"; +#endif + isProject = true; + } + else + isProject = false; + + int res = glue_loadPatch(patchFile.c_str(), browser->path_obj->value(), status, isProject); + + if (res == PATCH_UNREADABLE) { + status->hide(); + if (isProject) + gdAlert("This project is unreadable."); + else + gdAlert("This patch is unreadable."); + } + else if (res == PATCH_INVALID) { + status->hide(); + if (isProject) + gdAlert("This project is not valid."); + else + gdAlert("This patch is not valid."); + } + else + do_callback(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_save_sample() { + + if (strcmp(name->value(), "") == 0) { /// FIXME glue business + gdAlert("Please choose a file name."); + return; + } + + /* bruteforce check extension. */ + + std::string filename = gStripExt(name->value()); + char fullpath[PATH_MAX]; + sprintf(fullpath, "%s/%s.wav", where->value(), filename.c_str()); + + if (gFileExists(fullpath)) + if (!gdConfirmWin("Warning", "File exists: overwrite?")) + return; + + if (((SampleChannel*)ch)->save(fullpath)) + do_callback(); + else + gdAlert("Unable to save this sample!"); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_load_sample() { + if (browser->text(browser->value()) == NULL) + return; + + int res = glue_loadChannel((SampleChannel*) ch, browser->get_selected_item()); + + if (res == SAMPLE_LOADED_OK) { + do_callback(); + mainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open + } + else + mainWin->keyboard->printChannelMessage(res); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_down() { + const char *path = browser->get_selected_item(); + if (!path) // when click on an empty area + return; + if (!gIsDir(path)) { + + /* set the name of the patch/sample/project as the selected item */ + + if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) { + if (gIsProject(path)) { + std::string tmp = browser->text(browser->value()); + tmp.erase(0, 4); + name->value(tmp.c_str()); + } + else + name->value(browser->text(browser->value())); + } + return; + } + browser->clear(); + browser->down_dir(path); + browser->sort(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_up() { + browser->clear(); + browser->up_dir(); + browser->sort(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_save_patch() { + + if (strcmp(name->value(), "") == 0) { /// FIXME glue business + gdAlert("Please choose a file name."); + return; + } + + /* if name->value() contains ".gptc" */ + + char ext[6] = ".gptc"; + if (strstr(name->value(), ".gptc") != NULL) + ext[0] = '\0'; + + char fullpath[PATH_MAX]; + sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext); + if (gFileExists(fullpath)) + if (!gdConfirmWin("Warning", "File exists: overwrite?")) + return; + + if (glue_savePatch(fullpath, name->value(), false)) // false == not a project + do_callback(); + else + gdAlert("Unable to save the patch!"); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_save_project() { + + if (strcmp(name->value(), "") == 0) { /// FIXME glue business + gdAlert("Please choose a project name."); + return; + } + + /* check if name->value() contains ".gprj" */ + + char ext[6] = ".gprj"; + if (strstr(name->value(), ".gprj") != NULL) + ext[0] = '\0'; + + char fullpath[PATH_MAX]; +#if defined(_WIN32) + sprintf(fullpath, "%s\\%s%s", where->value(), name->value(), ext); +#else + sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext); +#endif + + if (gIsProject(fullpath) && !gdConfirmWin("Warning", "Project exists: overwrite?")) + return; + + if (glue_saveProject(fullpath, name->value())) + do_callback(); + else + gdAlert("Unable to save the project!"); +} + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST +void gdBrowser::__cb_loadPlugin() { + + if (browser->text(browser->value()) == NULL) + return; + + int res = G_PluginHost.addPlugin(browser->get_selected_item(), stackType, ch); + + /* store the folder path inside G_Conf, in order to reuse it the + * next time. */ + + G_Conf.setPath(G_Conf.pluginPath, where->value()); + + if (res) + do_callback(); + else + gdAlert("Unable to load the selected plugin!"); +} +#endif + + +/* -------------------------------------------------------------------------- */ + + +void gdBrowser::__cb_close() { + do_callback(); +} diff --git a/src/gui/dialogs/gd_browser.h b/src/gui/dialogs/gd_browser.h new file mode 100644 index 0000000..51bd0c5 --- /dev/null +++ b/src/gui/dialogs/gd_browser.h @@ -0,0 +1,102 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_browser + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GD_BROWSER_H +#define GD_BROWSER_H + + +#include +#include +#include "../elems/ge_window.h" + + +/* TODO - this class must be subclassed into gdPluginBrowser, gdFileBrowser, + * and so on. It's a real mess right now. */ + +class gdBrowser : public gWindow { + +private: + static void cb_down(Fl_Widget *v, void *p); + static void cb_up (Fl_Widget *v, void *p); + static void cb_load_sample (Fl_Widget *v, void *p); + static void cb_save_sample (Fl_Widget *v, void *p); + static void cb_load_patch (Fl_Widget *v, void *p); + static void cb_save_patch (Fl_Widget *v, void *p); + static void cb_save_project(Fl_Widget *v, void *p); + static void cb_close (Fl_Widget *w, void *p); +#ifdef WITH_VST + static void cb_loadPlugin (Fl_Widget *v, void *p); +#endif + + inline void __cb_down(); + inline void __cb_up(); + inline void __cb_load_sample(); + inline void __cb_save_sample(); + inline void __cb_save_project(); + inline void __cb_load_patch(); + inline void __cb_save_patch(); + inline void __cb_close(); +#ifdef WITH_VST + inline void __cb_loadPlugin(); +#endif + + class gBrowser *browser; + class gClick *ok; + class gClick *cancel; + class gInput *where; + class gInput *name; + class gClick *updir; + class gProgress *status; + + class Channel *ch; + + /* browser type: see const.h */ + + /** FIXME internal enum: + * enum browserType { + * TYPE_A, + * TYPE_B, + * .... + * }; */ + int type; + + /* PluginHost stack type. Used only when loading plugins */ + + int stackType; + + char selectedFile[FILENAME_MAX]; + +public: + gdBrowser(const char *title, const char *initPath, class Channel *ch, int type, int stackType=0); + ~gdBrowser(); + + char* SelectedFile(); +}; + +#endif diff --git a/src/gui/dialogs/gd_config.cpp b/src/gui/dialogs/gd_config.cpp new file mode 100644 index 0000000..45f8b15 --- /dev/null +++ b/src/gui/dialogs/gd_config.cpp @@ -0,0 +1,863 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_config + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/conf.h" +#include "../../core/midiMapConf.h" +#include "../../core/patch.h" +#include "../../core/kernelAudio.h" +#include "../../core/kernelMidi.h" +#include "../../utils/gui_utils.h" +#include "../../utils/log.h" +#include "../elems/ge_mixed.h" +#include "gd_config.h" +#include "gd_keyGrabber.h" +#include "gd_devInfo.h" +#include "gd_browser.h" + + +extern Patch G_Patch; +extern Conf G_Conf; +extern bool G_audio_status; +extern MidiMapConf G_MidiMap; + + +/* -------------------------------------------------------------------------- */ + + +gTabMisc::gTabMisc(int X, int Y, int W, int H) + : Fl_Group(X, Y, W, H, "Misc") +{ + begin(); + debugMsg = new gChoice(x()+92, y()+9, 253, 20, "Debug messages"); + end(); + + debugMsg->add("(disabled)"); + debugMsg->add("To standard output"); + debugMsg->add("To file"); + + labelsize(11); + + switch (G_Conf.logMode) { + case LOG_MODE_MUTE: + debugMsg->value(0); + break; + case LOG_MODE_STDOUT: + debugMsg->value(1); + break; + case LOG_MODE_FILE: + debugMsg->value(2); + break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabMisc::save() +{ + switch(debugMsg->value()) { + case 0: + G_Conf.logMode = LOG_MODE_MUTE; + break; + case 1: + G_Conf.logMode = LOG_MODE_STDOUT; + break; + case 2: + G_Conf.logMode = LOG_MODE_FILE; + break; + } +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gTabAudio::gTabAudio(int X, int Y, int W, int H) + : Fl_Group(X, Y, W, H, "Sound System") +{ + begin(); + soundsys = new gChoice(x()+92, y()+9, 253, 20, "System"); + buffersize = new gChoice(x()+92, y()+37, 55, 20, "Buffer size"); + samplerate = new gChoice(x()+290, y()+37, 55, 20, "Sample rate"); + sounddevOut = new gChoice(x()+92, y()+65, 225, 20, "Output device"); + devOutInfo = new gClick (x()+325, y()+65, 20, 20, "?"); + channelsOut = new gChoice(x()+92, y()+93, 55, 20, "Output channels"); + limitOutput = new gCheck (x()+155, y()+97, 55, 20, "Limit output"); + sounddevIn = new gChoice(x()+92, y()+121, 225, 20, "Input device"); + devInInfo = new gClick (x()+325, y()+121, 20, 20, "?"); + channelsIn = new gChoice(x()+92, y()+149, 55, 20, "Input channels"); + delayComp = new gInput (x()+290, y()+149, 55, 20, "Rec delay comp."); + rsmpQuality = new gChoice(x()+92, y()+177, 253, 20, "Resampling"); + new gBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92, "Restart Giada for the changes to take effect."); + end(); + labelsize(11); + +#if defined(__linux__) + + if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA)) + soundsys->add("ALSA"); + if (kernelAudio::hasAPI(RtAudio::UNIX_JACK)) + soundsys->add("Jack"); + if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE)) + soundsys->add("PulseAudio"); + + switch (G_Conf.soundSystem) { + case SYS_API_ALSA: + soundsys->show("ALSA"); + break; + case SYS_API_JACK: + soundsys->show("Jack"); + buffersize->deactivate(); + samplerate->deactivate(); + break; + case SYS_API_PULSE: + soundsys->show("PulseAudio"); + break; + } + soundsysInitValue = soundsys->value(); + +#elif defined(_WIN32) + + if (kernelAudio::hasAPI(RtAudio::WINDOWS_DS)) + soundsys->add("DirectSound"); + if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO)) + soundsys->add("ASIO"); + soundsys->show(G_Conf.soundSystem == SYS_API_DS ? "DirectSound" : "ASIO"); + soundsysInitValue = soundsys->value(); + +#elif defined (__APPLE__) + + if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE)) + soundsys->add("CoreAudio"); + soundsys->show("CoreAudio"); + soundsysInitValue = soundsys->value(); + +#endif + + sounddevIn->callback(cb_fetchInChans, this); + sounddevOut->callback(cb_fetchOutChans, this); + + devOutInfo->callback(cb_showOutputInfo, this); + devInInfo->callback(cb_showInputInfo, this); + + fetchSoundDevs(); + + fetchOutChans(sounddevOut->value()); + fetchInChans(sounddevIn->value()); + + buffersize->add("8"); + buffersize->add("16"); + buffersize->add("32"); + buffersize->add("64"); + buffersize->add("128"); + buffersize->add("256"); + buffersize->add("512"); + buffersize->add("1024"); + buffersize->add("2048"); + buffersize->add("4096"); + + char buf[8]; + sprintf(buf, "%d", G_Conf.buffersize); + buffersize->show(buf); + + /* fill frequency dropdown menu */ + + int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value()); + for (int i=0; ivalue(), i); + sprintf(buf, "%d", freq); + samplerate->add(buf); + if (freq == G_Conf.samplerate) + samplerate->value(i); + } + + rsmpQuality->add("Sinc best quality (very slow)"); + rsmpQuality->add("Sinc medium quality (slow)"); + rsmpQuality->add("Sinc basic quality (medium)"); + rsmpQuality->add("Zero Order Hold (fast)"); + rsmpQuality->add("Linear (very fast)"); + rsmpQuality->value(G_Conf.rsmpQuality); + + buf[0] = '\0'; + sprintf(buf, "%d", G_Conf.delayComp); + delayComp->value(buf); + delayComp->type(FL_INT_INPUT); + delayComp->maximum_size(5); + + limitOutput->value(G_Conf.limitOutput); + soundsys->callback(cb_deactivate_sounddev, (void*)this); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::cb_deactivate_sounddev(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_deactivate_sounddev(); } +void gTabAudio::cb_fetchInChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchInChans(); } +void gTabAudio::cb_fetchOutChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchOutChans(); } +void gTabAudio::cb_showInputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showInputInfo(); } +void gTabAudio::cb_showOutputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showOutputInfo(); } + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::__cb_fetchInChans() +{ + fetchInChans(sounddevIn->value()); + channelsIn->value(0); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::__cb_fetchOutChans() +{ + fetchOutChans(sounddevOut->value()); + channelsOut->value(0); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::__cb_showInputInfo() +{ + unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); + new gdDevInfo(dev); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::__cb_showOutputInfo() +{ + unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); + new gdDevInfo(dev); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::__cb_deactivate_sounddev() +{ + /* if the user changes sound system (eg ALSA->JACK) device menu deactivates. + * If it returns to the original sound system, we re-fill the list by + * querying kernelAudio. */ + + if (soundsysInitValue == soundsys->value()) { + sounddevOut->clear(); + sounddevIn->clear(); + + fetchSoundDevs(); + + /* the '?' button is added by fetchSoundDevs */ + + fetchOutChans(sounddevOut->value()); + sounddevOut->activate(); + channelsOut->activate(); + + /* chan menus and '?' button are activated by fetchInChans(...) */ + + fetchInChans(sounddevIn->value()); + sounddevIn->activate(); + } + else { + sounddevOut->deactivate(); + sounddevOut->clear(); + sounddevOut->add("-- restart to fetch device(s) --"); + sounddevOut->value(0); + channelsOut->deactivate(); + devOutInfo->deactivate(); + + sounddevIn->deactivate(); + sounddevIn->clear(); + sounddevIn->add("-- restart to fetch device(s) --"); + sounddevIn->value(0); + channelsIn->deactivate(); + devInInfo->deactivate(); + } +} + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::fetchInChans(int menuItem) +{ + /* if menuItem==0 device in input is disabled. */ + + if (menuItem == 0) { + devInInfo ->deactivate(); + channelsIn->deactivate(); + delayComp ->deactivate(); + return; + } + + devInInfo ->activate(); + channelsIn->activate(); + delayComp ->activate(); + + channelsIn->clear(); + + unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); + unsigned chs = kernelAudio::getMaxInChans(dev); + + if (chs == 0) { + channelsIn->add("none"); + channelsIn->value(0); + return; + } + for (unsigned i=0; iadd(str); + } + channelsIn->value(G_Conf.channelsIn); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::fetchOutChans(int menuItem) +{ + channelsOut->clear(); + + unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); + unsigned chs = kernelAudio::getMaxOutChans(dev); + + if (chs == 0) { + channelsOut->add("none"); + channelsOut->value(0); + return; + } + for (unsigned i=0; iadd(str); + } + channelsOut->value(G_Conf.channelsOut); +} + + +/* -------------------------------------------------------------------------- */ + + +int gTabAudio::findMenuDevice(gChoice *m, int device) +{ + if (device == -1) + return 0; + + if (G_audio_status == false) + return 0; + + for (int i=0; isize(); i++) { + if (kernelAudio::getDeviceName(device) == NULL) + continue; + if (m->text(i) == NULL) + continue; + if (strcmp(m->text(i), kernelAudio::getDeviceName(device))==0) + return i; + } + + return 0; +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::fetchSoundDevs() +{ + if (kernelAudio::numDevs == 0) { + sounddevOut->add("-- no devices found --"); + sounddevOut->value(0); + sounddevIn->add("-- no devices found --"); + sounddevIn->value(0); + devInInfo ->deactivate(); + devOutInfo->deactivate(); + } + else { + + devInInfo ->activate(); + devOutInfo->activate(); + + /* input device may be disabled: now device number -1 is the disabled + * one. KernelAudio knows how to handle it. */ + + sounddevIn->add("(disabled)"); + + for (unsigned i=0; i 0) + sounddevOut->add(tmp.c_str()); + + if (kernelAudio::getMaxInChans(i) > 0) + sounddevIn->add(tmp.c_str()); + } + + /* we show the device saved in the configuration file. */ + + if (sounddevOut->size() == 0) { + sounddevOut->add("-- no devices found --"); + sounddevOut->value(0); + devOutInfo->deactivate(); + } + else { + int outMenuValue = findMenuDevice(sounddevOut, G_Conf.soundDeviceOut); + sounddevOut->value(outMenuValue); + } + + if (sounddevIn->size() == 0) { + sounddevIn->add("-- no devices found --"); + sounddevIn->value(0); + devInInfo->deactivate(); + } + else { + int inMenuValue = findMenuDevice(sounddevIn, G_Conf.soundDeviceIn); + sounddevIn->value(inMenuValue); + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabAudio::save() +{ + /** FIXME - wrong, if API is missing! Right way in gTabMidi::save */ + +#ifdef __linux__ + if (soundsys->value() == 0) G_Conf.soundSystem = SYS_API_ALSA; + else if (soundsys->value() == 1) G_Conf.soundSystem = SYS_API_JACK; + else if (soundsys->value() == 2) G_Conf.soundSystem = SYS_API_PULSE; +#else +#ifdef _WIN32 + if (soundsys->value() == 0) G_Conf.soundSystem = SYS_API_DS; + else if (soundsys->value() == 1) G_Conf.soundSystem = SYS_API_ASIO; +#endif +#endif + + /* use the device name to search into the drop down menu's */ + + G_Conf.soundDeviceOut = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); + G_Conf.soundDeviceIn = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); + G_Conf.channelsOut = channelsOut->value(); + G_Conf.channelsIn = channelsIn->value(); + G_Conf.limitOutput = limitOutput->value(); + G_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 (G_Conf.soundDeviceOut == -1) + G_Conf.soundDeviceOut = 0; + + int bufsize = atoi(buffersize->text()); + if (bufsize % 2 != 0) bufsize++; + if (bufsize < 8) bufsize = 8; + if (bufsize > 8192) bufsize = 8192; + G_Conf.buffersize = bufsize; + + const Fl_Menu_Item *i = NULL; + i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked + if (i) + G_Conf.samplerate = atoi(i->label()); + + int _delayComp = atoi(delayComp->value()); + if (_delayComp < 0) _delayComp = 0; + G_Conf.delayComp = _delayComp; +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gTabMidi::gTabMidi(int X, int Y, int W, int H) + : Fl_Group(X, Y, W, H, "MIDI") +{ + begin(); + system = new gChoice(x()+92, y()+9, 253, 20, "System"); + portOut = new gChoice(x()+92, system->y()+system->h()+8, 253, 20, "Output port"); + portIn = new gChoice(x()+92, portOut->y()+portOut->h()+8, 253, 20, "Input port"); + noNoteOff = new gCheck (x()+92, portIn->y()+portIn->h()+8, 253, 20, "Device does not send NoteOff"); + midiMap = new gChoice(x()+92, noNoteOff->y()+noNoteOff->h(), 253, 20, "Output Midi Map"); + sync = new gChoice(x()+92, midiMap->y()+midiMap->h()+8, 253, 20, "Sync"); + new gBox(x(), sync->y()+sync->h()+8, w(), h()-125, "Restart Giada for the changes to take effect."); + end(); + + labelsize(11); + + system->callback(cb_changeSystem, (void*)this); + + fetchSystems(); + fetchOutPorts(); + fetchInPorts(); + fetchMidiMaps(); + + noNoteOff->value(G_Conf.noNoteOff); + + sync->add("(disabled)"); + sync->add("MIDI Clock (master)"); + sync->add("MTC (master)"); + if (G_Conf.midiSync == MIDI_SYNC_NONE) + sync->value(0); + else if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) + sync->value(1); + else if (G_Conf.midiSync == MIDI_SYNC_MTC_M) + sync->value(2); + + systemInitValue = system->value(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabMidi::fetchOutPorts() { + + if (kernelMidi::numOutPorts == 0) { + portOut->add("-- no ports found --"); + portOut->value(0); + portOut->deactivate(); + } + else { + + portOut->add("(disabled)"); + + for (unsigned i=0; iadd(t); + } + + portOut->value(G_Conf.midiPortOut+1); // +1 because midiPortOut=-1 is '(disabled)' + } +} + +/* -------------------------------------------------------------------------- */ + + +void gTabMidi::fetchInPorts() +{ + if (kernelMidi::numInPorts == 0) { + portIn->add("-- no ports found --"); + portIn->value(0); + portIn->deactivate(); + } + else { + + portIn->add("(disabled)"); + + for (unsigned i=0; iadd(t); + } + + portIn->value(G_Conf.midiPortIn+1); // +1 because midiPortIn=-1 is '(disabled)' + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabMidi::fetchMidiMaps() +{ + if (G_MidiMap.maps.size == 0) { + midiMap->add("(no MIDI maps available)"); + midiMap->value(0); + midiMap->deactivate(); + return; + } + for (unsigned i=0; iadd(imap); + if (strcmp(G_Conf.midiMapPath, imap) == 0) + midiMap->value(i); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabMidi::save() +{ + if (!strcmp("ALSA", system->text(system->value()))) + G_Conf.midiSystem = RtMidi::LINUX_ALSA; + else if (!strcmp("Jack", system->text(system->value()))) + G_Conf.midiSystem = RtMidi::UNIX_JACK; + else if (!strcmp("Multimedia MIDI", system->text(system->value()))) + G_Conf.midiSystem = RtMidi::WINDOWS_MM; + else if (!strcmp("OSX Core MIDI", system->text(system->value()))) + G_Conf.midiSystem = RtMidi::MACOSX_CORE; + + G_Conf.midiPortOut = portOut->value()-1; // -1 because midiPortOut=-1 is '(disabled)' + G_Conf.midiPortIn = portIn->value()-1; // -1 because midiPortIn=-1 is '(disabled)' + + G_Conf.noNoteOff = noNoteOff->value(); + + G_Conf.setPath(G_Conf.midiMapPath, midiMap->text(midiMap->value())); + + if (sync->value() == 0) + G_Conf.midiSync = MIDI_SYNC_NONE; + else if (sync->value() == 1) + G_Conf.midiSync = MIDI_SYNC_CLOCK_M; + else if (sync->value() == 2) + G_Conf.midiSync = MIDI_SYNC_MTC_M; +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabMidi::fetchSystems() +{ +#if defined(__linux__) + + if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA)) + system->add("ALSA"); + if (kernelMidi::hasAPI(RtMidi::UNIX_JACK)) + system->add("Jack"); + +#elif defined(_WIN32) + + if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM)) + system->add("Multimedia MIDI"); + +#elif defined (__APPLE__) + + system->add("OSX Core MIDI"); + +#endif + + switch (G_Conf.midiSystem) { + case RtMidi::LINUX_ALSA: system->show("ALSA"); break; + case RtMidi::UNIX_JACK: system->show("Jack"); break; + case RtMidi::WINDOWS_MM: system->show("Multimedia MIDI"); break; + case RtMidi::MACOSX_CORE: system->show("OSX Core MIDI"); break; + default: system->value(0); break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((gTabMidi*)p)->__cb_changeSystem(); } + + +/* -------------------------------------------------------------------------- */ + + +void gTabMidi::__cb_changeSystem() +{ + /* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates. + * If it returns to the original system, we re-fill the list by + * querying kernelMidi. */ + + if (systemInitValue == system->value()) { + portOut->clear(); + fetchOutPorts(); + portOut->activate(); + } + else { + portOut->deactivate(); + portOut->clear(); + portOut->add("-- restart to fetch device(s) --"); + portOut->value(0); + } + +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gTabBehaviors::gTabBehaviors(int X, int Y, int W, int H) + : Fl_Group(X, Y, W, H, "Behaviors") +{ + begin(); + Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex + new gBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT); + recsStopOnChanHalt_1 = new gRadio(x()+25, y()+35, 280, 20, "stop it immediately"); + recsStopOnChanHalt_0 = new gRadio(x()+25, y()+55, 280, 20, "play it until finished"); + radioGrp_1->end(); + + Fl_Group *radioGrp_2 = new Fl_Group(x(), y()+70, w(), 70); // radio group for the mutex + new gBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT); + chansStopOnSeqHalt_1 = new gRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels"); + chansStopOnSeqHalt_0 = new gRadio(x()+25, y()+125, 280, 20, "play all dynamic channels until finished"); + radioGrp_2->end(); + + treatRecsAsLoops = new gCheck(x(), y()+155, 280, 20, "Treat one shot channels with actions as loops"); + + end(); + labelsize(11); + + G_Conf.recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1); + G_Conf.chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1); + G_Conf.treatRecsAsLoops == 1 ? treatRecsAsLoops->value(1) : treatRecsAsLoops->value(0); + + 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 gTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((gTabBehaviors*)p)->__cb_radio_mutex(w); } + + +/* -------------------------------------------------------------------------- */ + + +void gTabBehaviors::__cb_radio_mutex(Fl_Widget *w) +{ + ((Fl_Button *)w)->type(FL_RADIO_BUTTON); +} + + +/* -------------------------------------------------------------------------- */ + + +void gTabBehaviors::save() +{ + G_Conf.recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0; + G_Conf.chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0; + G_Conf.treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0; +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gdConfig::gdConfig(int w, int h) : gWindow(w, h, "Configuration") +{ + set_modal(); + + if (G_Conf.configX) + resize(G_Conf.configX, G_Conf.configY, this->w(), this->h()); + + Fl_Tabs *tabs = new Fl_Tabs(8, 8, w-16, h-44); + tabAudio = new gTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); + tabMidi = new gTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); + tabBehaviors = new gTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); + tabMisc = new gTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40); + tabs->end(); + + save = new gClick (w-88, h-28, 80, 20, "Save"); + cancel = new gClick (w-176, h-28, 80, 20, "Cancel"); + + end(); + + tabs->box(FL_FLAT_BOX); // TODO - G_BOX crashes FLTK 1.3.3 + + tabs->labelcolor(COLOR_TEXT_0); + + save->callback(cb_save_config, (void*)this); + cancel->callback(cb_cancel, (void*)this); + + gu_setFavicon(this); + setId(WID_CONFIG); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +gdConfig::~gdConfig() +{ + G_Conf.configX = x(); + G_Conf.configY = y(); +} + + +/* -------------------------------------------------------------------------- */ + + +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() +{ + tabAudio->save(); + tabBehaviors->save(); + tabMidi->save(); + tabMisc->save(); + do_callback(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdConfig::__cb_cancel() +{ + do_callback(); +} diff --git a/src/gui/dialogs/gd_config.h b/src/gui/dialogs/gd_config.h new file mode 100644 index 0000000..64d06d4 --- /dev/null +++ b/src/gui/dialogs/gd_config.h @@ -0,0 +1,171 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_config + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GD_CONFIG_H +#define GD_CONFIG_H + +#include +#include +#include +#include "../elems/ge_window.h" + + +class gdConfig : public gWindow +{ +private: + static void cb_save_config (Fl_Widget *w, void *p); + static void cb_cancel (Fl_Widget *w, void *p); + inline void __cb_save_config(); + inline void __cb_cancel(); + +public: + gdConfig(int w, int h); + ~gdConfig(); + + class gTabAudio *tabAudio; + class gTabBehaviors *tabBehaviors; + class gTabMidi *tabMidi; + class gTabMisc *tabMisc; + class gClick *save; + class gClick *cancel; +}; + + +/* ------------------------------------------------------------------ */ + + +class gTabMidi : public Fl_Group +{ +private: + + void fetchSystems(); + void fetchOutPorts(); + void fetchInPorts(); + void fetchMidiMaps(); + + static void cb_changeSystem (Fl_Widget *w, void *p); + inline void __cb_changeSystem(); + + int systemInitValue; + +public: + + class gChoice *system; + class gChoice *portOut; + class gChoice *portIn; + class gCheck *noNoteOff; + class gChoice *midiMap; + class gChoice *sync; + + gTabMidi(int x, int y, int w, int h); + + void save(); +}; + + +/* ------------------------------------------------------------------ */ + + +class gTabAudio : public Fl_Group +{ +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); + inline void __cb_deactivate_sounddev(); + inline void __cb_fetchInChans(); + inline void __cb_fetchOutChans(); + inline void __cb_showInputInfo(); + inline void __cb_showOutputInfo(); + + void fetchSoundDevs(); + void fetchInChans(int menuItem); + void fetchOutChans(int menuItem); + int findMenuDevice(class gChoice *m, int device); + + int soundsysInitValue; + +public: + class gChoice *soundsys; + class gChoice *samplerate; + class gChoice *rsmpQuality; + class gChoice *sounddevIn; + class gClick *devInInfo; + class gChoice *channelsIn; + class gChoice *sounddevOut; + class gClick *devOutInfo; + class gChoice *channelsOut; + class gCheck *limitOutput; + class gChoice *buffersize; + class gInput *delayComp; + + gTabAudio(int x, int y, int w, int h); + + void save(); +}; + + +/* ------------------------------------------------------------------ */ + + +class gTabBehaviors : public Fl_Group +{ +private: + static void cb_radio_mutex (Fl_Widget *w, void *p); + inline void __cb_radio_mutex(Fl_Widget *w); + +public: + class gRadio *recsStopOnChanHalt_1; + class gRadio *recsStopOnChanHalt_0; + class gRadio *chansStopOnSeqHalt_1; + class gRadio *chansStopOnSeqHalt_0; + class gCheck *treatRecsAsLoops; + + gTabBehaviors(int x, int y, int w, int h); + + void save(); +}; + + +/* ------------------------------------------------------------------ */ + + +class gTabMisc : public Fl_Group +{ +public: + class gChoice *debugMsg; + + gTabMisc(int x, int y, int w, int h); + + void save(); +}; + + +#endif diff --git a/src/gui/dialogs/gd_devInfo.cpp b/src/gui/dialogs/gd_devInfo.cpp new file mode 100644 index 0000000..efdc064 --- /dev/null +++ b/src/gui/dialogs/gd_devInfo.cpp @@ -0,0 +1,107 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_devInfo + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "../../core/kernelAudio.h" +#include "../../utils/gui_utils.h" +#include "../elems/ge_mixed.h" +#include "gd_devInfo.h" + + +gdDevInfo::gdDevInfo(unsigned dev) +: Fl_Window(340, 300, "Device information") { + set_modal(); + + text = new gBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); + close = new gClick(252, h()-28, 80, 20, "Close"); + end(); + + std::string bufTxt; + char bufNum[128]; + int lines = 0; + + bufTxt = "Device name: "; + bufTxt += +kernelAudio::getDeviceName(dev); + bufTxt += "\n"; + lines++; + + bufTxt += "Total output(s): "; + sprintf(bufNum, "%d\n", kernelAudio::getMaxOutChans(dev)); + bufTxt += bufNum; + lines++; + + bufTxt += "Total intput(s): "; + sprintf(bufNum, "%d\n", kernelAudio::getMaxInChans(dev)); + bufTxt += bufNum; + lines++; + + bufTxt += "Duplex channel(s): "; + sprintf(bufNum, "%d\n", kernelAudio::getDuplexChans(dev)); + bufTxt += bufNum; + lines++; + + bufTxt += "Default output: "; + sprintf(bufNum, "%s\n", kernelAudio::isDefaultOut(dev) ? "yes" : "no"); + bufTxt += bufNum; + lines++; + + bufTxt += "Default input: "; + sprintf(bufNum, "%s\n", kernelAudio::isDefaultIn(dev) ? "yes" : "no"); + bufTxt += bufNum; + lines++; + + int totalFreq = kernelAudio::getTotalFreqs(dev); + bufTxt += "Supported frequencies: "; + sprintf(bufNum, "%d", totalFreq); + bufTxt += bufNum; + lines++; + + for (int i=0; icopy_label(bufTxt.c_str()); + + /* resize the window to fit the content. fl_height() returns the height + * of a line. fl_height() * total lines + margins + button size */ + + resize(x(), y(), w(), lines*fl_height() + 8 + 8 + 8 + 20); + close->position(close->x(), lines*fl_height() + 8 + 8); + + close->callback(__cb_window_closer, (void*)this); + gu_setFavicon(this); + show(); +} + + +gdDevInfo::~gdDevInfo() {} diff --git a/src/gui/dialogs/gd_devInfo.h b/src/gui/dialogs/gd_devInfo.h new file mode 100644 index 0000000..c5a06ca --- /dev/null +++ b/src/gui/dialogs/gd_devInfo.h @@ -0,0 +1,47 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_devInfo + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GD_DEV_INFO_H +#define GD_DEV_INFO_H + +#include +#include + + +class gdDevInfo : public Fl_Window { +private: + class gBox *text; + class gClick *close; + +public: + gdDevInfo(unsigned dev); + ~gdDevInfo(); +}; + +#endif diff --git a/src/gui/dialogs/gd_editor.cpp b/src/gui/dialogs/gd_editor.cpp new file mode 100644 index 0000000..231bdec --- /dev/null +++ b/src/gui/dialogs/gd_editor.cpp @@ -0,0 +1,491 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_editor + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "../../utils/gui_utils.h" +#include "../../glue/glue.h" +#include "../../core/waveFx.h" +#include "../../core/conf.h" +#include "../../core/graphics.h" +#include "../../core/mixerHandler.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../core/mixer.h" +#include "../../core/wave.h" +#include "../elems/ge_waveform.h" +#include "../elems/ge_mixed.h" +#include "../elems/ge_channel.h" +#include "../elems/ge_waveTools.h" +#include "../elems/ge_keyboard.h" +#include "gd_editor.h" +#include "gd_mainWindow.h" +#include "gd_warnings.h" + + +extern Mixer G_Mixer; +extern gdMainWindow *mainWin; +extern Conf G_Conf; + + +gdEditor::gdEditor(SampleChannel *ch) + : gWindow(640, 480), + ch(ch) +{ + set_non_modal(); + + if (G_Conf.sampleEditorX) + resize(G_Conf.sampleEditorX, G_Conf.sampleEditorY, G_Conf.sampleEditorW, G_Conf.sampleEditorH); + + /* top bar: grid and zoom tools */ + + Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20); + bar->begin(); + grid = new gChoice(bar->x(), bar->y(), 50, 20); + snap = new gCheck(grid->x()+grid->w()+4, bar->y()+4, 12, 12); + zoomOut = new gClick(bar->x()+bar->w()-20, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm); + zoomIn = new gClick(zoomOut->x()-24, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm); + bar->end(); + bar->resizable(new gBox(grid->x()+grid->w()+4, bar->y(), 80, bar->h())); + + /* waveform */ + + waveTools = new gWaveTools(8, 36, w()-16, h()-120, ch); + waveTools->end(); + + /* other tools */ + + Fl_Group *tools = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 130); + tools->begin(); + volume = new gDial (tools->x()+42, tools->y(), 20, 20, "Volume"); + volumeNum = new gInput(volume->x()+volume->w()+4, tools->y(), 46, 20, "dB"); + + boost = new gDial (volumeNum->x()+volumeNum->w()+80, tools->y(), 20, 20, "Boost"); + boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 46, 20, "dB"); + + normalize = new gClick(boostNum->x()+boostNum->w()+54, tools->y(), 70, 20, "Normalize"); + pan = new gDial (normalize->x()+normalize->w()+40, tools->y(), 20, 20, "Pan"); + panNum = new gInput(pan->x()+pan->w()+4, tools->y(), 45, 20, "%"); + + pitch = new gDial (tools->x()+42, volume->y()+volume->h()+4, 20, 20, "Pitch"); + pitchNum = new gInput(pitch->x()+pitch->w()+4, volume->y()+volume->h()+4, 46, 20); + pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 46, 20, "To bar"); + pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 46, 20, "To song"); + pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 21, 20, "÷"); + pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 21, 20, "×"); + pitchReset = new gClick(pitchDouble->x()+pitchDouble->w()+4, volume->y()+volume->h()+4, 46, 20, "Reset"); + reload = new gClick(pitchReset->x()+pitchReset->w()+4, volume->y()+volume->h()+4, 70, 20, "Reload"); + + chanStart = new gInput(tools->x()+52, pitch->y()+pitch->h()+4, 60, 20, "Start"); + chanEnd = new gInput(chanStart->x()+chanStart->w()+40, pitch->y()+pitch->h()+4, 60, 20, "End"); + resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 46, 20, "Reset"); + + tools->end(); + tools->resizable(new gBox(panNum->x()+panNum->w()+4, tools->y(), 80, tools->h())); + + /* grid tool setup */ + + grid->add("(off)"); + grid->add("2"); + grid->add("3"); + grid->add("4"); + grid->add("6"); + grid->add("8"); + grid->add("16"); + grid->add("32"); + grid->add("64"); + grid->value(G_Conf.sampleEditorGridVal); + grid->callback(cb_changeGrid, (void*)this); + + snap->value(G_Conf.sampleEditorGridOn); + snap->callback(cb_enableSnap, (void*)this); + + /* TODO - redraw grid if != (off) */ + + char buf[16]; + sprintf(buf, "%d", ch->begin / 2); // divided by 2 because stereo + chanStart->value(buf); + chanStart->type(FL_INT_INPUT); + chanStart->callback(cb_setChanPos, this); + + /* inputs callback: fire when they lose focus or Enter is pressed. */ + + chanStart->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); + chanEnd ->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); + + sprintf(buf, "%d", ch->end / 2); // divided by 2 because stereo + chanEnd->value(buf); + chanEnd->type(FL_INT_INPUT); + chanEnd->callback(cb_setChanPos, this); + + resetStartEnd->callback(cb_resetStartEnd, this); + + volume->callback(cb_setVolume, (void*)this); + volume->value(ch->guiChannel->vol->value()); + + float dB = 20*log10(ch->volume); // dB = 20*log_10(linear value) + if (dB > -INFINITY) sprintf(buf, "%.2f", dB); + else sprintf(buf, "-inf"); + volumeNum->value(buf); + volumeNum->align(FL_ALIGN_RIGHT); + volumeNum->callback(cb_setVolumeNum, (void*)this); + + boost->range(1.0f, 10.0f); + boost->callback(cb_setBoost, (void*)this); + if (ch->boost > 10.f) + boost->value(10.0f); + else + boost->value(ch->boost); + boost->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE); + + float boost = 20*log10(ch->boost); // dB = 20*log_10(linear value) + sprintf(buf, "%.2f", boost); + boostNum->value(buf); + boostNum->align(FL_ALIGN_RIGHT); + boostNum->callback(cb_setBoostNum, (void*)this); + + normalize->callback(cb_normalize, (void*)this); + + pan->range(0.0f, 2.0f); + pan->callback(cb_panning, (void*)this); + + pitch->range(0.01f, 4.0f); + pitch->value(ch->pitch); + pitch->callback(cb_setPitch, (void*)this); + pitch->when(FL_WHEN_RELEASE); + + sprintf(buf, "%.4f", ch->pitch); // 4 digits + pitchNum->value(buf); + pitchNum->align(FL_ALIGN_RIGHT); + pitchNum->callback(cb_setPitchNum, (void*)this); + pitchNum->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); + + reload->callback(cb_reload, (void*)this); + + zoomOut->callback(cb_zoomOut, (void*)this); + zoomIn->callback(cb_zoomIn, (void*)this); + + /* logical samples (aka takes) cannot be reloaded. So far. */ + + if (ch->wave->isLogical) + reload->deactivate(); + + if (ch->panRight < 1.0f) { + char buf[8]; + sprintf(buf, "%d L", abs((ch->panRight * 100.0f) - 100)); + pan->value(ch->panRight); + panNum->value(buf); + } + else if (ch->panRight == 1.0f && ch->panLeft == 1.0f) { + pan->value(1.0f); + panNum->value("C"); + } + else { + char buf[8]; + sprintf(buf, "%d R", abs((ch->panLeft * 100.0f) - 100)); + pan->value(2.0f - ch->panLeft); + panNum->value(buf); + } + + panNum->align(FL_ALIGN_RIGHT); + panNum->readonly(1); + panNum->cursor_color(FL_WHITE); + + gu_setFavicon(this); + size_range(640, 480); + resizable(waveTools); + + label(ch->wave->name.c_str()); + + show(); +} + + +/* ------------------------------------------------------------------ */ + + +gdEditor::~gdEditor() +{ + G_Conf.sampleEditorX = x(); + G_Conf.sampleEditorY = y(); + G_Conf.sampleEditorW = w(); + G_Conf.sampleEditorH = h(); + G_Conf.sampleEditorGridVal = grid->value(); + G_Conf.sampleEditorGridOn = snap->value(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::cb_setChanPos (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setChanPos(); } +void gdEditor::cb_resetStartEnd (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetStartEnd(); } +void gdEditor::cb_setVolume (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolume(); } +void gdEditor::cb_setVolumeNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolumeNum(); } +void gdEditor::cb_setBoost (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoost(); } +void gdEditor::cb_setBoostNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoostNum(); } +void gdEditor::cb_normalize (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_normalize(); } +void gdEditor::cb_panning (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_panning(); } +void gdEditor::cb_reload (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_reload(); } +void gdEditor::cb_setPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitch(); } +void gdEditor::cb_setPitchToBar (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToBar(); } +void gdEditor::cb_setPitchToSong (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToSong(); } +void gdEditor::cb_setPitchHalf (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchHalf(); } +void gdEditor::cb_setPitchDouble (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchDouble(); } +void gdEditor::cb_resetPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetPitch(); } +void gdEditor::cb_setPitchNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchNum(); } +void gdEditor::cb_zoomIn (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomIn(); } +void gdEditor::cb_zoomOut (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomOut(); } +void gdEditor::cb_changeGrid (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_changeGrid(); } +void gdEditor::cb_enableSnap (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_enableSnap(); } + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_enableSnap() +{ + waveTools->waveform->setSnap(!waveTools->waveform->getSnap()); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setPitchToBar() +{ + glue_setPitch(this, ch, ch->end/(float)G_Mixer.framesPerBar, true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setPitchToSong() +{ + glue_setPitch(this, ch, ch->end/(float)G_Mixer.totalFrames, true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_resetPitch() +{ + glue_setPitch(this, ch, 1.0f, true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setChanPos() +{ + glue_setBeginEndChannel( + this, + ch, + atoi(chanStart->value())*2, // glue halves printed values + atoi(chanEnd->value())*2, + true + ); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_resetStartEnd() +{ + glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setVolume() +{ + glue_setVolEditor(this, ch, volume->value(), false); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setVolumeNum() +{ + glue_setVolEditor(this, ch, atof(volumeNum->value()), true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setBoost() +{ + if (Fl::event() == FL_DRAG) + glue_setBoost(this, ch, boost->value(), false); + else if (Fl::event() == FL_RELEASE) { + glue_setBoost(this, ch, boost->value(), false); + waveTools->updateWaveform(); + } +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setBoostNum() +{ + glue_setBoost(this, ch, atof(boostNum->value()), true); + waveTools->updateWaveform(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_normalize() +{ + float val = wfx_normalizeSoft(ch->wave); + glue_setBoost(this, ch, val, false); // we pretend that a fake user turns the dial (numeric=false) + if (val < 0.0f) + boost->value(0.0f); + else + if (val > 20.0f) // a dial > than it's max value goes crazy + boost->value(10.0f); + else + boost->value(val); + waveTools->updateWaveform(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_panning() +{ + glue_setPanning(this, ch, pan->value()); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_reload() +{ + if (!gdConfirmWin("Warning", "Reload sample: are you sure?")) + return; + + /* no need for glue_loadChan, there's no gui to refresh */ + + ch->load(ch->wave->pathfile.c_str()); + + glue_setBoost(this, ch, DEFAULT_BOOST, true); + glue_setPitch(this, ch, gDEFAULT_PITCH, true); + glue_setPanning(this, ch, 1.0f); + pan->value(1.0f); // glue_setPanning doesn't do it + pan->redraw(); // glue_setPanning doesn't do it + + waveTools->waveform->stretchToWindow(); + waveTools->updateWaveform(); + + glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true); + + redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setPitch() +{ + glue_setPitch(this, ch, pitch->value(), false); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setPitchNum() +{ + glue_setPitch(this, ch, atof(pitchNum->value()), true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setPitchHalf() +{ + glue_setPitch(this, ch, pitch->value()/2, true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_setPitchDouble() +{ + glue_setPitch(this, ch, pitch->value()*2, true); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_zoomIn() +{ + waveTools->waveform->setZoom(-1); + waveTools->redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_zoomOut() +{ + waveTools->waveform->setZoom(0); + waveTools->redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +void gdEditor::__cb_changeGrid() +{ + waveTools->waveform->setGridLevel(atoi(grid->text())); +} diff --git a/src/gui/dialogs/gd_editor.h b/src/gui/dialogs/gd_editor.h new file mode 100644 index 0000000..54a8f79 --- /dev/null +++ b/src/gui/dialogs/gd_editor.h @@ -0,0 +1,116 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_editor + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GD_EDITOR_H +#define GD_EDITOR_H + +#include +#include +#include +#include "../elems/ge_window.h" + + +class gdEditor : public gWindow { + +private: + + static void cb_setChanPos (Fl_Widget *w, void *p); + static void cb_resetStartEnd (Fl_Widget *w, void *p); + static void cb_setVolume (Fl_Widget *w, void *p); + static void cb_setVolumeNum (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); + static void cb_panning (Fl_Widget *w, void *p); + static void cb_reload (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); + 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); + inline void __cb_setChanPos(); + inline void __cb_resetStartEnd(); + inline void __cb_setVolume(); + inline void __cb_setVolumeNum(); + inline void __cb_setBoost(); + inline void __cb_setBoostNum(); + inline void __cb_normalize(); + inline void __cb_panning(); + inline void __cb_reload(); + inline void __cb_setPitch(); + inline void __cb_setPitchToBar(); + inline void __cb_setPitchToSong(); + inline void __cb_setPitchHalf(); + inline void __cb_setPitchDouble(); + inline void __cb_resetPitch(); + inline void __cb_setPitchNum(); + inline void __cb_zoomIn(); + inline void __cb_zoomOut(); + inline void __cb_changeGrid(); + inline void __cb_enableSnap(); + +public: + + gdEditor(class SampleChannel *ch); + ~gdEditor(); + + class gClick *zoomIn; + class gClick *zoomOut; + class gWaveTools *waveTools; + class gInput *chanStart; + class gInput *chanEnd; + class gClick *resetStartEnd; + class gDial *volume; + class gInput *volumeNum; + class gDial *boost; + class gInput *boostNum; + class gClick *normalize; + class gDial *pan; + class gInput *panNum; + class gClick *reload; + class gDial *pitch; + class gInput *pitchNum; + class gClick *pitchToBar; + class gClick *pitchToSong; + class gClick *pitchHalf; + class gClick *pitchDouble; + class gClick *pitchReset; + class gClick *close; + class gChoice *grid; + class gCheck *snap; + + class SampleChannel *ch; +}; + +#endif diff --git a/src/gui/dialogs/gd_keyGrabber.cpp b/src/gui/dialogs/gd_keyGrabber.cpp new file mode 100644 index 0000000..6b4527f --- /dev/null +++ b/src/gui/dialogs/gd_keyGrabber.cpp @@ -0,0 +1,145 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_keyGrabber + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../utils/gui_utils.h" +#include "../../core/conf.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../core/midiChannel.h" +#include "../../utils/log.h" +#include "../elems/ge_keyboard.h" +#include "../elems/ge_mixed.h" +#include "../elems/ge_channel.h" +#include "../elems/ge_channelButton.h" +#include "gd_keyGrabber.h" +#include "gd_config.h" +#include "gd_mainWindow.h" + + +extern Conf G_Conf; +extern gdMainWindow *mainWin; + + +gdKeyGrabber::gdKeyGrabber(Channel *ch) + : gWindow(300, 126, "Key configuration"), ch(ch) +{ + set_modal(); + text = new gBox(8, 8, 284, 80, ""); + clear = new gClick(w()-88, text->y()+text->h()+8, 80, 20, "Clear"); + cancel = new gClick(clear->x()-88, clear->y(), 80, 20, "Close"); + end(); + + clear->callback(cb_clear, (void*)this); + cancel->callback(cb_cancel, (void*)this); + + updateText(ch->key); + + gu_setFavicon(this); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdKeyGrabber::cb_clear (Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_clear(); } +void gdKeyGrabber::cb_cancel(Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_cancel(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdKeyGrabber::__cb_cancel() +{ + do_callback(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdKeyGrabber::__cb_clear() +{ + updateText(0); + setButtonLabel(0); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdKeyGrabber::setButtonLabel(int key) +{ + char tmp[2]; sprintf(tmp, "%c", key); + ch->guiChannel->mainButton->setKey(tmp); + ch->key = key; +} + +/* -------------------------------------------------------------------------- */ + + +void gdKeyGrabber::updateText(int key) +{ + char tmp2[64]; + if (key != 0) + sprintf(tmp2, "Press a key.\n\nCurrent binding: %c", key); + else + sprintf(tmp2, "Press a key.\n\nCurrent binding: [none]"); + text->copy_label(tmp2); +} + + +/* -------------------------------------------------------------------------- */ + + +int gdKeyGrabber::handle(int e) +{ + int ret = Fl_Group::handle(e); + switch(e) { + case FL_KEYUP: { + int x = Fl::event_key(); + if (strlen(Fl::event_text()) != 0 + && x != FL_BackSpace + && x != FL_Enter + && x != FL_Delete + && x != FL_Tab + && x != FL_End + && x != ' ') + { + gLog("set key '%c' (%d) for channel %d\n", x, x, ch->index); + setButtonLabel(x); + updateText(x); + break; + } + else + gLog("invalid key\n"); + } + } + return(ret); +} diff --git a/src/gui/dialogs/gd_keyGrabber.h b/src/gui/dialogs/gd_keyGrabber.h new file mode 100644 index 0000000..02bbe85 --- /dev/null +++ b/src/gui/dialogs/gd_keyGrabber.h @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_keyGrabber + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GD_KEYGRABBER_H +#define GD_KEYGRABBER_H + + +#include +#include "../elems/ge_window.h" + + +class gdKeyGrabber : public gWindow +{ +private: + + class Channel *ch; + + class gBox *text; + class gClick *clear; + class gClick *cancel; + + static void cb_clear (Fl_Widget *w, void *p); + static void cb_cancel(Fl_Widget *w, void *p); + inline void __cb_clear (); + inline void __cb_cancel(); + + void setButtonLabel(int key); + + void updateText(int key); + +public: + + gdKeyGrabber(class Channel *ch); + int handle(int e); +}; + +#endif diff --git a/src/gui/dialogs/gd_mainWindow.cpp b/src/gui/dialogs/gd_mainWindow.cpp new file mode 100644 index 0000000..0289763 --- /dev/null +++ b/src/gui/dialogs/gd_mainWindow.cpp @@ -0,0 +1,543 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_mainWindow + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifdef __linux__ + #include // for mkdir +#endif + + +#include "../../core/graphics.h" +#include "../../core/mixer.h" +#include "../../core/recorder.h" +#include "../../core/mixerHandler.h" +#include "../../core/pluginHost.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../core/init.h" +#include "../../core/patch.h" +#include "../../core/conf.h" +#include "../../glue/glue.h" +#include "../elems/ge_keyboard.h" +#include "gd_warnings.h" +#include "gd_bpmInput.h" +#include "gd_beatsInput.h" +#include "gd_midiInput.h" +#include "gd_about.h" +#include "gd_config.h" +#include "gd_browser.h" +#include "gd_mainWindow.h" + + +#ifdef WITH_VST +#include "gd_pluginList.h" +#endif + + +extern Mixer G_Mixer; +extern Patch G_Patch; +extern Conf G_Conf; +extern gdMainWindow *mainWin; +extern bool G_quit; +extern bool G_audio_status; + +#if defined(WITH_VST) +extern PluginHost G_PluginHost; +#endif + + +gdMainWindow::gdMainWindow(int W, int H, const char *title, int argc, char **argv) + : gWindow(W, H, title) +{ + Fl::visible_focus(0); + Fl::background(25, 25, 25); + Fl::set_boxtype(G_BOX, gDrawBox, 1, 1, 2, 2); // custom box G_BOX + + size_range(GUI_WIDTH, GUI_HEIGHT); + + menu = new gMenu(8, -1); + inOut = new gInOut(408, 8); + controller = new gController(8, 39); + timing = new gTiming(632, 39); + beatMeter = new gBeatMeter(100, 83, 609, 20); + keyboard = new gKeyboard(8, 122, w()-16, 380); + + /* zone 1 - menus, and I/O tools */ + + Fl_Group *zone1 = new Fl_Group(8, 8, W-16, 20); + zone1->add(menu); + zone1->resizable(new Fl_Box(300, 8, 80, 20)); + zone1->add(inOut); + + /* zone 2 - controller and timing tools */ + + Fl_Group *zone2 = new Fl_Group(8, controller->y(), W-16, controller->h()); + zone2->add(controller); + zone2->resizable(new Fl_Box(controller->x()+controller->w()+4, zone2->y(), 80, 20)); + zone2->add(timing); + + /* zone 3 - beat meter */ + + Fl_Group *zone3 = new Fl_Group(8, beatMeter->y(), W-16, beatMeter->h()); + zone3->add(beatMeter); + + /* zone 4 - the keyboard (Fl_Group is unnecessary here, keyboard is + * a group by itself) */ + + resizable(keyboard); + + add(zone1); + add(zone2); + add(zone3); + add(keyboard); + callback(cb_endprogram); + gu_setFavicon(this); + show(argc, argv); +} + + +/* ------------------------------------------------------------------ */ + + +void gdMainWindow::cb_endprogram(Fl_Widget *v, void *p) { mainWin->__cb_endprogram(); } + + +/* ------------------------------------------------------------------ */ + + +void gdMainWindow::__cb_endprogram() +{ + if (!gdConfirmWin("Warning", "Quit Giada: are you sure?")) + return; + init_shutdown(); + hide(); + delete this; +} + + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +gInOut::gInOut(int x, int y) + : Fl_Group(x, y, 394, 20) +{ + begin(); + +#if defined(WITH_VST) + masterFxIn = new gFxButton (x, y, 20, 20, fxOff_xpm, fxOn_xpm); + inVol = new gDial (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20); + inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10); + inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+5, 10, 10, ""); + outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+5, 140, 10); + outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20); + masterFxOut = new gFxButton (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm); +#else + inVol = new gDial (x+62, y, 20, 20); + inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10); + outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 10); + outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20); +#endif + + end(); + + resizable(NULL); // don't resize any widget + + outVol->callback(cb_outVol, (void*)this); + outVol->value(G_Mixer.outVol); + inVol->callback(cb_inVol, (void*)this); + inVol->value(G_Mixer.inVol); + +#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); +#endif +} + + +/* ------------------------------------------------------------------ */ + + +void gInOut::cb_outVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_outVol(); } +void gInOut::cb_inVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_inVol(); } +#ifdef WITH_VST +void gInOut::cb_masterFxOut(Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_masterFxOut(); } +void gInOut::cb_masterFxIn (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_masterFxIn(); } +void gInOut::cb_inToOut (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_inToOut(); } +#endif + + +/* ------------------------------------------------------------------ */ + + +void gInOut::__cb_outVol() +{ + glue_setOutVol(outVol->value()); +} + + +/* ------------------------------------------------------------------ */ + + +void gInOut::__cb_inVol() +{ + glue_setInVol(inVol->value()); +} + + +/* ------------------------------------------------------------------ */ + + +#ifdef WITH_VST +void gInOut::__cb_masterFxOut() +{ + gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST); +} + +void gInOut::__cb_masterFxIn() +{ + gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST); +} + +void gInOut::__cb_inToOut() +{ + G_Mixer.inToOut = inToOut->value(); +} +#endif + + +/* ------------------------------------------------------------------ */ + + +void gInOut::refresh() +{ + outMeter->mixerPeak = G_Mixer.peakOut; + inMeter->mixerPeak = G_Mixer.peakIn; + outMeter->redraw(); + inMeter->redraw(); +} + + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +gMenu::gMenu(int x, int y) + : Fl_Group(x, y, 300, 20) +{ + begin(); + + file = new gClick(x, y, 70, 21, "file"); + edit = new gClick(file->x()+file->w()+4, y, 70, 21, "edit"); + config = new gClick(edit->x()+edit->w()+4, y, 70, 21, "config"); + about = new gClick(config->x()+config->w()+4, y, 70, 21, "about"); + + end(); + + resizable(NULL); // don't resize any widget + + about->callback(cb_about, (void*)this); + file->callback(cb_file, (void*)this); + edit->callback(cb_edit, (void*)this); + config->callback(cb_config, (void*)this); +} + + +/* ------------------------------------------------------------------ */ + + +void gMenu::cb_about (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_about(); } +void gMenu::cb_config(Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_config(); } +void gMenu::cb_file (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_file(); } +void gMenu::cb_edit (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_edit(); } + + +/* ------------------------------------------------------------------ */ + + +void gMenu::__cb_about() +{ + gu_openSubWindow(mainWin, new gdAbout(), WID_ABOUT); +} + + +/* ------------------------------------------------------------------ */ + + +void gMenu::__cb_config() +{ + gu_openSubWindow(mainWin, new gdConfig(380, 370), WID_CONFIG); +} + + +/* ------------------------------------------------------------------ */ + + +void gMenu::__cb_file() +{ + /* An Fl_Menu_Button is made of many Fl_Menu_Item */ + + Fl_Menu_Item menu[] = { + {"Open patch or project..."}, + {"Save patch..."}, + {"Save project..."}, + {"Quit Giada"}, + {0} + }; + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(11); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) return; + + + if (strcmp(m->label(), "Open patch or project...") == 0) { + gWindow *childWin = new gdBrowser("Load Patch", G_Conf.patchPath, 0, BROWSER_LOAD_PATCH); + gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); + return; + } + if (strcmp(m->label(), "Save patch...") == 0) { + if (G_Mixer.hasLogicalSamples() || G_Mixer.hasEditedSamples()) + if (!gdConfirmWin("Warning", "You should save a project in order to store\nyour takes and/or processed samples.")) + return; + gWindow *childWin = new gdBrowser("Save Patch", G_Conf.patchPath, 0, BROWSER_SAVE_PATCH); + gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); + return; + } + if (strcmp(m->label(), "Save project...") == 0) { + gWindow *childWin = new gdBrowser("Save Project", G_Conf.patchPath, 0, BROWSER_SAVE_PROJECT); + gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); + return; + } + if (strcmp(m->label(), "Quit Giada") == 0) { + mainWin->do_callback(); + return; + } +} + + +/* ------------------------------------------------------------------ */ + + +void gMenu::__cb_edit() +{ + Fl_Menu_Item menu[] = { + {"Clear all samples"}, + {"Clear all actions"}, + {"Remove empty columns"}, + {"Reset to init state"}, + {"Setup global MIDI input..."}, + {0} + }; + + /* clear all actions disabled if no recs, clear all samples disabled + * if no samples. */ + + menu[1].deactivate(); + + for (unsigned i=0; ihasActions) { + menu[1].activate(); + break; + } + for (unsigned i=0; itype == CHANNEL_SAMPLE) + if (((SampleChannel*)G_Mixer.channels.at(i))->wave != NULL) { + menu[0].activate(); + break; + } + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(11); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) return; + + if (strcmp(m->label(), "Clear all samples") == 0) { + if (!gdConfirmWin("Warning", "Clear all samples: are you sure?")) + return; + mainWin->delSubWindow(WID_SAMPLE_EDITOR); + glue_clearAllSamples(); + return; + } + if (strcmp(m->label(), "Clear all actions") == 0) { + if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) + return; + mainWin->delSubWindow(WID_ACTION_EDITOR); + glue_clearAllRecs(); + return; + } + if (strcmp(m->label(), "Reset to init state") == 0) { + if (!gdConfirmWin("Warning", "Reset to init state: are you sure?")) + return; + gu_closeAllSubwindows(); + glue_resetToInitState(); + return; + } + if (strcmp(m->label(), "Remove empty columns") == 0) { + mainWin->keyboard->organizeColumns(); + return; + } + if (strcmp(m->label(), "Setup global MIDI input...") == 0) { + gu_openSubWindow(mainWin, new gdMidiInputMaster(), 0); + return; + } +} + + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +gTiming::gTiming(int x, int y) + : Fl_Group(x, y, 170, 15) +{ + begin(); + + quantizer = new gChoice(x, y, 40, 15, "", false); + bpm = new gClick (quantizer->x()+quantizer->w()+4, y, 40, 15); + meter = new gClick (bpm->x()+bpm->w()+8, y, 40, 15, "4/1"); + multiplier = new gClick (meter->x()+meter->w()+4, y, 15, 15, "", beatsMultiplyOff_xpm, beatsMultiplyOn_xpm); + divider = new gClick (multiplier->x()+multiplier->w()+4, y, 15, 15, "÷", beatsDivideOff_xpm, beatsDivideOn_xpm); + + end(); + + resizable(NULL); // don't resize any widget + + char buf[6]; snprintf(buf, 6, "%f", G_Mixer.bpm); + bpm->copy_label(buf); + + 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("1b", 0, cb_quantizer, (void*)this); + quantizer->add("2b", 0, cb_quantizer, (void*)this); + quantizer->add("3b", 0, cb_quantizer, (void*)this); + quantizer->add("4b", 0, cb_quantizer, (void*)this); + quantizer->add("6b", 0, cb_quantizer, (void*)this); + quantizer->add("8b", 0, cb_quantizer, (void*)this); + quantizer->value(0); // "off" by default +} + + +/* ------------------------------------------------------------------ */ + + +void gTiming::cb_bpm (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_bpm(); } +void gTiming::cb_meter (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_meter(); } +void gTiming::cb_quantizer (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_quantizer(); } +void gTiming::cb_multiplier(Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_multiplier(); } +void gTiming::cb_divider (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_divider(); } + + +/* ------------------------------------------------------------------ */ + + +void gTiming::__cb_bpm() +{ + gu_openSubWindow(mainWin, new gdBpmInput(bpm->label()), WID_BPM); +} + + +/* ------------------------------------------------------------------ */ + + +void gTiming::__cb_meter() +{ + gu_openSubWindow(mainWin, new gdBeatsInput(), WID_BEATS); +} + + +/* ------------------------------------------------------------------ */ + + +void gTiming::__cb_quantizer() +{ + glue_quantize(quantizer->value()); +} + + +/* ------------------------------------------------------------------ */ + + +void gTiming::__cb_multiplier() +{ + glue_beatsMultiply(); +} + + +/* ------------------------------------------------------------------ */ + + +void gTiming::__cb_divider() +{ + glue_beatsDivide(); +} + + +/* ------------------------------------------------------------------ */ + + +void gTiming::setBpm(const char *v) +{ + bpm->copy_label(v); +} + + +void gTiming::setBpm(float v) +{ + char buf[6]; + sprintf(buf, "%.01f", v); // only 1 decimal place (e.g. 120.0) + bpm->copy_label(buf); +} + + +/* ------------------------------------------------------------------ */ + + +void gTiming::setMeter(int beats, int bars) +{ + char buf[8]; + sprintf(buf, "%d/%d", beats, bars); + meter->copy_label(buf); +} diff --git a/src/gui/dialogs/gd_mainWindow.h b/src/gui/dialogs/gd_mainWindow.h new file mode 100644 index 0000000..df2c911 --- /dev/null +++ b/src/gui/dialogs/gd_mainWindow.h @@ -0,0 +1,175 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * gd_mainWindow + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GD_MAINWINDOW_H +#define GD_MAINWINDOW_H + + +#include +#include +#include "../elems/ge_mixed.h" +#include "../elems/ge_window.h" +#include "../elems/ge_controller.h" + + +/* ------------------------------------------------------------------ */ + + +class gdMainWindow : public gWindow +{ +private: + + static void cb_endprogram (Fl_Widget *v, void *p); + inline void __cb_endprogram(); + +public: + + class gKeyboard *keyboard; + class gBeatMeter *beatMeter; + class gMenu *menu; + class gInOut *inOut; + class gController *controller; + class gTiming *timing; + + gdMainWindow(int w, int h, const char *title, int argc, char **argv); +}; + + +/* ------------------------------------------------------------------ */ + + +class gInOut : public Fl_Group +{ +private: + + class gSoundMeter *outMeter; + class gSoundMeter *inMeter; + class gBeatMeter *beatMeter; + class gDial *outVol; + class gDial *inVol; +#ifdef WITH_VST + class gFxButton *masterFxOut; + class gFxButton *masterFxIn; + class gClick *inToOut; +#endif + + 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 + + inline void __cb_outVol (); + inline void __cb_inVol (); +#ifdef WITH_VST + inline void __cb_masterFxOut(); + inline void __cb_masterFxIn (); + inline void __cb_inToOut (); +#endif + +public: + + gInOut(int x, int y); + + void refresh(); + + inline void setOutVol(float v) { outVol->value(v); } + inline void setInVol (float v) { inVol->value(v); } +#ifdef WITH_VST + inline void setMasterFxOutFull(bool v) { masterFxOut->full = v; masterFxOut->redraw(); } + inline void setMasterFxInFull(bool v) { masterFxIn->full = v; masterFxIn->redraw(); } +#endif +}; + + +/* ------------------------------------------------------------------ */ + + +class gMenu : public Fl_Group +{ +private: + + class gClick *file; + class gClick *edit; + class gClick *config; + class gClick *about; + + static void cb_about (Fl_Widget *v, void *p); + static void cb_config(Fl_Widget *v, void *p); + static void cb_file (Fl_Widget *v, void *p); + static void cb_edit (Fl_Widget *v, void *p); + + inline void __cb_about (); + inline void __cb_config(); + inline void __cb_file (); + inline void __cb_edit (); + +public: + + gMenu(int x, int y); +}; + + +/* ------------------------------------------------------------------ */ + + +class gTiming : public Fl_Group +{ +private: + + class gClick *bpm; + class gClick *meter; + class gChoice *quantizer; + class gClick *multiplier; + class gClick *divider; + + static void cb_bpm (Fl_Widget *v, void *p); + static void cb_meter (Fl_Widget *v, void *p); + static void cb_quantizer (Fl_Widget *v, void *p); + static void cb_multiplier(Fl_Widget *v, void *p); + static void cb_divider (Fl_Widget *v, void *p); + + inline void __cb_bpm(); + inline void __cb_meter(); + inline void __cb_quantizer(); + inline void __cb_multiplier(); + inline void __cb_divider(); + +public: + + gTiming(int x, int y); + + void setBpm(const char *v); + void setBpm(float v); + void setMeter(int beats, int bars); +}; + + +#endif diff --git a/src/gui/dialogs/gd_midiInput.cpp b/src/gui/dialogs/gd_midiInput.cpp new file mode 100644 index 0000000..27a7d67 --- /dev/null +++ b/src/gui/dialogs/gd_midiInput.cpp @@ -0,0 +1,184 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_midiInput + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../utils/gui_utils.h" +#include "../../core/kernelMidi.h" +#include "../../core/conf.h" +#include "../../core/sampleChannel.h" +#include "../../utils/log.h" +#include "../elems/ge_mixed.h" +#include "../elems/ge_midiIoTools.h" +#include "gd_midiInput.h" + + +extern Conf G_Conf; + + +gdMidiInput::gdMidiInput(int w, int h, const char *title) + : gWindow(w, h, title) +{ +} + + +/* -------------------------------------------------------------------------- */ + + +gdMidiInput::~gdMidiInput() { + kernelMidi::stopMidiLearn(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiInput::stopMidiLearn(gLearner *learner) { + kernelMidi::stopMidiLearn(); + learner->updateValue(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiInput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) { + *param = msg; + stopMidiLearn(l); + gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiInput::cb_learn(uint32_t msg, void *d) { + cbData *data = (cbData*) d; + gdMidiInput *window = (gdMidiInput*) data->window; + gLearner *learner = data->learner; + uint32_t *param = learner->param; + window->__cb_learn(param, msg, learner); + free(data); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiInput::cb_close(Fl_Widget *w, void *p) { ((gdMidiInput*)p)->__cb_close(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiInput::__cb_close() { + do_callback(); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gdMidiInputChannel::gdMidiInputChannel(Channel *ch) + : gdMidiInput(300, 206, "MIDI Input Setup"), + ch(ch) +{ + char title[64]; + sprintf(title, "MIDI Input Setup (channel %d)", ch->index+1); + label(title); + + set_modal(); + + enable = new gCheck(8, 8, 120, 20, "enable MIDI input"); + new gLearner(8, 30, w()-16, "key press", cb_learn, &ch->midiInKeyPress); + new gLearner(8, 54, w()-16, "key release", cb_learn, &ch->midiInKeyRel); + new gLearner(8, 78, w()-16, "key kill", cb_learn, &ch->midiInKill); + new gLearner(8, 102, w()-16, "mute", cb_learn, &ch->midiInMute); + new gLearner(8, 126, w()-16, "solo", cb_learn, &ch->midiInSolo); + new gLearner(8, 150, w()-16, "volume", cb_learn, &ch->midiInVolume); + int yy = 178; + + if (ch->type == CHANNEL_SAMPLE) { + size(300, 254); + new gLearner(8, 174, w()-16, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch); + new gLearner(8, 198, w()-16, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions); + yy = 226; + } + + ok = new gButton(w()-88, yy, 80, 20, "Close"); + ok->callback(cb_close, (void*)this); + + enable->value(ch->midiIn); + enable->callback(cb_enable, (void*)this); + + gu_setFavicon(this); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiInputChannel::cb_enable(Fl_Widget *w, void *p) { ((gdMidiInputChannel*)p)->__cb_enable(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiInputChannel::__cb_enable() { + ch->midiIn = enable->value(); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gdMidiInputMaster::gdMidiInputMaster() + : gdMidiInput(300, 256, "MIDI Input Setup (global)") +{ + set_modal(); + + new gLearner(8, 8, w()-16, "rewind", &cb_learn, &G_Conf.midiInRewind); + new gLearner(8, 32, w()-16, "play/stop", &cb_learn, &G_Conf.midiInStartStop); + new gLearner(8, 56, w()-16, "action recording", &cb_learn, &G_Conf.midiInActionRec); + new gLearner(8, 80, w()-16, "input recording", &cb_learn, &G_Conf.midiInInputRec); + new gLearner(8, 104, w()-16, "metronome", &cb_learn, &G_Conf.midiInMetronome); + new gLearner(8, 128, w()-16, "input volume", &cb_learn, &G_Conf.midiInVolumeIn); + new gLearner(8, 152, w()-16, "output volume", &cb_learn, &G_Conf.midiInVolumeOut); + new gLearner(8, 176, w()-16, "sequencer ×2", &cb_learn, &G_Conf.midiInBeatDouble); + new gLearner(8, 200, w()-16, "sequencer ÷2", &cb_learn, &G_Conf.midiInBeatHalf); + ok = new gButton(w()-88, 228, 80, 20, "Close"); + + ok->callback(cb_close, (void*)this); + + gu_setFavicon(this); + show(); +} diff --git a/src/gui/dialogs/gd_midiInput.h b/src/gui/dialogs/gd_midiInput.h new file mode 100644 index 0000000..e5d31f5 --- /dev/null +++ b/src/gui/dialogs/gd_midiInput.h @@ -0,0 +1,98 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_midiInput + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GD_MIDI_INPUT_H +#define GD_MIDI_INPUT_H + + +#include "../../core/kernelMidi.h" +#include "../../utils/utils.h" +#include "../elems/ge_window.h" +#include "../elems/ge_mixed.h" + + +class gdMidiInput : public gWindow +{ +protected: + + gClick *ok; + + void stopMidiLearn(class gLearner *l); + + /* cb_learn + * callback attached to kernelMidi to learn various actions. */ + + static void cb_learn (uint32_t msg, void *data); + inline void __cb_learn(uint32_t *param, uint32_t msg, gLearner *l); + + static void cb_close (Fl_Widget *w, void *p); + inline void __cb_close(); + +public: + + gdMidiInput(int w, int h, const char *title); + ~gdMidiInput(); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gdMidiInputChannel : public gdMidiInput +{ +private: + + class Channel *ch; + + gCheck *enable; + + + //gVector items; for future use, with vst parameters + + static void cb_enable (Fl_Widget *w, void *p); + inline void __cb_enable(); + +public: + + gdMidiInputChannel(class Channel *ch); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gdMidiInputMaster : public gdMidiInput +{ +public: + + gdMidiInputMaster(); +}; + + +#endif diff --git a/src/gui/dialogs/gd_midiOutput.cpp b/src/gui/dialogs/gd_midiOutput.cpp new file mode 100644 index 0000000..991b68a --- /dev/null +++ b/src/gui/dialogs/gd_midiOutput.cpp @@ -0,0 +1,249 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_midiOutput + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../core/conf.h" +#include "../../core/midiChannel.h" +#include "../../utils/gui_utils.h" +#include "../elems/ge_mixed.h" +#include "../elems/ge_channel.h" +#include "../elems/ge_midiIoTools.h" +#include "../elems/ge_keyboard.h" +#include "gd_midiOutput.h" + + +extern Conf G_Conf; + + +gdMidiOutput::gdMidiOutput(int w, int h) + //: gWindow(300, 64, "Midi Output Setup") + : gWindow(w, h, "Midi Output Setup") +{ +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::stopMidiLearn(gLearner *learner) { + kernelMidi::stopMidiLearn(); + learner->updateValue(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) { + *param = msg; + stopMidiLearn(l); + gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::cb_learn(uint32_t msg, void *d) { + cbData *data = (cbData*) d; + gdMidiOutput *window = (gdMidiOutput*) data->window; + gLearner *learner = data->learner; + uint32_t *param = learner->param; + window->__cb_learn(param, msg, learner); + free(data); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::cb_close(Fl_Widget *w, void *p) { ((gdMidiOutput*)p)->__cb_close(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::__cb_close() { + do_callback(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::cb_enableLightning(Fl_Widget *w, void *p) { + ((gdMidiOutput*)p)->__cb_enableLightning(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::__cb_enableLightning() {} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutput::setTitle(int chanNum) +{ + char title[64]; + sprintf(title, "MIDI Output Setup (channel %d)", chanNum); + copy_label(title); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gdMidiOutputMidiCh::gdMidiOutputMidiCh(MidiChannel *ch) + : gdMidiOutput(300, 168), ch(ch) +{ + setTitle(ch->index+1); + begin(); + + enableOut = new gCheck(x()+8, y()+8, 150, 20, "Enable MIDI output"); + chanListOut = new gChoice(w()-108, y()+8, 100, 20); + + enableLightning = new gCheck(x()+8, chanListOut->y()+chanListOut->h()+8, 120, 20, "Enable MIDI lightning output"); + new gLearner(x()+8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying); + new gLearner(x()+8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute); + new gLearner(x()+8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo); + + close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close"); + + end(); + + chanListOut->add("Channel 1"); + chanListOut->add("Channel 2"); + chanListOut->add("Channel 3"); + chanListOut->add("Channel 4"); + chanListOut->add("Channel 5"); + chanListOut->add("Channel 6"); + chanListOut->add("Channel 7"); + chanListOut->add("Channel 8"); + chanListOut->add("Channel 9"); + chanListOut->add("Channel 10"); + chanListOut->add("Channel 11"); + chanListOut->add("Channel 12"); + chanListOut->add("Channel 13"); + chanListOut->add("Channel 14"); + chanListOut->add("Channel 15"); + chanListOut->add("Channel 16"); + chanListOut->value(0); + + if (ch->midiOut) + enableOut->value(1); + else + chanListOut->deactivate(); + + if (ch->midiOutL) + enableLightning->value(1); + + chanListOut->value(ch->midiOutChan); + + enableOut->callback(cb_enableChanList, (void*)this); + close->callback(cb_close, (void*)this); + + set_modal(); + gu_setFavicon(this); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutputMidiCh::cb_close (Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->__cb_close(); } +void gdMidiOutputMidiCh::cb_enableChanList(Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->__cb_enableChanList(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutputMidiCh::__cb_enableChanList() { + enableOut->value() ? chanListOut->activate() : chanListOut->deactivate(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutputMidiCh::__cb_close() { + ch->midiOut = enableOut->value(); + ch->midiOutChan = chanListOut->value(); + ch->midiOutL = enableLightning->value(); + ch->guiChannel->update(); + do_callback(); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gdMidiOutputSampleCh::gdMidiOutputSampleCh(SampleChannel *ch) + : gdMidiOutput(300, 140), ch(ch) +{ + setTitle(ch->index+1); + + enableLightning = new gCheck(8, 8, 120, 20, "Enable MIDI lightning output"); + new gLearner(8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying); + new gLearner(8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute); + new gLearner(8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo); + + close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close"); + close->callback(cb_close, (void*)this); + + enableLightning->value(ch->midiOutL); + enableLightning->callback(cb_enableLightning, (void*)this); + + set_modal(); + gu_setFavicon(this); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutputSampleCh::cb_close(Fl_Widget *w, void *p) { ((gdMidiOutputSampleCh*)p)->__cb_close(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdMidiOutputSampleCh::__cb_close() { + ch->midiOutL = enableLightning->value(); + do_callback(); +} diff --git a/src/gui/dialogs/gd_midiOutput.h b/src/gui/dialogs/gd_midiOutput.h new file mode 100644 index 0000000..911a253 --- /dev/null +++ b/src/gui/dialogs/gd_midiOutput.h @@ -0,0 +1,134 @@ +/* ---------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_midiOutput + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GD_MIDI_OUTPUT_H +#define GD_MIDI_OUTPUT_H + + +#include +#include +#include "../elems/ge_window.h" + + +/* There's no such thing as a gdMidiOutputMaster vs gdMidiOutputChannel. MIDI +output master is managed by the configuration window, hence gdMidiOutput deals +only with channels. + +Both MidiOutputMidiCh and MidiOutputSampleCh have the MIDI lighting widget set. +In addition MidiOutputMidiCh has the MIDI message output box. */ + +/* TODO - gdMidiOutput is almost the same thing of gdMidiInput. Create another +parent class gdMidiIO to inherit from */ + +class gdMidiOutput : public gWindow +{ +protected: + + class gClick *close; + class gCheck *enableLightning; + + void stopMidiLearn(class gLearner *l); + + /* cb_learn + * callback attached to kernelMidi to learn various actions. */ + + static void cb_learn (uint32_t msg, void *data); + inline void __cb_learn(uint32_t *param, uint32_t msg, class gLearner *l); + + /* cb_close + close current window. */ + + static void cb_close (Fl_Widget *w, void *p); + inline void __cb_close(); + + /* cb_enableLightning + enable MIDI lightning output. */ + + static void cb_enableLightning (Fl_Widget *w, void *p); + inline void __cb_enableLightning(); + + /* setTitle + * set window title. */ + + void setTitle(int chanNum); + +public: + + gdMidiOutput(int w, int h); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gdMidiOutputMidiCh : public gdMidiOutput +{ +private: + + static void cb_enableChanList (Fl_Widget *w, void *p); + inline void __cb_enableChanList(); + + /* __cb_close + override parent method, we need to do more stuff on close. */ + + static void cb_close (Fl_Widget *w, void *p); + inline void __cb_close(); + + class gCheck *enableOut; + class gChoice *chanListOut; + + class MidiChannel *ch; + +public: + + gdMidiOutputMidiCh(class MidiChannel *ch); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gdMidiOutputSampleCh : public gdMidiOutput +{ +private: + + class SampleChannel *ch; + + /* __cb_close + override parent method, we need to do more stuff on close. */ + + static void cb_close (Fl_Widget *w, void *p); + inline void __cb_close(); + +public: + + gdMidiOutputSampleCh(class SampleChannel *ch); +}; + +#endif diff --git a/src/gui/dialogs/gd_pluginList.cpp b/src/gui/dialogs/gd_pluginList.cpp new file mode 100644 index 0000000..6ef967d --- /dev/null +++ b/src/gui/dialogs/gd_pluginList.cpp @@ -0,0 +1,394 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_pluginList + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + + +#include "../../utils/gui_utils.h" +#include "../../utils/utils.h" +#include "../../core/conf.h" +#include "../../core/graphics.h" +#include "../../core/pluginHost.h" +#include "../../core/mixer.h" +#include "../../core/channel.h" +#include "../elems/ge_mixed.h" +#include "../elems/ge_channel.h" +#include "gd_pluginList.h" +#include "gd_pluginWindow.h" +#include "gd_pluginWindowGUI.h" +#include "gd_browser.h" +#include "gd_mainWindow.h" + + +extern Conf G_Conf; +extern PluginHost G_PluginHost; +extern gdMainWindow *mainWin; + + +gdPluginList::gdPluginList(int stackType, Channel *ch) + : gWindow(468, 204), ch(ch), stackType(stackType) +{ + + if (G_Conf.pluginListX) + resize(G_Conf.pluginListX, G_Conf.pluginListY, w(), h()); + + list = new Fl_Scroll(8, 8, 476, 188); + list->type(Fl_Scroll::VERTICAL); + list->scrollbar.color(COLOR_BG_0); + list->scrollbar.selection_color(COLOR_BG_1); + list->scrollbar.labelcolor(COLOR_BD_1); + list->scrollbar.slider(G_BOX); + + list->begin(); + refreshList(); + list->end(); + + end(); + set_non_modal(); + + /* TODO - awful stuff... we should subclass into gdPluginListChannel and + gdPluginListMaster */ + + if (stackType == PluginHost::MASTER_OUT) + label("Master Out Plugins"); + else + if (stackType == PluginHost::MASTER_IN) + label("Master In Plugins"); + else { + char tmp[32]; + sprintf(tmp, "Channel %d Plugins", ch->index+1); + copy_label(tmp); + } + + gu_setFavicon(this); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +gdPluginList::~gdPluginList() { + G_Conf.pluginListX = x(); + G_Conf.pluginListY = y(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPluginList::cb_addPlugin(Fl_Widget *v, void *p) { ((gdPluginList*)p)->__cb_addPlugin(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdPluginList::cb_refreshList(Fl_Widget *v, void *p) { + + /* note: this callback is fired by gdBrowser. Close its window first, + * by calling the parent (pluginList) and telling it to delete its + * subwindow (i.e. gdBrowser). */ + + gWindow *child = (gWindow*) v; + if (child->getParent() != NULL) + (child->getParent())->delSubWindow(child); + + /* finally refresh plugin list: void *p is a pointer to gdPluginList. + * This callback works even when you click 'x' to close the window... + * well, who cares */ + + ((gdPluginList*)p)->refreshList(); + ((gdPluginList*)p)->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPluginList::__cb_addPlugin() { + + /* the usual callback that gWindow adds to each subwindow in this case + * is not enough, because when we close the browser the plugin list + * must be redrawn. We have a special callback, cb_refreshList, which + * we add to gdBrowser. It does exactly what we need. */ + + gdBrowser *b = new gdBrowser("Browse Plugin", G_Conf.pluginPath, ch, BROWSER_LOAD_PLUGIN, stackType); + addSubWindow(b); + b->callback(cb_refreshList, (void*)this); // 'this' refers to gdPluginList + +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPluginList::refreshList() { + + /* delete the previous list */ + + list->clear(); + list->scroll_to(0, 0); + + /* add new buttons, as many as the plugin in pluginHost::stack + 1, + * the 'add new' button. Warning: if ch == NULL we are working with + * master in/master out stacks. */ + + int numPlugins = G_PluginHost.countPlugins(stackType, ch); + int i = 0; + + while (ix(), list->y()-list->yposition()+(i*24), 800); + list->add(gdp); + i++; + } + + int addPlugY = numPlugins == 0 ? 90 : list->y()-list->yposition()+(i*24); + addPlugin = new gClick(8, addPlugY, 452, 20, "-- add new plugin --"); + addPlugin->callback(cb_addPlugin, (void*)this); + list->add(addPlugin); + + /* if num(plugins) > 7 make room for the side scrollbar. + * Scrollbar.width = 20 + 4(margin) */ + + if (i>7) + size(492, h()); + else + size(468, h()); + + redraw(); + + /* set 'full' flag to FX button */ + + /* TODO - awful stuff... we should subclass into gdPluginListChannel and + gdPluginListMaster */ + + if (stackType == PluginHost::MASTER_OUT) { + mainWin->inOut->setMasterFxOutFull(G_PluginHost.countPlugins(stackType, ch) > 0); + } + else + if (stackType == PluginHost::MASTER_IN) { + mainWin->inOut->setMasterFxInFull(G_PluginHost.countPlugins(stackType, ch) > 0); + } + else { + ch->guiChannel->fx->full = G_PluginHost.countPlugins(stackType, ch) > 0; + ch->guiChannel->fx->redraw(); + } +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gdPlugin::gdPlugin(gdPluginList *gdp, Plugin *p, int X, int Y, int W) + : Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p) +{ + begin(); + button = new gButton(8, y(), 220, 20); + program = new gChoice(button->x()+button->w()+4, y(), 132, 20); + bypass = new gButton(program->x()+program->w()+4, y(), 20, 20); + shiftUp = new gButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm); + shiftDown = new gButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm); + remove = new gButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm); + end(); + + if (pPlugin->status != 1) { // bad state + char name[256]; + sprintf(name, "* %s *", gBasename(pPlugin->pathfile).c_str()); + button->copy_label(name); + } + else { + char name[256]; + pPlugin->getProduct(name); + if (strcmp(name, " ")==0) + pPlugin->getName(name); + + button->copy_label(name); + button->callback(cb_openPluginWindow, (void*)this); + + program->callback(cb_setProgram, (void*)this); + + /* loading vst programs */ + /* FIXME - max programs = 128 (unknown source) */ + + for (int i=0; i<64; i++) { + char out[kVstMaxProgNameLen]; + pPlugin->getProgramName(i, out); + for (int j=0; j 0) + program->add(out); + } + if (program->size() == 0) { + program->add("-- no programs --\0"); + program->deactivate(); + } + if (pPlugin->getProgram() == -1) + program->value(0); + else + program->value(pPlugin->getProgram()); + + bypass->callback(cb_setBypass, (void*)this); + bypass->type(FL_TOGGLE_BUTTON); + bypass->value(pPlugin->bypass ? 0 : 1); + } + + shiftUp->callback(cb_shiftUp, (void*)this); + shiftDown->callback(cb_shiftDown, (void*)this); + remove->callback(cb_removePlugin, (void*)this); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPlugin::cb_removePlugin (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_removePlugin(); } +void gdPlugin::cb_openPluginWindow(Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_openPluginWindow(); } +void gdPlugin::cb_setBypass (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setBypass(); } +void gdPlugin::cb_shiftUp (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftUp(); } +void gdPlugin::cb_shiftDown (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftDown(); } +void gdPlugin::cb_setProgram (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setProgram(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdPlugin::__cb_shiftUp() { + + /*nothing to do if there's only one plugin */ + + if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1) + return; + + int pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch); + + if (pluginIndex == 0) // first of the stack, do nothing + return; + + G_PluginHost.swapPlugin(pluginIndex, pluginIndex-1, pParent->stackType, pParent->ch); + pParent->refreshList(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPlugin::__cb_shiftDown() { + + /*nothing to do if there's only one plugin */ + + if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1) + return; + + unsigned pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch); + unsigned stackSize = (G_PluginHost.getStack(pParent->stackType, pParent->ch))->size; + + if (pluginIndex == stackSize-1) // last one in the stack, do nothing + return; + + G_PluginHost.swapPlugin(pluginIndex, pluginIndex+1, pParent->stackType, pParent->ch); + pParent->refreshList(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPlugin::__cb_removePlugin() { + + /* os x hack: show window before deleting it */ + +#ifdef __APPLE__ + gdPluginWindowGUImac* w = (gdPluginWindowGUImac*) pParent->getChild(pPlugin->getId()+1); + if (w) + w->show(); +#endif + + /* any subwindow linked to the plugin must be destroyed */ + + pParent->delSubWindow(pPlugin->getId()+1); + G_PluginHost.freePlugin(pPlugin->getId(), pParent->stackType, pParent->ch); + pParent->refreshList(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPlugin::__cb_openPluginWindow() { + + /* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved + * for the window 'add plugin'. */ + + /* TODO - at the moment you can open a window for each plugin in the stack. + * This is not consistent with the rest of the gui. You can avoid this by + * calling + * + * gu_openSubWindow(this, new gdPluginWindow(pPlugin), WID_FX); + * + * instead of the following code. + * + * EDIT 2 - having only 1 plugin window would be very uncomfortable */ + + if (!pParent->hasWindow(pPlugin->getId()+1)) { + gWindow *w; + if (pPlugin->hasGui()) +#ifdef __APPLE__ + w = new gdPluginWindowGUImac(pPlugin); +#else + w = new gdPluginWindowGUI(pPlugin); +#endif + else + w = new gdPluginWindow(pPlugin); + w->setId(pPlugin->getId()+1); + pParent->addSubWindow(w); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPlugin::__cb_setBypass() { + pPlugin->bypass = !pPlugin->bypass; +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPlugin::__cb_setProgram() { + pPlugin->setProgram(program->value()); +} + + +#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_pluginList.h b/src/gui/dialogs/gd_pluginList.h new file mode 100644 index 0000000..5f96e89 --- /dev/null +++ b/src/gui/dialogs/gd_pluginList.h @@ -0,0 +1,107 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_pluginList + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + +#ifndef __GD_PLUGINLIST_H__ +#define __GD_PLUGINLIST_H__ + +#include +#include +#include "../elems/ge_window.h" + + +class gdPluginList : public gWindow { + +private: + + class gClick *addPlugin; + Fl_Scroll *list; + + //gVector subWindows; + + static void cb_addPlugin (Fl_Widget *v, void *p); + inline void __cb_addPlugin (); + +public: + + class Channel *ch; // ch == NULL ? masterOut + int stackType; + + gdPluginList(int stackType, class Channel *ch=NULL); + ~gdPluginList(); + + /* special callback, passed to browser. When closed (i.e. plugin + * has been selected) the same browser will refresh this window. */ + + static void cb_refreshList(Fl_Widget*, void*); + + void refreshList(); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gdPlugin : public Fl_Group { + +private: + + class gdPluginList *pParent; + class Plugin *pPlugin; + + static void cb_removePlugin (Fl_Widget *v, void *p); + static void cb_openPluginWindow (Fl_Widget *v, void *p); + static void cb_setBypass (Fl_Widget *v, void *p); + static void cb_shiftUp (Fl_Widget *v, void *p); + static void cb_shiftDown (Fl_Widget *v, void *p); + static void cb_setProgram (Fl_Widget *v, void *p); + inline void __cb_removePlugin (); + inline void __cb_openPluginWindow (); + inline void __cb_setBypass (); + inline void __cb_shiftUp (); + inline void __cb_shiftDown (); + inline void __cb_setProgram (); + +public: + + class gButton *button; + class gChoice *program; + class gButton *bypass; + class gButton *shiftUp; + class gButton *shiftDown; + class gButton *remove; + + gdPlugin(gdPluginList *gdp, class Plugin *p, int x, int y, int w); + +}; + +#endif + +#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_pluginWindow.cpp b/src/gui/dialogs/gd_pluginWindow.cpp new file mode 100644 index 0000000..a60ebe5 --- /dev/null +++ b/src/gui/dialogs/gd_pluginWindow.cpp @@ -0,0 +1,139 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_pluginWindow + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifdef WITH_VST + + +#include +#include "../../utils/gui_utils.h" +#include "../../core/pluginHost.h" +#include "../elems/ge_mixed.h" +#include "gd_pluginWindow.h" + + + +extern PluginHost G_PluginHost; + + +Parameter::Parameter(int id, Plugin *p, int X, int Y, int W) + : Fl_Group(X,Y,W-24,20), id(id), pPlugin(p) +{ + begin(); + + label = new gBox(x(), y(), 60, 20); + char name[kVstMaxParamStrLen]; + pPlugin->getParamName(id, name); + label->copy_label(name); + label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + + slider = new gSlider(label->x()+label->w()+8, y(), W-200, 20); + slider->value(pPlugin->getParam(id)); + slider->callback(cb_setValue, (void *)this); + + value = new gBox(slider->x()+slider->w()+8, y(), 100, 20); + char disp[kVstMaxParamStrLen]; + char labl[kVstMaxParamStrLen]; + char str [256]; + pPlugin->getParamDisplay(id, disp); + pPlugin->getParamLabel(id, labl); + sprintf(str, "%s %s", disp, labl); + value->copy_label(str); + value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + value->box(G_BOX); + + resizable(slider); + + end(); +} + + +/* ------------------------------------------------------------------ */ + + +void Parameter::cb_setValue(Fl_Widget *v, void *p) { ((Parameter*)p)->__cb_setValue(); } + + +/* ------------------------------------------------------------------ */ + + +void Parameter::__cb_setValue() { + + pPlugin->setParam(id, slider->value()); + + char disp[256]; + char labl[256]; + char str [256]; + + pPlugin->getParamDisplay(id, disp); + pPlugin->getParamLabel(id, labl); + sprintf(str, "%s %s", disp, labl); + + value->copy_label(str); + value->redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +gdPluginWindow::gdPluginWindow(Plugin *pPlugin) + : gWindow(400, 156), pPlugin(pPlugin) // 350 +{ + set_non_modal(); + + gLiquidScroll *list = new gLiquidScroll(8, 8, w()-16, h()-16); + list->type(Fl_Scroll::VERTICAL_ALWAYS); + list->begin(); + + int numParams = pPlugin->getNumParams(); + for (int i=0; ix(), list->y()+(i*24), list->w()); + list->end(); + + end(); + + char name[256]; + pPlugin->getProduct(name); + if (strcmp(name, " ")==0) + pPlugin->getName(name); + label(name); + + size_range(400, (24*1)+12); + resizable(list); + + gu_setFavicon(this); + show(); + +} + + +gdPluginWindow::~gdPluginWindow() {} + + +#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_pluginWindow.h b/src/gui/dialogs/gd_pluginWindow.h new file mode 100644 index 0000000..0f50529 --- /dev/null +++ b/src/gui/dialogs/gd_pluginWindow.h @@ -0,0 +1,76 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_pluginWindow + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifdef WITH_VST + +#ifndef __GD_PLUGINWINDOW_H__ +#define __GD_PLUGINWINDOW_H__ + + +#include +#include +#include "../elems/ge_window.h" + + +class gdPluginWindow : public gWindow { + +private: + class Plugin *pPlugin; + +public: + int id; + + gdPluginWindow(Plugin *pPlugin); + ~gdPluginWindow(); +}; + + +/* ------------------------------------------------------------------ */ + + +class Parameter : public Fl_Group { + +private: + int id; + class Plugin *pPlugin; + + static void cb_setValue(Fl_Widget *v, void *p); + inline void __cb_setValue(); + +public: + class gBox *label; + class gSlider *slider; + class gBox *value; + + Parameter(int id, class Plugin *p, int x, int y, int w); +}; + + +#endif + +#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_pluginWindowGUI.cpp b/src/gui/dialogs/gd_pluginWindowGUI.cpp new file mode 100644 index 0000000..7b6ff31 --- /dev/null +++ b/src/gui/dialogs/gd_pluginWindowGUI.cpp @@ -0,0 +1,199 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_pluginWindowGUI + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifdef WITH_VST + + +#include "../../utils/log.h" +#include "../../utils/gui_utils.h" +#include "../../core/pluginHost.h" +#include "../elems/ge_mixed.h" +#include "gd_pluginWindowGUI.h" + + +extern PluginHost G_PluginHost; + + +gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin) + : gWindow(450, 300), pPlugin(pPlugin) +{ + + /* some effects like to have us get their rect before opening them */ + + ERect *rect; + pPlugin->getRect(&rect); + + gu_setFavicon(this); + set_non_modal(); + resize(x(), y(), pPlugin->getGuiWidth(), pPlugin->getGuiWidth()); + show(); + + gLog("[gdPluginWindowGUI] open window, w=%d h=%d\n", + pPlugin->getGuiWidth(), pPlugin->getGuiWidth()); + + /* Fl::check(): Waits until "something happens" and then returns. It's + * mandatory on linux, otherwise X can't find 'this' window. */ + +#ifndef __APPLE__ + Fl::check(); +#endif + + pPlugin->openGui((void*)fl_xid(this)); + + char name[256]; + pPlugin->getProduct(name); + copy_label(name); + + /* add a pointer to this window to plugin */ + + pPlugin->window = this; + + pPlugin->idle(); +} + + +/* ------------------------------------------------------------------ */ + + +gdPluginWindowGUI::~gdPluginWindowGUI() { + pPlugin->closeGui(); +} + + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +#if defined(__APPLE__) + + +pascal OSStatus gdPluginWindowGUImac::windowHandler(EventHandlerCallRef ehc, EventRef e, void *data) { + return ((gdPluginWindowGUImac*)data)->__wh(ehc, e); +} + + +/* ------------------------------------------------------------------ */ + + +pascal OSStatus gdPluginWindowGUImac::__wh(EventHandlerCallRef inHandlerCallRef, EventRef inEvent) { + OSStatus result = eventNotHandledErr; // let the Carbon Event Manager close the window + UInt32 eventClass = GetEventClass(inEvent); + UInt32 eventKind = GetEventKind(inEvent); + + switch (eventClass) { + case kEventClassWindow: { + switch (eventKind) { + case kEventWindowClose: { + gLog("[pluginWindowMac] <<< CALLBACK >>> kEventWindowClose for gWindow=%p, window=%p\n", (void*)this, (void*)carbonWindow); + show(); + break; + } + case kEventWindowClosed: { + gLog("[pluginWindowMac] <<< CALLBACK >>> kEventWindowClosed for gWindow=%p, window=%p\n", (void*)this, (void*)carbonWindow); + open = false; + result = noErr; + break; + } + } + break; + } + } + return result; +} + + +/* ------------------------------------------------------------------ */ + + +gdPluginWindowGUImac::gdPluginWindowGUImac(Plugin *pPlugin) + : gWindow(450, 300), pPlugin(pPlugin), carbonWindow(NULL) +{ + + /* some effects like to have us get their rect before opening them */ + + ERect *rect; + pPlugin->getRect(&rect); + + /* window initialization */ + + Rect wRect; + + wRect.top = rect->top; + wRect.left = rect->left; + wRect.bottom = rect->bottom; + wRect.right = rect->right; + + int winclass = kDocumentWindowClass; + int winattr = kWindowStandardHandlerAttribute | + kWindowCloseBoxAttribute | + kWindowCompositingAttribute | + kWindowAsyncDragAttribute; + + // winattr &= GetAvailableWindowAttributes(winclass); // make sure that the window will open + + OSStatus status = CreateNewWindow(winclass, winattr, &wRect, &carbonWindow); + if (status != noErr) { + gLog("[pluginWindowMac] Unable to create window! Status=%d\n", (int) status); + return; + } + else + gLog("[pluginWindowMac] created window=%p\n", (void*)carbonWindow); + + /* install event handler, called when window is closed */ + + static EventTypeSpec eventTypes[] = { + { kEventClassWindow, kEventWindowClose }, + { kEventClassWindow, kEventWindowClosed } + }; + InstallWindowEventHandler(carbonWindow, windowHandler, GetEventTypeCount(eventTypes), eventTypes, this, NULL); + + /* open window, center it, show it and start the handler */ + + pPlugin->openGui((void*)carbonWindow); + RepositionWindow(carbonWindow, NULL, kWindowCenterOnMainScreen); + ShowWindow(carbonWindow); + open = true; +} + + + +/* ------------------------------------------------------------------ */ + + +gdPluginWindowGUImac::~gdPluginWindowGUImac() { + gLog("[pluginWindowMac] [[[ destructor ]]] gWindow=%p deleted, window=%p deleted\n", (void*)this, (void*)carbonWindow); + pPlugin->closeGui(); + if (open) + DisposeWindow(carbonWindow); +} + +#endif + +#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_pluginWindowGUI.h b/src/gui/dialogs/gd_pluginWindowGUI.h new file mode 100644 index 0000000..516b879 --- /dev/null +++ b/src/gui/dialogs/gd_pluginWindowGUI.h @@ -0,0 +1,84 @@ + +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_pluginWindowGUI + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifdef WITH_VST + + +#ifndef __GD_PLUGINWINDOW_GUI_H__ +#define __GD_PLUGINWINDOW_GUI_H__ + + +#include +#include +#include "../elems/ge_window.h" +#if defined(__APPLE__) + #include +#endif + + +class gdPluginWindowGUI : public gWindow { +private: + + class Plugin *pPlugin; + +public: + + gdPluginWindowGUI(Plugin *pPlugin); + ~gdPluginWindowGUI(); +}; + + +/* ------------------------------------------------------------------ */ + +#if defined(__APPLE__) + +class gdPluginWindowGUImac : public gWindow { + +private: + + static pascal OSStatus windowHandler(EventHandlerCallRef ehc, EventRef e, void *data); + inline pascal OSStatus __wh(EventHandlerCallRef ehc, EventRef e); + + class Plugin *pPlugin; + WindowRef carbonWindow; + bool open; + +public: + + gdPluginWindowGUImac(Plugin *pPlugin); + ~gdPluginWindowGUImac(); +}; + +#endif + + +#endif // include guard + +#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_warnings.cpp b/src/gui/dialogs/gd_warnings.cpp new file mode 100644 index 0000000..31a5dbc --- /dev/null +++ b/src/gui/dialogs/gd_warnings.cpp @@ -0,0 +1,78 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_warnings + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "gd_warnings.h" + + +void gdAlert(const char *c) { + Fl_Window *modal = new Fl_Window( + (Fl::w() / 2) - 150, + (Fl::h() / 2) - 47, + 300, 90, "Alert"); + modal->set_modal(); + modal->begin(); + gBox *box = new gBox(10, 10, 280, 40, c); + gClick *b = new gClick(210, 60, 80, 20, "Close"); + modal->end(); + box->labelsize(11); + b->callback(__cb_window_closer, (void *)modal); + b->shortcut(FL_Enter); + gu_setFavicon(modal); + modal->show(); +} + + +int gdConfirmWin(const char *title, const char *msg) { + Fl_Window *win = new Fl_Window( + (Fl::w() / 2) - 150, + (Fl::h() / 2) - 47, + 300, 90, title); + win->set_modal(); + win->begin(); + new gBox(10, 10, 280, 40, msg); + gClick *ok = new gClick(212, 62, 80, 20, "Ok"); + gClick *ko = new gClick(124, 62, 80, 20, "Cancel"); + win->end(); + ok->shortcut(FL_Enter); + gu_setFavicon(win); + win->show(); + + /* no callbacks here. readqueue() check the event stack. */ + + int r = 0; + while (true) { + Fl_Widget *o = Fl::readqueue(); + if (!o) Fl::wait(); + else if (o == ok) {r = 1; break;} + else if (o == ko) {r = 0; break;} + } + //delete win; + win->hide(); + return r; +} diff --git a/src/gui/dialogs/gd_warnings.h b/src/gui/dialogs/gd_warnings.h new file mode 100644 index 0000000..791d236 --- /dev/null +++ b/src/gui/dialogs/gd_warnings.h @@ -0,0 +1,43 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_warnings + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GD_WARNINGS_H +#define GD_WARNINGS_H + +#include +#include +#include +#include "../elems/ge_mixed.h" +#include "../../utils/gui_utils.h" + + +void gdAlert(const char *c); + +int gdConfirmWin(const char *title, const char *msg); + +#endif diff --git a/src/gui/elems/ge_actionChannel.cpp b/src/gui/elems/ge_actionChannel.cpp new file mode 100644 index 0000000..b744ac3 --- /dev/null +++ b/src/gui/elems/ge_actionChannel.cpp @@ -0,0 +1,676 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionChannel + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../../core/conf.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../glue/glue.h" +#include "../../utils/log.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_actionEditor.h" +#include "ge_keyboard.h" +#include "ge_actionChannel.h" + + +extern gdMainWindow *mainWin; +extern Mixer G_Mixer; +extern Conf G_Conf; + + +/* ------------------------------------------------------------------ */ + + +gActionChannel::gActionChannel(int x, int y, gdActionEditor *pParent, SampleChannel *ch) + : gActionWidget(x, y, 200, 40, pParent), ch(ch), selected(NULL) +{ + size(pParent->totalWidth, h()); + + /* add actions when the window opens. Their position is zoom-based; + * each frame is / 2 because we don't care about stereo infos. */ + + for (unsigned i=0; ichan == pParent->chan->index) { + + /* don't show actions > than the grey area */ + + if (recorder::frames.at(i) > G_Mixer.totalFrames) + continue; + + /* skip the killchan actions in a singlepress channel. They cannot be recorded + * in such mode, but they can exist if you change from another mode to singlepress */ + + if (ra->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS) + continue; + + /* also filter out ACTION_KEYREL: it's up to gAction to find the other piece + * (namely frame_b) */ + + if (ra->type & (ACTION_KEYPRESS | ACTION_KILLCHAN)) { + int ax = x+(ra->frame/pParent->zoom); + gAction *a = new gAction( + ax, // x + y+4, // y + h()-8, // h + ra->frame, // frame_a + i, // n. of recordings + pParent, // pointer to the pParent window + ch, // pointer to SampleChannel + false, // record = false: don't record it, we are just displaying it! + ra->type); // type of action + add(a); + } + } + } + } + end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions +} + + +/* ------------------------------------------------------------------ */ + + +gAction *gActionChannel::getSelectedAction() { + for (int i=0; ix(); + int action_w = ((gAction*)child(i))->w(); + if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w) + return (gAction*)child(i); + } + return NULL; +} + + +/* ------------------------------------------------------------------ */ + + +void gActionChannel::updateActions() { + + /* when zooming, don't delete and re-add actions, just MOVE them. This + * function shifts the action by a zoom factor. Those singlepress are + * stretched, as well */ + + gAction *a; + for (int i=0; iframe_a / pParent->zoom); + + if (ch->mode == SINGLE_PRESS) { + int newW = ((a->frame_b - a->frame_a) / pParent->zoom); + if (newW < gAction::MIN_WIDTH) + newW = gAction::MIN_WIDTH; + a->resize(newX, a->y(), newW, a->h()); + } + else + a->resize(newX, a->y(), gAction::MIN_WIDTH, a->h()); + } +} + + +/* ------------------------------------------------------------------ */ + + +void gActionChannel::draw() { + + /* draw basic boundaries (+ beat bars) and hide the unused area. Then + * draw the children (the actions) */ + + baseDraw(); + + /* print label */ + + fl_color(COLOR_BG_1); + fl_font(FL_HELVETICA, 12); + if (active()) + fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much! + else + fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much! + + draw_children(); +} + + +/* ------------------------------------------------------------------ */ + + +int gActionChannel::handle(int e) { + + int ret = Fl_Group::handle(e); + + /* do nothing if the widget is deactivated. It could happen for loopmode + * channels */ + + if (!active()) + return 1; + + switch (e) { + + case FL_DRAG: { + if (selected != NULL) { // if you don't drag an empty area + + /* if onLeftEdge o onRightEdge are true it means that you're resizing + * an action. Otherwise move the widget. */ + + if (selected->onLeftEdge || selected->onRightEdge) { + + /* some checks: a) cannot resize an action < N pixels, b) no beyond zero, + * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */ + + if (selected->onRightEdge) { + + int aw = Fl::event_x()-selected->x(); + int ah = selected->h(); + + if (Fl::event_x() < selected->x()+gAction::MIN_WIDTH) + aw = gAction::MIN_WIDTH; + else + if (Fl::event_x() > pParent->coverX) + aw = pParent->coverX-selected->x(); + + selected->size(aw, ah); + } + else { + + int ax = Fl::event_x(); + int ay = selected->y(); + int aw = selected->x()-Fl::event_x()+selected->w(); + int ah = selected->h(); + + if (Fl::event_x() < x()) { + ax = x(); + aw = selected->w()+selected->x()-x(); + } + else + if (Fl::event_x() > selected->x()+selected->w()-gAction::MIN_WIDTH) { + ax = selected->x()+selected->w()-gAction::MIN_WIDTH; + aw = gAction::MIN_WIDTH; + } + selected->resize(ax, ay, aw, ah); + } + } + + /* move the widget around */ + + else { + int real_x = Fl::event_x() - actionPickPoint; + if (real_x < x()) // don't go beyond the left border + selected->position(x(), selected->y()); + else + if (real_x+selected->w() > pParent->coverX+x()) // don't go beyond the right border + selected->position(pParent->coverX+x()-selected->w(), selected->y()); + else { + if (pParent->gridTool->isOn()) { + int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1; + selected->position(snpx, selected->y()); + } + else + selected->position(real_x, selected->y()); + } + } + redraw(); + } + ret = 1; + break; + } + + case FL_PUSH: { + + if (Fl::event_button1()) { + + /* avoid at all costs two overlapping actions. We use 'selected' because + * the selected action can be reused in FL_DRAG, in case you want to move + * it. */ + + selected = getSelectedAction(); + + if (selected == NULL) { + + /* avoid click on grey area */ + + if (Fl::event_x() >= pParent->coverX) { + ret = 1; + break; + } + + /* snap function, if enabled */ + + int ax = Fl::event_x(); + int fx = (ax - x()) * pParent->zoom; + if (pParent->gridTool->isOn()) { + ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1; + fx = pParent->gridTool->getSnapFrame(ax-x()); + + /* with snap=on an action can fall onto another */ + + if (actionCollides(fx)) { + ret = 1; + break; + } + } + + gAction *a = new gAction( + ax, // x + y()+4, // y + h()-8, // h + fx, // frame_a + recorder::frames.size-1, // n. of actions recorded + pParent, // pParent window pointer + ch, // pointer to SampleChannel + true, // record = true: record it! + pParent->getActionType()); // type of action + add(a); + mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel); // mainWindow update + redraw(); + ret = 1; + } + else { + actionOriginalX = selected->x(); + actionOriginalW = selected->w(); + actionPickPoint = Fl::event_x() - selected->x(); + ret = 1; // for dragging + } + } + else + if (Fl::event_button3()) { + gAction *a = getSelectedAction(); + if (a != NULL) { + a->delAction(); + remove(a); + delete a; + mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); + redraw(); + ret = 1; + } + } + break; + } + case FL_RELEASE: { + + if (selected == NULL) { + ret = 1; + break; + } + + /* noChanges = true when you click on an action without doing anything + * (dragging or moving it). */ + + bool noChanges = false; + if (actionOriginalX == selected->x()) + noChanges = true; + if (ch->mode == SINGLE_PRESS && + actionOriginalX+actionOriginalW != selected->x()+selected->w()) + noChanges = false; + + if (noChanges) { + ret = 1; + selected = NULL; + break; + } + + /* step 1: check if the action doesn't overlap with another one. + * In case of overlap the moved action returns to the original X + * value ("actionOriginalX"). */ + + bool overlap = false; + for (int i=0; ix(); + int action_w = ((gAction*)child(i))->w(); + if (ch->mode == SINGLE_PRESS) { + + /* when 2 segments overlap? + * start = the highest value between the two starting points + * end = the lowest value between the two ending points + * if start < end then there's an overlap of end-start pixels. */ + + int start = action_x >= selected->x() ? action_x : selected->x(); + int end = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w(); + if (start < end) { + selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h()); + redraw(); + overlap = true; // one overlap: stop checking + } + } + else { + if (selected->x() == action_x) { + selected->position(actionOriginalX, selected->y()); + redraw(); + overlap = true; // one overlap: stop checking + } + } + } + + /* step 2: no overlap? then update the coordinates of the action, ie + * delete the previous rec and create a new one. + * Anyway the selected action becomes NULL, because when you release + * the mouse button the dragging process ends. */ + + if (!overlap) { + if (pParent->gridTool->isOn()) { + int f = pParent->gridTool->getSnapFrame(selected->absx()); + selected->moveAction(f); + } + else + selected->moveAction(); + } + selected = NULL; + ret = 1; + break; + } + } + + return ret; +} + + +/* ------------------------------------------------------------------ */ + + +bool gActionChannel::actionCollides(int frame) { + + /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't + * overlap the head (frame) of the new one. First the general case, yet. */ + + bool collision = false; + + for (int i=0; iframe_a == frame) + collision = true; + + if (ch->mode == SINGLE_PRESS) { + for (int i=0; iframe_b && frame >= c->frame_a) + collision = true; + } + } + + return collision; +} + + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +const int gAction::MIN_WIDTH = 8; + + +/* ------------------------------------------------------------------ */ + + +/** TODO - index is useless? + * TODO - pass a record::action pointer and let gAction compute values */ + +gAction::gAction(int X, int Y, int H, int frame_a, unsigned index, gdActionEditor *parent, SampleChannel *ch, bool record, char type) +: Fl_Box (X, Y, MIN_WIDTH, H), + selected (false), + index (index), + parent (parent), + ch (ch), + type (type), + frame_a (frame_a), + onRightEdge(false), + onLeftEdge (false) +{ + /* bool 'record' defines how to understand the action. + * record = false: don't record it, just show it. It happens when you + * open the editor with some actions to be shown. + * + * record = true: record it AND show it. It happens when you click on + * an empty area in order to add a new actions. First you record it + * (addAction()) then you show it (FLTK::Draw()) */ + + if (record) + addAction(); + + /* in order to show a singlepress action we must compute the frame_b. We + * do that after the possible recording, otherwise we don't know which + * key_release is associated. */ + + if (ch->mode == SINGLE_PRESS && type == ACTION_KEYPRESS) { + recorder::action *a2 = NULL; + recorder::getNextAction(ch->index, ACTION_KEYREL, frame_a, &a2); + if (a2) { + frame_b = a2->frame; + w((frame_b - frame_a)/parent->zoom); + } + else + gLog("[gActionChannel] frame_b not found! [%d:???]\n", frame_a); + + /* a singlepress action narrower than 8 pixel is useless. So check it. + * Warning: if an action is 8 px narrow, it has no body space to drag + * it. It's up to the user to zoom in and drag it. */ + + if (w() < MIN_WIDTH) + size(MIN_WIDTH, h()); + } +} + + +/* ------------------------------------------------------------------ */ + + +void gAction::draw() { + + int color; + if (selected) /// && gActionChannel !disabled + color = COLOR_BD_1; + else + color = COLOR_BG_2; + + if (ch->mode == SINGLE_PRESS) { + fl_rectf(x(), y(), w(), h(), (Fl_Color) color); + } + else { + if (type == ACTION_KILLCHAN) + fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); + else { + fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); + if (type == ACTION_KEYPRESS) + fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0); + else + if (type == ACTION_KEYREL) + fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0); + } + } + +} + + +/* ------------------------------------------------------------------ */ + + +int gAction::handle(int e) { + + /* ret = 0 sends the event to the parent window. */ + + int ret = 0; + + switch (e) { + + case FL_ENTER: { + selected = true; + ret = 1; + redraw(); + break; + } + case FL_LEAVE: { + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + selected = false; + ret = 1; + redraw(); + break; + } + case FL_MOVE: { + + /* handling of the two margins, left & right. 4 pixels are good enough */ + + if (ch->mode == SINGLE_PRESS) { + onLeftEdge = false; + onRightEdge = false; + if (Fl::event_x() >= x() && Fl::event_x() < x()+4) { + onLeftEdge = true; + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + } + else + if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) { + onRightEdge = true; + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + } + else + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + } + } + } + + return ret; +} + + +/* ------------------------------------------------------------------ */ + + +void gAction::addAction() { + + /* always check frame parity */ + + if (frame_a % 2 != 0) + frame_a++; + + /* anatomy of an action + * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is + * a b the right margin, the ACTION_KEYREL. This is the + * theory behind the singleshot.press actions; for any other kind the + * (b) is just a graphical and meaningless point. */ + + if (ch->mode == SINGLE_PRESS) { + recorder::rec(parent->chan->index, ACTION_KEYPRESS, frame_a); + recorder::rec(parent->chan->index, ACTION_KEYREL, frame_a+4096); + //gLog("action added, [%d, %d]\n", frame_a, frame_a+4096); + } + else { + recorder::rec(parent->chan->index, parent->getActionType(), frame_a); + //gLog("action added, [%d]\n", frame_a); + } + + recorder::sortActions(); + + index++; // important! +} + + +/* ------------------------------------------------------------------ */ + + +void gAction::delAction() { + + /* if SINGLE_PRESS you must delete both the keypress and the keyrelease + * actions. */ + + if (ch->mode == SINGLE_PRESS) { + recorder::deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS, false); + recorder::deleteAction(parent->chan->index, frame_b, ACTION_KEYREL, false); + } + else + recorder::deleteAction(parent->chan->index, frame_a, type, false); + + /* restore the initial cursor shape, in case you delete an action and + * the double arrow (for resizing) is displayed */ + + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); +} + + +/* ------------------------------------------------------------------ */ + + +void gAction::moveAction(int frame_a) { + + /* easy one: delete previous action and record the new ones. As usual, + * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame + * value. */ + + delAction(); + + if (frame_a != -1) + this->frame_a = frame_a; + else + this->frame_a = xToFrame_a(); + + + /* always check frame parity */ + + if (this->frame_a % 2 != 0) + this->frame_a++; + + recorder::rec(parent->chan->index, type, this->frame_a); + + if (ch->mode == SINGLE_PRESS) { + frame_b = xToFrame_b(); + recorder::rec(parent->chan->index, ACTION_KEYREL, frame_b); + } + + recorder::sortActions(); +} + + +/* ------------------------------------------------------------------ */ + + +int gAction::absx() { + return x() - parent->ac->x(); +} + + +/* ------------------------------------------------------------------ */ + + +int gAction::xToFrame_a() { + return (absx()) * parent->zoom; +} + + +/* ------------------------------------------------------------------ */ + + +int gAction::xToFrame_b() { + return (absx() + w()) * parent->zoom; +} diff --git a/src/gui/elems/ge_actionChannel.h b/src/gui/elems/ge_actionChannel.h new file mode 100644 index 0000000..67413fd --- /dev/null +++ b/src/gui/elems/ge_actionChannel.h @@ -0,0 +1,135 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionChannel + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GE_ACTIONCHANNEL_H +#define GE_ACTIONCHANNEL_H + +#include +#include +#include "../../utils/gui_utils.h" +#include "../../core/mixer.h" +#include "../../core/recorder.h" +#include "ge_actionWidget.h" + + +class gAction : public Fl_Box { + +private: + + bool selected; + unsigned index; + class gdActionEditor *parent; // pointer to parent (gActionEditor) + class SampleChannel *ch; + char type; // type of action + +public: + gAction(int x, int y, int h, int frame_a, unsigned index, + gdActionEditor *parent, class SampleChannel *ch, bool record, + char type); + void draw(); + int handle(int e); + void addAction(); + void delAction(); + + /* moveAction + * shift the action on the x-axis and update Recorder. If frame_a != -1 + * use the new frame in input (used while snapping) */ + + void moveAction(int frame_a=-1); + + /* absx + * x() is relative to scrolling position. absx() returns the absolute + * x value of the action, from the leftmost edge. */ + + int absx(); + + /* xToFrame_a,b + * return the real frames of x() position */ + + int xToFrame_a(); + int xToFrame_b(); + + int frame_a; // initial frame (KEYPRESS for singlemode.press) + int frame_b; // terminal frame (KEYREL for singlemode.press, null for others) + + bool onRightEdge; + bool onLeftEdge; + + static const int MIN_WIDTH; +}; + + +/* ------------------------------------------------------------------ */ + + +class gActionChannel : public gActionWidget { + +private: + + class SampleChannel *ch; + + /* getSelectedAction + * get the action under the mouse. NULL if nothing found. */ + + gAction *getSelectedAction(); + + /* selected + * pointer to the selected action. Useful when dragging around. */ + + gAction *selected; + + /* actionOriginalX, actionOriginalW + * x and w of the action, when moved. Useful for checking if the action + * overlaps another one: in that case the moved action returns to + * actionOriginalX (and to actionOriginalW if resized). */ + + int actionOriginalX; + int actionOriginalW; + + /* actionPickPoint + * the precise x point in which the action has been picked with the mouse, + * before a dragging action. */ + + int actionPickPoint; + + + /* actionCollides + * true if an action collides with another. Used while adding new points + * with snap active.*/ + + bool actionCollides(int frame); + +public: + gActionChannel(int x, int y, gdActionEditor *pParent, class SampleChannel *ch); + void draw(); + int handle(int e); + void updateActions(); +}; + + +#endif diff --git a/src/gui/elems/ge_actionWidget.cpp b/src/gui/elems/ge_actionWidget.cpp new file mode 100644 index 0000000..05b4db0 --- /dev/null +++ b/src/gui/elems/ge_actionWidget.cpp @@ -0,0 +1,101 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionWidget + * + * pParent class of any widget inside the action editor. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../../core/mixer.h" +#include "../dialogs/gd_actionEditor.h" +#include "ge_actionWidget.h" +#include "ge_mixed.h" + + +extern Mixer G_Mixer; + + +gActionWidget::gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent) + : Fl_Group(x, y, w, h), pParent(pParent) {} + + +/* ------------------------------------------------------------------ */ + + +gActionWidget::~gActionWidget() {} + + +/* ------------------------------------------------------------------ */ + + +void gActionWidget::baseDraw(bool clear) { + + /* clear the screen */ + + if (clear) + fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); + + /* draw the container */ + + fl_color(COLOR_BD_0); + fl_rect(x(), y(), w(), h()); + + /* grid drawing, if > 1 */ + + if (pParent->gridTool->getValue() > 1) { + + fl_color(fl_rgb_color(54, 54, 54)); + fl_line_style(FL_DASH, 0, NULL); + + for (int i=0; i<(int) pParent->gridTool->points.size; i++) { + int px = pParent->gridTool->points.at(i)+x()-1; + fl_line(px, y()+1, px, y()+h()-2); + } + fl_line_style(0); + } + + /* bars and beats drawing */ + + fl_color(COLOR_BD_0); + for (int i=0; i<(int) pParent->gridTool->beats.size; i++) { + int px = pParent->gridTool->beats.at(i)+x()-1; + fl_line(px, y()+1, px, y()+h()-2); + } + + fl_color(COLOR_BG_2); + for (int i=0; i<(int) pParent->gridTool->bars.size; i++) { + int px = pParent->gridTool->bars.at(i)+x()-1; + fl_line(px, y()+1, px, y()+h()-2); + } + + /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats + * are 32) */ + + int coverWidth = pParent->totalWidth-pParent->coverX; + if (coverWidth != 0) + fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1); +} diff --git a/src/gui/elems/ge_actionWidget.h b/src/gui/elems/ge_actionWidget.h new file mode 100644 index 0000000..b02f8d7 --- /dev/null +++ b/src/gui/elems/ge_actionWidget.h @@ -0,0 +1,53 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionWidget + * + * parent class of any widget inside the action editor. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef __GE_ACTIONWIDGET_H__ +#define __GE_ACTIONWIDGET_H__ + +#include +#include +#include "../../core/const.h" + + +class gActionWidget : public Fl_Group { + +protected: + class gdActionEditor *pParent; + void baseDraw(bool clear=true); + +public: + virtual void updateActions() = 0; + + gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent); + ~gActionWidget(); +}; + +#endif diff --git a/src/gui/elems/ge_browser.cpp b/src/gui/elems/ge_browser.cpp new file mode 100644 index 0000000..fd70dcc --- /dev/null +++ b/src/gui/elems/ge_browser.cpp @@ -0,0 +1,307 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gd_browser + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../../core/const.h" +#include "../../utils/utils.h" +#include "../../utils/log.h" +#include "ge_browser.h" + + +gBrowser::gBrowser(int x, int y, int w, int h, const char *L) + : Fl_Hold_Browser(x, y, w, h, L) +{ + box(G_BOX); + textsize(11); + textcolor(COLOR_TEXT_0); + selection_color(COLOR_BG_1); + color(COLOR_BG_0); + + this->scrollbar.color(COLOR_BG_0); + this->scrollbar.selection_color(COLOR_BG_1); + this->scrollbar.labelcolor(COLOR_BD_1); + this->scrollbar.slider(G_BOX); + + this->hscrollbar.color(COLOR_BG_0); + this->hscrollbar.selection_color(COLOR_BG_1); + this->hscrollbar.labelcolor(COLOR_BD_1); + this->hscrollbar.slider(G_BOX); +} + + +/* ------------------------------------------------------------------ */ + + +gBrowser::~gBrowser() {} + + +/* ------------------------------------------------------------------ */ + + +void gBrowser::init(const char *init_path) { + + gLog("[gBrowser] init path = '%s'\n", init_path); + + if (init_path == NULL || !gIsDir(init_path)) { +#if defined(__linux__) || defined(__APPLE__) + path_obj->value("/home"); +#elif defined(_WIN32) + + /* SHGetFolderPath is deprecated. We should use SHGetKnownFolderPath + * but that would break compatibility with XP. On Vista, GetFolderPath + * is a wrapper of GetKnownFolderPath, so no problem. */ + + char winRoot[1024]; + SHGetFolderPath(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, 0, winRoot); // si parte dal Desktop + path_obj->value(winRoot); +#endif + gLog("[gBrowser] init_path null or invalid, using default\n"); + } + else + path_obj->value(init_path); + + refresh(); + sort(); +} + + +/* ------------------------------------------------------------------ */ + + +void gBrowser::refresh() { + DIR *dp; + struct dirent *ep; + dp = opendir(path_obj->value()); + if (dp != NULL) { + while ((ep = readdir(dp))) { + + /* skip: + * - "." e ".." + * - hidden files */ + + if (strcmp(ep->d_name, ".") != 0 && strcmp(ep->d_name, "..") != 0) { + if (ep->d_name[0] != '.') { + + /* is it a folder? add square brackets. Is it a file? Append + * a '/' (on Windows seems useless, though) */ + + std::string file = path_obj->value(); + file.insert(file.size(), gGetSlash()); + file += ep->d_name; + + if (gIsDir(file.c_str())) { + char name[PATH_MAX]; + sprintf(name, "@b[%s]", ep->d_name); + add(name); + } + else + if (gIsProject(file.c_str())) { + char name[PATH_MAX]; + sprintf(name, "@i@b%s", ep->d_name); + add(name); + } + else + add(ep->d_name); + } + } + } + closedir(dp); + } + else + gLog("[gBrowser] Couldn't open the directory '%s'\n", path_obj->value()); +} + + +/* ------------------------------------------------------------------ */ + + +void gBrowser::sort() { + for (int t=1; t<=size(); t++) + for (int r=t+1; r<=size(); r++) + if (strcmp(text(t), text(r)) > 0) + swap(t,r); +} + + +/* ------------------------------------------------------------------ */ + + +void gBrowser::up_dir() { + + /* updir = remove last folder from the path. Start from strlen(-1) to + * skip the trailing slash */ + + int i = strlen(path_obj->value())-1; + + /* on Windows an updir from the path "X:\" (3 chars long) must redirect + * to the list of available devices. */ + +#if defined(_WIN32) + if (i <= 3 || !strcmp(path_obj->value(), "All drives")) { + path_obj->value("All drives"); + showDrives(); + return; + } + else { + while (i >= 0) { + if (path_obj->value()[i] == '\\') + break; + i--; + } + + /* delete the last part of the string, from i to len-i, ie everything + * after the "/" */ + + std::string tmp = path_obj->value(); + tmp.erase(i, tmp.size()-i); + + /* if tmp.size == 2 we have something like 'C:'. Add a trailing + * slash */ + + if (tmp.size() == 2) + tmp += "\\"; + + path_obj->value(tmp.c_str()); + refresh(); + } +#elif defined(__linux__) || defined (__APPLE__) + while (i >= 0) { + if (path_obj->value()[i] == '/') + break; + i--; + } + + /* i == 0 means '/', the root dir. It's meaningless to go updir */ + + if (i==0) + path_obj->value("/"); + else { + + /* delete the last part of the string, from i to len-i, ie everything + * after the "/" */ + + std::string tmp = path_obj->value(); + tmp.erase(i, tmp.size()-i); + path_obj->value(tmp.c_str()); + } + refresh(); +#endif +} + + +/* ------------------------------------------------------------------ */ + + +void gBrowser::down_dir(const char *path) { + path_obj->value(path); + refresh(); +} + + +/* ------------------------------------------------------------------ */ + + +const char *gBrowser::get_selected_item() { + + /* click on an empty line */ + + if (text(value()) == NULL) + return NULL; + + selected_item = text(value()); + + /* @ = formatting marks. + * @b = bold, i.e. a directory. Erease '@b[' and ']' */ + + if (selected_item[0] == '@') { + if (selected_item[1] == 'b') { + selected_item.erase(0, 3); + selected_item.erase(selected_item.size()-1, 1); + } + else + if (selected_item[1] == 'i') + selected_item.erase(0, 4); + } + +#if defined(__linux__) || defined(__APPLE__) + + /* add path to file name, to get an absolute path. Avoid double + * slashes like '//' */ + + if (strcmp("/", path_obj->value())) + selected_item.insert(0, "/"); + + selected_item.insert(0, path_obj->value()); + return selected_item.c_str(); +#elif defined(_WIN32) + + /* if path is 'All drives' we are in the devices list and the user + * has clicked on a device such as 'X:\' */ + + if (strcmp(path_obj->value(), "All drives") == 0) + return selected_item.c_str(); + else { + + /* add '\' if the path is like 'X:\' */ + + if (strlen(path_obj->value()) > 3) /// shouln't it be == 3? + selected_item.insert(0, "\\"); + + selected_item.insert(0, path_obj->value()); + return selected_item.c_str(); + } +#endif +} + + +/* ------------------------------------------------------------------ */ + + +#ifdef _WIN32 +void gBrowser::showDrives() { + + /* GetLogicalDriveStrings fills drives like that: + * + * a:\[null]b:\[null]c:\[null]...[null][null] + * + * where [null] stands for \0. */ + + char drives[64]; + char *i = drives; // pointer to 0th element in drives + GetLogicalDriveStrings(64, drives); + + /* code stolen from the web, still unknown. (Jan 09, 2012). */ + + while (*i) { + add(i); + i = &i[strlen(i) + 1]; + } +} + +#endif diff --git a/src/gui/elems/ge_browser.h b/src/gui/elems/ge_browser.h new file mode 100644 index 0000000..8445319 --- /dev/null +++ b/src/gui/elems/ge_browser.h @@ -0,0 +1,68 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_browser + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GE_BROWSER_H +#define GE_BROWSER_H + +#include +#include +#include +#include "ge_mixed.h" + +class gBrowser : public Fl_Hold_Browser { +public: + gBrowser(int x, int y, int w, int h, const char *L=0); + ~gBrowser(); + void init(const char *init_path=NULL); + void refresh(); + void sort(); + void up_dir(); + void down_dir(const char *path); + const char *get_selected_item(); + + /* path_obj + * the actual path*/ + + class gInput *path_obj; + + /* selected_item + * choosen item */ + + std::string selected_item; + +#ifdef _WIN32 +private: + + /* showDrives [WIN32 only] + * lists all the available drivers */ + + void showDrives(); +#endif +}; + +#endif diff --git a/src/gui/elems/ge_channel.cpp b/src/gui/elems/ge_channel.cpp new file mode 100644 index 0000000..945ece4 --- /dev/null +++ b/src/gui/elems/ge_channel.cpp @@ -0,0 +1,148 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/pluginHost.h" +#include "../../core/mixer.h" +#include "../../core/conf.h" +#include "../../core/patch.h" +#include "../../core/graphics.h" +#include "../../core/channel.h" +#include "../../core/wave.h" +#include "../../core/sampleChannel.h" +#include "../../core/midiChannel.h" +#include "../../glue/glue.h" +#include "../../utils/gui_utils.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_keyGrabber.h" +#include "../dialogs/gd_midiInput.h" +#include "../dialogs/gd_editor.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_warnings.h" +#include "../dialogs/gd_browser.h" +#include "../dialogs/gd_midiOutput.h" +#include "ge_keyboard.h" +#include "ge_channel.h" +#include "ge_sampleChannel.h" + +#ifdef WITH_VST +#include "../dialogs/gd_pluginList.h" +#endif + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Patch G_Patch; +extern gdMainWindow *mainWin; + + +gChannel::gChannel(int X, int Y, int W, int H, int type) + : Fl_Group(X, Y, W, H, NULL), type(type) {} + + +/* -------------------------------------------------------------------------- */ + + +int gChannel::getColumnIndex() +{ + return ((gColumn*)parent())->getIndex(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannel::blink() +{ + if (gu_getBlinker() > 6) + mainButton->setPlayMode(); + else + mainButton->setDefaultMode(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannel::setColorsByStatus(int playStatus, int recStatus) +{ + switch (playStatus) { + case STATUS_OFF: + mainButton->setDefaultMode(); + button->imgOn = channelPlay_xpm; + button->imgOff = channelStop_xpm; + button->redraw(); + break; + case STATUS_PLAY: + mainButton->setPlayMode(); + button->imgOn = channelStop_xpm; + button->imgOff = channelPlay_xpm; + button->redraw(); + break; + case STATUS_WAIT: + blink(); + break; + case STATUS_ENDING: + mainButton->setEndingMode(); + break; + } + + switch (recStatus) { + case REC_WAITING: + blink(); + break; + case REC_ENDING: + mainButton->setEndingMode(); + break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +int gChannel::handleKey(int e, int key) +{ + int ret; + if (e == FL_KEYDOWN && button->value()) // key already pressed! skip it + ret = 1; + else + if (Fl::event_key() == key && !button->value()) { + button->take_focus(); // move focus to this button + button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state + button->do_callback(); // invoke the button's callback + ret = 1; + } + else + ret = 0; + + if (Fl::event_key() == key) + button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state + + return ret; +} diff --git a/src/gui/elems/ge_channel.h b/src/gui/elems/ge_channel.h new file mode 100644 index 0000000..5af2d64 --- /dev/null +++ b/src/gui/elems/ge_channel.h @@ -0,0 +1,119 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_CHANNEL_H +#define GE_CHANNEL_H + + +#include +#include +#include +#include "ge_mixed.h" + + +class gChannel : public Fl_Group +{ +protected: + + /* define some breakpoints for dynamic resize */ + +#ifdef WITH_VST + static const int BREAK_READ_ACTIONS = 212; + static const int BREAK_MODE_BOX = 188; + static const int BREAK_FX = 164; + static const int BREAK_DELTA = 120; +#else + static const int BREAK_READ_ACTIONS = 188; + static const int BREAK_MODE_BOX = 164; + static const int BREAK_FX = 140; + static const int BREAK_DELTA = 96; +#endif + static const int BREAK_UNIT = 24; + + /* blink + * blink button when channel is in wait/ending status. */ + + void blink(); + + /* setColorByStatus + * update colors depending on channel status. */ + + void setColorsByStatus(int playStatus, int recStatus); + + /* handleKey + * method wrapped by virtual handle(int e). */ + + int handleKey(int e, int key); + +public: + + gChannel(int x, int y, int w, int h, int type); + + /* reset + * reset channel to initial status. */ + + virtual void reset() = 0; + + /* update + * update the label of sample button and everything else such as 'R' + * button, key box and so on, according to global values. */ + + virtual void update() = 0; + + /* refresh + * update graphics. */ + + virtual void refresh() = 0; + + /* keypress + * what to do when the corresponding key is pressed. */ + + virtual int keyPress(int event) = 0; + + /* getColumnIndex + * return the numeric index of the column in which this channel is + * located. */ + + int getColumnIndex(); + + class gButton *button; + class gStatus *status; + class gChannelButton *mainButton; + class gDial *vol; + class gClick *mute; + class gClick *solo; +#ifdef WITH_VST + class gFxButton *fx; +#endif + + int type; +}; + + +#endif diff --git a/src/gui/elems/ge_channelButton.cpp b/src/gui/elems/ge_channelButton.cpp new file mode 100644 index 0000000..4eef502 --- /dev/null +++ b/src/gui/elems/ge_channelButton.cpp @@ -0,0 +1,118 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channelButton + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/const.h" +#include "ge_channelButton.h" + + +gChannelButton::gChannelButton(int x, int y, int w, int h, const char *l) + : gClick(x, y, w, h, l), key("") {} + + +/* -------------------------------------------------------------------------- */ + + +void gChannelButton::setKey(const char *k) +{ + key = k; +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannelButton::draw() +{ + gClick::draw(); + + if (key == "") + return; + + /* draw background */ + + fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0); + + /* draw key */ + + fl_color(COLOR_TEXT_0); + fl_font(FL_HELVETICA, 11); + fl_draw(key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER); +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannelButton::setInputRecordMode() +{ + bgColor0 = COLOR_BG_3; +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannelButton::setActionRecordMode() +{ + bgColor0 = COLOR_BG_4; + txtColor = COLOR_TEXT_0; +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannelButton::setDefaultMode(const char *l) +{ + bgColor0 = COLOR_BG_0; + bdColor = COLOR_BD_0; + txtColor = COLOR_TEXT_0; + if (l) + label(l); +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannelButton::setPlayMode() +{ + bgColor0 = COLOR_BG_2; + bdColor = COLOR_BD_1; + txtColor = COLOR_TEXT_1; +} + + +/* -------------------------------------------------------------------------- */ + + +void gChannelButton::setEndingMode() +{ + bgColor0 = COLOR_BD_0; +} diff --git a/src/gui/elems/ge_channelButton.h b/src/gui/elems/ge_channelButton.h new file mode 100644 index 0000000..a3bb22b --- /dev/null +++ b/src/gui/elems/ge_channelButton.h @@ -0,0 +1,59 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channelButton + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_CHANNEL_BUTTON_H +#define GE_CHANNEL_BUTTON_H + + +#include "ge_mixed.h" + + +class gChannelButton : public gClick +{ +private: + + std::string key; + +public: + + gChannelButton(int x, int y, int w, int h, const char *l=0); + + virtual int handle(int e) = 0; + + void draw(); + void setKey(const char *k); + void setPlayMode(); + void setEndingMode(); + void setDefaultMode(const char *l=0); + void setInputRecordMode(); + void setActionRecordMode(); +}; + + +#endif diff --git a/src/gui/elems/ge_column.cpp b/src/gui/elems/ge_column.cpp new file mode 100644 index 0000000..12c479c --- /dev/null +++ b/src/gui/elems/ge_column.cpp @@ -0,0 +1,304 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_column + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/mixer.h" +#include "../../core/conf.h" +#include "../../core/patch.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../core/midiChannel.h" +#include "../../glue/glue.h" +#include "../../utils/log.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_warnings.h" +#include "../elems/ge_keyboard.h" +#include "ge_column.h" +#include "ge_channel.h" +#include "ge_sampleChannel.h" +#include "ge_midiChannel.h" + +#ifdef WITH_VST + #include "../dialogs/gd_pluginList.h" +#endif + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Patch G_Patch; +extern gdMainWindow *mainWin; + + +gColumn::gColumn(int X, int Y, int W, int H, int index, gKeyboard *parent) + : Fl_Group(X, Y, W, H), parent(parent), index(index) +{ + /* gColumn does a bit of a mess: we pass a pointer to its parent (gKeyboard) and + the gColumn itself deals with the creation of another widget, outside gColumn + and inside gKeyboard, which handles the vertical resize bar (gResizerBar). + The resizer cannot stay inside gColumn: it needs a broader view on the other + side widgets. The view can be obtained from gKeyboard only (the upper level). + Unfortunately, parent() can be NULL: at this point (i.e the constructor) + gColumn is still detached from any parent. We use a custom gKeyboard *parent + instead. */ + + begin(); + addChannelBtn = new gClick(x(), y(), w(), 20, "Add new channel"); + end(); + + resizer = new gResizerBar(x()+w(), y(), 16, h(), false); + resizer->setMinSize(140); + parent->add(resizer); + + addChannelBtn->callback(cb_addChannel, (void*)this); +} + + +/* -------------------------------------------------------------------------- */ + + +gColumn::~gColumn() +{ + /* FIXME - this could actually cause a memory leak. resizer is + just removed, not deleted. But we cannot delete it right now. */ + + parent->remove(resizer); +} + + +/* -------------------------------------------------------------------------- */ + + +int gColumn::handle(int e) +{ + switch (e) { + case FL_DND_ENTER: // return(1) for these events to 'accept' dnd + case FL_DND_DRAG: + case FL_DND_RELEASE: { + return 1; + } + case FL_PASTE: { // handle actual drop (paste) operation + gVector paths; + gSplit(Fl::event_text(), "\n", &paths); + bool fails = false; + int result = 0; + for (unsigned i=0; iguiChannel); + fails = true; + } + } + if (fails) { + if (paths.size > 1) + gdAlert("Some files were not loaded successfully."); + else + parent->printChannelMessage(result); + } + return 1; + } + } + + /* we return fl_Group::handle only if none of the cases above are fired. That + is because we don't want to propagate a dnd drop to all the sub widgets. */ + + return Fl_Group::handle(e); +} + + +/* -------------------------------------------------------------------------- */ + + +void gColumn::resize(int X, int Y, int W, int H) +{ + /* resize all children */ + + int ch = children(); + for (int i=0; iresize(X, Y + (i * (c->h() + 4)), W, c->h()); + } + + /* resize group itself */ + + x(X); y(Y); w(W); h(H); + + /* resize resizerBar */ + + resizer->size(16, H); +} + + +/* -------------------------------------------------------------------------- */ + + +void gColumn::refreshChannels() +{ + for (int i=1; irefresh(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gColumn::draw() +{ + fl_color(fl_rgb_color(27, 27, 27)); + fl_rectf(x(), y(), w(), h()); + + /* call draw and then redraw in order to avoid channel corruption when + scrolling horizontally */ + + for (int i=0; idraw(); + child(i)->redraw(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gColumn::cb_addChannel(Fl_Widget *v, void *p) { ((gColumn*)p)->__cb_addChannel(); } + + +/* -------------------------------------------------------------------------- */ + + +gChannel *gColumn::addChannel(class Channel *ch) +{ + gChannel *gch = NULL; + + if (ch->type == CHANNEL_SAMPLE) + gch = (gSampleChannel*) new gSampleChannel( + x(), + y() + children() * 24, + 600, // (1) see notes below + 20, + (SampleChannel*) ch); + else + gch = (gMidiChannel*) new gMidiChannel( + x(), + y() + children() * 24, + w(), + 20, + (MidiChannel*) ch); + + /* (1) we create a new sample channel with a fake width, instead of w() (i.e. + the column width), in case the column is too narrow to display all widgets. + This workaround prevents the widgets to disappear if they have an initial + negative width. MidiChannel does not need such hack because it already fits + nicely in a collapsed column. */ + + add(gch); + resize(x(), y(), w(), (children() * 24) + 66); // evil space for drag n drop + gch->redraw(); // avoid corruption + parent->redraw(); // redraw Keyboard + return gch; +} + + +/* -------------------------------------------------------------------------- */ + + +void gColumn::deleteChannel(gChannel *gch) +{ + gch->hide(); + remove(gch); + delete gch; + + /* reposition all other channels and resize this group */ + /** TODO + * reposition is useless when called by gColumn::clear(). Add a new + * parameter to skip the operation */ + + for (int i=0; iposition(gch->x(), y()+(i*24)); + } + size(w(), children() * 24 + 66); // evil space for drag n drop + redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gColumn::__cb_addChannel() +{ + gLog("[gColumn::__cb_addChannel] index = %d\n", index); + int type = openTypeMenu(); + if (type) + glue_addChannel(index, type); +} + + +/* -------------------------------------------------------------------------- */ + + +int gColumn::openTypeMenu() +{ + Fl_Menu_Item rclick_menu[] = { + {"Sample channel"}, + {"MIDI channel"}, + {0} + }; + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(11); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) return 0; + + if (strcmp(m->label(), "Sample channel") == 0) + return CHANNEL_SAMPLE; + if (strcmp(m->label(), "MIDI channel") == 0) + return CHANNEL_MIDI; + return 0; +} + + +/* -------------------------------------------------------------------------- */ + + +void gColumn::clear(bool full) +{ + if (full) + Fl_Group::clear(); + else { + while (children() >= 2) { // skip "add new channel" btn + int i = children()-1; + deleteChannel((gChannel*)child(i)); + } + } +} diff --git a/src/gui/elems/ge_column.h b/src/gui/elems/ge_column.h new file mode 100644 index 0000000..84ebc8a --- /dev/null +++ b/src/gui/elems/ge_column.h @@ -0,0 +1,98 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_column + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_COLUMN_H +#define GE_COLUMN_H + + +#include +#include + + +class gColumn : public Fl_Group +{ +private: + + static void cb_addChannel (Fl_Widget *v, void *p); + inline void __cb_addChannel(); + + int openTypeMenu(); + + class gClick *addChannelBtn; + class gResizerBar *resizer; + class gKeyboard *parent; + + int index; + +public: + + gColumn(int x, int y, int w, int h, int index, class gKeyboard *parent); + ~gColumn(); + + /* addChannel + * add a new channel in this column and set the internal pointer + * to channel to 'ch'. */ + + class gChannel *addChannel(class Channel *ch); + + /* handle */ + + int handle(int e); + + /* resize + * custom resize behavior. */ + + void resize(int x, int y, int w, int h); + + /* deleteChannel + * remove the channel 'gch' from this column. */ + + void deleteChannel(gChannel *gch); + + /* refreshChannels + * update channels' graphical statues. Called on each GUI cycle. */ + + void refreshChannels(); + + /* clear + * remove all channels from the column. If full==true, delete also the + * "add new channel" button. This method ovverrides the inherited one + * from Fl_Group. */ + + void clear(bool full=false); + + void draw(); + + inline int getIndex() { return index; } + inline void setIndex(int i) { index = i; } + inline bool isEmpty() { return children() == 1; } +}; + + +#endif diff --git a/src/gui/elems/ge_controller.cpp b/src/gui/elems/ge_controller.cpp new file mode 100644 index 0000000..2557ae6 --- /dev/null +++ b/src/gui/elems/ge_controller.cpp @@ -0,0 +1,158 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * ge_controller + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/graphics.h" +#include "../../glue/glue.h" +#include "ge_mixed.h" +#include "ge_controller.h" + + +gController::gController(int x, int y) + : Fl_Group(x, y, 131, 25) +{ + begin(); + + rewind = new gClick(x, y, 25, 25, "", rewindOff_xpm, rewindOn_xpm); + play = new gClick(rewind->x()+rewind->w()+4, y, 25, 25, "", play_xpm, pause_xpm); + recAction = new gClick(play->x()+play->w()+4, y, 25, 25, "", recOff_xpm, recOn_xpm); + recInput = new gClick(recAction->x()+recAction->w()+4, y, 25, 25, "", inputRecOff_xpm, inputRecOn_xpm); + metronome = new gClick(recInput->x()+recInput->w()+4, y+10, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm); + + end(); + + resizable(NULL); // don't resize any widget + + rewind->callback(cb_rewind, (void*)this); + + play->callback(cb_play); + play->type(FL_TOGGLE_BUTTON); + + recAction->callback(cb_recAction, (void*)this); + recAction->type(FL_TOGGLE_BUTTON); + + recInput->callback(cb_recInput, (void*)this); + recInput->type(FL_TOGGLE_BUTTON); + + metronome->callback(cb_metronome); + metronome->type(FL_TOGGLE_BUTTON); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::cb_rewind (Fl_Widget *v, void *p) { ((gController*)p)->__cb_rewind(); } +void gController::cb_play (Fl_Widget *v, void *p) { ((gController*)p)->__cb_play(); } +void gController::cb_recAction(Fl_Widget *v, void *p) { ((gController*)p)->__cb_recAction(); } +void gController::cb_recInput (Fl_Widget *v, void *p) { ((gController*)p)->__cb_recInput(); } +void gController::cb_metronome(Fl_Widget *v, void *p) { ((gController*)p)->__cb_metronome(); } + + +/* -------------------------------------------------------------------------- */ + + +void gController::__cb_rewind() +{ + glue_rewindSeq(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::__cb_play() +{ + glue_startStopSeq(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::__cb_recAction() +{ + glue_startStopActionRec(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::__cb_recInput() +{ + glue_startStopInputRec(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::__cb_metronome() +{ + glue_startStopMetronome(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::updatePlay(int v) +{ + play->value(v); + play->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::updateMetronome(int v) +{ + metronome->value(v); + metronome->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::updateRecInput(int v) +{ + recInput->value(v); + recInput->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gController::updateRecAction(int v) +{ + recAction->value(v); + recAction->redraw(); +} diff --git a/src/gui/elems/ge_controller.h b/src/gui/elems/ge_controller.h new file mode 100644 index 0000000..7056948 --- /dev/null +++ b/src/gui/elems/ge_controller.h @@ -0,0 +1,68 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * ge_controller + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_CONTROLLER_H +#define GE_CONTROLLER_H + + +#include + + +class gController : public Fl_Group +{ +private: + + class gClick *rewind; + class gClick *play; + class gClick *recAction; + class gClick *recInput; + class gClick *metronome; + + static void cb_rewind (Fl_Widget *v, void *p); + static void cb_play (Fl_Widget *v, void *p); + static void cb_recAction(Fl_Widget *v, void *p); + static void cb_recInput (Fl_Widget *v, void *p); + static void cb_metronome(Fl_Widget *v, void *p); + + inline void __cb_rewind (); + inline void __cb_play (); + inline void __cb_recAction(); + inline void __cb_recInput (); + inline void __cb_metronome(); + +public: + + gController(int x, int y); + + void updatePlay (int v); + void updateMetronome(int v); + void updateRecInput (int v); + void updateRecAction(int v); +}; + +#endif diff --git a/src/gui/elems/ge_envelopeChannel.cpp b/src/gui/elems/ge_envelopeChannel.cpp new file mode 100644 index 0000000..d340375 --- /dev/null +++ b/src/gui/elems/ge_envelopeChannel.cpp @@ -0,0 +1,399 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_envelopeWidget + * + * Parent class of any envelope controller, from volume to VST parameter + * automations. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../../core/channel.h" +#include "../../core/recorder.h" +#include "../../core/mixer.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_mainWindow.h" +#include "ge_keyboard.h" +#include "ge_envelopeChannel.h" + + +extern Mixer G_Mixer; +extern gdMainWindow *mainWin; + + +gEnvelopeChannel::gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l) + : gActionWidget(x, y, 200, 80, pParent), l(l), type(type), range(range), + selectedPoint(-1), draggedPoint(-1) +{ + size(pParent->totalWidth, h()); +} + + +/* ------------------------------------------------------------------ */ + + +gEnvelopeChannel::~gEnvelopeChannel() { + clearPoints(); +} + + +/* ------------------------------------------------------------------ */ + + +void gEnvelopeChannel::addPoint(int frame, int iValue, float fValue, int px, int py) { + point p; + p.frame = frame; + p.iValue = iValue; + p.fValue = fValue; + p.x = px; + p.y = py; + points.add(p); +} + + +/* ------------------------------------------------------------------ */ + + +void gEnvelopeChannel::updateActions() { + for (unsigned i=0; izoom; +} + + +/* ------------------------------------------------------------------ */ + + +void gEnvelopeChannel::draw() { + + baseDraw(); + + /* print label */ + + fl_color(COLOR_BG_1); + fl_font(FL_HELVETICA, 12); + fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT)); + + int pxOld = x()-3; + int pyOld = y()+1; + int pxNew = 0; + int pyNew = 0; + + fl_color(COLOR_BG_2); + + for (unsigned i=0; i 0) + fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3); + + pxOld = pxNew; + pyOld = pyNew; + } +} + + +/* ------------------------------------------------------------------ */ + + +int gEnvelopeChannel::handle(int e) { + + /* Adding an action: no further checks required, just record it on frame + * mx*pParent->zoom. Deleting action is trickier: find the active + * point and derive from it the corresponding frame. */ + + int ret = 0; + int mx = Fl::event_x()-x(); // mouse x + int my = Fl::event_y()-y(); // mouse y + + switch (e) { + + case FL_ENTER: { + ret = 1; + break; + } + + case FL_MOVE: { + selectedPoint = getSelectedPoint(); + redraw(); + ret = 1; + break; + } + + case FL_LEAVE: { + draggedPoint = -1; + selectedPoint = -1; + redraw(); + ret = 1; + break; + } + + case FL_PUSH: { + + /* left click on point: drag + * right click on point: delete + * left click on void: add */ + + if (Fl::event_button1()) { + + if (selectedPoint != -1) { + draggedPoint = selectedPoint; + } + else { + + /* top & border fix */ + + if (my > h()-8) my = h()-8; + if (mx > pParent->coverX-x()) mx = pParent->coverX-x(); + + if (range == RANGE_FLOAT) { + + /* if this is the first point ever, add other two points at the beginning + * and the end of the range */ + + if (points.size == 0) { + addPoint(0, 0, 1.0f, 0, 1); + recorder::rec(pParent->chan->index, type, 0, 0, 1.0f); + addPoint(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1); + recorder::rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f); + } + + /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */ + + int frame = mx * pParent->zoom; + float value = (my - h() + 8) / (float) (1 - h() + 8); + addPoint(frame, 0, value, mx, my); + recorder::rec(pParent->chan->index, type, frame, 0, value); + recorder::sortActions(); + sortPoints(); + } + else { + /// TODO + } + mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow + redraw(); + } + } + else { + + /* right click on point 0 or point size-1 deletes the entire envelope */ + + if (selectedPoint != -1) { + if (selectedPoint == 0 || (unsigned) selectedPoint == points.size-1) { + recorder::clearAction(pParent->chan->index, type); + points.clear(); + } + else { + recorder::deleteAction(pParent->chan->index, points.at(selectedPoint).frame, type, false); + recorder::sortActions(); + points.del(selectedPoint); + } + mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow + redraw(); + } + } + + ret = 1; + break; + } + + case FL_RELEASE: { + if (draggedPoint != -1) { + + if (points.at(draggedPoint).x == previousXPoint) { + //gLog("nothing to do\n"); + } + else { + int newFrame = points.at(draggedPoint).x * pParent->zoom; + + /* x edge correction */ + + if (newFrame < 0) + newFrame = 0; + else if (newFrame > G_Mixer.totalFrames) + newFrame = G_Mixer.totalFrames; + + /* vertical line check */ + + int vp = verticalPoint(points.at(draggedPoint)); + if (vp == 1) newFrame -= 256; + else if (vp == -1) newFrame += 256; + + /* delete previous point and record a new one */ + + recorder::deleteAction(pParent->chan->index, points.at(draggedPoint).frame, type, false); + + if (range == RANGE_FLOAT) { + float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8); + recorder::rec(pParent->chan->index, type, newFrame, 0, value); + } + else { + /// TODO + } + + recorder::sortActions(); + points.at(draggedPoint).frame = newFrame; + draggedPoint = -1; + selectedPoint = -1; + } + } + ret = 1; + break; + } + + case FL_DRAG: { + + if (draggedPoint != -1) { + + /* y constraint */ + + if (my > h()-8) + points.at(draggedPoint).y = h()-8; + else + if (my < 1) + points.at(draggedPoint).y = 1; + else + points.at(draggedPoint).y = my; + + /* x constraint + * constrain the point between two ends (leftBorder-point, point-point, + * point-rightBorder). First & last points cannot be shifted on x */ + + if (draggedPoint == 0) + points.at(draggedPoint).x = x()-8; + else + if ((unsigned) draggedPoint == points.size-1) + points.at(draggedPoint).x = pParent->coverX; + else { + int prevPoint = points.at(draggedPoint-1).x; + int nextPoint = points.at(draggedPoint+1).x; + if (mx <= prevPoint) + points.at(draggedPoint).x = prevPoint; + else + if (mx >= nextPoint) + points.at(draggedPoint).x = nextPoint; + //else + // points.at(draggedPoint).x = mx; + else { + if (pParent->gridTool->isOn()) + points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1; + else + points.at(draggedPoint).x = mx; + } + } + redraw(); + } + + ret = 1; + break; + } + } + + return ret; +} + + +/* ------------------------------------------------------------------ */ + + +int gEnvelopeChannel::verticalPoint(const point &p) { + for (unsigned i=0; i points.at(i).x) + points.swap(j, i); +} + + +/* ------------------------------------------------------------------ */ + + +int gEnvelopeChannel::getSelectedPoint() { + + /* point is a 7x7 dot */ + + for (unsigned i=0; i= points.at(i).x+x()-4 && + Fl::event_x() <= points.at(i).x+x()+4 && + Fl::event_y() >= points.at(i).y+y() && + Fl::event_y() <= points.at(i).y+y()+7) + return i; + } + return -1; +} + + +/* ------------------------------------------------------------------ */ + + +void gEnvelopeChannel::fill() { + points.clear(); + for (unsigned i=0; itype == type && a->chan == pParent->chan->index) { + if (range == RANGE_FLOAT) + addPoint( + a->frame, // frame + 0, // int value (unused) + a->fValue, // float value + a->frame / pParent->zoom, // x + ((1-h()+8)*a->fValue)+h()-8); // y = (b-a)x + a (line between two points) + // else: TODO + } + } + +} diff --git a/src/gui/elems/ge_envelopeChannel.h b/src/gui/elems/ge_envelopeChannel.h new file mode 100644 index 0000000..0bb7039 --- /dev/null +++ b/src/gui/elems/ge_envelopeChannel.h @@ -0,0 +1,115 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_envelopeWidget + * + * parent class of any envelope controller, from volume to VST parameter + * automations. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef __GE_ENVELOPECHANNEL_H__ +#define __GE_ENVELOPECHANNEL_H__ + +#include +#include +#include "../../utils/utils.h" +#include "ge_actionWidget.h" + + +class gEnvelopeChannel : public gActionWidget { + + const char *l; // internal label + int type; // type of action + int range; + + /* point + * a single dot in the graph. x = relative frame, y = relative value */ + + struct point { + int frame; + int iValue; + float fValue; + int x; + int y; + }; + + /* points + * array of points, filled by fillPoints() */ + + gVector points; + + /* selectedPoint + * which point we are selecting? */ + + int selectedPoint; + + /* draggedPoint + * which point we are dragging? */ + + int draggedPoint; + + /* previousXPoint + * x coordinate of point at time t-1. Used to check effective shifts */ + + int previousXPoint; + + void draw(); + + int handle(int e); + + int getSelectedPoint(); + + void sortPoints(); + + /* verticalPoint + * check if two points form a vertical line. In that case the frame value + * would be the same and recorder would go crazy, so shift by a small value + * of frames to create a minimal fadein/fadeout level. Return 0: no + * vertical points; return 1: vertical with the next one, return -1: vertical + * with the previous one. */ + + int verticalPoint(const point &p); + +public: + gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l); + ~gEnvelopeChannel(); + + /* addPoint + * add a point made of frame+value to internal points[]. */ + + void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1); + + void updateActions(); + + /* fill + * parse recorder's stack and fill the widget with points. It's up to + * the caller to call this method as initialization. */ + + void fill(); + + inline void clearPoints() { points.clear(); } +}; + +#endif diff --git a/src/gui/elems/ge_keyboard.cpp b/src/gui/elems/ge_keyboard.cpp new file mode 100644 index 0000000..24d47bd --- /dev/null +++ b/src/gui/elems/ge_keyboard.cpp @@ -0,0 +1,366 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gg_keyboard + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/mixer.h" +#include "../../core/conf.h" +#include "../../core/const.h" +#include "../../core/patch.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../glue/glue.h" +#include "../../utils/log.h" +#include "../dialogs/gd_browser.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_editor.h" +#include "../dialogs/gd_warnings.h" +#include "ge_channel.h" +#include "ge_sampleChannel.h" +#include "ge_keyboard.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Patch G_Patch; +extern gdMainWindow *mainWin; + + +int gKeyboard::indexColumn = 0; + + +/* -------------------------------------------------------------------------- */ + + +gKeyboard::gKeyboard(int X, int Y, int W, int H) +: Fl_Scroll (X, Y, W, H), + bckspcPressed(false), + endPressed (false), + spacePressed (false), + addColumnBtn (NULL) +{ + color(COLOR_BG_MAIN); + type(Fl_Scroll::BOTH_ALWAYS); + scrollbar.color(COLOR_BG_0); + scrollbar.selection_color(COLOR_BG_1); + scrollbar.labelcolor(COLOR_BD_1); + scrollbar.slider(G_BOX); + hscrollbar.color(COLOR_BG_0); + hscrollbar.selection_color(COLOR_BG_1); + hscrollbar.labelcolor(COLOR_BD_1); + hscrollbar.slider(G_BOX); + + addColumnBtn = new gClick(8, y(), 200, 20, "Add new column"); + addColumnBtn->callback(cb_addColumn, (void*) this); + add(addColumnBtn); + + init(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::init() +{ + /* add 6 empty columns as init layout */ + + __cb_addColumn(); + __cb_addColumn(); + __cb_addColumn(); + __cb_addColumn(); + __cb_addColumn(); + __cb_addColumn(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::freeChannel(gChannel *gch) +{ + gch->reset(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::deleteChannel(gChannel *gch) +{ + for (unsigned i=0; ifind(gch); + if (k != columns.at(i)->children()) { + columns.at(i)->deleteChannel(gch); + return; + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::updateChannel(gChannel *gch) +{ + gch->update(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::organizeColumns() +{ + /* if only one column exists don't cleanup: the initial column must + * stay here. */ + + if (columns.size == 1) + return; + + /* otherwise delete all empty columns */ + /** FIXME - this for loop might not work correctly! */ + + for (unsigned i=columns.size-1; i>=1; i--) { + if (columns.at(i)->isEmpty()) { + //Fl::delete_widget(columns.at(i)); + delete columns.at(i); + columns.del(i); + } + } + + /* compact column, avoid empty spaces */ + + for (unsigned i=1; iposition(columns.at(i-1)->x() + columns.at(i-1)->w() + 16, y()); + + addColumnBtn->position(columns.last()->x() + columns.last()->w() + 16, y()); + + redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::cb_addColumn(Fl_Widget *v, void *p) { ((gKeyboard*)p)->__cb_addColumn(); } + + +/* -------------------------------------------------------------------------- */ + + +gChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build) +{ + gColumn *col = getColumn(colIndex); + + /* no column with index 'colIndex' found? Just create it and set its index + to 'colIndex'. */ + + if (!col) { + __cb_addColumn(); + col = columns.last(); + col->setIndex(colIndex); + gLog("[gKeyboard::addChannel] created new column with index=%d\n", colIndex); + } + + gLog("[gKeyboard::addChannel] add to column with index = %d\n", col->getIndex()); + return col->addChannel(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::refreshColumns() +{ + for (unsigned i=0; irefreshChannels(); +} + + +/* -------------------------------------------------------------------------- */ + + +gColumn *gKeyboard::getColumn(int index) +{ + for (unsigned i=0; igetIndex() == index) + return columns.at(i); + return NULL; +} + + +/* -------------------------------------------------------------------------- */ + + +int gKeyboard::handle(int e) +{ + int ret = Fl_Group::handle(e); // assume the buttons won't handle the Keyboard events + switch (e) { + case FL_FOCUS: + case FL_UNFOCUS: { + ret = 1; // enables receiving Keyboard events + break; + } + case FL_SHORTCUT: // in case widget that isn't ours has focus + case FL_KEYDOWN: // Keyboard key pushed + case FL_KEYUP: { // Keyboard key released + + /* rewind session. Avoid retrigs */ + + if (e == FL_KEYDOWN) { + if (Fl::event_key() == FL_BackSpace && !bckspcPressed) { + bckspcPressed = true; + glue_rewindSeq(); + ret = 1; + break; + } + else if (Fl::event_key() == FL_End && !endPressed) { + endPressed = true; + glue_startStopInputRec(false); // update gui + ret = 1; + break; + } + else if (Fl::event_key() == FL_Enter && !enterPressed) { + enterPressed = true; + glue_startStopActionRec(); + ret = 1; + break; + } + else if (Fl::event_key() == ' ' && !spacePressed) { + spacePressed = true; + G_Mixer.running ? glue_stopSeq() : glue_startSeq(); // TODO - glue_startStopSeq, no core logic here + ret = 1; + break; + } + } + else if (e == FL_KEYUP) { + if (Fl::event_key() == FL_BackSpace) + bckspcPressed = false; + else if (Fl::event_key() == FL_End) + endPressed = false; + else if (Fl::event_key() == ' ') + spacePressed = false; + else if (Fl::event_key() == FL_Enter) + enterPressed = false; + } + + /* Walk button arrays, trying to match button's label with the Keyboard event. + * If found, set that button's value() based on up/down event, + * and invoke that button's callback() */ + + for (unsigned i=0; ichildren(); k++) + ret &= ((gChannel*)columns.at(i)->child(k))->keyPress(e); + break; + } + } + return ret; +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::clear() +{ + for (unsigned i=0; iposition(8, y()); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::setChannelWithActions(gSampleChannel *gch) +{ + if (gch->ch->hasActions) + gch->addActionButton(); + else + gch->delActionButton(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::printChannelMessage(int res) +{ + if (res == SAMPLE_NOT_VALID) + gdAlert("This is not a valid WAVE file."); + else if (res == SAMPLE_MULTICHANNEL) + gdAlert("Multichannel samples not supported."); + else if (res == SAMPLE_WRONG_BIT) + gdAlert("This sample has an\nunsupported bit-depth (> 32 bit)."); + else if (res == SAMPLE_WRONG_ENDIAN) + gdAlert("This sample has a wrong\nbyte order (not little-endian)."); + else if (res == SAMPLE_WRONG_FORMAT) + gdAlert("This sample is encoded in\nan unsupported audio format."); + else if (res == SAMPLE_READ_ERROR) + gdAlert("Unable to read this sample."); + else if (res == SAMPLE_PATH_TOO_LONG) + gdAlert("File path too long."); + else + gdAlert("Unknown error."); +} + +/* -------------------------------------------------------------------------- */ + + +void gKeyboard::__cb_addColumn() +{ + int colx; + int colxw; + int colw = 380; + if (columns.size == 0) { + colx = x() - xposition(); // mind the offset with xposition() + colxw = colx + colw; + } + else { + gColumn *prev = columns.last(); + colx = prev->x()+prev->w() + 16; + colxw = colx + colw; + } + + /* add gColumn to gKeyboard and to columns vector */ + + gColumn *gc = new gColumn(colx, y(), colw-20, 2000, indexColumn, this); + add(gc); + columns.add(gc); + indexColumn++; + + /* move addColumn button */ + + addColumnBtn->position(colxw-4, y()); + redraw(); + + gLog("[gKeyboard::__cb_addColumn] new column added (index = %d), total count=%d, addColumn(x)=%d\n", + gc->getIndex(), columns.size, addColumnBtn->x()); +} diff --git a/src/gui/elems/ge_keyboard.h b/src/gui/elems/ge_keyboard.h new file mode 100644 index 0000000..be79c39 --- /dev/null +++ b/src/gui/elems/ge_keyboard.h @@ -0,0 +1,145 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gg_keyboard + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_KEYBOARD_H +#define GE_KEYBOARD_H + + +#include +#include +#include +#include +#include +#include "../elems/ge_column.h" +#include "../../utils/utils.h" + + +class gKeyboard : public Fl_Scroll +{ +private: + + static void cb_addColumn (Fl_Widget *v, void *p); + inline void __cb_addColumn(); + + bool bckspcPressed; + bool endPressed; + bool spacePressed; + bool enterPressed; + + /* indexColumn + * the last index used for column. */ + + static int indexColumn; + + class gClick *addColumnBtn; + + /* columns + * a vector of columns which in turn contain channels. */ + + gVector columns; + +public: + + gKeyboard(int X, int Y, int W, int H); + + int handle(int e); + + /* init + * build the initial setup of empty channels. */ + + void init(); + + /* addChannel + * add a new channel to gChannels. Used by callbacks and during + * patch loading. Requires Channel (and not gChannel). If build is + * set to true, also generate the corresponding column.*/ + + gChannel *addChannel(int column, class Channel *ch, bool build=false); + + /* deleteChannel + * delete a channel from gChannels<> where gChannel->ch == ch and remove + * it from the stack. */ + + void deleteChannel(gChannel *gch); + + /* freeChannel + * free a channel from gChannels<> where gChannel->ch == ch. No channels + * are deleted */ + + void freeChannel(gChannel *gch); + + /* updateChannel + * wrapper function to call gch->update(). */ + + void updateChannel(gChannel *gch); + + /* organizeColumns + * reorganize columns layout by removing empty gaps. */ + + void organizeColumns(); + + /* refreshColumns + * refresh each column's channel, called on each GUI cycle. */ + + void refreshColumns(); + + /* getColumn + * return the column with index 'index', or NULL if not found. */ + + gColumn *getColumn(int index); + + /* clear + * delete all channels and groups. */ + + void clear(); + + /* setChannelWithActions + * add 'R' button if channel has actions, and set recorder to active. */ + + void setChannelWithActions(class gSampleChannel *gch); + + /* printChannelMessage + * given any output by glue_loadChannel, print the message on screen + * on a gdAlert subwindow. */ + + void printChannelMessage(int res); + + /* getTotalColumns */ + + inline unsigned getTotalColumns() { return columns.size; } + + /* getColumnWidth + * return the width in pixel of i-th column. Warning: 'i' is the i-th column + * in the column array, NOT the index. */ + + inline int getColumnWidth(int i) { return getColumn(i)->w(); } +}; + + +#endif diff --git a/src/gui/elems/ge_midiChannel.cpp b/src/gui/elems/ge_midiChannel.cpp new file mode 100644 index 0000000..68ba4e7 --- /dev/null +++ b/src/gui/elems/ge_midiChannel.cpp @@ -0,0 +1,340 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_midiChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/pluginHost.h" +#include "../../core/mixer.h" +#include "../../core/conf.h" +#include "../../core/patch.h" +#include "../../core/graphics.h" +#include "../../core/channel.h" +#include "../../core/wave.h" +#include "../../core/sampleChannel.h" +#include "../../core/midiChannel.h" +#include "../../glue/glue.h" +#include "../../utils/gui_utils.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_keyGrabber.h" +#include "../dialogs/gd_midiInput.h" +#include "../dialogs/gd_editor.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_warnings.h" +#include "../dialogs/gd_browser.h" +#include "../dialogs/gd_keyGrabber.h" +#include "../dialogs/gd_midiOutput.h" +#include "ge_keyboard.h" +#include "ge_midiChannel.h" +#include "ge_channel.h" +#include "ge_sampleChannel.h" + +#ifdef WITH_VST +#include "../dialogs/gd_pluginList.h" +#endif + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Patch G_Patch; +extern gdMainWindow *mainWin; + + +gMidiChannel::gMidiChannel(int X, int Y, int W, int H, class MidiChannel *ch) + : gChannel(X, Y, W, H, CHANNEL_MIDI), ch(ch) +{ + begin(); + +#if defined(WITH_VST) + int delta = 120; // (5 widgets * 20) + (5 paddings * 4) +#else + int delta = 96; // (4 widgets * 20) + (4 paddings * 4) +#endif + + button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); + mainButton = new gMidiChannelButton(button->x()+button->w()+4, y(), w() - delta, 20, "-- MIDI --"); + mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); + solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); +#if defined(WITH_VST) + fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); + vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); +#else + vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); +#endif + + end(); + + resizable(mainButton); + + update(); + + button->callback(cb_button, (void*)this); + button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease + +#ifdef WITH_VST + fx->callback(cb_openFxWindow, (void*)this); +#endif + + mute->type(FL_TOGGLE_BUTTON); + mute->callback(cb_mute, (void*)this); + + solo->type(FL_TOGGLE_BUTTON); + solo->callback(cb_solo, (void*)this); + + mainButton->callback(cb_openMenu, (void*)this); + vol->callback(cb_changeVol, (void*)this); + + ch->guiChannel = this; +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::cb_button (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_button(); } +void gMidiChannel::cb_mute (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_mute(); } +void gMidiChannel::cb_solo (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_solo(); } +void gMidiChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openMenu(); } +void gMidiChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_changeVol(); } +#ifdef WITH_VST +void gMidiChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openFxWindow(); } +#endif + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::__cb_mute() +{ + glue_setMute(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::__cb_solo() +{ + solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::__cb_changeVol() +{ + glue_setChanVol(ch, vol->value()); +} + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST +void gMidiChannel::__cb_openFxWindow() +{ + gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST); +} +#endif + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::__cb_button() +{ + if (button->value()) + glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::__cb_openMenu() +{ + Fl_Menu_Item rclick_menu[] = { + {"Edit actions..."}, // 0 + {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1 + {"All"}, // 2 + {0}, // 3 + {"Setup keyboard input..."}, // 5 + {"Setup MIDI input..."}, // 6 + {"Setup MIDI output..."}, // 7 + {"Delete channel"}, // 8 + {0} + }; + + /* no 'clear actions' if there are no actions */ + + if (!ch->hasActions) + rclick_menu[1].deactivate(); + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(11); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) return; + + if (strcmp(m->label(), "Delete channel") == 0) { + if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) + return; + glue_deleteChannel(ch); + return; + } + + if (strcmp(m->label(), "Setup keyboard input...") == 0) { + gu_openSubWindow(mainWin, new gdKeyGrabber(ch), 0); + //new gdKeyGrabber(ch); + return; + } + + if (strcmp(m->label(), "All") == 0) { + if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) + return; + recorder::clearChan(ch->index); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Edit actions...") == 0) { + gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); + return; + } + + if (strcmp(m->label(), "Setup MIDI input...") == 0) { + gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0); + return; + } + + if (strcmp(m->label(), "Setup MIDI output...") == 0) { + //gu_openSubWindow(mainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0); + gu_openSubWindow(mainWin, new gdMidiOutputMidiCh(ch), 0); + return; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::refresh() +{ + setColorsByStatus(ch->status, ch->recStatus); + mainButton->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::reset() +{ + mainButton->setDefaultMode("-- MIDI --"); + mainButton->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::update() +{ + if (ch->midiOut) { + char tmp[32]; + sprintf(tmp, "-- MIDI (channel %d) --", ch->midiOutChan+1); + mainButton->copy_label(tmp); + } + else + mainButton->label("-- MIDI --"); + + vol->value(ch->volume); + mute->value(ch->mute); + solo->value(ch->solo); + +#ifdef WITH_VST + fx->full = ch->plugins.size > 0; +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void gMidiChannel::resize(int X, int Y, int W, int H) +{ + gChannel::resize(X, Y, W, H); + + /* this stuff makes sense only with FX button available. Do nothing + * otherwise */ + +#ifdef WITH_VST + if (w() < BREAK_FX) { + fx->hide(); + + mainButton->size(w() - (BREAK_DELTA - BREAK_UNIT), mainButton->h()); + } + else { + fx->show(); + mainButton->size(w() - BREAK_DELTA, mainButton->h()); + } + mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); + solo->resize(mute->x()+mute->w()+4, y(), 20, 20); + + gChannel::init_sizes(); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +int gMidiChannel::keyPress(int e) +{ + return handleKey(e, ch->key); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gMidiChannelButton::gMidiChannelButton(int x, int y, int w, int h, const char *l) + : gChannelButton(x, y, w, h, l) {} + + +/* -------------------------------------------------------------------------- */ + + +int gMidiChannelButton::handle(int e) +{ + // MIDI drag-n-drop does nothing so far. + return gClick::handle(e); +} diff --git a/src/gui/elems/ge_midiChannel.h b/src/gui/elems/ge_midiChannel.h new file mode 100644 index 0000000..d08669d --- /dev/null +++ b/src/gui/elems/ge_midiChannel.h @@ -0,0 +1,90 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_midiChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_MIDI_CHANNEL_H +#define GE_MIDI_CHANNEL_H + + +#include +#include +#include +#include "ge_channel.h" +#include "ge_channelButton.h" +#include "ge_mixed.h" + + +class gMidiChannel : public gChannel +{ +private: + + static void cb_button (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_openMenu (Fl_Widget *v, void *p); + static void cb_changeVol (Fl_Widget *v, void *p); +#ifdef WITH_VST + static void cb_openFxWindow (Fl_Widget *v, void *p); +#endif + + inline void __cb_mute (); + inline void __cb_solo (); + inline void __cb_changeVol (); + inline void __cb_button (); + inline void __cb_openMenu (); + inline void __cb_readActions (); +#ifdef WITH_VST + inline void __cb_openFxWindow(); +#endif + +public: + + gMidiChannel(int x, int y, int w, int h, class MidiChannel *ch); + + void reset (); + void update (); + void refresh (); + int keyPress(int event); + void resize (int x, int y, int w, int h); + + class MidiChannel *ch; +}; + + +/* -------------------------------------------------------------------------- */ + + +class gMidiChannelButton : public gChannelButton +{ +public: + gMidiChannelButton(int x, int y, int w, int h, const char *l=0); + int handle(int e); +}; + + +#endif diff --git a/src/gui/elems/ge_midiIoTools.cpp b/src/gui/elems/ge_midiIoTools.cpp new file mode 100644 index 0000000..da9b873 --- /dev/null +++ b/src/gui/elems/ge_midiIoTools.cpp @@ -0,0 +1,105 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_midiIoTools + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "ge_midiIoTools.h" +#include "ge_mixed.h" + + +gLearner::gLearner(int X, int Y, int W, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param) + : Fl_Group(X, Y, W, 20), + callback(cb), + param (param) +{ + begin(); + text = new gBox(x(), y(), 156, 20, l); + value = new gClick(text->x()+text->w()+4, y(), 80, 20, "(not set)"); + button = new gButton(value->x()+value->w()+4, y(), 40, 20, "learn"); + end(); + + text->box(G_BOX); + text->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + + value->box(G_BOX); + value->callback(cb_value, (void*)this); + value->when(FL_WHEN_RELEASE); + updateValue(); + + button->type(FL_TOGGLE_BUTTON); + button->callback(cb_button, (void*)this); +} + + +/* -------------------------------------------------------------------------- */ + + +void gLearner::updateValue() { + char buf[16]; + if (*param != 0x0) + snprintf(buf, 9, "0x%X", *param); + else + snprintf(buf, 16, "(not set)"); + value->copy_label(buf); + button->value(0); +} + + +/* -------------------------------------------------------------------------- */ + + +void gLearner::cb_button(Fl_Widget *v, void *p) { ((gLearner*)p)->__cb_button(); } +void gLearner::cb_value(Fl_Widget *v, void *p) { ((gLearner*)p)->__cb_value(); } + + +/* -------------------------------------------------------------------------- */ + + +void gLearner::__cb_value() { + if (Fl::event_button() == FL_RIGHT_MOUSE) { + *param = 0x0; + updateValue(); + } + /// TODO - elif (LEFT_MOUSE) : insert values by hand +} + + +/* -------------------------------------------------------------------------- */ + + +/* FIXME - do not malloc on each callback! do it on the constructor! */ + +void gLearner::__cb_button() { + if (button->value() == 1) { + cbData *data = (cbData*) malloc(sizeof(cbData)); + data->window = (gdMidiInput*) parent(); // parent = gdMidiGrabberChannel + data->learner = this; + kernelMidi::startMidiLearn(callback, (void*)data); + } + else + kernelMidi::stopMidiLearn(); +} diff --git a/src/gui/elems/ge_midiIoTools.h b/src/gui/elems/ge_midiIoTools.h new file mode 100644 index 0000000..abec2df --- /dev/null +++ b/src/gui/elems/ge_midiIoTools.h @@ -0,0 +1,92 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_midiIoTools + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_LEARNER_H +#define GE_LEARNER_H + + +#include +#include +#include "../../core/kernelMidi.h" +#include "../dialogs/gd_midiInput.h" + + +class gLearner : public Fl_Group +{ +private: + + /* callback + * cb to pass to kernelMidi. Requires two parameters: + * uint32_t msg - MIDI message + * void *data - extra data */ + + kernelMidi::cb_midiLearn *callback; + + class gBox *text; + class gClick *value; + class gButton *button; + + static void cb_button(Fl_Widget *v, void *p); + static void cb_value (Fl_Widget *v, void *p); + inline void __cb_button(); + inline void __cb_value(); + +public: + + /* param + * pointer to ch->midiIn[value] */ + + uint32_t *param; + + gLearner(int x, int y, int w, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param); + + void updateValue(); +}; + + +/* -------------------------------------------------------------------------- */ + + +/* cbData + * struct we pass to kernelMidi as extra parameter. Local scope made + * with unnamed namespace. Infos: + * http://stackoverflow.com/questions/4422507/superiority-of-unnamed-namespace-over-static */ + +/* TODO - instead of the unnamed namespace, why don't we make the struct as a +(static) member of gLearner? */ + +namespace { + struct cbData { + gdMidiInput *window; + gLearner *learner; + }; +} + + +#endif diff --git a/src/gui/elems/ge_mixed.cpp b/src/gui/elems/ge_mixed.cpp new file mode 100644 index 0000000..ef166a6 --- /dev/null +++ b/src/gui/elems/ge_mixed.cpp @@ -0,0 +1,666 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_mixed + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include "../../core/const.h" +#include "../../core/mixer.h" +#include "../../core/graphics.h" +#include "../../core/recorder.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../utils/gui_utils.h" +#include "../dialogs/gd_mainWindow.h" +#include "ge_mixed.h" + + +extern Mixer G_Mixer; +extern unsigned G_beats; +extern bool G_audio_status; +extern gdMainWindow *mainWin; + + +void __cb_window_closer(Fl_Widget *v, void *p) +{ + delete (Fl_Window*)p; +} + + +/* -------------------------------------------------------------------------- */ + + +gButton::gButton(int X, int Y, int W, int H, const char *L, const char **imgOff, const char **imgOn) + : gClick(X, Y, W, H, L, imgOff, imgOn) {} + + +/* -------------------------------------------------------------------------- */ + + +gClick::gClick(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn) +: gBaseButton(x, y, w, h, L), + imgOff(imgOff), + imgOn(imgOn), + bgColor0(COLOR_BG_0), + bgColor1(COLOR_BG_1), + bdColor(COLOR_BD_0), + txtColor(COLOR_TEXT_0) {} + +void gClick::draw() +{ + if (!active()) txtColor = bdColor; + else txtColor = COLOR_TEXT_0; + + fl_rect(x(), y(), w(), h(), bdColor); // borders + if (value()) { // -- clicked + if (imgOn != NULL) + fl_draw_pixmap(imgOn, x()+1, y()+1); + else + fl_rectf(x(), y(), w(), h(), bgColor1); // covers the border + } + else { // -- not clicked + fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor0); // bg inside the border + if (imgOff != NULL) + fl_draw_pixmap(imgOff, x()+1, y()+1); + } + if (!active()) + fl_color(FL_INACTIVE_COLOR); + + fl_color(txtColor); + fl_font(FL_HELVETICA, 11); + fl_draw(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER); +} + + +/* -------------------------------------------------------------------------- */ + + +gClickRepeat::gClickRepeat(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn) +: Fl_Repeat_Button(x, y, w, h, L), imgOff(imgOff), imgOn(imgOn) {} + +void gClickRepeat::draw() +{ + if (value()) { // -- clicked + fl_rectf(x(), y(), w(), h(), COLOR_BG_1); // bg + if (imgOn != NULL) + fl_draw_pixmap(imgOn, x()+1, y()+1); + } + else { // -- not clicked + fl_rectf(x(), y(), w(), h(), COLOR_BG_0); // bg + fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border + if (imgOff != NULL) + fl_draw_pixmap(imgOff, x()+1, y()+1); + } + if (!active()) + fl_color(FL_INACTIVE_COLOR); + + fl_color(COLOR_TEXT_0); + fl_font(FL_HELVETICA, 11); + fl_draw(label(), x(), y(), w(), h(), FL_ALIGN_CENTER); +} + + +/* -------------------------------------------------------------------------- */ + + +gInput::gInput(int x, int y, int w, int h, const char *L) +: Fl_Input(x, y, w, h, L) +{ + //Fl::set_boxtype(G_BOX, gDrawBox, 1, 1, 2, 2); + box(G_BOX); + labelsize(11); + labelcolor(COLOR_TEXT_0); + color(COLOR_BG_DARK); + textcolor(COLOR_TEXT_0); + cursor_color(COLOR_TEXT_0); + selection_color(COLOR_BD_0); + textsize(11); + +} + + +/* -------------------------------------------------------------------------- */ + + +gDial::gDial(int x, int y, int w, int h, const char *L) +: Fl_Dial(x, y, w, h, L) +{ + labelsize(11); + labelcolor(COLOR_TEXT_0); + align(FL_ALIGN_LEFT); + type(FL_FILL_DIAL); + angles(0, 360); + color(COLOR_BG_0); // background + selection_color(COLOR_BG_1); // selection +} + +void gDial::draw() +{ + double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1(); + + fl_color(COLOR_BG_0); + fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle); + + fl_color(COLOR_BD_0); + fl_arc(x(), y(), w(), h(), 0, 360); + fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1()); +} + +/* -------------------------------------------------------------------------- */ + + +gBox::gBox(int x, int y, int w, int h, const char *L, Fl_Align al) +: Fl_Box(x, y, w, h, L) +{ + labelsize(11); + box(FL_NO_BOX); + labelcolor(COLOR_TEXT_0); + if (al != 0) + align(al | FL_ALIGN_INSIDE); +} + + +/* -------------------------------------------------------------------------- */ + + +gCheck::gCheck(int x, int y, int w, int h, const char *L) +: Fl_Check_Button(x, y, w, h, L) {} + +void gCheck::draw() +{ + int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0; + + if (value()) { + fl_rect(x(), y(), 12, 12, (Fl_Color) color); + fl_rectf(x(), y(), 12, 12, (Fl_Color) color); + } + else { + fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR); + fl_rect(x(), y(), 12, 12, (Fl_Color) color); + } + + fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer + fl_font(FL_HELVETICA, 11); + fl_color(COLOR_TEXT_0); + fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); +} + + +/* -------------------------------------------------------------------------- */ + + +gRadio::gRadio(int x, int y, int w, int h, const char *L) +: Fl_Radio_Button(x, y, w, h, L) {} + +void gRadio::draw() +{ + int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0; + + if (value()) { + fl_rect(x(), y(), 12, 12, (Fl_Color) color); + fl_rectf(x(), y(), 12, 12, (Fl_Color) color); + } + else { + fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR); + fl_rect(x(), y(), 12, 12, (Fl_Color) color); + } + + fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer + fl_font(FL_HELVETICA, 11); + fl_color(COLOR_TEXT_0); + fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); +} + + +/* -------------------------------------------------------------------------- */ + + +gProgress::gProgress(int x, int y, int w, int h, const char *L) +: Fl_Progress(x, y, w, h, L) { + color(COLOR_BG_0, COLOR_BD_0); + box(G_BOX); + +} + + +/* -------------------------------------------------------------------------- */ + + +gSoundMeter::gSoundMeter(int x, int y, int w, int h, const char *L) + : Fl_Box(x, y, w, h, L), + clip(false), + mixerPeak(0.0f), + peak(0.0f), + peak_old(0.0f), + db_level(0.0f), + db_level_old(0.0f) {} + +void gSoundMeter::draw() +{ + fl_rect(x(), y(), w(), h(), COLOR_BD_0); + + /* peak = the highest value inside the frame */ + + peak = 0.0f; + float tmp_peak = 0.0f; + + tmp_peak = fabs(mixerPeak); + if (tmp_peak > peak) + peak = tmp_peak; + + clip = peak >= 1.0f ? true : false; // 1.0f is considered clip + + + /* dBFS (full scale) calculation, plus decay of -2dB per frame */ + + db_level = 20 * log10(peak); + if (db_level < db_level_old) + if (db_level_old > -DB_MIN_SCALE) + db_level = db_level_old - 2.0f; + + db_level_old = db_level; + + /* graphical part */ + + float px_level = 0.0f; + if (db_level < 0.0f) + px_level = ((w()/DB_MIN_SCALE) * db_level) + w(); + else + px_level = w(); + + fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); + fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !G_audio_status ? COLOR_ALERT : COLOR_BD_0); +} + +/* -------------------------------------------------------------------------- */ + +gBeatMeter::gBeatMeter(int x, int y, int w, int h, const char *L) + : Fl_Box(x, y, w, h, L) {} + +void gBeatMeter::draw() +{ + int cursorW = w() / MAX_BEATS; + int greyX = G_Mixer.beats * cursorW; + + fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border + fl_rectf(x()+1, y()+1, w()-2, h()-2, FL_BACKGROUND_COLOR); // bg + fl_rectf(x()+(G_Mixer.actualBeat*cursorW)+3, y()+3, cursorW-5, h()-6, COLOR_BG_2); // cursor + + /* beat cells */ + + fl_color(COLOR_BD_0); + for (int i=1; i<=G_Mixer.beats; i++) + fl_line(x()+cursorW*i, y()+1, x()+cursorW*i, y()+h()-2); + + /* bar line */ + + fl_color(COLOR_BG_2); + int delta = G_Mixer.beats / G_Mixer.bars; + for (int i=1; i= w()-16) { + tmp.resize(size); + size--; + } + tmp += "..."; + fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER); + } + + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gDrawBox(int x, int y, int w, int h, Fl_Color c) +{ + fl_color(c); + fl_rectf(x, y, w, h); + fl_color(COLOR_BD_0); + fl_rect(x, y, w, h); +} + + +/* -------------------------------------------------------------------------- */ + + +gLiquidScroll::gLiquidScroll(int x, int y, int w, int h, const char *l) + : Fl_Scroll(x, y, w, h, l) +{ + type(Fl_Scroll::VERTICAL); + scrollbar.color(COLOR_BG_0); + scrollbar.selection_color(COLOR_BG_1); + scrollbar.labelcolor(COLOR_BD_1); + scrollbar.slider(G_BOX); +} + + +void gLiquidScroll::resize(int X, int Y, int W, int H) +{ + int nc = children()-2; // skip hscrollbar and vscrollbar + for ( int t=0; tresize(c->x(), c->y(), W-24, c->h()); // W-24: leave room for scrollbar + } + init_sizes(); // tell scroll children changed in size + Fl_Scroll::resize(X,Y,W,H); +} + + +/* -------------------------------------------------------------------------- */ + + +gSlider::gSlider(int x, int y, int w, int h, const char *l) + : Fl_Slider(x, y, w, h, l) +{ + type(FL_HOR_FILL_SLIDER); + + labelsize(11); + align(FL_ALIGN_LEFT); + labelcolor(COLOR_TEXT_0); + + box(G_BOX); + color(COLOR_BG_0); + selection_color(COLOR_BD_0); +} + + +/* -------------------------------------------------------------------------- */ + + +gResizerBar::gResizerBar(int X,int Y,int W,int H, bool vertical) + : Fl_Box(X,Y,W,H), vertical(vertical) +{ + last_y = 0; + min_h = 30; + if (vertical) { + orig_h = H; + labelsize(H); + } + else { + orig_h = W; + labelsize(W); + } + align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); + labelfont(FL_COURIER); + visible_focus(0); +} + +/* +gResizerBar::~gResizerBar() +{ + gLog("------ resizerbar %p destroyed\n", (void*)this); +} +*/ + +void gResizerBar::HandleDrag(int diff) +{ + Fl_Scroll *grp = (Fl_Scroll*)parent(); + int top; + int bot; + if (vertical) { + top = y(); + bot = y()+h(); + } + else { + top = x(); + bot = x()+w(); + } + + // First pass: find widget directly above us with common edge + // Possibly clamp 'diff' if widget would get too small.. + + for (int t=0; tchildren(); t++) { + Fl_Widget *wd = grp->child(t); + if (vertical) { + if ((wd->y()+wd->h()) == top) { // found widget directly above? + if ((wd->h()+diff) < min_h) + diff = wd->h() - min_h; // clamp + wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height + break; // done with first pass + } + } + else { + if ((wd->x()+wd->w()) == top) { // found widget directly above? + if ((wd->w()+diff) < min_h) + diff = wd->w() - min_h; // clamp + wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height + break; // done with first pass + } + } + } + + // Second pass: find widgets below us, move based on clamped diff + + for (int t=0; tchildren(); t++) { + Fl_Widget *wd = grp->child(t); + if (vertical) { + if (wd->y() >= bot) // found widget below us? + wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position + } + else { + if (wd->x() >= bot) + wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h()); + } + } + + // Change our position last + + if (vertical) + resize(x(), y()+diff, w(), h()); + else + resize(x()+diff, y(), w(), h()); + + grp->init_sizes(); + grp->redraw(); +} + + +int gResizerBar::handle(int e) +{ + int ret = 0; + int this_y; + if (vertical) + this_y = Fl::event_y_root(); + else + this_y = Fl::event_x_root(); + switch (e) { + case FL_FOCUS: + ret = 1; + break; + case FL_ENTER: + ret = 1; + fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE); + break; + case FL_LEAVE: + ret = 1; + fl_cursor(FL_CURSOR_DEFAULT); + break; + case FL_PUSH: + ret = 1; + last_y = this_y; + break; + case FL_DRAG: + HandleDrag(this_y-last_y); + last_y = this_y; + ret = 1; + break; + default: break; + } + return(Fl_Box::handle(e) | ret); +} + + +void gResizerBar::resize(int X,int Y,int W,int H) +{ + if (vertical) + Fl_Box::resize(X,Y,W,orig_h); // height of resizer stays constant size + else + Fl_Box::resize(X,Y,orig_h,H); +} + + +/* -------------------------------------------------------------------------- */ + + +gScroll::gScroll(int x, int y, int w, int h, int t) + : Fl_Scroll(x, y, w, h) +{ + type(t); + + scrollbar.color(COLOR_BG_0); + scrollbar.selection_color(COLOR_BG_1); + scrollbar.labelcolor(COLOR_BD_1); + scrollbar.slider(G_BOX); + + hscrollbar.color(COLOR_BG_0); + hscrollbar.selection_color(COLOR_BG_1); + hscrollbar.labelcolor(COLOR_BD_1); + hscrollbar.slider(G_BOX); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gBaseButton::gBaseButton(int x, int y, int w, int h, const char *l) + : Fl_Button(x, y, w, h, l) +{ + initLabel = l ? l : ""; +} + + +/* -------------------------------------------------------------------------- */ + + +void gBaseButton::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 gBaseButton::label(const char *l) +{ + Fl_Button::label(l); + initLabel = l; + trimLabel(); +} + +const char *gBaseButton::label() +{ + return Fl_Button::label(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gBaseButton::resize(int X, int Y, int W, int H) +{ + trimLabel(); + Fl_Button::resize(X, Y, W, H); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gFxButton::gFxButton(int x, int y, int w, int h, const char **imgOff, const char **imgOn) + : gClick(x, y, w, h, NULL, imgOff, imgOn), full(false) {} + + +/* -------------------------------------------------------------------------- */ + + +void gFxButton::draw() +{ + gClick::draw(); + if (full) + fl_draw_pixmap(imgOn, x()+1, y()+1, COLOR_BD_0); +} diff --git a/src/gui/elems/ge_mixed.h b/src/gui/elems/ge_mixed.h new file mode 100644 index 0000000..26942d5 --- /dev/null +++ b/src/gui/elems/ge_mixed.h @@ -0,0 +1,355 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_mixed + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_MIXED_H +#define GE_MIXED_H + +#include +#include +#include // for intptr_t +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include // for SHGetFolderPath +#endif + + +/* cb_window_closer + * callback for when closing windows. Deletes the widget (delete). */ + +void __cb_window_closer(Fl_Widget *v, void *p); + + +/* -------------------------------------------------------------------------- */ + + +class gBaseButton : public Fl_Button +{ +private: + std::string initLabel; + + void trimLabel(); + +public: + gBaseButton(int x, int y, int w, int h, const char *l=0); + void resize(int x, int y, int w, int h); + void label(const char *l); + const char *label(); +}; + + +/* -------------------------------------------------------------------------- */ + + +/* gClick + * a normal button. */ + +class gClick : public gBaseButton +{ +public: + gClick(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL); + void draw(); + const char **imgOff; + const char **imgOn; + Fl_Color bgColor0; // background not clicked + Fl_Color bgColor1; // background clicked + Fl_Color bdColor; // border + Fl_Color txtColor; // testo +}; + + +/* -------------------------------------------------------------------------- */ + + +class gClickRepeat : public Fl_Repeat_Button +{ +public: + gClickRepeat(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL); + void draw(); + const char **imgOff; + const char **imgOn; +}; + + +/* -------------------------------------------------------------------------- */ + + +/* gButton + * exactly as gClick but with a unique id inside of it. Used for the buttons in + * channels and for FXs. */ + +class gButton : public gClick +{ +public: + gButton(int X,int Y,int W,int H,const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL); + int key; + int id; +}; + + +/* -------------------------------------------------------------------------- */ + + +class gInput : public Fl_Input +{ +public: + gInput(int x, int y, int w, int h, const char *L=0); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gDial : public Fl_Dial +{ +public: + gDial(int x, int y, int w, int h, const char *L=0); + void draw(); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gBox : public Fl_Box +{ +public: + gBox(int x, int y, int w, int h, const char *L=0, Fl_Align al=FL_ALIGN_CENTER); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gCheck : public Fl_Check_Button +{ +public: + gCheck(int x, int y, int w, int h, const char *L=0); + void draw(); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gRadio : public Fl_Radio_Button +{ +public: + gRadio(int x, int y, int w, int h, const char *L=0); + void draw(); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gProgress : public Fl_Progress +{ +public: + gProgress(int x, int y, int w, int h, const char *L=0); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gSoundMeter : public Fl_Box +{ +public: + gSoundMeter(int X,int Y,int W,int H,const char *L=0); + void draw(); + bool clip; + float mixerPeak; // peak from mixer +private: + float peak; + float peak_old; + float db_level; + float db_level_old; +}; + + +/* -------------------------------------------------------------------------- */ + + +class gBeatMeter : public Fl_Box +{ +public: + gBeatMeter(int X,int Y,int W,int H,const char *L=0); + void draw(); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gChoice : public Fl_Choice +{ +public: + + gChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true); + void draw(); + + inline void show(const char *c) {value(find_index(c)); } + + bool angle; + int id; +}; + + +/* -------------------------------------------------------------------------- */ + + +/* gDrawBox + * custom boxes in FLTK. */ + +#define G_BOX FL_FREE_BOXTYPE +void gDrawBox(int x, int y, int w, int h, Fl_Color c); + + +/* -------------------------------------------------------------------------- */ + + +/* gLiquidScroll + * custom scroll that tells children to follow scroll's width when + * resized. Thanks to Greg Ercolano from FLTK dev team. + * http://seriss.com/people/erco/fltk/ */ + +class gLiquidScroll : public Fl_Scroll +{ +public: + gLiquidScroll(int x, int y, int w, int h, const char *l=0); + void resize(int x, int y, int w, int h); +}; + + +/* -------------------------------------------------------------------------- */ + + +/* gScroll + * custom scroll with nice scrollbars and something else. */ + +class gScroll : public Fl_Scroll +{ +public: + gScroll(int x, int y, int w, int h, int type=Fl_Scroll::BOTH); +}; + + +/* -------------------------------------------------------------------------- */ + +/* gResizerBar + * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from + * FLTK dev team. http://seriss.com/people/erco/fltk/ + * + * Shows a resize cursor when hovered over. + * Assumes: + * - Parent is an Fl_Scroll + * - All children of Fl_Scroll are vertically arranged + * - The widget above us has a bottom edge touching our top edge + * ie. (w->y()+w->h() == this->y()) + * + * When this widget is dragged: + * - The widget above us (with a common edge) will be /resized/ + * vertically + * - All children below us will be /moved/ vertically */ + +/* TODO - use more general variable names + * (last_y -> last_?, min_h -> min_?, ...) */ + +class gResizerBar : public Fl_Box +{ +private: + bool vertical; + int orig_h; + int last_y; + int min_h; // min height for widget above us + + void HandleDrag(int diff); + +public: + + /* 'vertical' defines the bar movement. Vertical=true: the bar moves + * vertically (up and down). */ + + gResizerBar(int x, int y, int w, int h, bool vertical=true); + //~gResizerBar(); + + inline void setMinSize(int val) { min_h = val; } + inline int getMinSize() { return min_h; } + + int handle(int e); + void resize(int x, int y, int w, int h); +}; + + +/* -------------------------------------------------------------------------- */ + + +class gSlider : public Fl_Slider +{ +public: + gSlider(int x, int y, int w, int h, const char *l=0); + int id; +}; + + +/* -------------------------------------------------------------------------- */ + + +/* gFxButton + * a simple gClick with 'full' parameter (i.e. has plugins). If 'full' is true, + * draw something somewhere. */ + +class gFxButton : public gClick +{ +public: + gFxButton(int x, int y, int w, int h, const char **imgOff=NULL, const char **imgOn=NULL); + void draw(); + bool full; +}; + + +#endif diff --git a/src/gui/elems/ge_modeBox.cpp b/src/gui/elems/ge_modeBox.cpp new file mode 100644 index 0000000..a4c13f5 --- /dev/null +++ b/src/gui/elems/ge_modeBox.cpp @@ -0,0 +1,109 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_modeBox + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../utils/gui_utils.h" +#include "../../core/graphics.h" +#include "../../core/sampleChannel.h" +#include "../../core/const.h" +#include "../dialogs/gd_mainWindow.h" +#include "ge_modeBox.h" + + +gModeBox::gModeBox(int x, int y, int w, int h, SampleChannel *ch, const char *L) + : Fl_Menu_Button(x, y, w, h, L), ch(ch) +{ + box(G_BOX); + textsize(11); + textcolor(COLOR_TEXT_0); + color(COLOR_BG_0); + + add("Loop . basic", 0, cb_changeMode, (void *)LOOP_BASIC); + add("Loop . once", 0, cb_changeMode, (void *)LOOP_ONCE); + add("Loop . once . bar", 0, cb_changeMode, (void *)LOOP_ONCE_BAR); + add("Loop . repeat", 0, cb_changeMode, (void *)LOOP_REPEAT); + add("Oneshot . basic", 0, cb_changeMode, (void *)SINGLE_BASIC); + add("Oneshot . press", 0, cb_changeMode, (void *)SINGLE_PRESS); + add("Oneshot . retrig", 0, cb_changeMode, (void *)SINGLE_RETRIG); + add("Oneshot . endless", 0, cb_changeMode, (void *)SINGLE_ENDLESS); +} + + +/* -------------------------------------------------------------------------- */ + + +void gModeBox::draw() { + fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border + switch (ch->mode) { + case LOOP_BASIC: + fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1); + break; + case LOOP_ONCE: + fl_draw_pixmap(loopOnce_xpm, x()+1, y()+1); + break; + case LOOP_ONCE_BAR: + fl_draw_pixmap(loopOnceBar_xpm, x()+1, y()+1); + break; + case LOOP_REPEAT: + fl_draw_pixmap(loopRepeat_xpm, x()+1, y()+1); + break; + case SINGLE_BASIC: + fl_draw_pixmap(oneshotBasic_xpm, x()+1, y()+1); + break; + case SINGLE_PRESS: + fl_draw_pixmap(oneshotPress_xpm, x()+1, y()+1); + break; + case SINGLE_RETRIG: + fl_draw_pixmap(oneshotRetrig_xpm, x()+1, y()+1); + break; + case SINGLE_ENDLESS: + fl_draw_pixmap(oneshotEndless_xpm, x()+1, y()+1); + break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gModeBox::cb_changeMode(Fl_Widget *v, void *p) { ((gModeBox*)v)->__cb_changeMode((intptr_t)p); } + + +/* -------------------------------------------------------------------------- */ + + +void gModeBox::__cb_changeMode(int mode) +{ + ch->mode = mode; + + /* what to do when the channel is playing and you change the mode? + * Nothing, since v0.5.3. Just refresh the action editor window, in + * case it's open */ + + gu_refreshActionEditor(); +} diff --git a/src/gui/elems/ge_modeBox.h b/src/gui/elems/ge_modeBox.h new file mode 100644 index 0000000..f6c661b --- /dev/null +++ b/src/gui/elems/ge_modeBox.h @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_modeBox + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_MODEBOX_H +#define GE_MODEBOX_H + + +#include + + +class gModeBox : public Fl_Menu_Button +{ +private: + + static void cb_changeMode (Fl_Widget *v, void *p); + inline void __cb_changeMode(int mode); + + class SampleChannel *ch; + +public: + + gModeBox(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0); + void draw(); +}; + + +#endif diff --git a/src/gui/elems/ge_muteChannel.cpp b/src/gui/elems/ge_muteChannel.cpp new file mode 100644 index 0000000..96f8cc8 --- /dev/null +++ b/src/gui/elems/ge_muteChannel.cpp @@ -0,0 +1,411 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_muteChannel + * a widget that represents mute actions inside the action editor. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "../../core/recorder.h" +#include "../../core/mixer.h" +#include "../../core/channel.h" +#include "../../glue/glue.h" +#include "../../utils/log.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_mainWindow.h" +#include "ge_keyboard.h" +#include "ge_actionWidget.h" +#include "ge_muteChannel.h" + + +extern gdMainWindow *mainWin; +extern Mixer G_Mixer; + + +gMuteChannel::gMuteChannel(int x, int y, gdActionEditor *pParent) + : gActionWidget(x, y, 200, 80, pParent), draggedPoint(-1), selectedPoint(-1) +{ + size(pParent->totalWidth, h()); + extractPoints(); +} + + +/* ------------------------------------------------------------------ */ + + +void gMuteChannel::draw() { + + baseDraw(); + + /* print label */ + + fl_color(COLOR_BG_1); + fl_font(FL_HELVETICA, 12); + fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); + + /* draw "on" and "off" labels. Must stay in background */ + + fl_color(COLOR_BG_1); + fl_font(FL_HELVETICA, 9); + fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); + fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); + + /* draw on-off points. On = higher rect, off = lower rect. It always + * starts with a note_off */ + + fl_color(COLOR_BG_2); + + int pxOld = x()+1; + int pxNew = 0; + int py = y()+h()-5; + int pyDot = py-6; + + for (unsigned i=0; icoverX+x()-1, py); +} + + +/* ------------------------------------------------------------------ */ + + +void gMuteChannel::extractPoints() { + + points.clear(); + + /* actions are already sorted by recorder::sortActions() */ + + for (unsigned i=0; ichan == pParent->chan->index) { + if (recorder::global.at(i).at(j)->type & (ACTION_MUTEON | ACTION_MUTEOFF)) { + point p; + p.frame = recorder::frames.at(i); + p.type = recorder::global.at(i).at(j)->type; + p.x = p.frame / pParent->zoom; + points.add(p); + //gLog("[gMuteChannel::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame); + } + } + } + } +} + + +/* ------------------------------------------------------------------ */ + + +void gMuteChannel::updateActions() { + for (unsigned i=0; izoom; +} + + +/* ------------------------------------------------------------------ */ + + +int gMuteChannel::handle(int e) { + + int ret = 0; + int mouseX = Fl::event_x()-x(); + + switch (e) { + + case FL_ENTER: { + ret = 1; + break; + } + + case FL_MOVE: { + selectedPoint = getSelectedPoint(); + redraw(); + ret = 1; + break; + } + + case FL_LEAVE: { + draggedPoint = -1; + selectedPoint = -1; + redraw(); + ret = 1; + break; + } + + case FL_PUSH: { + + /* left click on point: drag + * right click on point: delete + * left click on void: add */ + + if (Fl::event_button1()) { + + if (selectedPoint != -1) { + draggedPoint = selectedPoint; + previousXPoint = points.at(selectedPoint).x; + } + else { + + /* click on the grey area leads to nowhere */ + + if (mouseX > pParent->coverX) { + ret = 1; + break; + } + + /* click in the middle of a long mute_on (between two points): new actions + * must be added in reverse: first mute_off then mute_on. Let's find the + * next point from here. */ + + unsigned nextPoint = points.size; + for (unsigned i=0; izoom; + int frame_b = frame_a+2048; + + if (pParent->gridTool->isOn()) { + frame_a = pParent->gridTool->getSnapFrame(mouseX); + frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize()); + + /* with snap=on a point can fall onto another */ + + if (pointCollides(frame_a) || pointCollides(frame_b)) { + ret = 1; + break; + } + } + + /* ensure frame parity */ + + if (frame_a % 2 != 0) frame_a++; + if (frame_b % 2 != 0) frame_b++; + + /* avoid overflow: frame_b must be within the sequencer range. In that + * case shift the ON-OFF block */ + + if (frame_b >= G_Mixer.totalFrames) { + frame_b = G_Mixer.totalFrames; + frame_a = frame_b-2048; + } + + if (nextPoint % 2 != 0) { + recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_a); + recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_b); + } + else { + recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_a); + recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_b); + } + recorder::sortActions(); + + mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow + extractPoints(); + redraw(); + } + } + else { + + /* delete points pair */ + + if (selectedPoint != -1) { + + unsigned a; + unsigned b; + + if (points.at(selectedPoint).type == ACTION_MUTEOFF) { + a = selectedPoint-1; + b = selectedPoint; + } + else { + a = selectedPoint; + b = selectedPoint+1; + } + + //gLog("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n", + // a, b, points.at(a).frame, points.at(b).frame); + + recorder::deleteAction(pParent->chan->index, points.at(a).frame, points.at(a).type, false); // false = don't check vals + recorder::deleteAction(pParent->chan->index, points.at(b).frame, points.at(b).type, false); // false = don't check vals + recorder::sortActions(); + + mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow + extractPoints(); + redraw(); + } + } + ret = 1; + break; + } + + case FL_RELEASE: { + + if (draggedPoint != -1) { + + if (points.at(draggedPoint).x == previousXPoint) { + //gLog("nothing to do\n"); + } + else { + + int newFrame = points.at(draggedPoint).x * pParent->zoom; + + recorder::deleteAction( + pParent->chan->index, + points.at(draggedPoint).frame, + points.at(draggedPoint).type, + false); // don't check values + + recorder::rec( + pParent->chan->index, + points.at(draggedPoint).type, + newFrame); + + recorder::sortActions(); + + points.at(draggedPoint).frame = newFrame; + } + } + draggedPoint = -1; + selectedPoint = -1; + + ret = 1; + break; + } + + case FL_DRAG: { + + if (draggedPoint != -1) { + + /* constrain the point between two ends (leftBorder-point, + * point-point, point-rightBorder) */ + + int prevPoint; + int nextPoint; + + if (draggedPoint == 0) { + prevPoint = 0; + nextPoint = points.at(draggedPoint+1).x - 1; + if (pParent->gridTool->isOn()) + nextPoint -= pParent->gridTool->getCellSize(); + } + else + if ((unsigned) draggedPoint == points.size-1) { + prevPoint = points.at(draggedPoint-1).x + 1; + nextPoint = pParent->coverX-x(); + if (pParent->gridTool->isOn()) + prevPoint += pParent->gridTool->getCellSize(); + } + else { + prevPoint = points.at(draggedPoint-1).x + 1; + nextPoint = points.at(draggedPoint+1).x - 1; + if (pParent->gridTool->isOn()) { + prevPoint += pParent->gridTool->getCellSize(); + nextPoint -= pParent->gridTool->getCellSize(); + } + } + + if (mouseX <= prevPoint) + points.at(draggedPoint).x = prevPoint; + else + if (mouseX >= nextPoint) + points.at(draggedPoint).x = nextPoint; + else + if (pParent->gridTool->isOn()) + points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1; + else + points.at(draggedPoint).x = mouseX; + + redraw(); + } + ret = 1; + break; + } + } + + + return ret; +} + + +/* ------------------------------------------------------------------ */ + + +bool gMuteChannel::pointCollides(int frame) { + for (unsigned i=0; i= points.at(i).x+x()-3 && + Fl::event_x() <= points.at(i).x+x()+3) + return i; + } + return -1; +} diff --git a/src/gui/elems/ge_muteChannel.h b/src/gui/elems/ge_muteChannel.h new file mode 100644 index 0000000..e1835cb --- /dev/null +++ b/src/gui/elems/ge_muteChannel.h @@ -0,0 +1,103 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_muteChannel + * a widget representing mute actions inside the action editor. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GE_MUTECHANNEL_H +#define GE_MUTECHANNEL_H + +#include +#include +#include +#include "../../utils/utils.h" +#include "ge_actionWidget.h" + + +class gMuteChannel : public gActionWidget { + +private: + + /* point + * a single dot in the graph. */ + + struct point { + int frame; + char type; + int x; + }; + + /* points + * array of on/off points, in frames */ + + gVector points; + + /* draggedPoint + * which point we are dragging? */ + + int draggedPoint; + + /* selectedPoint + * which point we are selecting? */ + + int selectedPoint; + + /* previousXPoint + * x coordinate of point at time t-1. Used to check effective shifts */ + + int previousXPoint; + + /* extractPoints + * va a leggere l'array di azioni di Recorder ed estrae tutti i punti + * interessanti mute_on o mute_off. Li mette poi nel gVector points. */ + void extractPoints(); + + /* getSelectedPoint + * ritorna l'indice di points[] in base al punto selezionato (quello + * con il mouse hover). Ritorna -1 se non trova niente. */ + int getSelectedPoint(); + + /* pointCollides + * true if a point collides with another. Used while adding new points + * with snap active.*/ + + bool pointCollides(int frame); + +public: + + gMuteChannel(int x, int y, class gdActionEditor *pParent); + void draw(); + int handle(int e); + + /* updateActions + * calculates new points affected by the zoom. Call this one after + * each zoom update. */ + + void updateActions(); +}; + +#endif diff --git a/src/gui/elems/ge_pianoRoll.cpp b/src/gui/elems/ge_pianoRoll.cpp new file mode 100644 index 0000000..c10e653 --- /dev/null +++ b/src/gui/elems/ge_pianoRoll.cpp @@ -0,0 +1,730 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_pianoRoll + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include "../../core/channel.h" +#include "../../core/midiChannel.h" +#include "../../core/const.h" +#include "../../core/kernelMidi.h" +#include "../../core/conf.h" +#include "../../utils/log.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_actionEditor.h" +#include "ge_pianoRoll.h" + + +extern gdMainWindow *mainWin; +extern Mixer G_Mixer; +extern Conf G_Conf; + + +gPianoRollContainer::gPianoRollContainer(int x, int y, class gdActionEditor *pParent) + : Fl_Scroll(x, y, 200, 422), pParent(pParent) +{ + size(pParent->totalWidth, G_Conf.pianoRollH); + pianoRoll = new gPianoRoll(x, y, pParent->totalWidth, pParent); +} + + +/* ------------------------------------------------------------------ */ + + +gPianoRollContainer::~gPianoRollContainer() { + clear(); + G_Conf.pianoRollH = h(); + G_Conf.pianoRollY = pianoRoll->y(); +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoRollContainer::updateActions() { + pianoRoll->updateActions(); +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoRollContainer::draw() { + + pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal + + /* clear background */ + + fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); + + /* clip pianoRoll to pianoRollContainer size */ + + fl_push_clip(x(), y(), w(), h()); + draw_child(*pianoRoll); + fl_pop_clip(); + + fl_color(COLOR_BD_0); + fl_line_style(0); + fl_rect(x(), y(), pParent->totalWidth, h()); +} + + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +gPianoRoll::gPianoRoll(int X, int Y, int W, class gdActionEditor *pParent) + : gActionWidget(X, Y, W, 40, pParent) +{ + resizable(NULL); // don't resize children (i.e. pianoItem) + size(W, (MAX_NOTES+1) * CELL_H); // 128 MIDI channels * 15 px height + + if (G_Conf.pianoRollY == -1) + position(x(), y()-(h()/2)); // center + else + position(x(), G_Conf.pianoRollY); + + drawSurface1(); + drawSurface2(); + + /* add actions when the window is opened. Position is zoom-based. MIDI + * actions come always in pair: start + end. */ + + recorder::sortActions(); + + recorder::action *a2 = NULL; + recorder::action *prev = NULL; + + for (unsigned i=0; i than the grey area */ + /** FIXME - can we move this to the outer cycle? */ + + if (recorder::frames.at(i) > G_Mixer.totalFrames) + continue; + + recorder::action *a1 = recorder::global.at(i).at(j); + + if (a1->chan != pParent->chan->index) + continue; + + if (a1->type == ACTION_MIDI) { + + /* if this action is == to previous one: skip it, we have already + * checked it */ + + if (a1 == prev) { + //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was previous\n"); + continue; + } + + /* extract MIDI infos from a1: if is note off skip it, we are looking + * for note on only */ + + int a1_type = kernelMidi::getB1(a1->iValue); + int a1_note = kernelMidi::getB2(a1->iValue); + int a1_velo = kernelMidi::getB3(a1->iValue); + + if (a1_type == 0x80) { + //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was note off\n"); + continue; + } + + /* search for the next action. Must have: same channel, ACTION_MIDI, greater + * than a1->frame and with MIDI properties of note_off (0x80), same note + * of a1, same velocity of a1 */ + + recorder::getNextAction( + a1->chan, + ACTION_MIDI, + a1->frame, + &a2, + kernelMidi::getIValue(0x80, a1_note, a1_velo)); + + /* next action note off found: add a new gPianoItem to piano roll */ + + if (a2) { + //gLog("[gPianoRoll] ACTION_MIDI pair found, frame_a=%d frame_b=%d, note_a=%d, note_b=%d, type_a=%d, type_b=%d\n", + // a1->frame, a2->frame, kernelMidi::getNoteValue(a1->iValue), kernelMidi::getNoteValue(a2->iValue), + // kernelMidi::getNoteOnOff(a1->iValue), kernelMidi::getNoteOnOff(a2->iValue)); + new gPianoItem(0, 0, x(), y()+3, a1, a2, pParent); + prev = a2; + a2 = NULL; + } + else + gLog("[gPianoRoll] recorder didn't find action!\n"); + + } + } + } + + end(); +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoRoll::drawSurface1() { + + surface1 = fl_create_offscreen(40, h()); + fl_begin_offscreen(surface1); + + /* warning: only w() and h() come from this widget, x and y coordinates + * are absolute, since we are writing in a memory chunk */ + + fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN); + + fl_line_style(FL_DASH, 0, NULL); + fl_font(FL_HELVETICA, 11); + + int octave = 9; + + for (int i=1; i<=MAX_NOTES+1; i++) { + + /* print key note label. C C# D D# E F F# G G# A A# B */ + + char note[6]; + int step = i % 12; + + switch (step) { + case 1: + fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); + sprintf(note, "%dG", octave); + break; + case 2: + sprintf(note, "%dF#", octave); + break; + case 3: + sprintf(note, "%dF", octave); + break; + case 4: + fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); + sprintf(note, "%dE", octave); + break; + case 5: + sprintf(note, "%dD#", octave); + break; + case 6: + fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); + sprintf(note, "%dD", octave); + break; + case 7: + sprintf(note, "%dC#", octave); + break; + case 8: + sprintf(note, "%dC", octave); + break; + case 9: + fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); + sprintf(note, "%dB", octave); + break; + case 10: + sprintf(note, "%dA#", octave); + break; + case 11: + fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); + sprintf(note, "%dA", octave); + break; + case 0: + sprintf(note, "%dG#", octave); + octave--; + break; + } + + fl_color(fl_rgb_color(54, 54, 54)); + fl_draw(note, 4, ((i-1)*CELL_H)+1, 30, CELL_H, (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); + + /* print horizontal line */ + + if (i < 128) + fl_line(0, i*CELL_H, 40, +i*CELL_H); + } + + fl_line_style(0); + fl_end_offscreen(); +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoRoll::drawSurface2() { + surface2 = fl_create_offscreen(40, h()); + fl_begin_offscreen(surface2); + fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN); + fl_color(fl_rgb_color(54, 54, 54)); + fl_line_style(FL_DASH, 0, NULL); + for (int i=1; i<=MAX_NOTES+1; i++) { + int step = i % 12; + switch (step) { + case 1: + case 4: + case 6: + case 9: + case 11: + fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); + break; + } + if (i < 128) { + fl_color(fl_rgb_color(54, 54, 54)); + fl_line(0, i*CELL_H, 40, +i*CELL_H); + } + } + fl_line_style(0); + fl_end_offscreen(); +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoRoll::draw() { + + fl_copy_offscreen(x(), y(), 40, h(), surface1, 0, 0); + +#if defined(__APPLE__) + for (int i=36; itotalWidth; i+=36) /// TODO: i < pParent->coverX is faster + fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 1, 0); +#else + for (int i=40; itotalWidth; i+=40) /// TODO: i < pParent->coverX is faster + fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 0, 0); +#endif + + baseDraw(false); + draw_children(); +} + + +/* ------------------------------------------------------------------ */ + + +int gPianoRoll::handle(int e) { + + int ret = Fl_Group::handle(e); + + switch (e) { + case FL_PUSH: { + + /* avoid click on grey area */ + + if (Fl::event_x() >= pParent->coverX) { + ret = 1; + break; + } + + + push_y = Fl::event_y() - y(); + + if (Fl::event_button1()) { + + /* ax is driven by grid, ay by the height in px of each note */ + + int ax = Fl::event_x(); + int ay = Fl::event_y(); + + /* vertical snap */ + + int edge = (ay-y()-3) % 15; + if (edge != 0) ay -= edge; + + /* if no overlap, add new piano item. Also check that it doesn't + * overflow on the grey area, by shifting it to the left if + * necessary. */ + + if (!onItem(ax, ay-y()-3)) { + int greyover = ax+20 - pParent->coverX-x(); + if (greyover > 0) + ax -= greyover; + add(new gPianoItem(ax, ay, ax-x(), ay-y()-3, NULL, NULL, pParent)); + redraw(); + } + } + ret = 1; + break; + } + case FL_DRAG: { + + if (Fl::event_button3()) { + + gPianoRollContainer *prc = (gPianoRollContainer*) parent(); + position(x(), Fl::event_y() - push_y); + + if (y() > prc->y()) + position(x(), prc->y()); + else + if (y() < prc->y()+prc->h()-h()) + position(x(), prc->y()+prc->h()-h()); + + prc->redraw(); + } + ret = 1; + break; + } + case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll + ret = 1; + break; + } + } + return ret; +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoRoll::updateActions() { + + /* when zooming, don't delete and re-add actions, just MOVE them. This + * function shifts the action by a zoom factor. Those singlepress are + * stretched, as well */ + + gPianoItem *i; + for (int k=0; kgetFrame_a(), i->getFrame_b(), i->x()); + + int newX = x() + (i->getFrame_a() / pParent->zoom); + int newW = ((i->getFrame_b() - i->getFrame_a()) / pParent->zoom); + if (newW < 8) + newW = 8; + i->resize(newX, i->y(), newW, i->h()); + i->redraw(); + + //gLog("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x()); + } +} + + +/* ------------------------------------------------------------------ */ + + +bool gPianoRoll::onItem(int rel_x, int rel_y) { + + if (!pParent->chan->hasActions) + return false; + + int note = MAX_NOTES - (rel_y / CELL_H); + + int n = children(); + for (int i=0; igetNote() != note) + continue; + + /* when 2 segments overlap? + * start = the highest value between the two starting points + * end = the lowest value between the two ending points + * if start < end then there's an overlap of end-start pixels. We + * also add 1 px to the edges in order to gain some space: + * [ ][ ] ---> no + * [ ] [ ] ---> yes! */ + + int start = p->x() > rel_x ? p->x() : rel_x-1; + int end = p->x()+p->w() < rel_x + 20 ? p->x()+p->w() : rel_x + 21; + if (start < end) + return true; + } + return false; +} + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + + +gPianoItem::gPianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, gdActionEditor *pParent) + : Fl_Box (X, Y, 20, gPianoRoll::CELL_H-5), + a (a), + b (b), + pParent (pParent), + selected(false), + event_a (0x00), + event_b (0x00), + changed (false) +{ + + /* a is a pointer: action exists, needs to be displayed */ + + if (a) { + note = kernelMidi::getB2(a->iValue); + frame_a = a->frame; + frame_b = b->frame; + event_a = a->iValue; + event_b = b->iValue; + int newX = rel_x + (frame_a / pParent->zoom); + int newY = rel_y + getY(note); + int newW = (frame_b - frame_a) / pParent->zoom; + resize(newX, newY, newW, h()); + } + + /* a is null: action needs to be recorded from scratch */ + + else { + note = getNote(rel_y); + frame_a = rel_x * pParent->zoom; + frame_b = (rel_x + 20) * pParent->zoom; + record(); + size((frame_b - frame_a) / pParent->zoom, h()); + } +} + + +/* ------------------------------------------------------------------ */ + + +bool gPianoItem::overlap() { + + /* when 2 segments overlap? + * start = the highest value between the two starting points + * end = the lowest value between the two ending points + * if start < end then there's an overlap of end-start pixels. */ + + gPianoRoll *pPiano = (gPianoRoll*) parent(); + + for (int i=0; ichildren(); i++) { + + gPianoItem *pItem = (gPianoItem*) pPiano->child(i); + + /* don't check against itself and with different y positions */ + + if (pItem == this || pItem->y() != y()) + continue; + + int start = pItem->x() >= x() ? pItem->x() : x(); + int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w(); + if (start < end) + return true; + } + + return false; +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoItem::draw() { + int _w = w() > 4 ? w() : 4; + //gLog("[gPianoItem] draw me (%p) at x=%d\n", (void*)this, x()); + fl_rectf(x(), y(), _w, h(), (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2); +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoItem::record() { + + /* avoid frame overflow */ + + int overflow = frame_b - G_Mixer.totalFrames; + if (overflow > 0) { + frame_b -= overflow; + frame_a -= overflow; + } + + /* note off */ + /** FIXME - use constants */ + event_a |= (0x90 << 24); // note on + event_a |= (note << 16); // note value + event_a |= (0x3F << 8); // velocity + event_a |= (0x00); + + event_b |= (0x80 << 24); // note off + event_b |= (note << 16); // note value + event_b |= (0x3F << 8); // velocity + event_b |= (0x00); + + recorder::rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a); + recorder::rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b); +} + + +/* ------------------------------------------------------------------ */ + + +void gPianoItem::remove() { + recorder::deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true, event_a, 0.0); + recorder::deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true, event_b, 0.0); + + /* send a note-off in case we are deleting it in a middle of a key_on + * key_off sequence. */ + + ((MidiChannel*) pParent->chan)->sendMidi(event_b); +} + + +/* ------------------------------------------------------------------ */ + + +int gPianoItem::handle(int e) { + + int ret = 0; + + switch (e) { + + case FL_ENTER: { + selected = true; + ret = 1; + redraw(); + break; + } + + case FL_LEAVE: { + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + selected = false; + ret = 1; + redraw(); + break; + } + + case FL_MOVE: { + onLeftEdge = false; + onRightEdge = false; + + if (Fl::event_x() >= x() && Fl::event_x() < x()+4) { + onLeftEdge = true; + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + } + else + if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) { + onRightEdge = true; + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + } + else + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + + ret = 1; + break; + } + + case FL_PUSH: { + + push_x = Fl::event_x() - x(); + old_x = x(); + old_w = w(); + + if (Fl::event_button3()) { + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + remove(); + hide(); // for Windows + Fl::delete_widget(this); + ((gPianoRoll*)parent())->redraw(); + } + ret = 1; + break; + } + + case FL_DRAG: { + + changed = true; + + gPianoRoll *pr = (gPianoRoll*) parent(); + int coverX = pParent->coverX + pr->x(); // relative coverX + int nx, ny, nw; + + if (onLeftEdge) { + nx = Fl::event_x(); + ny = y(); + nw = x()-Fl::event_x()+w(); + if (nx < pr->x()) { + nx = pr->x(); + nw = w()+x()-pr->x(); + } + else + if (nx > x()+w()-8) { + nx = x()+w()-8; + nw = 8; + } + resize(nx, ny, nw, h()); + } + else + if (onRightEdge) { + nw = Fl::event_x()-x(); + if (Fl::event_x() < x()+8) + nw = 8; + else + if (Fl::event_x() > coverX) + nw = coverX-x(); + size(nw, h()); + } + else { + nx = Fl::event_x() - push_x; + if (nx < pr->x()+1) + nx = pr->x()+1; + else + if (nx+w() > coverX) + nx = coverX-w(); + + /* snapping */ + + if (pParent->gridTool->isOn()) + nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1; + + position(nx, y()); + } + + /* update screen */ + + redraw(); + ((gPianoRoll*)parent())->redraw(); + ret = 1; + break; + } + + case FL_RELEASE: { + + /* delete & record the action, only if it doesn't overlap with + * another one */ + + if (overlap()) { + resize(old_x, y(), old_w, h()); + redraw(); + } + else + if (changed) { + remove(); + note = getNote(getRelY()); + frame_a = getRelX() * pParent->zoom; + frame_b = (getRelX()+w()) * pParent->zoom; + record(); + changed = false; + } + + ((gPianoRoll*)parent())->redraw(); + + ret = 1; + break; + } + } + return ret; +} diff --git a/src/gui/elems/ge_pianoRoll.h b/src/gui/elems/ge_pianoRoll.h new file mode 100644 index 0000000..aad0618 --- /dev/null +++ b/src/gui/elems/ge_pianoRoll.h @@ -0,0 +1,183 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_pianoRoll + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GE_PIANOROLL_H +#define GE_PIANOROLL_H + +#include +#include +#include +#include "../../core/recorder.h" +#include "ge_actionWidget.h" + + +class gPianoRollContainer : public Fl_Scroll { + +private: + class gdActionEditor *pParent; + class gPianoRoll *pianoRoll; + +public: + gPianoRollContainer(int x, int y, class gdActionEditor *parent); + ~gPianoRollContainer(); + void draw(); + void updateActions(); +}; + + +/* ------------------------------------------------------------------ */ + + +class gPianoRoll : public gActionWidget { + +private: + + /* onItem + * is curson on a gPianoItem? */ + + bool onItem(int rel_x, int rel_y); + + /* drawSurface* + * generate a complex drawing in memory first and copy it to the + * screen at a later point in time. Fl_Offscreen surface holds the + * necessary data. */ + + /* drawSurface1 + * draw first tile of note values. */ + + void drawSurface1(); + + /* drawSurface2 + * draw the rest of the piano roll. */ + + void drawSurface2(); + + int push_y; + Fl_Offscreen surface1; // notes, no repeat + Fl_Offscreen surface2; // lines, x-repeat + + +public: + gPianoRoll(int x, int y, int w, class gdActionEditor *pParent); + + void draw(); + int handle(int e); + void updateActions(); + + enum { MAX_NOTES = 127, CELL_H = 15 }; +}; + + +/* ------------------------------------------------------------------ */ + + +class gPianoItem : public Fl_Box { + +private: + + /* getRelX/Y + * return x/y point of this item, relative to piano roll (and not to + * entire screen) */ + + inline int getRelY() { return y() - parent()->y() - 3; }; + inline int getRelX() { return x() - parent()->x(); }; + + /* getNote + * from a relative_y return the real MIDI note, range 0-127. 15 is + * the hardcoded value for note height in pixels */ + + inline int getNote(int rel_y) { + return gPianoRoll::MAX_NOTES - (rel_y / gPianoRoll::CELL_H); + }; + + /* getY + * from a note, return the y position on piano roll */ + + inline int getY(int note) { + return (gPianoRoll::MAX_NOTES * gPianoRoll::CELL_H) - (note * gPianoRoll::CELL_H); + }; + + /* overlap + * check if this item don't overlap with another one. */ + + bool overlap(); + + recorder::action *a; + recorder::action *b; + class gdActionEditor *pParent; + + bool selected; + int push_x; + + /* MIDI note, start frame, end frame - Used only if it's a newly added + * action */ /** FIXME - is it true? */ + + int note; + int frame_a; + int frame_b; + + /* event - bitmasked MIDI events, generated by record() or by ctor if + * not newly added action */ + + int event_a; + int event_b; + + /* changed - if Item has been moved or resized: re-recording needed */ + + bool changed; + + /* onLeft,RightEdge - if cursor is on a widget's edge */ + + bool onLeftEdge; + bool onRightEdge; + + /* old_x, old_w - store previous width and position while dragging + * and moving, in order to restore it if overlap */ + + int old_x, old_w; + +public: + + /* pianoItem ctor + * if action *a == NULL, record a new action */ + + gPianoItem(int x, int y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, class gdActionEditor *pParent); + + void draw(); + int handle(int e); + void record(); + void remove(); + + inline int getFrame_a() { return frame_a; } + inline int getFrame_b() { return frame_b; } + inline int getNote() { return note; } + +}; + +#endif diff --git a/src/gui/elems/ge_sampleChannel.cpp b/src/gui/elems/ge_sampleChannel.cpp new file mode 100644 index 0000000..8498391 --- /dev/null +++ b/src/gui/elems/ge_sampleChannel.cpp @@ -0,0 +1,600 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_sampleChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/pluginHost.h" +#include "../../core/mixer.h" +#include "../../core/conf.h" +#include "../../core/patch.h" +#include "../../core/graphics.h" +#include "../../core/channel.h" +#include "../../core/wave.h" +#include "../../core/sampleChannel.h" +#include "../../core/midiChannel.h" +#include "../../glue/glue.h" +#include "../../utils/gui_utils.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_keyGrabber.h" +#include "../dialogs/gd_midiInput.h" +#include "../dialogs/gd_editor.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_warnings.h" +#include "../dialogs/gd_browser.h" +#include "../dialogs/gd_midiOutput.h" +#include "ge_keyboard.h" +#include "ge_sampleChannel.h" +#include "ge_status.h" +#include "ge_modeBox.h" + +#ifdef WITH_VST +#include "../dialogs/gd_pluginList.h" +#endif + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Patch G_Patch; +extern gdMainWindow *mainWin; + + +gSampleChannel::gSampleChannel(int X, int Y, int W, int H, class SampleChannel *ch) + : gChannel(X, Y, W, H, CHANNEL_SAMPLE), ch(ch) +{ + begin(); + +#if defined(WITH_VST) + int delta = 168; // (7 widgets * 20) + (7 paddings * 4) +#else + int delta = 144; // (6 widgets * 20) + (6 paddings * 4) +#endif + + button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); + status = new gStatus(button->x()+button->w()+4, y(), 20, 20, ch); + mainButton = new gSampleChannelButton(status->x()+status->w()+4, y(), w() - delta, 20, "-- no sample --"); + modeBox = new gModeBox(mainButton->x()+mainButton->w()+4, y(), 20, 20, ch); + mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); + solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); + readActions = NULL; // no 'R' button + +#if defined(WITH_VST) + fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); + vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); +#else + vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); +#endif + + end(); + + resizable(mainButton); + + update(); + + button->callback(cb_button, (void*)this); + button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease + +#ifdef WITH_VST + fx->callback(cb_openFxWindow, (void*)this); +#endif + + mute->type(FL_TOGGLE_BUTTON); + mute->callback(cb_mute, (void*)this); + + solo->type(FL_TOGGLE_BUTTON); + solo->callback(cb_solo, (void*)this); + + mainButton->callback(cb_openMenu, (void*)this); + vol->callback(cb_changeVol, (void*)this); + + ch->guiChannel = this; +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::cb_button (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_button(); } +void gSampleChannel::cb_mute (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_mute(); } +void gSampleChannel::cb_solo (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_solo(); } +void gSampleChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openMenu(); } +void gSampleChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_changeVol(); } +void gSampleChannel::cb_readActions (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_readActions(); } +#ifdef WITH_VST +void gSampleChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openFxWindow(); } +#endif + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::__cb_mute() +{ + glue_setMute(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::__cb_solo() +{ + solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::__cb_changeVol() +{ + glue_setChanVol(ch, vol->value()); +} + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST +void gSampleChannel::__cb_openFxWindow() +{ + gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST); +} +#endif + + +/* -------------------------------------------------------------------------- */ + + + +void gSampleChannel::__cb_button() +{ + if (button->value()) // pushed + glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); + else // released + glue_keyRelease(ch, Fl::event_ctrl(), Fl::event_shift()); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::__cb_openMenu() +{ + /* if you're recording (actions or input) no menu is allowed; you can't + * do anything, especially deallocate the channel */ + + if (G_Mixer.chanInput == ch || recorder::active) + return; + + /* the following is a trash workaround for a FLTK menu. We need a gMenu + * widget asap */ + + Fl_Menu_Item rclick_menu[] = { + {"Load new sample..."}, // 0 + {"Export sample to file..."}, // 1 + {"Setup keyboard input..."}, // 2 + {"Setup MIDI input..."}, // 3 + {"Setup MIDI output..."}, // 4 + {"Edit sample..."}, // 5 + {"Edit actions..."}, // 6 + {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 7 + {"All"}, // 8 + {"Mute"}, // 9 + {"Volume"}, // 10 + {"Start/Stop"}, // 11 + {0}, // 12 + {"Free channel"}, // 13 + {"Delete channel"}, // 14 + {0} + }; + + if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) { + rclick_menu[1].deactivate(); + rclick_menu[5].deactivate(); + rclick_menu[13].deactivate(); + } + + /* no 'clear actions' if there are no actions */ + + if (!ch->hasActions) + rclick_menu[6].deactivate(); + + /* no 'clear start/stop actions' for those channels in loop mode: + * they cannot have start/stop actions. */ + + if (ch->mode & LOOP_ANY) + rclick_menu[10].deactivate(); + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(11); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) return; + + if (strcmp(m->label(), "Load new sample...") == 0) { + openBrowser(BROWSER_LOAD_SAMPLE); + return; + } + + if (strcmp(m->label(), "Setup keyboard input...") == 0) { + new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow + return; + } + + if (strcmp(m->label(), "Setup MIDI input...") == 0) { + gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0); + return; + } + + if (strcmp(m->label(), "Setup MIDI output...") == 0) { + gu_openSubWindow(mainWin, new gdMidiOutputSampleCh(ch), 0); + return; + } + + if (strcmp(m->label(), "Edit sample...") == 0) { + gu_openSubWindow(mainWin, new gdEditor(ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor + return; + } + + if (strcmp(m->label(), "Export sample to file...") == 0) { + openBrowser(BROWSER_SAVE_SAMPLE); + return; + } + + if (strcmp(m->label(), "Delete channel") == 0) { + if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) + return; + glue_deleteChannel(ch); + return; + } + + if (strcmp(m->label(), "Free channel") == 0) { + if (ch->status == STATUS_PLAY) { + if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) + return; + } + else if (!gdConfirmWin("Warning", "Free channel: are you sure?")) + return; + + glue_freeChannel(ch); + + /* delete any related subwindow */ + + /** FIXME - use gu_closeAllSubwindows() */ + + mainWin->delSubWindow(WID_FILE_BROWSER); + mainWin->delSubWindow(WID_ACTION_EDITOR); + mainWin->delSubWindow(WID_SAMPLE_EDITOR); + mainWin->delSubWindow(WID_FX_LIST); + + return; + } + + if (strcmp(m->label(), "Mute") == 0) { + if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?")) + return; + recorder::clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF); + if (!ch->hasActions) + delActionButton(); + + /* TODO - set mute=false */ + + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Start/Stop") == 0) { + if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?")) + return; + recorder::clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN); + if (!ch->hasActions) + delActionButton(); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Volume") == 0) { + if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?")) + return; + recorder::clearAction(ch->index, ACTION_VOLUME); + if (!ch->hasActions) + delActionButton(); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "All") == 0) { + if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) + return; + recorder::clearChan(ch->index); + delActionButton(); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Edit actions...") == 0) { + gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); + return; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::__cb_readActions() +{ + ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::openBrowser(int type) +{ + const char *title = ""; + switch (type) { + case BROWSER_LOAD_SAMPLE: + title = "Browse Sample"; + break; + case BROWSER_SAVE_SAMPLE: + title = "Save Sample"; + break; + case -1: + title = "Edit Sample"; + break; + } + gWindow *childWin = new gdBrowser(title, G_Conf.samplePath, ch, type); + gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::refresh() +{ + if (!mainButton->visible()) // mainButton invisible? status too (see below) + return; + + setColorsByStatus(ch->status, ch->recStatus); + + if (ch->wave != NULL) { + if (G_Mixer.chanInput == ch) + mainButton->setInputRecordMode(); + if (recorder::active) { + if (recorder::canRec(ch)) + mainButton->setActionRecordMode(); + } + status->redraw(); // status invisible? sampleButton too (see below) + } + mainButton->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::reset() +{ + delActionButton(true); // force==true, don't check, just remove it + mainButton->setDefaultMode("-- no sample --"); + mainButton->redraw(); + status->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::update() +{ + /* update sample button's label */ + + switch (ch->status) { + case STATUS_EMPTY: + mainButton->label("-- no sample --"); + break; + case STATUS_MISSING: + case STATUS_WRONG: + mainButton->label("* file not found! *"); + break; + default: + mainButton->label(ch->wave->name.c_str()); + break; + } + + /* update channels. If you load a patch with recorded actions, the 'R' + * button must be shown. Moreover if the actions are active, the 'R' + * button must be activated accordingly. */ + + if (ch->hasActions) + addActionButton(); + else + delActionButton(); + + /* updates modebox */ + + modeBox->value(ch->mode); + modeBox->redraw(); + + /* update volumes+mute+solo */ + + vol->value(ch->volume); + mute->value(ch->mute); + solo->value(ch->solo); + +#ifdef WITH_VST + fx->full = ch->plugins.size > 0; +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +int gSampleChannel::keyPress(int e) +{ + return handleKey(e, ch->key); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::addActionButton() +{ + /* quit if 'R' exists yet. */ + + if (readActions != NULL) + return; + + mainButton->size(mainButton->w()-24, mainButton->h()); + + redraw(); + + readActions = new gClick(mainButton->x() + mainButton->w() + 4, + mainButton->y(), 20, 20, "", readActionOff_xpm, + readActionOn_xpm); + readActions->type(FL_TOGGLE_BUTTON); + readActions->value(ch->readActions); + readActions->callback(cb_readActions, (void*)this); + add(readActions); + + /* hard redraw: there's no other way to avoid glitches when moving + * the 'R' button */ + + mainWin->keyboard->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::delActionButton(bool force) +{ + if (readActions == NULL) + return; + + /* TODO - readActions check is useless here */ + + if (!force && (readActions == NULL || ch->hasActions)) + return; + + remove(readActions); // delete from Keyboard group (FLTK) + delete readActions; // delete (C++) + readActions = NULL; + + mainButton->size(mainButton->w()+24, mainButton->h()); + mainButton->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSampleChannel::resize(int X, int Y, int W, int H) +{ + gChannel::resize(X, Y, W, H); + + if (w() < BREAK_FX) { +#ifdef WITH_VST + fx->hide(); +#endif + mainButton->size(w() - BREAK_DELTA, mainButton->h()); + mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); + solo->resize(mute->x()+mute->w()+4, y(), 20, 20); + } + else + if (w() < BREAK_MODE_BOX) { +#ifdef WITH_VST + fx->show(); +#endif + mainButton->size(w() - (BREAK_DELTA + BREAK_UNIT), mainButton->h()); + mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); + solo->resize(mute->x()+mute->w()+4, y(), 20, 20); + modeBox->hide(); + } + else + if (w() < BREAK_READ_ACTIONS) { + modeBox->show(); + mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 2)), mainButton->h()); + modeBox->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); + if (readActions) { + readActions->hide(); + } + } + else { + if (readActions) { + mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 3)), mainButton->h()); + readActions->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); + readActions->show(); + } + } + + gChannel::init_sizes(); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +gSampleChannelButton::gSampleChannelButton(int x, int y, int w, int h, const char *l) + : gChannelButton(x, y, w, h, l) {} + + +/* -------------------------------------------------------------------------- */ + + +int gSampleChannelButton::handle(int e) +{ + int ret = gClick::handle(e); + switch (e) { + case FL_DND_ENTER: + case FL_DND_DRAG: + case FL_DND_RELEASE: { + ret = 1; + break; + } + case FL_PASTE: { + gSampleChannel *gch = (gSampleChannel*) parent(); // parent is gSampleChannel + SampleChannel *ch = gch->ch; + int result = glue_loadChannel(ch, gTrim(gStripFileUrl(Fl::event_text())).c_str()); + if (result != SAMPLE_LOADED_OK) + mainWin->keyboard->printChannelMessage(result); + ret = 1; + break; + } + } + return ret; +} diff --git a/src/gui/elems/ge_sampleChannel.h b/src/gui/elems/ge_sampleChannel.h new file mode 100644 index 0000000..0aad470 --- /dev/null +++ b/src/gui/elems/ge_sampleChannel.h @@ -0,0 +1,104 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_sampleChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_SAMPLE_CHANNEL_H +#define GE_SAMPLE_CHANNEL_H + + +#include +#include +#include +#include "ge_channel.h" +#include "ge_channelButton.h" +#include "ge_mixed.h" + + +class gSampleChannel : public gChannel +{ +private: + + static void cb_button (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_openMenu (Fl_Widget *v, void *p); + static void cb_changeVol (Fl_Widget *v, void *p); + static void cb_readActions (Fl_Widget *v, void *p); +#ifdef WITH_VST + static void cb_openFxWindow (Fl_Widget *v, void *p); +#endif + + inline void __cb_mute (); + inline void __cb_solo (); + inline void __cb_changeVol (); + inline void __cb_button (); + inline void __cb_openMenu (); + inline void __cb_readActions (); +#ifdef WITH_VST + inline void __cb_openFxWindow(); +#endif + + void openBrowser(int type); + +public: + + gSampleChannel(int x, int y, int w, int h, class SampleChannel *ch); + + void reset (); + void update (); + void refresh (); + int keyPress(int event); + void resize (int x, int y, int w, int h); + + /* add/delActionButton + * add or remove 'R' button when actions are available. 'Status' is + * the initial status of the button: on or off. + * If force==true remove the button with no further checks. */ + + void addActionButton(); + void delActionButton(bool force=false); + + class gModeBox *modeBox; + class gClick *readActions; + + class SampleChannel *ch; +}; + + +/* -------------------------------------------------------------------------- */ + + +class gSampleChannelButton : public gChannelButton +{ +public: + gSampleChannelButton(int x, int y, int w, int h, const char *l=0); + int handle(int e); +}; + + +#endif diff --git a/src/gui/elems/ge_status.cpp b/src/gui/elems/ge_status.cpp new file mode 100644 index 0000000..564b9ab --- /dev/null +++ b/src/gui/elems/ge_status.cpp @@ -0,0 +1,78 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_status + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../core/mixer.h" +#include "ge_status.h" + + +extern Mixer G_Mixer; + + +gStatus::gStatus(int x, int y, int w, int h, SampleChannel *ch, const char *L) + : Fl_Box(x, y, w, h, L), ch(ch) {} + + +/* -------------------------------------------------------------------------- */ + + +void gStatus::draw() +{ + fl_rect(x(), y(), w(), h(), COLOR_BD_0); // reset border + fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // reset background + + if (ch != NULL) { + if (ch->status & (STATUS_WAIT | STATUS_ENDING | REC_ENDING | REC_WAITING) || + ch->recStatus & (REC_WAITING | REC_ENDING)) + { + fl_rect(x(), y(), w(), h(), COLOR_BD_1); + } + else + if (ch->status == STATUS_PLAY) + fl_rect(x(), y(), w(), h(), COLOR_BD_1); + else + fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // status empty + + + if (G_Mixer.chanInput == ch) + fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_3); // take in progress + else + if (recorder::active && recorder::canRec(ch)) + fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_4); // action record + + /* equation for the progress bar: + * ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */ + + int pos = ch->getPosition(); + if (pos == -1) + pos = 0; + else + pos = (pos * (w()-1)) / (ch->end - ch->begin); + fl_rectf(x()+1, y()+1, pos, h()-2, COLOR_BG_2); + } +} diff --git a/src/gui/elems/ge_status.h b/src/gui/elems/ge_status.h new file mode 100644 index 0000000..07e9b28 --- /dev/null +++ b/src/gui/elems/ge_status.h @@ -0,0 +1,48 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_status + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_STATUS_H +#define GE_STATUS_H + + +#include +#include "../../core/sampleChannel.h" +#include "ge_mixed.h" + + +class gStatus : public Fl_Box +{ +public: + gStatus(int X, int Y, int W, int H, class SampleChannel *ch, const char *L=0); + void draw(); + class SampleChannel *ch; +}; + + +#endif diff --git a/src/gui/elems/ge_waveTools.cpp b/src/gui/elems/ge_waveTools.cpp new file mode 100644 index 0000000..d7712c1 --- /dev/null +++ b/src/gui/elems/ge_waveTools.cpp @@ -0,0 +1,103 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gg_waveTools + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "../../core/graphics.h" +#include "../../core/mixer.h" +#include "../elems/ge_mixed.h" +#include "../elems/ge_waveform.h" +#include "ge_waveTools.h" + + +gWaveTools::gWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l) + : Fl_Scroll(x, y, w, h, l) +{ + type(Fl_Scroll::HORIZONTAL_ALWAYS); + hscrollbar.color(COLOR_BG_0); + hscrollbar.selection_color(COLOR_BG_1); + hscrollbar.labelcolor(COLOR_BD_1); + hscrollbar.slider(G_BOX); + + waveform = new gWaveform(x, y, w, h-24, ch); + + + //resizable(waveform); +} + + + +/* ------------------------------------------------------------------ */ + + +void gWaveTools::updateWaveform() +{ + waveform->alloc(w()); + waveform->redraw(); +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveTools::resize(int x, int y, int w, int h) +{ + if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize + Fl_Widget::resize(x, y, w, h); + waveform->resize(x, y, waveform->w(), h-24); + updateWaveform(); + } + else { // horizontal resize + Fl_Widget::resize(x, y, w, h); + } + + if (this->w() > waveform->w()) + waveform->stretchToWindow(); + + int offset = waveform->x() + waveform->w() - this->w() - this->x(); + if (offset < 0) + waveform->position(waveform->x()-offset, this->y()); +} + + +/* ------------------------------------------------------------------ */ + + +int gWaveTools::handle(int e) +{ + int ret = Fl_Group::handle(e); + switch (e) { + case FL_MOUSEWHEEL: { + waveform->setZoom(Fl::event_dy()); + redraw(); + ret = 1; + break; + } + } + return ret; +} + diff --git a/src/gui/elems/ge_waveTools.h b/src/gui/elems/ge_waveTools.h new file mode 100644 index 0000000..51cbc65 --- /dev/null +++ b/src/gui/elems/ge_waveTools.h @@ -0,0 +1,49 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gg_waveTools + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GE_WAVETOOLS_H +#define GE_WAVETOOLS_H + +#include +#include +#include + + +class gWaveTools : public Fl_Scroll { +public: + class gWaveform *waveform; + + gWaveTools(int X,int Y,int W, int H, class SampleChannel *ch, const char *L=0); + void resize(int x, int y, int w, int h); + int handle(int e); + + void updateWaveform(); +}; + +#endif diff --git a/src/gui/elems/ge_waveform.cpp b/src/gui/elems/ge_waveform.cpp new file mode 100644 index 0000000..52cf539 --- /dev/null +++ b/src/gui/elems/ge_waveform.cpp @@ -0,0 +1,838 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_waveform + * an element which represents a waveform. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include +#include +#include "../../core/wave.h" +#include "../../core/conf.h" +#include "../../core/mixer.h" +#include "../../core/waveFx.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../glue/glue.h" +#include "../dialogs/gd_editor.h" +#include "ge_waveTools.h" +#include "ge_mixed.h" +#include "ge_waveform.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; + + +gWaveform::gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l) +: Fl_Widget(x, y, w, h, l), + chan(ch), + menuOpen(false), + chanStart(0), + chanStartLit(false), + chanEnd(0), + chanEndLit(false), + ratio(0.0f), + selectionA(0), + selectionB(0), + selectionA_abs(0), + selectionB_abs(0) +{ + data.sup = NULL; + data.inf = NULL; + data.size = 0; + + grid.snap = G_Conf.sampleEditorGridOn; + grid.level = G_Conf.sampleEditorGridVal; + + stretchToWindow(); +} + + +/* ------------------------------------------------------------------ */ + + +gWaveform::~gWaveform() +{ + freeData(); +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::freeData() +{ + if (data.sup != NULL) { + free(data.sup); + free(data.inf); + data.sup = NULL; + data.inf = NULL; + data.size = 0; + } + grid.points.clear(); +} + + +/* ------------------------------------------------------------------ */ + + +int gWaveform::alloc(int datasize) +{ + ratio = chan->wave->size / (float) datasize; + + if (ratio < 2) + return 0; + + freeData(); + + data.size = datasize; + data.sup = (int*) malloc(data.size * sizeof(int)); + data.inf = (int*) malloc(data.size * sizeof(int)); + + int offset = h() / 2; + int zero = y() + offset; // center, zero amplitude (-inf dB) + + /* grid frequency: store a grid point every 'gridFreq' pixel. Must be + * even, as always */ + + int gridFreq = 0; + if (grid.level != 0) { + gridFreq = chan->wave->size / grid.level; + if (gridFreq % 2 != 0) + gridFreq--; + } + + for (int i=0; iwave->size - 1) / (float) datasize); + pn = (i+1) * ((chan->wave->size - 1) / (float) datasize); + + if (pp % 2 != 0) pp -= 1; + if (pn % 2 != 0) pn -= 1; + + float peaksup = 0.0f; + float peakinf = 0.0f; + + /* scan the original data in chunks */ + + int k = pp; + while (k < pn) { + + if (chan->wave->data[k] > peaksup) + peaksup = chan->wave->data[k]; // FIXME - Left data only + else + if (chan->wave->data[k] <= peakinf) + peakinf = chan->wave->data[k]; // FIXME - Left data only + + /* print grid */ + + if (gridFreq != 0) + if (k % gridFreq == 0 && k != 0) + grid.points.add(i); + + k += 2; + } + + data.sup[i] = zero - (peaksup * chan->boost * offset); + data.inf[i] = zero - (peakinf * chan->boost * offset); + + // avoid window overflow + + if (data.sup[i] < y()) data.sup[i] = y(); + if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1; + } + + recalcPoints(); + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::recalcPoints() +{ + selectionA = relativePoint(selectionA_abs); + selectionB = relativePoint(selectionB_abs); + chanStart = relativePoint(chan->begin / 2); + + /* fix the rounding error when chanEnd is set on the very end of the + * sample */ + + if (chan->end == chan->wave->size) + chanEnd = data.size - 2; // 2 px border + else + chanEnd = relativePoint(chan->end / 2); +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::draw() +{ + /* blank canvas */ + + fl_rectf(x(), y(), w(), h(), COLOR_BG_0); + + /* draw selection (if any) */ + + if (selectionA != selectionB) { + + int a_x = selectionA + x() - BORDER; // - start; + int b_x = selectionB + x() - BORDER; // - start; + + if (a_x < 0) + a_x = 0; + if (b_x >= w()-1) + b_x = w()-1; + + if (selectionA < selectionB) + fl_rectf(a_x+BORDER, y(), b_x-a_x, h(), COLOR_BD_0); + else + fl_rectf(b_x+BORDER, y(), a_x-b_x, h(), COLOR_BD_0); + } + + /* draw waveform from x1 (offset driven by the scrollbar) to x2 + * (width of parent window). We don't draw the entire waveform, + * only the visibile part. */ + + int offset = h() / 2; + int zero = y() + offset; // sample zero (-inf dB) + + int wx1 = abs(x() - ((gWaveTools*)parent())->x()); + int wx2 = wx1 + ((gWaveTools*)parent())->w(); + if (x()+w() < ((gWaveTools*)parent())->w()) + wx2 = x() + w() - BORDER; + + fl_color(0, 0, 0); + for (int i=wx1; i w()+x()-2) + fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT); + else { + fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT); + fl_color(255, 255, 255); + fl_draw("s", lineX+4, y()+h()-3); + } + + /* print chanEnd */ + + lineX = x()+chanEnd; + if (chanEndLit) fl_color(COLOR_BD_1); + else fl_color(COLOR_BD_0); + + fl_line(lineX, y()+1, lineX, y()+h()-2); + + if (lineX-FLAG_WIDTH < x()) + fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT); + else { + fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT); + fl_color(255, 255, 255); + fl_draw("e", lineX-10, y()+10); + } +} + + +/* ------------------------------------------------------------------ */ + + +int gWaveform::handle(int e) +{ + int ret = 0; + + switch (e) { + + case FL_PUSH: { + + mouseX = Fl::event_x(); + pushed = true; + + if (!mouseOnEnd() && !mouseOnStart()) { + + /* right button? show the menu. Don't set selectionA,B,etc */ + + if (Fl::event_button3()) { + openEditMenu(); + } + else + if (mouseOnSelectionA() || mouseOnSelectionB()) { + resized = true; + } + else { + dragged = true; + selectionA = Fl::event_x() - x(); + + if (selectionA >= data.size) selectionA = data.size; + + selectionB = selectionA; + selectionA_abs = absolutePoint(selectionA); + selectionB_abs = selectionA_abs; + } + } + + ret = 1; + break; + } + + case FL_RELEASE: { + + /* don't recompute points if something is selected */ + + if (selectionA != selectionB) { + pushed = false; + dragged = false; + ret = 1; + break; + } + + int realChanStart = chan->begin; + int realChanEnd = chan->end; + + if (chanStartLit) + realChanStart = absolutePoint(chanStart)*2; + else + if (chanEndLit) + realChanEnd = absolutePoint(chanEnd)*2; + + glue_setBeginEndChannel((gdEditor *) window(), chan, realChanStart, realChanEnd, false); + + pushed = false; + dragged = false; + + redraw(); + ret = 1; + break; + } + + case FL_ENTER: { // enables FL_DRAG + ret = 1; + break; + } + + case FL_LEAVE: { + if (chanStartLit || chanEndLit) { + chanStartLit = false; + chanEndLit = false; + redraw(); + } + ret = 1; + break; + } + + case FL_MOVE: { + mouseX = Fl::event_x(); + mouseY = Fl::event_y(); + + if (mouseOnStart()) { + chanStartLit = true; + redraw(); + } + else + if (chanStartLit) { + chanStartLit = false; + redraw(); + } + + if (mouseOnEnd()) { + chanEndLit = true; + redraw(); + } + else + if (chanEndLit) { + chanEndLit = false; + redraw(); + } + + if (mouseOnSelectionA()) + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + else + if (mouseOnSelectionB()) + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + else + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + + ret = 1; + break; + } + + case FL_DRAG: { + + /* here the mouse is on the chanStart tool */ + + if (chanStartLit && pushed) { + + chanStart = Fl::event_x() - x(); + + if (grid.snap) + chanStart = applySnap(chanStart); + + if (chanStart < 0) + chanStart = 0; + else + if (chanStart >= chanEnd) + chanStart = chanEnd-2; + + redraw(); + } + else + if (chanEndLit && pushed) { + + chanEnd = Fl::event_x() - x(); + + if (grid.snap) + chanEnd = applySnap(chanEnd); + + if (chanEnd >= data.size - 2) + chanEnd = data.size - 2; + else + if (chanEnd <= chanStart) + chanEnd = chanStart + 2; + + redraw(); + } + + /* here the mouse is on the waveform, i.e. a selection */ + + else + if (dragged) { + + selectionB = Fl::event_x() - x(); + + if (selectionB >= data.size) + selectionB = data.size; + + if (selectionB <= 0) + selectionB = 0; + + if (grid.snap) + selectionB = applySnap(selectionB); + + selectionB_abs = absolutePoint(selectionB); + redraw(); + } + + /* here the mouse is on a selection boundary i.e. resize */ + + else + if (resized) { + int pos = Fl::event_x() - x(); + if (mouseOnSelectionA()) { + selectionA = grid.snap ? applySnap(pos) : pos; + selectionA_abs = absolutePoint(selectionA); + } + else + if (mouseOnSelectionB()) { + selectionB = grid.snap ? applySnap(pos) : pos; + selectionB_abs = absolutePoint(selectionB); + } + redraw(); + } + mouseX = Fl::event_x(); + ret = 1; + break; + } + } + return ret; +} + + +/* ------------------------------------------------------------------ */ + +/* pixel snap disances (10px) must be equal to those defined in + * gWaveform::mouseOnSelectionA() and gWaverfrom::mouseOnSelectionB() */ +/* TODO - use constant for 10px */ + +int gWaveform::applySnap(int pos) +{ + for (unsigned i=0; i= grid.points.at(i) - 10 && + pos <= grid.points.at(i) + 10) + { + return grid.points.at(i); + } + } + return pos; +} + + +/* ------------------------------------------------------------------ */ + + +bool gWaveform::mouseOnStart() +{ + return mouseX-10 > chanStart + x() - BORDER && + mouseX-10 <= chanStart + x() - BORDER + FLAG_WIDTH && + mouseY > h() + y() - FLAG_HEIGHT; +} + + +/* ------------------------------------------------------------------ */ + + +bool gWaveform::mouseOnEnd() +{ + return mouseX-10 >= chanEnd + x() - BORDER - FLAG_WIDTH && + mouseX-10 <= chanEnd + x() - BORDER && + mouseY <= y() + FLAG_HEIGHT + 1; +} + + +/* ------------------------------------------------------------------ */ + +/* pixel boundaries (10px) must be equal to the snap factor distance + * defined in gWaveform::applySnap() */ + +bool gWaveform::mouseOnSelectionA() +{ + if (selectionA == selectionB) + return false; + return mouseX >= selectionA-10+x() && mouseX <= selectionA+10+x(); +} + + +bool gWaveform::mouseOnSelectionB() +{ + if (selectionA == selectionB) + return false; + return mouseX >= selectionB-10+x() && mouseX <= selectionB+10+x(); +} + + +/* ------------------------------------------------------------------ */ + + +int gWaveform::absolutePoint(int p) +{ + if (p <= 0) + return 0; + + if (p > data.size) + return chan->wave->size / 2; + + return (p * ratio) / 2; +} + + +/* ------------------------------------------------------------------ */ + + +int gWaveform::relativePoint(int p) +{ + return (ceilf(p / ratio)) * 2; +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::openEditMenu() +{ + if (selectionA == selectionB) + return; + + menuOpen = true; + + Fl_Menu_Item menu[] = { + {"Cut"}, + {"Trim"}, + {"Silence"}, + {"Fade in"}, + {"Fade out"}, + {"Smooth edges"}, + {"Set start/end here"}, + {0} + }; + + if (chan->status == STATUS_PLAY) { + menu[0].deactivate(); + menu[1].deactivate(); + } + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(11); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) { + menuOpen = false; + return; + } + + /* straightSel() to ensure that point A is always lower than B */ + + straightSel(); + + if (strcmp(m->label(), "Silence") == 0) { + wfx_silence(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); + + selectionA = 0; + selectionB = 0; + + stretchToWindow(); + redraw(); + menuOpen = false; + return; + } + + if (strcmp(m->label(), "Set start/end here") == 0) { + + glue_setBeginEndChannel( + (gdEditor *) window(), // parent + chan, + absolutePoint(selectionA) * 2, // stereo! + absolutePoint(selectionB) * 2, // stereo! + false, // no recalc (we do it here) + false // don't check + ); + + selectionA = 0; + selectionB = 0; + selectionA_abs = 0; + selectionB_abs = 0; + + recalcPoints(); + redraw(); + menuOpen = false; + return; + } + + if (strcmp(m->label(), "Cut") == 0) { + wfx_cut(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); + + /* for convenience reset start/end points */ + + glue_setBeginEndChannel( + (gdEditor *) window(), + chan, + 0, + chan->wave->size, + false); + + selectionA = 0; + selectionB = 0; + selectionA_abs = 0; + selectionB_abs = 0; + + setZoom(0); + + menuOpen = false; + return; + } + + if (strcmp(m->label(), "Trim") == 0) { + wfx_trim(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); + + glue_setBeginEndChannel( + (gdEditor *) window(), + chan, + 0, + chan->wave->size, + false); + + selectionA = 0; + selectionB = 0; + selectionA_abs = 0; + selectionB_abs = 0; + + stretchToWindow(); + menuOpen = false; + redraw(); + return; + } + + if (!strcmp(m->label(), "Fade in") || !strcmp(m->label(), "Fade out")) { + + int type = !strcmp(m->label(), "Fade in") ? 0 : 1; + wfx_fade(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB), type); + + selectionA = 0; + selectionB = 0; + + stretchToWindow(); + redraw(); + menuOpen = false; + return; + } + + if (!strcmp(m->label(), "Smooth edges")) { + + wfx_smooth(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB)); + + selectionA = 0; + selectionB = 0; + + stretchToWindow(); + redraw(); + menuOpen = false; + return; + } +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::straightSel() +{ + if (selectionA > selectionB) { + unsigned tmp = selectionB; + selectionB = selectionA; + selectionA = tmp; + } +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::setZoom(int type) +{ + int newSize; + if (type == -1) newSize = data.size*2; // zoom in + else newSize = data.size/2; // zoom out + + if (alloc(newSize)) { + size(data.size, h()); + + /* zoom to pointer */ + + int shift; + if (x() > 0) + shift = Fl::event_x() - x(); + else + if (type == -1) + shift = Fl::event_x() + abs(x()); + else + shift = (Fl::event_x() + abs(x())) / -2; + + if (x() - shift > BORDER) + shift = 0; + + position(x() - shift, y()); + + + /* avoid overflow when zooming out with scrollbar like that: + * |----------[scrollbar]| + * + * offset vs smaller: + * |[wave------------| offset > 0 smaller = false + * |[wave----] | offset < 0, smaller = true + * |-------------] | offset < 0, smaller = false */ + + int parentW = ((gWaveTools*)parent())->w(); + int thisW = x() + w() - BORDER; // visible width, not full width + + if (thisW < parentW) + position(x() + parentW - thisW, y()); + if (smaller()) + stretchToWindow(); + + redraw(); + } +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::stretchToWindow() +{ + int s = ((gWaveTools*)parent())->w(); + alloc(s); + position(BORDER, y()); + size(s, h()); +} + + +/* ------------------------------------------------------------------ */ + + +bool gWaveform::smaller() +{ + return w() < ((gWaveTools*)parent())->w(); +} + + +/* ------------------------------------------------------------------ */ + + +void gWaveform::setGridLevel(int l) +{ + grid.points.clear(); + grid.level = l; + alloc(data.size); + redraw(); +} diff --git a/src/gui/elems/ge_waveform.h b/src/gui/elems/ge_waveform.h new file mode 100644 index 0000000..45abd5b --- /dev/null +++ b/src/gui/elems/ge_waveform.h @@ -0,0 +1,194 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_waveform + * an element which represents a waveform. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef GE_WAVEFORM_H +#define GE_WAVEFORM_H + +#include +#include +#include +#include +#include "../../utils/utils.h" + + +#define FLAG_WIDTH 14 +#define FLAG_HEIGHT 12 +#define BORDER 8 // window border <-> widget border + + +class gWaveform : public Fl_Widget { + +private: + + /* data + * real graphic stuff from the underlying waveform */ + + struct data { + int *sup; + int *inf; + int size; + } data; + + /* grid */ + + struct grid { + bool snap; + int level; + gVector points; + } grid; + + /* chan + * chan in use. */ + + class SampleChannel *chan; + + /* menuOpen + * is the menu open? */ + + bool menuOpen; + + /* mouseOnStart/end + * is mouse on start or end flag? */ + + bool mouseOnStart(); + bool mouseOnEnd(); + + /* mouseOnSelectionA/B + * as above, for the selection */ + + bool mouseOnSelectionA(); + bool mouseOnSelectionB(); + + /* absolutePoint + * from a relative 'p' point (zoom affected) returns the same point + * zoom 1:1 based */ + + int absolutePoint(int p); + + /* relativePoint + * from an absolute 'p' point (1:1 zoom), returns the same point zoom + * affected */ + + int relativePoint(int p); + + /* straightSel + * helper function which flattens the selection if it was made from + * right to left (inverse selection) */ + + void straightSel(); + + /* freeData + * destroy any graphical buffer */ + + void freeData(); + + /* smaller + * is the waveform smaller than the parent window? */ + + bool smaller(); + + /* applySnap + * snap a point at 'pos' pixel */ + + int applySnap(int pos); + +public: + + gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0); + ~gWaveform(); + void draw(); + int handle(int e); + + /* alloc + * allocate memory for the picture */ + + int alloc(int datasize=0); + + /* recalcPoints + * re-calc chanStart, chanEnd, ... */ + + void recalcPoints(); + + /* openEditMenu + * show edit menu on right-click */ + + void openEditMenu(); + + /* displayRatio + * how much of the waveform is being displayed on screen */ + + inline float displayRatio() { return 1.0f / (data.size / (float) w()); }; + + /* zoom + * type == 1 : zoom out, type == -1: zoom in */ + + void setZoom(int type); + + /* strecthToWindow + * shrink or enlarge the waveform to match parent's width (gWaveTools) */ + + void stretchToWindow(); + + /* setGridLevel + * set a new frequency level for the grid. 0 means disabled. */ + + void setGridLevel(int l); + + inline void setSnap(bool v) { grid.snap = v; } + inline bool getSnap() { return grid.snap; } + + inline int getSize() { return data.size; } + + int chanStart; + bool chanStartLit; + int chanEnd; + bool chanEndLit; + bool pushed; + bool dragged; + bool resized; + + float ratio; + + /* TODO - useless! use Fl::mouse_x() and Fl::mouse_y() instead */ + int mouseX; // mouse pos for drag.n.drop + int mouseY; + + /* selectionA/B = portion of the selected wave + * " " "" " _abs = selectionA/B not affected by zoom */ + /** TODO - change selectionA to selectionA_rel + TODO - change selectionB to selectionB_rel */ + int selectionA; + int selectionB; + int selectionA_abs; + int selectionB_abs; +}; + + +#endif diff --git a/src/gui/elems/ge_window.cpp b/src/gui/elems/ge_window.cpp new file mode 100644 index 0000000..5d4763b --- /dev/null +++ b/src/gui/elems/ge_window.cpp @@ -0,0 +1,183 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_window + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include "ge_window.h" +#include "../../utils/log.h" + + +gWindow::gWindow(int x, int y, int w, int h, const char *title, int id) + : Fl_Double_Window(x, y, w, h, title), id(id), parent(NULL) { } + + +/* ------------------------------------------------------------------ */ + + +gWindow::gWindow(int w, int h, const char *title, int id) + : Fl_Double_Window(w, h, title), id(id), parent(NULL) { } + + +/* ------------------------------------------------------------------ */ + + +gWindow::~gWindow() { + + /* delete all subwindows in order to empty the stack */ + + for (unsigned i=0; igetParent() != NULL) + (child->getParent())->delSubWindow(child); +} + + +/* ------------------------------------------------------------------ */ + + +void gWindow::addSubWindow(gWindow *w) { + + /** TODO - useless: delete ---------------------------------------- */ + for (unsigned i=0; igetId() == subWindows.at(i)->getId()) { + //gLog("[gWindow] 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.add(w); + //debug(); +} + + +/* ------------------------------------------------------------------ */ + + +void gWindow::delSubWindow(gWindow *w) { + for (unsigned i=0; igetId() == subWindows.at(i)->getId()) { + delete subWindows.at(i); + subWindows.del(i); + //debug(); + return; + } + //debug(); +} + + +/* ------------------------------------------------------------------ */ + + +void gWindow::delSubWindow(int id) { + for (unsigned i=0; igetId() == id) { + delete subWindows.at(i); + subWindows.del(i); + //debug(); + return; + } + //debug(); +} + + +/* ------------------------------------------------------------------ */ + + +int gWindow::getId() { + return id; +} + + +/* ------------------------------------------------------------------ */ + + +void gWindow::setId(int id) { + this->id = id; +} + + +/* ------------------------------------------------------------------ */ + + +void gWindow::debug() { + gLog("---- window stack (id=%d): ----\n", getId()); + for (unsigned i=0; igetId()); + gLog("----\n"); +} + + +/* ------------------------------------------------------------------ */ + + +gWindow *gWindow::getParent() { + return parent; +} + + +/* ------------------------------------------------------------------ */ + + +void gWindow::setParent(gWindow *w) { + parent = w; +} + + +/* ------------------------------------------------------------------ */ + + +bool gWindow::hasWindow(int id) { + for (unsigned i=0; igetId()) + return true; + return false; +} + + +/* ------------------------------------------------------------------ */ + + +gWindow *gWindow::getChild(int id) { + for (unsigned i=0; igetId()) + return subWindows.at(i); + return NULL; +} diff --git a/src/gui/elems/ge_window.h b/src/gui/elems/ge_window.h new file mode 100644 index 0000000..74b5254 --- /dev/null +++ b/src/gui/elems/ge_window.h @@ -0,0 +1,73 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_window + * A custom window. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef __GE_WINDOW_H__ +#define __GE_WINDOW_H__ + + +#include +#include "../../utils/utils.h" + + +class gWindow : public Fl_Double_Window { + +protected: + gVector subWindows; + int id; + gWindow *parent; + +public: + gWindow(int x, int y, int w, int h, const char *title=0, int id=0); + gWindow(int w, int h, const char *title=0, int id=0); + ~gWindow(); + + static void cb_closeChild(Fl_Widget *v, void *p); + + void addSubWindow(gWindow *w); + void delSubWindow(gWindow *w); + void delSubWindow(int id); + + int getId(); + void setId(int id); + void debug(); + + void setParent(gWindow *); + gWindow *getParent(); + gWindow *getChild(int id); + + /* hasWindow + * true if the window with id 'id' exists in the stack. */ + + bool hasWindow(int id); + +}; + + +#endif diff --git a/src/gui_utils.cpp b/src/gui_utils.cpp deleted file mode 100644 index 4c3ffa7..0000000 --- a/src/gui_utils.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gui_utils - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "mixer.h" -#include "patch.h" -#include "gui_utils.h" -#include "graphics.h" -#include "gd_warnings.h" -#include "ge_window.h" -#include "ge_channel.h" -#include "gd_mainWindow.h" -#include "gg_keyboard.h" -#include "gd_actionEditor.h" -#include "recorder.h" -#include "wave.h" -#include "pluginHost.h" -#include "channel.h" -#include "log.h" -#include "conf.h" - - -extern Mixer G_Mixer; -extern unsigned G_beats; -extern bool G_audio_status; -extern Patch G_patch; -extern Conf G_conf; -extern uint32_t G_time; -extern gdMainWindow *mainWin; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -static int blinker = 0; - - -void gu_refresh() -{ - Fl::lock(); - - /* update dynamic elements: in and out meters, beat meter and - * each channel */ - - mainWin->inOut->refresh(); - mainWin->beatMeter->redraw(); - mainWin->keyboard->refreshColumns(); - - /* compute timer for blinker */ - - blinker++; - if (blinker > 12) - blinker = 0; - - /* redraw GUI */ - - Fl::unlock(); - Fl::awake(); -} - - -/* -------------------------------------------------------------------------- */ - - -int gu_getBlinker() -{ - return blinker; -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_updateControls() -{ - for (unsigned i=0; iguiChannel->update(); - - mainWin->inOut->setOutVol(G_Mixer.outVol); - mainWin->inOut->setInVol(G_Mixer.inVol); -#ifdef WITH_VST - mainWin->inOut->setMasterFxOutFull(G_PluginHost.masterOut.size > 0); - mainWin->inOut->setMasterFxInFull(G_PluginHost.masterIn.size > 0); -#endif - - mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); - mainWin->timing->setBpm(G_Mixer.bpm); - - /* if you reset to init state while the seq is in play: it's better to - * update the button status */ - - mainWin->controller->updatePlay(G_Mixer.running); - mainWin->controller->updateMetronome(0); -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_update_win_label(const char *c) -{ - std::string out = VERSIONE_STR; - out += " - "; - out += c; - mainWin->copy_label(out.c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_setFavicon(Fl_Window *w) -{ -#if defined(__linux__) - fl_open_display(); - Pixmap p, mask; - XpmCreatePixmapFromData( - fl_display, - DefaultRootWindow(fl_display), - (char **)giada_icon, - &p, - &mask, - NULL); - w->icon((char *)p); -#elif defined(_WIN32) - w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1))); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_openSubWindow(gWindow *parent, gWindow *child, int id) -{ - if (parent->hasWindow(id)) { - gLog("[GU] parent has subwindow with id=%d, deleting\n", id); - parent->delSubWindow(id); - } - child->setId(id); - parent->addSubWindow(child); -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_refreshActionEditor() -{ - /** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */ - - gdActionEditor *aeditor = (gdActionEditor*) mainWin->getChild(WID_ACTION_EDITOR); - if (aeditor) { - Channel *chan = aeditor->chan; - mainWin->delSubWindow(WID_ACTION_EDITOR); - gu_openSubWindow(mainWin, new gdActionEditor(chan), WID_ACTION_EDITOR); - } -} - - -/* -------------------------------------------------------------------------- */ - - -gWindow *gu_getSubwindow(gWindow *parent, int id) -{ - if (parent->hasWindow(id)) - return parent->getChild(id); - else - return NULL; -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_closeAllSubwindows() -{ - /* don't close WID_FILE_BROWSER, because it's the caller of this - * function */ - - mainWin->delSubWindow(WID_ACTION_EDITOR); - mainWin->delSubWindow(WID_SAMPLE_EDITOR); - mainWin->delSubWindow(WID_FX_LIST); - mainWin->delSubWindow(WID_FX); -} diff --git a/src/gui_utils.h b/src/gui_utils.h deleted file mode 100644 index 80c873c..0000000 --- a/src/gui_utils.h +++ /dev/null @@ -1,93 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gui_utils - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef GUI_UTILS_H -#define GUI_UTILS_H - -#include -#include -#include -#include -#ifdef __APPLE__ - #include // in osx, for basename() (but linux?) -#endif - -/* including stuff for the favicon (or whatever called) */ - -#if defined(_WIN32) - #include "resource.h" -#elif defined(__linux__) - #include -#endif - - -/* refresh - * refresh all GUI elements. */ - -void gu_refresh(); - -/* getBlinker -* return blinker value, used to make widgets blink. */ - -int gu_getBlinker(); - -/* updateControls - * update attributes of control elements (sample names, volumes, ...). - * Useful when loading a new patch. */ - -void gu_updateControls(); - -/* update_win_label - * update the name of the main window */ - -void gu_update_win_label(const char *c); - -void gu_setFavicon(Fl_Window *w); - -void gu_openSubWindow(class gWindow *parent, gWindow *child, int id); - -/* refreshActionEditor - * reload the action editor window by closing and reopening it. It's used - * when you delete some actions from the mainWindow and the action editor - * window is open. */ - -void gu_refreshActionEditor(); - - -/* closeAllSubwindows - * close all subwindows attached to mainWin. */ - -void gu_closeAllSubwindows(); - - -/* getSubwindow - * return a pointer to an open subwindow, otherwise NULL. */ - -gWindow *gu_getSubwindow(class gWindow *parent, int id); - -#endif diff --git a/src/init.cpp b/src/init.cpp deleted file mode 100644 index beeab04..0000000 --- a/src/init.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * init - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include "init.h" -#include "log.h" -#include "mixer.h" -#include "wave.h" -#include "const.h" -#include "utils.h" -#include "mixerHandler.h" -#include "patch.h" -#include "conf.h" -#include "pluginHost.h" -#include "recorder.h" -#include "midiMapConf.h" -#include "gd_mainWindow.h" -#include "gui_utils.h" -#include "gd_warnings.h" -#include "kernelMidi.h" - - -extern Mixer G_Mixer; -extern bool G_audio_status; -extern bool G_quit; -extern Patch G_Patch; -extern Conf G_Conf; -extern MidiMapConf G_MidiMap; -extern gdMainWindow *mainWin; - -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -void init_prepareParser() -{ - G_Conf.read(); - G_Patch.setDefault(); - if (!gLog_init(G_Conf.logMode)) - gLog("[init] log init failed! Using default stdout\n"); - time_t t; - time (&t); - gLog("[init] Giada "VERSIONE" - %s", ctime(&t)); - gLog("[init] configuration file ready\n"); -} - - -/* -------------------------------------------------------------------------- */ - - -void init_prepareKernelAudio() -{ - kernelAudio::openDevice( - G_Conf.soundSystem, - G_Conf.soundDeviceOut, - G_Conf.soundDeviceIn, - G_Conf.channelsOut, - G_Conf.channelsIn, - G_Conf.samplerate, - G_Conf.buffersize); - G_Mixer.init(); - recorder::init(); -} - - -/* -------------------------------------------------------------------------- */ - - -void init_prepareKernelMIDI() -{ - kernelMidi::setApi(G_Conf.midiSystem); - kernelMidi::openOutDevice(G_Conf.midiPortOut); - kernelMidi::openInDevice(G_Conf.midiPortIn); -} - - -/* -------------------------------------------------------------------------- */ - - -void init_prepareMidiMap() -{ - G_MidiMap.init(); - G_MidiMap.setDefault(); - G_MidiMap.readMap(G_Conf.midiMapPath); -} - - -/* -------------------------------------------------------------------------- */ - - -void init_startGUI(int argc, char **argv) -{ - char win_label[32]; - sprintf(win_label, "%s - %s", - VERSIONE_STR, - !strcmp(G_Patch.name, "") ? "(default patch)" : G_Patch.name); - - mainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, win_label, argc, argv); - mainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW, G_Conf.mainWindowH); - - /* never update the GUI elements if G_audio_status is bad, segfaults - * are around the corner */ - - if (G_audio_status) - gu_updateControls(); - - if (!G_audio_status) - gdAlert( - "Your soundcard isn't configured correctly.\n" - "Check the configuration and restart Giada." - ); -} - -/* -------------------------------------------------------------------------- */ - - -void init_startKernelAudio() -{ - if (G_audio_status) - kernelAudio::startStream(); - -#ifdef WITH_VST - G_PluginHost.allocBuffers(); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -void init_shutdown() -{ - G_quit = true; - - /* store position and size of the main window for the next startup */ - - G_Conf.mainWindowX = mainWin->x(); - G_Conf.mainWindowY = mainWin->y(); - G_Conf.mainWindowW = mainWin->w(); - G_Conf.mainWindowH = mainWin->h(); - - /* close any open subwindow, especially before cleaning PluginHost to - * avoid mess */ - - gu_closeAllSubwindows(); - gLog("[init] all subwindows closed\n"); - - /* write configuration file */ - - if (!G_Conf.write()) - gLog("[init] error while saving configuration file!\n"); - else - gLog("[init] configuration saved\n"); - - /* if G_audio_status we close the kernelAudio FIRST, THEN the mixer. - * The opposite could cause random segfaults (even now with RtAudio?). */ - - if (G_audio_status) { - kernelAudio::closeDevice(); - G_Mixer.close(); - gLog("[init] Mixer closed\n"); - } - - recorder::clearAll(); - gLog("[init] Recorder cleaned up\n"); - -#ifdef WITH_VST - G_PluginHost.freeAllStacks(); - gLog("[init] Plugin Host cleaned up\n"); -#endif - - gLog("[init] Giada "VERSIONE" closed\n\n"); - gLog_close(); -} diff --git a/src/init.h b/src/init.h deleted file mode 100644 index 0824f65..0000000 --- a/src/init.h +++ /dev/null @@ -1,50 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * init - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef INIT_H -#define INIT_H - - -#include -#include -#ifdef __APPLE__ - #include -#endif - - -void init_prepareParser(); -void init_startGUI(int argc, char **argv); -void init_prepareKernelAudio(); -void init_prepareKernelMIDI(); -void init_prepareMidiMap(); -void init_startKernelAudio(); -void init_shutdown(); - - -#endif diff --git a/src/kernelAudio.cpp b/src/kernelAudio.cpp deleted file mode 100644 index 094422c..0000000 --- a/src/kernelAudio.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * KernelAudio - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "kernelAudio.h" -#include "mixer.h" -#include "glue.h" -#include "conf.h" -#include "log.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern bool G_audio_status; - - -namespace kernelAudio { - -RtAudio *system = NULL; -unsigned numDevs = 0; -bool inputEnabled = 0; -unsigned realBufsize = 0; -int api = 0; - -int openDevice( - int _api, - int outDev, - int inDev, - int outChan, - int inChan, - int samplerate, - int buffersize) -{ - api = _api; - gLog("[KA] using system 0x%x\n", api); -#if defined(__linux__) - if (api == SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK)) - system = new RtAudio(RtAudio::UNIX_JACK); - else - if (api == SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA)) - system = new RtAudio(RtAudio::LINUX_ALSA); - else - if (api == SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE)) - system = new RtAudio(RtAudio::LINUX_PULSE); -#elif defined(_WIN32) - if (api == SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS)) - system = new RtAudio(RtAudio::WINDOWS_DS); - else - if (api == SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO)) - system = new RtAudio(RtAudio::WINDOWS_ASIO); -#elif defined(__APPLE__) - if (api == SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE)) - system = new RtAudio(RtAudio::MACOSX_CORE); -#endif - else { - G_audio_status = false; - return 0; - } - - - - //gLog("[KA] %d\n", sizeof(system->rtapi_)); - - gLog("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate); - - numDevs = system->getDeviceCount(); - - if (numDevs < 1) { - gLog("[KA] no devices found with this API\n"); - closeDevice(); - G_audio_status = false; - return 0; - } - else { - gLog("[KA] %d device(s) found\n", numDevs); - for (unsigned i=0; iopenStream( - &outParams, // output params - &inParams, // input params - RTAUDIO_FLOAT32, // audio format - samplerate, // sample rate - &realBufsize, // buffer size in byte - &G_Mixer.masterPlay, // audio callback - NULL, // user data (unused) - &options); - } - else { - system->openStream( - &outParams, // output params - NULL, // input params - RTAUDIO_FLOAT32, // audio format - samplerate, // sample rate - &realBufsize, // buffer size in byte - &G_Mixer.masterPlay, // audio callback - NULL, // user data (unused) - &options); - } - G_audio_status = true; - -#if defined(__linux__) - if (api == SYS_API_JACK) - jackSetSyncCb(); -#endif - - return 1; - } - catch (RtAudioError &e) { - gLog("[KA] system init error: %s\n", e.getMessage().c_str()); - closeDevice(); - G_audio_status = false; - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int startStream() { - try { - system->startStream(); - gLog("[KA] latency = %lu\n", system->getStreamLatency()); - return 1; - } - catch (RtAudioError &e) { - gLog("[KA] Start stream error: %s\n", e.getMessage().c_str()); - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int stopStream() { - try { - system->stopStream(); - return 1; - } - catch (RtAudioError &e) { - gLog("[KA] Stop stream error\n"); - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -const char *getDeviceName(unsigned dev) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).name.c_str(); - } - catch (RtAudioError &e) { - gLog("[KA] invalid device ID = %d\n", dev); - return NULL; - } -} - - -/* ------------------------------------------------------------------ */ - - -int closeDevice() { - if (system->isStreamOpen()) { -#if defined(__linux__) || defined(__APPLE__) - system->abortStream(); // stopStream seems to lock the thread -#elif defined(_WIN32) - system->stopStream(); // on Windows it's the opposite -#endif - system->closeStream(); - delete system; - system = NULL; - } - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -unsigned getMaxInChans(int dev) { - - if (dev == -1) return 0; - - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).inputChannels; - } - catch (RtAudioError &e) { - gLog("[KA] Unable to get input channels\n"); - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -unsigned getMaxOutChans(unsigned dev) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).outputChannels; - } - catch (RtAudioError &e) { - gLog("[KA] Unable to get output channels\n"); - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -bool isProbed(unsigned dev) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).probed; - } - catch (RtAudioError &e) { - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -unsigned getDuplexChans(unsigned dev) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).duplexChannels; - } - catch (RtAudioError &e) { - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -bool isDefaultIn(unsigned dev) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultInput; - } - catch (RtAudioError &e) { - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -bool isDefaultOut(unsigned dev) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultOutput; - } - catch (RtAudioError &e) { - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int getTotalFreqs(unsigned dev) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.size(); - } - catch (RtAudioError &e) { - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int getFreq(unsigned dev, int i) { - try { - return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.at(i); - } - catch (RtAudioError &e) { - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int getDefaultIn() { - return system->getDefaultInputDevice(); -} - -int getDefaultOut() { - return system->getDefaultOutputDevice(); -} - - -/* ------------------------------------------------------------------ */ - - -int getDeviceByName(const char *name) { - for (unsigned i=0; i APIs; - RtAudio::getCompiledApi(APIs); - for (unsigned i=0; i -#include -#include - -jack_client_t *jackGetHandle() { - return (jack_client_t*) system->rtapi_->__HACK__getJackClient(); -} - -void jackStart() { - if (api == SYS_API_JACK) { - jack_client_t *client = jackGetHandle(); - jack_transport_start(client); - } -} - - -void jackStop() { - if (api == SYS_API_JACK) { - jack_client_t *client = jackGetHandle(); - jack_transport_stop(client); - } -} - - -void jackSetSyncCb() { - jack_client_t *client = jackGetHandle(); - jack_set_sync_callback(client, jackSyncCb, NULL); - //jack_set_sync_timeout(client, 8); -} - - -int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, - void *arg) -{ - switch (state) { - case JackTransportStopped: - gLog("[KA] Jack transport stopped, frame=%d\n", pos->frame); - glue_stopSeq(false); // false = not from GUI - if (pos->frame == 0) - glue_rewindSeq(); - break; - - case JackTransportRolling: - gLog("[KA] Jack transport rolling\n"); - break; - - case JackTransportStarting: - gLog("[KA] Jack transport starting, frame=%d\n", pos->frame); - glue_startSeq(false); // false = not from GUI - if (pos->frame == 0) - glue_rewindSeq(); - break; - - default: - gLog("[KA] Jack transport [unknown]\n"); - } - return 1; -} - -#endif - -} - - diff --git a/src/kernelAudio.h b/src/kernelAudio.h deleted file mode 100644 index 0d566e8..0000000 --- a/src/kernelAudio.h +++ /dev/null @@ -1,96 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * KernelAudio - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef KERNELAUDIO_H -#define KERNELAUDIO_H - - -#include "rtaudio-mod/RtAudio.h" -#if defined(__linux__) - #include - #include - #include -#endif - - -namespace kernelAudio { - - int openDevice( - int api, - int outDev, - int inDev, - int outChan, - int inChan, - int samplerate, - int buffersize); - int closeDevice(); - - int startStream(); - int stopStream(); - - bool isProbed (unsigned dev); - bool isDefaultIn (unsigned dev); - bool isDefaultOut (unsigned dev); - const char *getDeviceName (unsigned dev); - unsigned getMaxInChans (int dev); - unsigned getMaxOutChans (unsigned dev); - unsigned getDuplexChans (unsigned dev); - int getTotalFreqs (unsigned dev); - int getFreq (unsigned dev, int i); - int getDeviceByName(const char *name); - int getDefaultOut (); - int getDefaultIn (); - bool hasAPI (int API); - - std::string getRtAudioVersion(); - -#ifdef __linux__ - jack_client_t *jackGetHandle(); - void jackStart(); - void jackStop(); - void jackSetSyncCb(); - int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg); -#endif - - /* *** how to avoid multiple definition of *** - * When you declare a variable in a header file, every source file that - * includes that header, either directly or indirectly, gets its own - * separate copy of the variable. Then when you go to link all the .o - * files together, the linker sees that the variable is instantiated - * in a bunch of .o files. Make it extern in the header file and - * instantiate it in memory.cpp. */ - - extern RtAudio *system; - extern unsigned numDevs; - extern bool inputEnabled; - extern unsigned realBufsize; // reale bufsize from the soundcard - extern int api; -} - -#endif diff --git a/src/kernelMidi.cpp b/src/kernelMidi.cpp deleted file mode 100644 index 1d4f99b..0000000 --- a/src/kernelMidi.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * KernelMidi - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include "kernelMidi.h" -#include "glue.h" -#include "mixer.h" -#include "channel.h" -#include "sampleChannel.h" -#include "pluginHost.h" -#include "conf.h" -#include "midiMapConf.h" -#include "log.h" - - -extern bool G_midiStatus; -extern Conf G_Conf; -extern Mixer G_Mixer; -extern MidiMapConf G_MidiMap; - -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -namespace kernelMidi -{ - -int api = 0; // one api for both in & out -RtMidiOut *midiOut = NULL; -RtMidiIn *midiIn = NULL; -unsigned numOutPorts = 0; -unsigned numInPorts = 0; - -cb_midiLearn *cb_learn = NULL; -void *cb_data = NULL; - - -/* -------------------------------------------------------------------------- */ - - -void startMidiLearn(cb_midiLearn *cb, void *data) -{ - cb_learn = cb; - cb_data = data; -} - - -/* -------------------------------------------------------------------------- */ - - -void stopMidiLearn() -{ - cb_learn = NULL; - cb_data = NULL; -} - - -/* -------------------------------------------------------------------------- */ - - -void setApi(int _api) -{ - api = api; - gLog("[KM] using system 0x%x\n", api); -} - - -/* -------------------------------------------------------------------------- */ - - -int openOutDevice(int port) -{ - try { - midiOut = new RtMidiOut((RtMidi::Api) api, "Giada MIDI Output"); - G_midiStatus = true; - } - catch (RtMidiError &error) { - gLog("[KM] MIDI out device error: %s\n", error.getMessage().c_str()); - G_midiStatus = false; - return 0; - } - - /* print output ports */ - - numOutPorts = midiOut->getPortCount(); - gLog("[KM] %d output MIDI ports found\n", numOutPorts); - for (unsigned i=0; i 0) { - try { - midiOut->openPort(port, getOutPortName(port)); - gLog("[KM] MIDI out port %d open\n", port); - - /* for each init command of MidiMap, send the init commands to the - external world. TODO 1 - we shold do that only if there is a map loaded - and available in MidiMap. - TODO 2 - move the map initialization to another function */ - - for(int i=0; igetPortCount(); - gLog("[KM] %d input MIDI ports found\n", numInPorts); - for (unsigned i=0; i 0) { - try { - midiIn->openPort(port, getInPortName(port)); - midiIn->ignoreTypes(true, false, true); // ignore all system/time msgs, for now - gLog("[KM] MIDI in port %d open\n", port); - midiIn->setCallback(&callback); - return 1; - } - catch (RtMidiError &error) { - gLog("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str()); - G_midiStatus = false; - return 0; - } - } - else - return 2; -} - - -/* -------------------------------------------------------------------------- */ - - -bool hasAPI(int API) -{ - std::vector APIs; - RtMidi::getCompiledApi(APIs); - for (unsigned i=0; igetPortName(p).c_str(); } - catch (RtMidiError &error) { return NULL; } -} - -const char *getInPortName(unsigned p) -{ - try { return midiIn->getPortName(p).c_str(); } - catch (RtMidiError &error) { return NULL; } -} - - -/* -------------------------------------------------------------------------- */ - - -void send(uint32_t data) -{ - if (!G_midiStatus) - return; - - std::vector msg(1, getB1(data)); - msg.push_back(getB2(data)); - msg.push_back(getB3(data)); - - midiOut->sendMessage(&msg); - gLog("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]); -} - - -/* -------------------------------------------------------------------------- */ - - -void send(int b1, int b2, int b3) -{ - if (!G_midiStatus) - return; - - std::vector msg(1, b1); - - if (b2 != -1) - msg.push_back(b2); - if (b3 != -1) - msg.push_back(b3); - - midiOut->sendMessage(&msg); - //gLog("[KM] send msg=(%X %X %X)\n", b1, b2, b3); -} - - -/* -------------------------------------------------------------------------- */ - - -void callback(double t, std::vector *msg, void *data) -{ - /* 0.8.0 - for now we handle other midi signals (common and real-time - * messages) as unknown, for debugging purposes */ - - if (msg->size() < 3) { - gLog("[KM] MIDI received - unkown signal - size=%d, value=0x", (int) msg->size()); - for (unsigned i=0; isize(); i++) - gLog("%X", (int) msg->at(i)); - gLog("\n"); - return; - } - - /* in this place we want to catch two things: a) note on/note off - * from a keyboard and b) knob/wheel/slider movements from a - * controller */ - - uint32_t input = getIValue(msg->at(0), msg->at(1), msg->at(2)); - uint32_t chan = input & 0x0F000000; - uint32_t value = input & 0x0000FF00; - uint32_t pure = 0x00; - if (!G_Conf.noNoteOff) - pure = input & 0xFFFF0000; // input without 'value' byte - else - pure = input & 0xFFFFFF00; // input with 'value' byte - - gLog("[KM] MIDI received - 0x%X (chan %d)", input, chan >> 24); - - /* start dispatcher. If midi learn is on don't parse channels, just - * learn incoming midi signal. Otherwise process master events first, - * then each channel in the stack. This way incoming signals don't - * get processed by glue_* when midi learning is on. */ - - if (cb_learn) { - gLog("\n"); - cb_learn(pure, cb_data); - } - else { - - /* process master events */ - - if (pure == G_Conf.midiInRewind) { - gLog(" >>> rewind (global) (pure=0x%X)", pure); - glue_rewindSeq(); - } - else if (pure == G_Conf.midiInStartStop) { - gLog(" >>> startStop (global) (pure=0x%X)", pure); - glue_startStopSeq(); - } - else if (pure == G_Conf.midiInActionRec) { - gLog(" >>> actionRec (global) (pure=0x%X)", pure); - glue_startStopActionRec(); - } - else if (pure == G_Conf.midiInInputRec) { - gLog(" >>> inputRec (global) (pure=0x%X)", pure); - glue_startStopInputRec(false, false); // update gui, no popup messages - } - else if (pure == G_Conf.midiInMetronome) { - gLog(" >>> metronome (global) (pure=0x%X)", pure); - glue_startStopMetronome(false); - } - else if (pure == G_Conf.midiInVolumeIn) { - float vf = (value >> 8)/127.0f; - gLog(" >>> input volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf); - glue_setInVol(vf, false); - } - else if (pure == G_Conf.midiInVolumeOut) { - float vf = (value >> 8)/127.0f; - gLog(" >>> output volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf); - glue_setOutVol(vf, false); - } - else if (pure == G_Conf.midiInBeatDouble) { - gLog(" >>> sequencer x2 (global) (pure=0x%X)", pure); - glue_beatsMultiply(); - } - else if (pure == G_Conf.midiInBeatHalf) { - gLog(" >>> sequencer /2 (global) (pure=0x%X)", pure); - glue_beatsDivide(); - } - - /* process channels */ - - for (unsigned i=0; imidiIn) continue; - - if (pure == ch->midiInKeyPress) { - gLog(" >>> keyPress, ch=%d (pure=0x%X)", ch->index, pure); - glue_keyPress(ch, false, false); - } - else if (pure == ch->midiInKeyRel) { - gLog(" >>> keyRel ch=%d (pure=0x%X)", ch->index, pure); - glue_keyRelease(ch, false, false); - } - else if (pure == ch->midiInMute) { - gLog(" >>> mute ch=%d (pure=0x%X)", ch->index, pure); - glue_setMute(ch, false); - } - else if (pure == ch->midiInSolo) { - gLog(" >>> solo ch=%d (pure=0x%X)", ch->index, pure); - ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false); - } - else if (pure == ch->midiInVolume) { - float vf = (value >> 8)/127.0f; - gLog(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf); - glue_setChanVol(ch, vf, false); - } - else if (pure == ((SampleChannel*)ch)->midiInPitch) { - float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0] - gLog(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf); - glue_setPitch(NULL, (SampleChannel*)ch, vf, false); - } - else if (pure == ((SampleChannel*)ch)->midiInReadActions) { - gLog(" >>> start/stop read actions ch=%d (pure=0x%X)", ch->index, pure); - glue_startStopReadingRecs((SampleChannel*)ch, false); - } - } - gLog("\n"); - } -} - - -/* -------------------------------------------------------------------------- */ - - -std::string getRtMidiVersion() -{ - return midiOut->getVersion(); -} - - -} // namespace diff --git a/src/kernelMidi.h b/src/kernelMidi.h deleted file mode 100644 index d446995..0000000 --- a/src/kernelMidi.h +++ /dev/null @@ -1,104 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * KernelMidi - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef KERNELMIDI_H -#define KERNELMIDI_H - - -#include -#include -#include "channel.h" - - -namespace kernelMidi { - - extern int api; // one api for both in & out - extern unsigned numOutPorts; - extern unsigned numInPorts; - - typedef void (cb_midiLearn) (uint32_t, void *); - - /* cb_learn - * callback prepared by the gdMidiGrabber window and called by - * kernelMidi. It contains things to do once the midi message has been - * stored. */ - - extern cb_midiLearn *cb_learn; - extern void *cb_data; - - void startMidiLearn(cb_midiLearn *cb, void *data); - void stopMidiLearn(); - - inline int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; } - inline int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; } - inline int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; } - - inline uint32_t getIValue(int b1, int b2, int b3) { - return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00); - } - - /* send - * send a MIDI message 's' (uint32_t). */ - - void send(uint32_t s); - - /* send (2) - * send separate bytes of MIDI message. */ - - void send(int b1, int b2=-1, int b3=-1); - - /* setApi - * set the Api in use for both in & out messages. */ - - void setApi(int api); - - /* open/close/in/outDevice */ - - int openOutDevice(int port); - int openInDevice(int port); - int closeInDevice(); - int closeOutDevice(); - - /* getIn/OutPortName - * return the name of the port 'p'. */ - - const char *getInPortName(unsigned p); - const char *getOutPortName(unsigned p); - - bool hasAPI(int API); - - /* callback - * master callback for input events. */ - - void callback(double t, std::vector *msg, void *data); - - std::string getRtMidiVersion(); -} - -#endif diff --git a/src/log.cpp b/src/log.cpp deleted file mode 100644 index 0f5be34..0000000 --- a/src/log.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * log - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include -#include -#include "log.h" -#include "const.h" -#include "utils.h" - - -static FILE *f; -static int mode; -static bool stat; - - -int gLog_init(int m) { - mode = m; - stat = true; - if (mode == LOG_MODE_FILE) { - std::string fpath = gGetHomePath() + "/giada.log"; - f = fopen(fpath.c_str(), "a"); - if (!f) { - stat = false; - return 0; - } - } - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -void gLog_close() { - if (mode == LOG_MODE_FILE) - fclose(f); -} - - -/* ------------------------------------------------------------------ */ - - -void gLog(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); - else - vprintf(format, args); - va_end(args); -} diff --git a/src/log.h b/src/log.h deleted file mode 100644 index c53f9c7..0000000 --- a/src/log.h +++ /dev/null @@ -1,45 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * log - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef __LOG_H__ -#define __LOG_H__ - - -/* init - * init logger. Mode defines where to write the output: LOG_MODE_STDOUT, - * LOG_MODE_FILE and LOG_MODE_MUTE. */ - -int gLog_init (int mode); - -void gLog_close(); - -void gLog(const char *format, ...); - - -#endif diff --git a/src/main.cpp b/src/main.cpp index e714162..a70c616 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,19 +29,19 @@ #if defined(__linux__) || defined(__APPLE__) #include #endif -#include "init.h" -#include "const.h" -#include "patch.h" -#include "conf.h" -#include "midiMapConf.h" -#include "mixer.h" -#include "mixerHandler.h" -#include "kernelAudio.h" -#include "recorder.h" -#include "gui_utils.h" -#include "gd_mainWindow.h" +#include "core/init.h" +#include "core/const.h" +#include "core/patch.h" +#include "core/conf.h" +#include "core/midiMapConf.h" +#include "core/mixer.h" +#include "core/mixerHandler.h" +#include "core/kernelAudio.h" +#include "core/recorder.h" +#include "utils/gui_utils.h" +#include "gui/dialogs/gd_mainWindow.h" #ifdef WITH_VST -#include "pluginHost.h" +#include "core/pluginHost.h" #endif diff --git a/src/midiChannel.cpp b/src/midiChannel.cpp deleted file mode 100644 index 6f38684..0000000 --- a/src/midiChannel.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * channel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "channel.h" -#include "midiChannel.h" -#include "pluginHost.h" -#include "patch.h" -#include "conf.h" -#include "kernelMidi.h" -#include "log.h" - - -extern Patch G_Patch; -extern Mixer G_Mixer; -extern Conf G_Conf; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -MidiChannel::MidiChannel(int bufferSize) - : Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize), - midiOut (false), - midiOutChan(MIDI_CHANS[0]) -{ -#ifdef WITH_VST // init VstEvents stack - freeVstMidiEvents(true); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -MidiChannel::~MidiChannel() {} - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST - -void MidiChannel::freeVstMidiEvents(bool init) -{ - if (events.numEvents == 0 && !init) - return; - memset(events.events, 0, sizeof(VstEvent*) * MAX_VST_EVENTS); - events.numEvents = 0; - events.reserved = 0; -} - -#endif - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST - -void MidiChannel::addVstMidiEvent(uint32_t msg) -{ - addVstMidiEvent(G_PluginHost.createVstMidiEvent(msg)); -} - -#endif - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST - -void MidiChannel::addVstMidiEvent(VstMidiEvent *e) -{ - if (events.numEvents < MAX_VST_EVENTS) { - events.events[events.numEvents] = (VstEvent*) e; - events.numEvents++; - /* - gLog("[MidiChannel] VstMidiEvent added - numEvents=%d offset=%d note=%d number=%d velo=%d\n", - events.numEvents, - e->deltaFrames, - e->midiData[0], - e->midiData[1], - e->midiData[2] - );*/ - } - else - gLog("[MidiChannel] channel %d VstEvents = %d > MAX_VST_EVENTS, nothing to do\n", index, events.numEvents); -} - -#endif - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::onBar(int frame) {} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::stop() {} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::empty() {} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::quantize(int index, int localFrame, int globalFrame) {} - - -/* -------------------------------------------------------------------------- */ - -#ifdef WITH_VST - -VstEvents *MidiChannel::getVstEvents() -{ - return (VstEvents *) &events; -} - -#endif - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::parseAction(recorder::action *a, int localFrame, int globalFrame) -{ - if (a->type == ACTION_MIDI) - sendMidi(a, localFrame/2); -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::onZero(int frame) -{ - if (status == STATUS_ENDING) { - status = STATUS_OFF; - sendMidiLplay(); - } - else - if (status == STATUS_WAIT) { - status = STATUS_PLAY; - sendMidiLplay(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::setMute(bool internal) -{ - mute = true; // internal mute does not exist for midi (for now) - if (midiOut) - kernelMidi::send(MIDI_ALL_NOTES_OFF); -#ifdef WITH_VST - addVstMidiEvent(MIDI_ALL_NOTES_OFF); -#endif - sendMidiLmute(); -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::unsetMute(bool internal) -{ - mute = false; // internal mute does not exist for midi (for now) - sendMidiLmute(); -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::process(float *buffer) -{ -#ifdef WITH_VST - G_PluginHost.processStack(vChan, PluginHost::CHANNEL, this); - freeVstMidiEvents(); -#endif - - for (int j=0; jiValue | MIDI_CHANS[midiOutChan]); - -#ifdef WITH_VST - a->event->deltaFrames = localFrame; - addVstMidiEvent(a->event); -#endif - } -} - - -void MidiChannel::sendMidi(uint32_t data) -{ - if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) { - if (midiOut) - kernelMidi::send(data | MIDI_CHANS[midiOutChan]); -#ifdef WITH_VST - addVstMidiEvent(data); -#endif - } -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::rewind() -{ - if (midiOut) - kernelMidi::send(MIDI_ALL_NOTES_OFF); -#ifdef WITH_VST - addVstMidiEvent(MIDI_ALL_NOTES_OFF); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiChannel::writePatch(FILE *fp, int i, bool isProject) -{ - Channel::writePatch(fp, i, isProject); - - fprintf(fp, "chanMidiOut%d=%u\n", i, midiOut); - fprintf(fp, "chanMidiOutChan%d=%u\n", i, midiOutChan); -} diff --git a/src/midiChannel.h b/src/midiChannel.h deleted file mode 100644 index e6e1a09..0000000 --- a/src/midiChannel.h +++ /dev/null @@ -1,136 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * channel - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef MIDI_CHANNEL_H -#define MIDI_CHANNEL_H - - -#ifdef WITH_VST - -/* before including aeffetx(x).h we must define __cdecl, otherwise VST - * headers can't be compiled correctly. In windows __cdecl is already - * defined. */ - - #ifdef __GNUC__ - #ifndef _WIN32 - #define __cdecl - #endif - #endif - #include "vst/aeffectx.h" - -#endif - - -class MidiChannel : public Channel { - -public: - - MidiChannel(int bufferSize); - ~MidiChannel(); - - bool midiOut; // enable midi output - uint8_t midiOutChan; // midi output channel - - void process (float *buffer); - void start (int frame, bool doQuantize); - void kill (int frame); - void empty (); - void stopBySeq (); - void stop (); - void rewind (); - void setMute (bool internal); - void unsetMute (bool internal); - int loadByPatch(const char *file, int i); - void writePatch (FILE *fp, int i, bool isProject); - void quantize (int index, int localFrame, int globalFrame); - void onZero (int frame); - void onBar (int frame); - void parseAction(recorder::action *a, int localFrame, int globalFrame); - - /* ---------------------------------------------------------------- */ - - /* sendMidi - * send Midi event to the outside world. */ - - void sendMidi(recorder::action *a, int localFrame); - void sendMidi(uint32_t data); - -#ifdef WITH_VST - - /* getVstEvents - * return a pointer to gVstEvents. */ - - VstEvents *getVstEvents(); - - /* freeVstMidiEvents - * empty vstEvents structure. Init: use the method for channel - * initialization. */ - - void freeVstMidiEvents(bool init=false); - - /* addVstMidiEvent - * take a composite MIDI event, decompose it and add it to channel. The - * other version creates a VstMidiEvent on the fly. */ - - void addVstMidiEvent(struct VstMidiEvent *e); - void addVstMidiEvent(uint32_t msg); - -#endif - - /* ---------------------------------------------------------------- */ - -#ifdef WITH_VST - - /* VST struct containing MIDI events. When ready, events are sent to - * each plugin in the channel. - * - * Anatomy of VstEvents - * -------------------- - * - * VstInt32 numEvents = number of Events in array - * VstIntPtr reserved = zero (Reserved for future use) - * VstEvent *events[n] = event pointer array, variable size - * - * Note that by default VstEvents only holds three events- if you want - * it to hold more, create an equivalent struct with a larger array, - * and then cast it to a VstEvents object when you've populated it. - * That's what we do with gVstEvents! */ - - struct gVstEvents { - VstInt32 numEvents; - VstIntPtr reserved; - VstEvent *events[MAX_VST_EVENTS]; - } events; - -#endif - -}; - - -#endif diff --git a/src/midiMapConf.cpp b/src/midiMapConf.cpp deleted file mode 100644 index 7fa8268..0000000 --- a/src/midiMapConf.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * midiMapConf - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include -#include -#include -#include -#include "midiMapConf.h" -#include "const.h" -#include "utils.h" -#include "log.h" - - -using std::string; - - -void MidiMapConf::init() -{ - midimapsPath = gGetHomePath() + "/midimaps/"; - - /* scan dir of midi maps and load the filenames into <>maps. */ - - gLog("[MidiMapConf::init] scanning midimaps directory...\n"); - - DIR *dp; - dirent *ep; - dp = opendir(midimapsPath.c_str()); - - if (!dp) { - gLog("[MidiMapConf::init] unable to scan midimaps directory!\n"); - return; - } - - while ((ep = readdir(dp))) { - if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, "..")) - continue; - - // TODO - check if is a valid midimap file (verify headers) - - gLog("[MidiMapConf::init] found midimap '%s'\n", ep->d_name); - - maps.add(ep->d_name); - } - - closedir(dp); -} - - -/* -------------------------------------------------------------------------- */ - - -void MidiMapConf::setDefault() -{ - brand = ""; - device = ""; - - for (int i=0; i ic; - gSplit(getValue("init_commands"), ";", &ic); - for (unsigned i=0; i. - * - * -------------------------------------------------------------------------- */ - - -#ifndef __MIDIMAPCONF_H__ -#define __MIDIMAPCONF_H__ - - -#include -#include -#include "dataStorage.h" -#include "utils.h" -#if defined(__APPLE__) -#include -#endif - - -using std::string; - - -class MidiMapConf : public DataStorage -{ -private: - - void close(); - void parse(string key, int *chan, uint32_t *msg, int *offset); - -public: - - static const int MAX_INIT_COMMANDS = 32; - static const int MAX_MIDI_BYTES = 4; - static const int MAX_MIDI_NIBBLES = 8; - - /* midimapsPath - * path of midimap files, different between OSes. */ - - string midimapsPath; - - /* maps - * Maps are the available .giadamap files. Each element of the vector - * represents a .giadamap filename. */ - - gVector maps; - - string brand; - string device; - - /* init_* - * init_commands. These messages are sent to the physical device as a wake up - * signal. */ - - int init_channels[MAX_INIT_COMMANDS]; - uint32_t init_messages[MAX_INIT_COMMANDS]; - - /* events - * [event]Channel: the MIDI output channel to send the event to - * [event]notePos: the byte where the note is stored ('nn' placeholder) - * [event]offset: the note offset (i.e. of 'nn' placeholder) */ - - int muteOnChan; - int muteOnOffset; - uint32_t muteOnMsg; - - int muteOffChan; - int muteOffOffset; - uint32_t muteOffMsg; - - int soloOnChan; - int soloOnOffset; - uint32_t soloOnMsg; - - int soloOffChan; - int soloOffOffset; - uint32_t soloOffMsg; - - int waitingChan; - int waitingOffset; - uint32_t waitingMsg; - - int playingChan; - int playingOffset; - uint32_t playingMsg; - - int stoppingChan; - int stoppingOffset; - uint32_t stoppingMsg; - - int stoppedChan; - int stoppedOffset; - uint32_t stoppedMsg; - - /* init - Parse the midi maps folders and find the available maps. */ - - void init(); - - /* setDefault - Set default values in case no maps are available/choosen. */ - - void setDefault(); - - /* readMap - Read a midi map from file 'file'. */ - - int readMap(string file); -}; - -#endif diff --git a/src/mixer.cpp b/src/mixer.cpp deleted file mode 100644 index 4c25065..0000000 --- a/src/mixer.cpp +++ /dev/null @@ -1,684 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * mixer - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "mixer.h" -#include "init.h" -#include "wave.h" -#include "gui_utils.h" -#include "recorder.h" -#include "pluginHost.h" -#include "patch.h" -#include "conf.h" -#include "mixerHandler.h" -#include "channel.h" -#include "sampleChannel.h" -#include "midiChannel.h" -#include "kernelMidi.h" -#include "log.h" - - -extern Mixer G_Mixer; -extern Patch G_Patch; -extern Conf G_Conf; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -Mixer::Mixer() {} -Mixer::~Mixer() {} - - -#define TICKSIZE 38 - - -float Mixer::tock[TICKSIZE] = { - 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 Mixer::tick[TICKSIZE] = { - 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 -}; - - -/* ------------------------------------------------------------------ */ - - -void Mixer::init() { - quanto = 1; - docross = false; - rewindWait = false; - running = false; - ready = true; - waitRec = 0; - actualFrame = 0; - bpm = DEFAULT_BPM; - bars = DEFAULT_BARS; - beats = DEFAULT_BEATS; - quantize = DEFAULT_QUANTIZE; - metronome = false; - - tickTracker = 0; - tockTracker = 0; - tickPlay = false; - tockPlay = false; - - outVol = DEFAULT_OUT_VOL; - inVol = DEFAULT_IN_VOL; - peakOut = 0.0f; - peakIn = 0.0f; - chanInput = NULL; - inputTracker = 0; - - actualBeat = 0; - - midiTCstep = 0; - midiTCrate = (G_Conf.samplerate / G_Conf.midiTCfps) * 2; // dealing with stereo vals - midiTCframes = 0; - midiTCseconds = 0; - midiTCminutes = 0; - midiTChours = 0; - - /* alloc virtual input channels. vChanInput malloc is done in - * updateFrameBars, because of its variable size */ - /** TODO - set kernelAudio::realBufsize * 2 as private member */ - - vChanInput = NULL; - vChanInToOut = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float)); - - pthread_mutex_init(&mutex_recs, NULL); - pthread_mutex_init(&mutex_chans, NULL); - pthread_mutex_init(&mutex_plugins, NULL); - - updateFrameBars(); - rewind(); -} - - -/* ------------------------------------------------------------------ */ - - -Channel *Mixer::addChannel(int type) { - - Channel *ch; - int bufferSize = kernelAudio::realBufsize*2; - - if (type == CHANNEL_SAMPLE) - ch = new SampleChannel(bufferSize); - else - ch = new MidiChannel(bufferSize); - - while (true) { - int lockStatus = pthread_mutex_trylock(&mutex_chans); - if (lockStatus == 0) { - channels.add(ch); - pthread_mutex_unlock(&mutex_chans); - break; - } - } - - ch->index = getNewIndex(); - gLog("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size); - return ch; -} - - -/* ------------------------------------------------------------------ */ - - -int Mixer::getNewIndex() { - - /* always skip last channel: it's the last one just added */ - - if (channels.size == 1) - return 0; - - int index = 0; - for (unsigned i=0; iindex > index) - index = channels.at(i)->index; - } - index += 1; - return index; -} - - -/* ------------------------------------------------------------------ */ - - -int Mixer::deleteChannel(Channel *ch) { - int lockStatus; - while (true) { - lockStatus = pthread_mutex_trylock(&mutex_chans); - if (lockStatus == 0) { - channels.del(ch); - delete ch; - pthread_mutex_unlock(&mutex_chans); - return 1; - } - //else - // gLog("[mixer::deleteChannel] waiting for mutex...\n"); - } -} - - -/* ------------------------------------------------------------------ */ - - -Channel *Mixer::getChannelByIndex(int index) { - for (unsigned i=0; iindex == index) - return channels.at(i); - gLog("[mixer::getChannelByIndex] channel at index %d not found!\n", index); - return NULL; -} - - -/* ------------------------------------------------------------------ */ - - -void Mixer::sendMIDIsync() { - - if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) { - if (actualFrame % (framesPerBeat/24) == 0) - kernelMidi::send(MIDI_CLOCK, -1, -1); - } - else - if (G_Conf.midiSync == MIDI_SYNC_MTC_M) { - - /* check if a new timecode frame has passed. If so, send MIDI TC - * quarter frames. 8 quarter frames, divided in two branches: - * 1-4 and 5-8. We check timecode frame's parity: if even, send - * range 1-4, if odd send 5-8. */ - - if (actualFrame % midiTCrate == 0) { - - /* frame low nibble - * frame high nibble - * seconds low nibble - * seconds high nibble */ - - if (midiTCframes % 2 == 0) { - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1); - } - - /* minutes low nibble - * minutes high nibble - * hours low nibble - * hours high nibble SMPTE frame rate */ - - else { - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1); - } - - midiTCframes++; - - /* check if total timecode frames are greater than timecode fps: - * if so, a second has passed */ - - if (midiTCframes > G_Conf.midiTCfps) { - midiTCframes = 0; - midiTCseconds++; - if (midiTCseconds >= 60) { - midiTCminutes++; - midiTCseconds = 0; - if (midiTCminutes >= 60) { - midiTChours++; - midiTCminutes = 0; - } - } - //gLog("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes); - } - } - } -} - - -/* ------------------------------------------------------------------ */ - - -void Mixer::sendMIDIrewind() { - - midiTCframes = 0; - midiTCseconds = 0; - midiTCminutes = 0; - midiTChours = 0; - - /* For cueing the slave to a particular start point, Quarter Frame - * messages are not used. Instead, an MTC Full Frame message should - * be sent. The Full Frame is a SysEx message that encodes the entire - * SMPTE time in one message */ - - if (G_Conf.midiSync == MIDI_SYNC_MTC_M) { - kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0 - kernelMidi::send(0x01, 0x01, 0x00); // hours 0 - kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0 - kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex - } -} - -/* ------------------------------------------------------------------ */ - - -int Mixer::masterPlay( - void *out_buf, void *in_buf, unsigned n_frames, - double streamTime, RtAudioStreamStatus status, void *userData) { - return G_Mixer.__masterPlay(out_buf, in_buf, n_frames); -} - - -/* ------------------------------------------------------------------ */ - - -int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames) { - - if (!ready) - return 0; - - float *outBuf = ((float *) out_buf); - float *inBuf = ((float *) in_buf); - bufferFrames *= 2; // stereo - peakOut = 0.0f; // reset peak calculator - peakIn = 0.0f; // reset peak calculator - - /* always clean each buffer */ - - memset(outBuf, 0, sizeof(float) * bufferFrames); // out - memset(vChanInToOut, 0, sizeof(float) * bufferFrames); // inToOut vChan - - pthread_mutex_lock(&mutex_chans); - for (unsigned i=0; itype == CHANNEL_SAMPLE) - ((SampleChannel*)channels.at(i))->clear(); - pthread_mutex_unlock(&mutex_chans); - - for (unsigned j=0; j peakIn) - peakIn = inBuf[j] * inVol; - - /* "hear what you're playing" - process, copy and paste the input buffer - * onto the output buffer */ - - if (inToOut) { - vChanInToOut[j] = inBuf[j] * inVol; - vChanInToOut[j+1] = inBuf[j+1] * inVol; - } - } - - /* operations to do if the sequencer is running: - * - compute quantizer - * - time check for LOOP_REPEAT - * - reset loops at beat 0 - * - read recorded actions - * - reset actualFrame */ - - if (running) { - - /* line in recording */ - - if (chanInput != NULL && kernelAudio::inputEnabled) { - - /* delay comp: wait until waitRec reaches delayComp. WaitRec - * returns to 0 in mixerHandler, as soon as the recording ends */ - - if (waitRec < G_Conf.delayComp) - waitRec += 2; - else { - vChanInput[inputTracker] += inBuf[j] * inVol; - vChanInput[inputTracker+1] += inBuf[j+1] * inVol; - inputTracker += 2; - if (inputTracker >= totalFrames) - inputTracker = 0; - } - } - - /* quantizer computations: quantize rewind and all channels. */ - - if (quantize > 0 && quanto > 0) { - if (actualFrame % (quanto) == 0) { // is quanto! - if (rewindWait) { - rewindWait = false; - rewind(); - } - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; kquantize(k, j, actualFrame); // j == localFrame - pthread_mutex_unlock(&mutex_chans); - } - } - - /* reset LOOP_REPEAT, if a bar has passed */ - - if (actualFrame % framesPerBar == 0 && actualFrame != 0) { - if (metronome) - tickPlay = true; - - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; konBar(j); - pthread_mutex_unlock(&mutex_chans); - } - - /* reset loops on beat 0 */ - - if (actualFrame == 0) { - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; konZero(j); - pthread_mutex_unlock(&mutex_chans); - } - - /* reading all actions recorded */ - - pthread_mutex_lock(&mutex_recs); - for (unsigned y=0; ychan; - Channel *ch = getChannelByIndex(index); - ch->parseAction(recorder::global.at(y).at(z), j, actualFrame); - } - break; - } - } - pthread_mutex_unlock(&mutex_recs); - - /* increase actualFrame */ - - actualFrame += 2; - - /* if actualFrame > totalFrames the sequencer returns to frame 0, - * beat 0. This must be the last operation. */ - - if (actualFrame > totalFrames) { - actualFrame = 0; - actualBeat = 0; - } - else - if (actualFrame % framesPerBeat == 0 && actualFrame > 0) { - actualBeat++; - - /* avoid tick and tock to overlap when a new bar has passed (which - * is also a beat) */ - - if (metronome && !tickPlay) - tockPlay = true; - } - - sendMIDIsync(); - - } // if (running) - - /* sum channels, CHANNEL_SAMPLE only */ - - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; ktype == CHANNEL_SAMPLE) - ((SampleChannel*)channels.at(k))->sum(j, running); - } - pthread_mutex_unlock(&mutex_chans); - - /* metronome play */ - /** FIXME - move this one after the peak meter calculation */ - - if (tockPlay) { - outBuf[j] += tock[tockTracker]; - outBuf[j+1] += tock[tockTracker]; - tockTracker++; - if (tockTracker >= TICKSIZE-1) { - tockPlay = false; - tockTracker = 0; - } - } - if (tickPlay) { - outBuf[j] += tick[tickTracker]; - outBuf[j+1] += tick[tickTracker]; - tickTracker++; - if (tickTracker >= TICKSIZE-1) { - tickPlay = false; - tickTracker = 0; - } - } - } // end loop J - - - /* final loop: sum virtual channels and process plugins. */ - - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; kprocess(outBuf); - pthread_mutex_unlock(&mutex_chans); - - /* processing fxs master in & out, if any. */ - -#ifdef WITH_VST - pthread_mutex_lock(&mutex_plugins); - G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT); - G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN); - pthread_mutex_unlock(&mutex_plugins); -#endif - - /* post processing master fx + peak calculation. */ - - for (unsigned j=0; j peakOut) - peakOut = outBuf[j]; - - if (G_Conf.limitOutput) { - if (outBuf[j] > 1.0f) - outBuf[j] = 1.0f; - else if (outBuf[j] < -1.0f) - outBuf[j] = -1.0f; - - if (outBuf[j+1] > 1.0f) - outBuf[j+1] = 1.0f; - else if (outBuf[j+1] < -1.0f) - outBuf[j+1] = -1.0f; - } - } - - return 0; -} - - -/* ------------------------------------------------------------------ */ - - -void Mixer::updateFrameBars() { - - /* seconds ....... total time of play (in seconds) of the whole - * sequencer. 60 / bpm == how many seconds lasts one bpm - * totalFrames ... number of frames in the whole sequencer, x2 because - * it's stereo - * framesPerBar .. n. of frames within a bar - * framesPerBeat . n. of frames within a beat */ - - float seconds = (60.0f / bpm) * beats; - totalFrames = G_Conf.samplerate * seconds * 2; - framesPerBar = totalFrames / bars; - framesPerBeat = totalFrames / beats; - framesInSequencer = framesPerBeat * MAX_BEATS; - - /* big troubles if frames are odd. */ - - if (totalFrames % 2 != 0) - totalFrames--; - if (framesPerBar % 2 != 0) - framesPerBar--; - if (framesPerBeat % 2 != 0) - framesPerBeat--; - - updateQuanto(); - - /* realloc input virtual channel, if not NULL. TotalFrames is changed! */ - - if (vChanInput != NULL) - free(vChanInput); - vChanInput = (float*) malloc(totalFrames * sizeof(float)); - if (!vChanInput) - gLog("[Mixer] vChanInput realloc error!\n"); -} - - -/* ------------------------------------------------------------------ */ - - -int Mixer::close() { - running = false; - while (channels.size > 0) - deleteChannel(channels.at(0)); - free(vChanInput); - free(vChanInToOut); - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -bool Mixer::isSilent() { - for (unsigned i=0; istatus == STATUS_PLAY) - return false; - return true; -} - - -/* ------------------------------------------------------------------ */ - - -void Mixer::rewind() { - - actualFrame = 0; - actualBeat = 0; - - if (running) - for (unsigned i=0; irewind(); - - sendMIDIrewind(); -} - - -/* ------------------------------------------------------------------ */ - - -void Mixer::updateQuanto() { - - /* big troubles if frames are odd. */ - - if (quantize != 0) - quanto = framesPerBeat / quantize; - if (quanto % 2 != 0) - quanto++; -} - - -/* ------------------------------------------------------------------ */ - - -bool Mixer::hasLogicalSamples() { - for (unsigned i=0; itype == CHANNEL_SAMPLE) - if (((SampleChannel*)channels.at(i))->wave) - if (((SampleChannel*)channels.at(i))->wave->isLogical) - return true; - return false; -} - - -/* ------------------------------------------------------------------ */ - - -bool Mixer::hasEditedSamples() { - for (unsigned i=0; itype == CHANNEL_SAMPLE) - if (((SampleChannel*)channels.at(i))->wave) - if (((SampleChannel*)channels.at(i))->wave->isEdited) - return true; - return false; -} - - -/* ------------------------------------------------------------------ */ - - -bool Mixer::mergeVirtualInput() { - if (vChanInput == NULL) { - gLog("[Mixer] virtual input channel not alloc'd\n"); - return false; - } - else { -#ifdef WITH_VST - G_PluginHost.processStackOffline(vChanInput, PluginHost::MASTER_IN, 0, totalFrames); -#endif - int numFrames = totalFrames*sizeof(float); - memcpy(chanInput->wave->data, vChanInput, numFrames); - memset(vChanInput, 0, numFrames); // clear vchan - return true; - } -} diff --git a/src/mixer.h b/src/mixer.h deleted file mode 100644 index 586bacc..0000000 --- a/src/mixer.h +++ /dev/null @@ -1,209 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * mixer - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef MIXER_H -#define MIXER_H - -#include -#include -#include "const.h" -#include "kernelAudio.h" -#include "utils.h" - - -class Mixer { - -public: - - Mixer(); - ~Mixer(); - - void init(); - int close(); - - /* addChannel - * add a new channel without any wave inside of it. */ - - class Channel *addChannel(int type); - - /* deleteChannel - * completely remove a channel from the stack. */ - - int deleteChannel(class Channel *ch); - - /* masterPlay - * core method (callback) */ - - static int masterPlay( - void *out_buf, void *in_buf, unsigned n_frames, - double streamTime, RtAudioStreamStatus status, void *userData - ); - int __masterPlay(void *out_buf, void *in_buf, unsigned n_frames); - - /* updateFrameBars - * updates bpm, frames, beats and so on. */ - - void updateFrameBars(); - - /* isSilent - * is mixer silent? */ - - bool isSilent(); - - /* rewind - * rewind sequencer to sample 0. */ - - void rewind(); - - /* updateQuanto - * recomputes the quanto between two quantizations */ - - void updateQuanto(); - - /* hasLogicalSamples - * true if 1 or more samples are logical (memory only, such as takes) */ - - bool hasLogicalSamples(); - - /* hasEditedSamples - * true if 1 or more samples was edited via gEditor */ - - bool hasEditedSamples(); - - /* mergeVirtualInput - * memcpy the virtual channel input in the channel designed for input - * recording. Called by mixerHandler on stopInputRec() */ - - bool mergeVirtualInput(); - - /* getChannelByIndex - * return channel with given index 'i'. */ - - Channel *getChannelByIndex(int i); - - inline Channel* getLastChannel() { return channels.at(channels.size-1); } - - - /* ---------------------------------------------------------------- */ - - - enum { // const - what to do when a fadeout ends - DO_STOP = 0x01, - DO_MUTE = 0x02, - DO_MUTE_I = 0x04 - }; - - enum { // const - fade types - FADEOUT = 0x01, - XFADE = 0x02 - }; - - gVector channels; - - bool running; - bool ready; - float *vChanInput; // virtual channel for recording - float *vChanInToOut; // virtual channel in->out bridge (hear what you're playin) - int frameSize; - float outVol; - float inVol; - float peakOut; - float peakIn; - int quanto; - char quantize; - bool metronome; - float bpm; - int bars; - int beats; - int waitRec; // delayComp guard - - bool docross; // crossfade guard - bool rewindWait; // rewind guard, if quantized - - int framesPerBar; // frames in one bar - int framesPerBeat; // frames in one beat - int framesInSequencer; // frames in the whole sequencer - int totalFrames; // frames in the selected range (e.g. 4/4) - int actualFrame; - int actualBeat; - -#define TICKSIZE 38 - static float tock[TICKSIZE]; - static float tick[TICKSIZE]; - int tickTracker, tockTracker; - bool tickPlay, tockPlay; // 1 = play, 0 = stop - - /* chanInput - * the active channel during a recording. NULL = no channels active */ - - class SampleChannel *chanInput; - - /* inputTracker - * position of the sample in the input side (recording) */ - - int inputTracker; - - /* inToOut - * copy, process and paste the input into the output, in order to - * obtain a "hear what you're playing" feature. */ - - bool inToOut; - - pthread_mutex_t mutex_recs; - pthread_mutex_t mutex_chans; - pthread_mutex_t mutex_plugins; - - -private: - - int midiTCstep; // part of MTC to send (0 to 7) - int midiTCrate; // send MTC data every midiTCrate frames - int midiTCframes; - int midiTCseconds; - int midiTCminutes; - int midiTChours; - - /* getNewIndex - * compute new index value for new channels */ - - int getNewIndex(); - - /* sendMIDIsync - * generate MIDI sync output data */ - - void sendMIDIsync(); - - /* sendMIDIrewind - * rewind timecode to beat 0 and also send a MTC full frame to cue - * the slave */ - - void sendMIDIrewind(); -}; - -#endif diff --git a/src/mixerHandler.cpp b/src/mixerHandler.cpp deleted file mode 100644 index 4bd520f..0000000 --- a/src/mixerHandler.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * mixerHandler - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#if defined(__linux__) - #include - #include - #include -#endif - -#include "mixerHandler.h" -#include "kernelMidi.h" -#include "mixer.h" -#include "const.h" -#include "utils.h" -#include "init.h" -#include "pluginHost.h" -#include "plugin.h" -#include "waveFx.h" -#include "glue.h" -#include "conf.h" -#include "patch.h" -#include "recorder.h" -#include "channel.h" -#include "sampleChannel.h" -#include "wave.h" -#include "log.h" - - -extern Mixer G_Mixer; -extern Patch G_Patch; -extern Conf G_Conf; - -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -void mh_stopSequencer() -{ - G_Mixer.running = false; - for (unsigned i=0; istopBySeq(); -} - - -/* -------------------------------------------------------------------------- */ - - -void mh_clear() -{ - G_Mixer.running = false; - while (G_Mixer.channels.size > 0) - G_Mixer.channels.del(0U); // unsigned -} - - -/* -------------------------------------------------------------------------- */ - - -bool mh_uniqueSolo(Channel *ch) -{ - int solos = 0; - for (unsigned i=0; isolo) solos++; - if (solos > 1) return false; - } - return true; -} - - -/* -------------------------------------------------------------------------- */ - - -/** TODO - revision needed: mh should not call glue_addChannel */ - -void mh_loadPatch(bool isProject, const char *projPath) -{ - G_Mixer.init(); - G_Mixer.ready = false; // put it in wait mode - - int numChans = G_Patch.getNumChans(); - for (int i=0; i= 0.10.0 - old stuff, remove backward compatibility */ - - if (isProject && G_Patch.version >= 0.63f) - sprintf(smpPath, "%s%s%s", gDirname(projPath).c_str(), gGetSlash().c_str(), G_Patch.getSamplePath(i).c_str()); - else - sprintf(smpPath, "%s", G_Patch.getSamplePath(i).c_str()); - - ch->loadByPatch(smpPath, i); - } - - G_Mixer.outVol = G_Patch.getOutVol(); - G_Mixer.inVol = G_Patch.getInVol(); - G_Mixer.bpm = G_Patch.getBpm(); - G_Mixer.bars = G_Patch.getBars(); - G_Mixer.beats = G_Patch.getBeats(); - G_Mixer.quantize = G_Patch.getQuantize(); - G_Mixer.metronome = G_Patch.getMetronome(); - G_Patch.lastTakeId = G_Patch.getLastTakeId(); - G_Patch.samplerate = G_Patch.getSamplerate(); - - /* rewind and update frames in Mixer (it's vital) */ - - G_Mixer.rewind(); - G_Mixer.updateFrameBars(); - G_Mixer.ready = true; -} - - -/* -------------------------------------------------------------------------- */ - - -void mh_rewindSequencer() -{ - if (G_Mixer.quantize > 0 && G_Mixer.running) // quantize rewind - G_Mixer.rewindWait = true; - else - G_Mixer.rewind(); -} - - -/* -------------------------------------------------------------------------- */ - - -SampleChannel *mh_startInputRec() -{ - /* search for the next available channel */ - - SampleChannel *chan = NULL; - for (unsigned i=0; itype == CHANNEL_SAMPLE) - if (((SampleChannel*) G_Mixer.channels.at(i))->canInputRec()) { - chan = (SampleChannel*) G_Mixer.channels.at(i); - break; - } - } - - /* no chans available? */ - - if (chan == NULL) - return NULL; - - Wave *w = new Wave(); - if (!w->allocEmpty(G_Mixer.totalFrames)) - return NULL; - - /* increase lastTakeId until the sample name TAKE-[n] is unique */ - - char name[32]; - sprintf(name, "TAKE-%d", G_Patch.lastTakeId); - while (!mh_uniqueSamplename(chan, name)) { - G_Patch.lastTakeId++; - sprintf(name, "TAKE-%d", G_Patch.lastTakeId); - } - - chan->allocEmpty(G_Mixer.totalFrames, G_Patch.lastTakeId); - G_Mixer.chanInput = chan; - - /* start to write from the actualFrame, not the beginning */ - /** FIXME: move this before wave allocation*/ - - G_Mixer.inputTracker = G_Mixer.actualFrame; - - gLog( - "[mh] start input recs using chan %d with size %d, frame=%d\n", - chan->index, G_Mixer.totalFrames, G_Mixer.inputTracker - ); - - return chan; -} - - -/* -------------------------------------------------------------------------- */ - - -SampleChannel *mh_stopInputRec() -{ - gLog("[mh] stop input recs\n"); - G_Mixer.mergeVirtualInput(); - SampleChannel *ch = G_Mixer.chanInput; - G_Mixer.chanInput = NULL; - G_Mixer.waitRec = 0; // if delay compensation is in use - return ch; -} - - -/* -------------------------------------------------------------------------- */ - - -bool mh_uniqueSamplename(SampleChannel *ch, const char *name) -{ - for (unsigned i=0; itype == CHANNEL_SAMPLE) { - SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i); - if (other->wave != NULL) - if (!strcmp(name, other->wave->name.c_str())) - return false; - } - } - } - return true; -} diff --git a/src/mixerHandler.h b/src/mixerHandler.h deleted file mode 100644 index f842004..0000000 --- a/src/mixerHandler.h +++ /dev/null @@ -1,76 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * mixerHandler - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef MIXERHANDLER_H -#define MIXERHANDLER_H - - -#include "recorder.h" - - -/* stopSequencer - * stop the sequencer, with special case if samplesStopOnSeqHalt is - * true. */ - -void mh_stopSequencer(); - -void mh_rewindSequencer(); - -/* clear - * stop everything and clear all channels. */ - -void mh_clear(); - -/* uniqueSolo - * true if ch is the only solo'd channel in mixer. */ - -bool mh_uniqueSolo(class Channel *ch); - -/* loadPatch - * load a path or a project (if isProject) into Mixer. If isProject, path - * must contain the address of the project folder. */ - -void mh_loadPatch(bool isProject, const char *projPath=0); - -/* startInputRec - record from line in - * creates a new empty wave in the first available channels and returns - * the chan number chosen, otherwise -1 if there are no more empty - * channels available. */ - -SampleChannel *mh_startInputRec(); - -SampleChannel *mh_stopInputRec(); - -/* uniqueSamplename - * return true if samplename 'n' is unique. Requires SampleChannel *ch - * in order to skip check against itself. */ - -bool mh_uniqueSamplename(class SampleChannel *ch, const char *name); - -#endif diff --git a/src/patch.cpp b/src/patch.cpp deleted file mode 100644 index 005c691..0000000 --- a/src/patch.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * patch - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include "patch.h" -#include "init.h" -#include "recorder.h" -#include "utils.h" -#include "conf.h" -#include "pluginHost.h" -#include "wave.h" -#include "mixer.h" -#include "channel.h" -#include "log.h" -#include "gd_mainWindow.h" -#include "gg_keyboard.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif -extern gdMainWindow *mainWin; - - -int Patch::open(const char *file) -{ - fp = fopen(file, "r"); - if (fp == NULL) - return PATCH_UNREADABLE; - - if (getValue("header") != "GIADAPTC") - return PATCH_INVALID; - - version = atof(getValue("versionf").c_str()); - gLog("[PATCH] open patch version %f\n", version); - if (version == 0.0) - gLog("[PATCH] patch < 0.6.1, backward compatibility mode\n"); - - return PATCH_OPEN_OK; -} - - -/* -------------------------------------------------------------------------- */ - - -void Patch::setDefault() -{ - name[0] = '\0'; - lastTakeId = 0; - samplerate = DEFAULT_SAMPLERATE; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::close() -{ - return fclose(fp); -} - - -/* -------------------------------------------------------------------------- */ - - -void Patch::getName() -{ - std::string out = getValue("patchname"); - strncpy(name, out.c_str(), MAX_PATCHNAME_LEN); -} - - -/* -------------------------------------------------------------------------- */ - - -std::string Patch::getSamplePath(int c) -{ - char tmp[16]; - sprintf(tmp, "samplepath%d", c); - return getValue(tmp); -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getPitch(int c) -{ - char tmp[16]; - sprintf(tmp, "chanPitch%d", c); - float out = atof(getValue(tmp).c_str()); - if (out > 2.0f || out < 0.1f) - return 1.0f; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getNumChans() -{ - if (version == 0.0) // backward compatibility with version < 0.6.1 - return 32; - return atoi(getValue("channels").c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getNumColumns() -{ - return atoi(getValue("columns").c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getColumn(int c) -{ - if (version == 0.0) // backward compatibility with version < 0.6.1 - return 0; - char tmp[16]; - sprintf(tmp, "chanColumn%d", c); - return atoi(getValue(tmp).c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getIndex(int c) -{ - if (version == 0.0) // backward compatibility with version < 0.6.1 - return c; - - char tmp[16]; - sprintf(tmp, "chanIndex%d", c); - return atoi(getValue(tmp).c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getVol(int c) -{ - char tmp[16]; - sprintf(tmp, "chanvol%d", c); - float out = atof(getValue(tmp).c_str()); - if (out > 1.0f || out < 0.0f) - return DEFAULT_VOL; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getMode(int c) -{ - char tmp[16]; - sprintf(tmp, "chanmode%d", c); - int out = atoi(getValue(tmp).c_str()); - if (out & (LOOP_ANY | SINGLE_ANY)) - return out; - return DEFAULT_CHANMODE; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getMute(int c) -{ - char tmp[16]; - sprintf(tmp, "chanMute%d", c); - return atoi(getValue(tmp).c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getMute_s(int c) -{ - char tmp[16]; - sprintf(tmp, "chanMute_s%d", c); - return atoi(getValue(tmp).c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getSolo(int c) -{ - char tmp[16]; - sprintf(tmp, "chanSolo%d", c); - return atoi(getValue(tmp).c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getType(int c) -{ - char tmp[16]; - sprintf(tmp, "chanType%d", c); - int out = atoi(getValue(tmp).c_str()); - if (out == 0) - return CHANNEL_SAMPLE; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getBegin(int c) -{ - char tmp[16]; - if (version < 0.73f) - sprintf(tmp, "chanstart%d", c); - else - sprintf(tmp, "chanBegin%d", c); - int out = atoi(getValue(tmp).c_str()); - if (out < 0) - return 0; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getEnd(int c, unsigned size) -{ - char tmp[16]; - sprintf(tmp, "chanend%d", c); - - /* if chanEnd doesn't exist, it returns an atoi(empty string) == 0. - * good in theory, a disaster in practice. */ - - std::string val = getValue(tmp); - if (val == "") - return size; - - unsigned out = atoi(val.c_str()); - if (out <= 0 || out > size) - return size; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getBoost(int c) -{ - char tmp[16]; - sprintf(tmp, "chanBoost%d", c); - float out = atof(getValue(tmp).c_str()); - if (out < 1.0f) - return DEFAULT_BOOST; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getPanLeft(int c) -{ - char tmp[16]; - sprintf(tmp, "chanPanLeft%d", c); - std::string val = getValue(tmp); - if (val == "") - return 1.0f; - - float out = atof(val.c_str()); - if (out < 0.0f || out > 1.0f) - return 1.0f; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getKey(int c) -{ - if (version == 0.0) // backward compatibility with version < 0.6.1 - return 0; - char tmp[16]; - sprintf(tmp, "chanKey%d", c); - return atoi(getValue(tmp).c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getPanRight(int c) -{ - char tmp[16]; - sprintf(tmp, "chanPanRight%d", c); - std::string val = getValue(tmp); - if (val == "") - return 1.0f; - - float out = atof(val.c_str()); - if (out < 0.0f || out > 1.0f) - return 1.0f; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -bool Patch::getRecActive(int c) -{ - char tmp[16]; - sprintf(tmp, "chanRecActive%d", c); - return atoi(getValue(tmp).c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getOutVol() -{ - return atof(getValue("outVol").c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getInVol() -{ - return atof(getValue("inVol").c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -float Patch::getBpm() -{ - float out = atof(getValue("bpm").c_str()); - if (out < 20.0f || out > 999.0f) - return DEFAULT_BPM; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getBars() -{ - int out = atoi(getValue("bars").c_str()); - if (out <= 0 || out > 32) - return DEFAULT_BARS; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getBeats() -{ - int out = atoi(getValue("beats").c_str()); - if (out <= 0 || out > 32) - return DEFAULT_BEATS; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getQuantize() -{ - int out = atoi(getValue("quantize").c_str()); - if (out < 0 || out > 8) - return DEFAULT_QUANTIZE; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -bool Patch::getMetronome() -{ - bool out = atoi(getValue("metronome").c_str()); - if (out != true || out != false) - return false; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getLastTakeId() -{ - return atoi(getValue("lastTakeId").c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::getSamplerate() -{ - int out = atoi(getValue("samplerate").c_str()); - if (out <= 0) - return DEFAULT_SAMPLERATE; - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -uint32_t Patch::getMidiValue(int i, const char *c) -{ - char tmp[32]; - sprintf(tmp, "chanMidi%s%d", c, i); - return strtoul(getValue(tmp).c_str(), NULL, 10); -} - - -/* -------------------------------------------------------------------------- */ - - -int Patch::readRecs() -{ - gLog("[PATCH] Reading recs...\n"); - - unsigned numrecs = atoi(getValue("numrecs").c_str()); - - for (unsigned i=0; istatus & ~(STATUS_WRONG | STATUS_MISSING | STATUS_EMPTY)) { - if (version < 0.83f) - recorder::rec(ch->index, type, frame, iValue_fix, fValue); - else - recorder::rec(ch->index, type, frame, iValue, fValue); - } - } - } - return 1; -} - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST -int Patch::readPlugins() -{ - gLog("[PATCH] Reading plugins...\n"); - - int globalOut = 1; - - /* master plugins */ - - globalOut &= readMasterPlugins(PluginHost::MASTER_IN); - globalOut &= readMasterPlugins(PluginHost::MASTER_OUT); - - /* channel plugins */ - - for (unsigned i=0; iindex); - int np = atoi(getValue(tmp).c_str()); - - for (int j=0; jindex, j); - int out = G_PluginHost.addPlugin(getValue(tmp).c_str(), PluginHost::CHANNEL, ch); - if (out != 0) { - sprintf(tmp, "chan%d_p%dnumParams", ch->index, j); - int nparam = atoi(getValue(tmp).c_str()); - Plugin *pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch); - sprintf(tmp, "chan%d_p%dbypass", ch->index, j); - pPlugin->bypass = atoi(getValue(tmp).c_str()); - for (int k=0; kindex, j, k); - float pval = atof(getValue(tmp).c_str()); - pPlugin->setParam(k, pval); - } - } - globalOut &= out; - } - } - return globalOut; -} -#endif - - -/* -------------------------------------------------------------------------- */ - - -int Patch::write(const char *file, const char *name, bool project) -{ - fp = fopen(file, "w"); - if (fp == NULL) - return 0; - - fprintf(fp, "# --- Giada patch file --- \n"); - fprintf(fp, "header=GIADAPTC\n"); - fprintf(fp, "version=%s\n", VERSIONE); - fprintf(fp, "versionf=%f\n", VERSIONE_FLOAT); - fprintf(fp, "patchname=%s\n", name); - fprintf(fp, "bpm=%f\n", G_Mixer.bpm); - fprintf(fp, "bars=%d\n", G_Mixer.bars); - fprintf(fp, "beats=%d\n", G_Mixer.beats); - fprintf(fp, "quantize=%d\n", G_Mixer.quantize); - fprintf(fp, "outVol=%f\n", G_Mixer.outVol); - fprintf(fp, "inVol=%f\n", G_Mixer.inVol); - fprintf(fp, "metronome=%d\n", G_Mixer.metronome); - fprintf(fp, "lastTakeId=%d\n", lastTakeId); - fprintf(fp, "samplerate=%d\n", G_Conf.samplerate); // original samplerate when the patch was saved - fprintf(fp, "channels=%d\n", G_Mixer.channels.size); - fprintf(fp, "columns=%d\n", mainWin->keyboard->getTotalColumns()); - - for (unsigned i=0; iwritePatch(fp, i, project); - } - - /* writing recs. Warning: channel index is not mixer.channels.at(chan), - * but mixer.channels.at(chan)->index! */ - - fprintf(fp, "# --- actions --- \n"); - fprintf(fp, "numrecs=%d\n", recorder::global.size); - for (unsigned i=0; ichan, - recorder::global.at(i).at(k)->type, - recorder::global.at(i).at(k)->fValue, - recorder::global.at(i).at(k)->iValue); - } - } - -#ifdef WITH_VST - - /* writing master VST parameters */ - - writeMasterPlugins(PluginHost::MASTER_IN); - writeMasterPlugins(PluginHost::MASTER_OUT); - - /* writing VST parameters, channels. chan%d is mixer::channels.at(%d)->index, - * not mixer::chanels.at(%d)! */ - - int numPlugs; - int numParams; - Plugin *pPlugin; - - fprintf(fp, "# --- VST / channels --- \n"); - for (unsigned i=0; iindex, numPlugs); - - for (int j=0; jstatus) { - gLog("[PATCH] Plugin %d is in a bad status, skip writing params\n", i); - continue; - } - fprintf(fp, "chan%d_p%dpathfile=%s\n", ch->index, j, pPlugin->pathfile); - fprintf(fp, "chan%d_p%dbypass=%d\n", ch->index, j, pPlugin->bypass); - numParams = pPlugin->getNumParams(); - fprintf(fp, "chan%d_p%dnumParams=%d\n", ch->index, j, numParams); - - for (int k=0; kindex, j, k, pPlugin->getParam(k)); - } - } - -#endif - - fclose(fp); - return 1; -} - - -/* -------------------------------------------------------------------------- */ - -#ifdef WITH_VST - -int Patch::readMasterPlugins(int type) -{ - int nmp; - char chr; - int res = 1; - - if (type == PluginHost::MASTER_IN) { - chr = 'I'; - nmp = atoi(getValue("masterIPlugins").c_str()); - } - else { - chr = 'O'; - nmp = atoi(getValue("masterOPlugins").c_str()); - } - - for (int i=0; ibypass = atoi(getValue(tmp).c_str()); - sprintf(tmp, "master%c_p%dnumParams", chr, i); - int nparam = atoi(getValue(tmp).c_str()); - for (int j=0; jsetParam(j, pval); - } - } - res &= out; - } - - return res; -} - - -/* -------------------------------------------------------------------------- */ - - -void Patch::writeMasterPlugins(int type) -{ - char chr; - - if (type == PluginHost::MASTER_IN) { - fprintf(fp, "# --- VST / master in --- \n"); - chr = 'I'; - } - else { - fprintf(fp, "# --- VST / master out --- \n"); - chr = 'O'; - } - - int nmp = G_PluginHost.countPlugins(type); - fprintf(fp, "master%cPlugins=%d\n", chr, nmp); - - for (int i=0; istatus) { - gLog("[PATCH] Plugin %d is in a bad status, skip writing params\n", i); - continue; - } - - fprintf(fp, "master%c_p%dpathfile=%s\n", chr, i, pPlugin->pathfile); - fprintf(fp, "master%c_p%dbypass=%d\n", chr, i, pPlugin->bypass); - int numParams = pPlugin->getNumParams(); - fprintf(fp, "master%c_p%dnumParams=%d\n", chr, i, numParams); - - for (int j=0; jgetParam(j)); - } -} - -#endif diff --git a/src/patch.h b/src/patch.h deleted file mode 100644 index e431b85..0000000 --- a/src/patch.h +++ /dev/null @@ -1,95 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * patch - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef __PATCH_H__ -#define __PATCH_H__ - -#include -#include -#include -#include "dataStorage.h" -#include "const.h" - - -class Patch : public DataStorage { - -private: - int readMasterPlugins(int type); - void writeMasterPlugins(int type); - -public: - - char name[MAX_PATCHNAME_LEN]; - float version; - int lastTakeId; - int samplerate; - - int open(const char *file); - void setDefault(); - int close(); - - void getName (); - int getNumChans (); - int getNumColumns (); - std::string getSamplePath (int i); - float getVol (int i); - int getMode (int i); - int getMute (int i); - int getMute_s (int i); - int getSolo (int i); - int getBegin (int i); - int getEnd (int i, unsigned sampleSize); - float getBoost (int i); - float getPanLeft (int i); - float getPanRight (int i); - float getPitch (int i); - bool getRecActive (int i); - int getColumn (int i); - int getIndex (int i); - int getType (int i); - int getKey (int i); - uint32_t getMidiValue (int i, const char *c); - float getOutVol (); - float getInVol (); - float getBpm (); - int getBars (); - int getBeats (); - int getQuantize (); - bool getMetronome (); - int getLastTakeId (); - int getSamplerate (); - - int write(const char *file, const char *name, bool isProject); - int readRecs(); -#ifdef WITH_VST - int readPlugins(); -#endif -}; - -#endif diff --git a/src/plugin.cpp b/src/plugin.cpp deleted file mode 100644 index 17cace3..0000000 --- a/src/plugin.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifdef WITH_VST - - -#include "plugin.h" -#include "log.h" - - -int Plugin::id_generator = 0; - - -/* ------------------------------------------------------------------ */ - - -Plugin::Plugin() - : module (NULL), - entryPoint(NULL), - plugin (NULL), - id (id_generator++), - program (-1), - bypass (false), - suspended (false) -{} - - -/* ------------------------------------------------------------------ */ - - -Plugin::~Plugin() { - unload(); -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::unload() { - - if (module == NULL) - return 1; - -#if defined(_WIN32) - - FreeLibrary((HMODULE)module); // FIXME - error checking - return 1; - -#elif defined(__linux__) - - return dlclose(module) == 0 ? 1 : 0; - -#elif defined(__APPLE__) - - /* we must unload bundles but because bundles may be in use for other - plug-in types it is important (and mandatory on certain plug-ins, - e.g. Korg) to do a check on the retain count. */ - - CFIndex retainCount = CFGetRetainCount(module); - - if (retainCount == 1) { - gLog("[plugin] retainCount == 1, can unload dlyb\n"); - CFBundleUnloadExecutable(module); - CFRelease(module); - } - else - gLog("[plugin] retainCount > 1 (%d), leave dlyb alone\n", (int) retainCount); - - return 1; - -#endif -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::load(const char *fname) { - - strcpy(pathfile, fname); - -#if defined(_WIN32) - - module = LoadLibrary(pathfile); - -#elif defined(__linux__) - - module = dlopen(pathfile, RTLD_LAZY); - -#elif defined(__APPLE__) - - /* creates the path to the bundle. In OSX vsts are stored inside the - * so-called bundles, just a directory with '.vst' extension. Finally - * we open the bundle with CFBundleCreate. */ - - CFStringRef pathStr = CFStringCreateWithCString(NULL, pathfile, kCFStringEncodingASCII); - CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStr, kCFURLPOSIXPathStyle, true); - if(bundleUrl == NULL) { - gLog("[plugin] unable to create URL reference for plugin\n"); - status = 0; - return 0; - } - module = CFBundleCreate(kCFAllocatorDefault, bundleUrl); - -#endif - - if (module) { - - /* release (free) any old string */ - -#ifdef __APPLE__ - CFRelease(pathStr); - CFRelease(bundleUrl); -#endif - //strcpy(pathfile, fname); ??????????? - status = 1; - return 1; - } - else { - -#if defined(_WIN32) - - gLog("[plugin] unable to load %s, error: %d\n", fname, (int) GetLastError()); - -#elif defined(__linux__) - - gLog("[plugin] unable to load %s, error: %s\n", fname, dlerror()); - -#elif defined(__APPLE__) - - gLog("[plugin] unable to create bundle reference\n"); - CFRelease(pathStr); - CFRelease(bundleUrl); - -#endif - status = 0; - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::init(VstIntPtr VSTCALLBACK (*HostCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)) { - -#if defined(_WIN32) - - entryPoint = (vstPluginFuncPtr) GetProcAddress((HMODULE)module, "VSTPluginMain"); - if (!entryPoint) - entryPoint = (vstPluginFuncPtr) GetProcAddress((HMODULE)module, "main"); - -#elif defined(__linux__) - - /* bad stuff here: main() is a function pointer, dlsym(module, "main") - * returns a pointer to an object (void*) which should be casted to - * a pointer to function (main(), precisely). Unfortunately the standard - * forbids the conversion from void* to function pointer. So we do a raw - * mem copy from tmp to entryPoint. */ - - void *tmp; - tmp = dlsym(module, "VSTPluginMain"); - if (!tmp) - tmp = dlsym(module, "main"); - memcpy(&entryPoint, &tmp, sizeof(tmp)); - -#elif defined(__APPLE__) - - /* same also for Unix/OSX. */ - - void *tmp = NULL; - tmp = CFBundleGetFunctionPointerForName(module, CFSTR("VSTPluginMain")); - - if (!tmp) { - gLog("[plugin] entryPoint 'VSTPluginMain' not found\n"); - tmp = CFBundleGetFunctionPointerForName(module, CFSTR("main_macho")); // VST SDK < 2.4 - } - if (!tmp) { - gLog("[plugin] entryPoint 'main_macho' not found\n"); - tmp = CFBundleGetFunctionPointerForName(module, CFSTR("main")); - } - if (tmp) - memcpy(&entryPoint, &tmp, sizeof(tmp)); - else - gLog("[plugin] entryPoint 'main' not found\n"); - -#endif - - /* if entry point is found, add to plugin a pointer to hostCallback. Or - * in other words bind the callback to the plugin. */ - - if (entryPoint) { - gLog("[plugin] entryPoint found\n"); - plugin = entryPoint(HostCallback); - if (!plugin) { - gLog("[plugin] failed to create effect instance!\n"); - return 0; - } - } - else { - gLog("[plugin] entryPoint not found, unable to proceed\n"); - return 0; - } - - - /* check the magicNumber */ - /** WARNING: on Windows one can load any DLL! Why!?! */ - - if(plugin->magic == kEffectMagic) { - gLog("[plugin] magic number OK\n"); - return 1; - } - else { - gLog("[plugin] magic number is bad\n"); - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::setup(int samplerate, int frames) { - - /* init plugin through the dispatcher with some basic infos */ - - plugin->dispatcher(plugin, effOpen, 0, 0, 0, 0); - plugin->dispatcher(plugin, effSetSampleRate, 0, 0, 0, samplerate); - plugin->dispatcher(plugin, effSetBlockSize, 0, frames, 0, 0); - - /* check SDK compatibility */ - - if (getSDKVersion() != kVstVersion) - gLog("[plugin] warning: different VST version (host: %d, plugin: %d)\n", kVstVersion, getSDKVersion()); - - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -AEffect *Plugin::getPlugin() { - return plugin; -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::getId() { return id; } - - -/* ------------------------------------------------------------------ */ - -int Plugin::getSDKVersion() { - return plugin->dispatcher(plugin, effGetVstVersion, 0, 0, 0, 0); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getName(char *out) { - char tmp[128] = "\0"; - plugin->dispatcher(plugin, effGetEffectName, 0, 0, tmp, 0); - tmp[kVstMaxEffectNameLen-1] = '\0'; - strncpy(out, tmp, kVstMaxEffectNameLen); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getVendor(char *out) { - char tmp[128] = "\0"; - plugin->dispatcher(plugin, effGetVendorString, 0, 0, tmp, 0); - tmp[kVstMaxVendorStrLen-1] = '\0'; - strncpy(out, tmp, kVstMaxVendorStrLen); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getProduct(char *out) { - char tmp[128] = "\0"; - plugin->dispatcher(plugin, effGetProductString, 0, 0, tmp, 0); - tmp[kVstMaxProductStrLen-1] = '\0'; - strncpy(out, tmp, kVstMaxProductStrLen); -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::getNumPrograms() { return plugin->numPrograms; } - - -/* ------------------------------------------------------------------ */ - - -int Plugin::setProgram(int index) { - plugin->dispatcher(plugin, effBeginSetProgram, 0, 0, 0, 0); - plugin->dispatcher(plugin, effSetProgram, 0, index, 0, 0); - gLog("[plugin] program changed, index %d\n", index); - program = index; - return plugin->dispatcher(plugin, effEndSetProgram, 0, 0, 0, 0); -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::getNumParams() { return plugin->numParams; } - - -/* ------------------------------------------------------------------ */ - - -int Plugin::getNumInputs() { return plugin->numInputs; } - - -/* ------------------------------------------------------------------ */ - - -int Plugin::getNumOutputs() { return plugin->numOutputs; } - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getProgramName(int index, char *out) { - char tmp[128] = "\0"; - plugin->dispatcher(plugin, effGetProgramNameIndexed, index, 0, tmp, 0); - tmp[kVstMaxProgNameLen-1] = '\0'; - strncpy(out, tmp, kVstMaxProgNameLen); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getParamName(int index, char *out) { - char tmp[128] = "\0"; - plugin->dispatcher(plugin, effGetParamName, index, 0, tmp, 0); - tmp[kVstMaxParamStrLen-1] = '\0'; - strncpy(out, tmp, kVstMaxParamStrLen); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getParamLabel(int index, char *out) { - char tmp[128] = "\0"; - plugin->dispatcher(plugin, effGetParamLabel, index, 0, tmp, 0); - tmp[kVstMaxParamStrLen-1] = '\0'; - strncpy(out, tmp, kVstMaxParamStrLen); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getParamDisplay(int index, char *out) { - char tmp[128] = "\0"; - plugin->dispatcher(plugin, effGetParamDisplay, index, 0, tmp, 0); - tmp[kVstMaxParamStrLen-1] = '\0'; - strncpy(out, tmp, kVstMaxParamStrLen); -} - - -/* ------------------------------------------------------------------ */ - - -float Plugin::getParam(int index) { - return plugin->getParameter(plugin, index); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::setParam(int index, float value) { - plugin->setParameter(plugin, index, value); -} - - -/* ------------------------------------------------------------------ */ - - -bool Plugin::hasGui() { - return plugin->flags & effFlagsHasEditor; -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::openGui(void *w) { - long val = 0; -#ifdef __linux__ - val = (long) w; -#endif - plugin->dispatcher(plugin, effEditOpen, 0, val, w, 0); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::closeGui() { - plugin->dispatcher(plugin, effEditClose, 0, 0, 0, 0); -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::getGuiWidth() { - ERect *pErect = NULL; - plugin->dispatcher(plugin, effEditGetRect, 0, 0, &pErect, 0); - return pErect->top + pErect->right; -} - - -/* ------------------------------------------------------------------ */ - - -int Plugin::getGuiHeight() { - ERect *pErect = NULL; - plugin->dispatcher(plugin, effEditGetRect, 0, 0, &pErect, 0); - return pErect->top + pErect->bottom; -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::idle() { - plugin->dispatcher(plugin, effEditIdle, 0, 0, NULL, 0); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::processAudio(float **in, float **out, long frames) { - plugin->processReplacing(plugin, in, out, frames); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::processEvents(VstEvents *events) { - plugin->dispatcher(plugin, effProcessEvents, 0, 0, events, 0.0); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::resume() { - plugin->dispatcher(plugin, effMainsChanged, 0, 1, 0, 0); - suspended = false; -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::suspend() { - plugin->dispatcher(plugin, effMainsChanged, 0, 0, 0, 0); - suspended = true; -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::close() { - plugin->dispatcher(plugin, effClose, 0, 0, 0, 0); -} - - -/* ------------------------------------------------------------------ */ - - -void Plugin::getRect(ERect **out) { - plugin->dispatcher(plugin, effEditGetRect, 0, 0, out, 0); -} - - -#endif diff --git a/src/plugin.h b/src/plugin.h deleted file mode 100644 index 0dbaef7..0000000 --- a/src/plugin.h +++ /dev/null @@ -1,168 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * plugin - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifdef WITH_VST - -#ifndef __PLUGIN_H -#define __PLUGIN_H - -#include - -/* before including aeffetx(x).h we must define __cdecl, otherwise VST - * headers can't be compiled correctly. In windows __cdecl is already - * defined. */ - -#ifdef __GNUC__ - #ifndef _WIN32 - #define __cdecl - #endif -#endif - -#include "vst/aeffectx.h" - -#if defined(_WIN32) - #include -#elif defined(__linux__) - #include - #include -#elif defined(__APPLE__) - #include -#endif - -#include // PATH_MAX - - -// Plugin's entry point -typedef AEffect* (*vstPluginFuncPtr)(audioMasterCallback host); - - -class Plugin { - -private: - -#if defined(_WIN32) || defined(__linux__) - void *module; // dll, so, ... -#elif defined(__APPLE__) - CFBundleRef module; // OSX bundle -#endif - - vstPluginFuncPtr entryPoint; // VST entry point - AEffect *plugin; // real plugin - - /* each plugin has an unique ID */ - - static int id_generator; - int id; - - /* program - * selected program. -1 if no program available */ - - int program; - - /* unload - * free plugin from memory. Calls dlclose and similars. */ - - int unload(); - -public: - Plugin(); - ~Plugin(); - - int load(const char *fname); - int init(VstIntPtr VSTCALLBACK (*HostCallback)(AEffect*, VstInt32, VstInt32, VstIntPtr, void*, float)); - int setup(int samplerate, int frames); - - AEffect *getPlugin(); - - /* get[Item]. - * Wrappers called by host when it wants info from the plugin. */ - - int getId(); - int getSDKVersion(); - void getName (char *out); - void getVendor (char *out); - void getProduct(char *out); - int getNumPrograms(); // list all programs - int setProgram(int index); // load a program - int getNumParams(); - int getNumInputs(); - int getNumOutputs(); - void getProgramName(int index, char *out); // program = preset - void getParamName(int index, char *out); - void getParamLabel(int index, char *out); // parameter's value(0, -39, ...) - void getParamDisplay(int index, char *out); // parameter's unit measurement (dB, Pan, ...) - float getParam(int index); - void getRect(ERect **out); - void setParam(int index, float value); - - bool hasGui(); - void openGui(void *w); - void closeGui(); - int getGuiWidth(); - int getGuiHeight(); - void idle(); - - void processAudio (float **in, float **out, long frames); - void processEvents(VstEvents *events); - void resume(); - void suspend(); - void close(); - - inline int getProgram() { return program; } - - /* there's a specific opcode for the bypass, but we don't trust the - * plugin's developers. */ - - bool bypass; - - /* the status of the plugin: - * 1: ok - * 0: missing (file not found) */ - - int status; - - /* suspended - * true after suspend(), false after resume(). A suspended plugin isn't - * processed by pluginHost. */ - - bool suspended; - - /* pathfile - * full filename path */ - - char pathfile[PATH_MAX]; - - /* window - * plugin must know its window in case of a resize via opcode */ - - class gWindow *window; -}; - -#endif - -#endif // #ifdef WITH_VST diff --git a/src/pluginHost.cpp b/src/pluginHost.cpp deleted file mode 100644 index f28f5aa..0000000 --- a/src/pluginHost.cpp +++ /dev/null @@ -1,679 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * pluginHost - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifdef WITH_VST - - -#include "pluginHost.h" -#include "conf.h" -#include "const.h" -#include "mixer.h" -#include "gd_mainWindow.h" -#include "channel.h" -#include "sampleChannel.h" -#include "midiChannel.h" -#include "kernelMidi.h" -#include "log.h" - - -extern Conf G_Conf; -extern Mixer G_Mixer; -extern PluginHost G_PluginHost; -extern unsigned G_beats; -extern gdMainWindow *mainWin; - - -PluginHost::PluginHost() { - - /* initially we fill vstTimeInfo with trash. Only when the plugin requests - * the opcode we load the right infos from G_Mixer. */ - - vstTimeInfo.samplePos = 0.0; - vstTimeInfo.sampleRate = G_Conf.samplerate; - vstTimeInfo.nanoSeconds = 0.0; - vstTimeInfo.ppqPos = 0.0; - vstTimeInfo.tempo = 120.0; - vstTimeInfo.barStartPos = 0.0; - vstTimeInfo.cycleStartPos = 0.0; - vstTimeInfo.cycleEndPos = 0.0; - vstTimeInfo.timeSigNumerator = 4; - vstTimeInfo.timeSigDenominator = 4; - vstTimeInfo.smpteOffset = 0; - vstTimeInfo.smpteFrameRate = 1; - vstTimeInfo.samplesToNextClock = 0; - vstTimeInfo.flags = 0; -} - - -/* ------------------------------------------------------------------ */ - - -PluginHost::~PluginHost() {} - - -/* ------------------------------------------------------------------ */ - - -int PluginHost::allocBuffers() { - - /** FIXME - ERROR CHECKING! */ - - /* never, ever use G_Conf.buffersize to alloc these chunks of memory. - * If you use JACK, that value would be meaningless. Always refer to - * kernelAudio::realBufsize. */ - - int bufSize = kernelAudio::realBufsize*sizeof(float); - - bufferI = (float **) malloc(2 * sizeof(float*)); - bufferI[0] = (float *) malloc(bufSize); - bufferI[1] = (float *) malloc(bufSize); - - bufferO = (float **) malloc(2 * sizeof(float*)); - bufferO[0] = (float *) malloc(bufSize); - bufferO[1] = (float *) malloc(bufSize); - - memset(bufferI[0], 0, bufSize); - memset(bufferI[1], 0, bufSize); - memset(bufferO[0], 0, bufSize); - memset(bufferO[1], 0, bufSize); - - gLog("[pluginHost] buffers allocated, buffersize = %d\n", 2*kernelAudio::realBufsize); - - //printOpcodes(); - - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -VstIntPtr VSTCALLBACK PluginHost::HostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt) { - return G_PluginHost.gHostCallback(effect, opcode, index, value, ptr, opt); -} - - -/* ------------------------------------------------------------------ */ - - -VstIntPtr PluginHost::gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt) { - - /* warning: VST headers compiled with DECLARE_VST_DEPRECATED. */ - - switch (opcode) { - - /* 0 - Called after a control has changed in the editor and when - * the associated parameter should be automated. Index contains the - * param, opt the value. Thanks, but we don't need it now. It will - * be useful when recording actions from VST (in the future). */ - - case audioMasterAutomate: - return 0; - - /* 1 - host version (2.4) */ - - case audioMasterVersion: - return kVstVersion; - - /* 3 - Give idle time to Host application, e.g. if plug-in editor is - * doing mouse tracking in a modal loop. This a is multithread app, - * we don't need it. */ - - case audioMasterIdle: - return 0; - - /* 6 - tells the host that the plugin is an instrument. Deprecated. */ - - case DECLARE_VST_DEPRECATED(audioMasterWantMidi): - return 0; - - /* 7 - time infos */ - - case audioMasterGetTime: - vstTimeInfo.samplePos = G_Mixer.actualFrame; - vstTimeInfo.sampleRate = G_Conf.samplerate; - vstTimeInfo.tempo = G_Mixer.bpm; - vstTimeInfo.timeSigNumerator = G_Mixer.beats; - vstTimeInfo.timeSigDenominator = G_Mixer.bars; - vstTimeInfo.ppqPos = (G_Mixer.actualFrame / (float) G_Conf.samplerate) * (float) G_Mixer.bpm / 60.0f; - return (VstIntPtr) &vstTimeInfo; - - /* ? - requires a pointer to VstEvents. No vstEvents so far (v0.5.4) */ - - case audioMasterProcessEvents: - return 0; - - /* 13 - tells that numInputs/numOutputs are changed. Not supported and - * not needed. */ - - case audioMasterIOChanged: - return false; - - /* 14 - plugin needs idle calls (outside its editor window). Deprecated */ - - case DECLARE_VST_DEPRECATED(audioMasterNeedIdle): - return 0; - - /* 15 - requests to resize the editor window. w = index, h = value*/ - - case audioMasterSizeWindow: { - gWindow *window = NULL; - for (unsigned i=0; igetPlugin() == effect) - window = masterOut.at(i)->window; - - for (unsigned i=0; igetPlugin() == effect) - window = masterIn.at(i)->window; - - for (unsigned i=0; iplugins.size && !window; j++) - if (ch->plugins.at(j)->getPlugin() == effect) - window = ch->plugins.at(j)->window; - } - - if (window) { - gLog("[pluginHost] audioMasterSizeWindow: resizing window from plugin %p\n", (void*) effect); - if (index == 1 || value == 1) - gLog("[pluginHost] warning: non-sense values!\n"); - else - window->size((int)index, (int)value); - return 1; - } - else { - gLog("[pluginHost] audioMasterSizeWindow: window from plugin %p not found\n", (void*) effect); - return 0; - } - } - - /* 16 - sample rate */ - - case audioMasterGetSampleRate: - return G_Conf.samplerate; - - /* ?? - buffer size */ - - case audioMasterGetBlockSize: - return kernelAudio::realBufsize; - - case audioMasterGetInputLatency: - gLog("[pluginHost] requested opcode 'audioMasterGetInputLatency' (%d)\n", opcode); - return 0; - - case audioMasterGetOutputLatency: - gLog("[pluginHost] requested opcode 'audioMasterGetOutputLatency' (%d)\n", opcode); - return 0; - - /* 23 - wants to know what kind of process is that. - * kVstProcessLevelRealtime = currently in audio thread (where - * process is called). */ - - case audioMasterGetCurrentProcessLevel: - return kVstProcessLevelRealtime; - - /* 32 - vendor name */ - - case audioMasterGetVendorString: - strcpy((char*)ptr, "Monocasual"); - return 1; - - /* 32 - product name */ - - case audioMasterGetProductString: - strcpy((char*)ptr, "Giada"); - return 1; - - /* 33 - product version */ - - case audioMasterGetVendorVersion: - return (int) VERSIONE_FLOAT * 100; - - - /* 37 - Plugin asks Host if it implements the feature text. */ - - case audioMasterCanDo: - gLog("[pluginHost] audioMasterCanDo: %s\n", (char*)ptr); - if (!strcmp((char*)ptr, "sizeWindow") || - !strcmp((char*)ptr, "sendVstTimeInfo") || - !strcmp((char*)ptr, "sendVstMidiEvent") || - !strcmp((char*)ptr, "sendVstMidiEventFlagIsRealtime")) - return 1; // we can do all of that - else - return 0; - - /* 42 - Something has changed, update the host's 'multi-fx' display. - * Not supported right now, return 0. This opcode deals with the program - * changes, more infos http://www.asseca.com/vst-24-specs/amUpdateDisplay.html */ - - case audioMasterUpdateDisplay: - return 0; - - case audioMasterGetLanguage: - return kVstLangEnglish; - - /* ?? */ - - case audioMasterGetAutomationState: - gLog("[pluginHost] requested opcode 'audioMasterGetAutomationState' (%d)\n", opcode); - return 0; - - /* 43 - It tells the Host that if it needs to, it has to record - * automation data for this control. In other words this opcode is fired - * when the user starts to tweak a parameter with the mouse. - * Useful when the plugin actions will be recorded. */ - - case audioMasterBeginEdit: - return 0; - - /* 44 - no more interaction for the user, started with the previous - * opcode. */ - - case audioMasterEndEdit: - return 0; - - default: - gLog("[pluginHost] FIXME: host callback called with opcode %d\n", opcode); - return 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) { - - Plugin *p = new Plugin(); - bool success = true; - - gVector *pStack; - pStack = getStack(stackType, ch); - - if (!p->load(fname)) { - //delete p; - //return 0; - success = false; - } - - /* if the load failed we add a 'dead' plugin into the stack. This is - * useful to report a missing plugin. */ - - if (!success) { - pStack->add(p); - return 0; - } - - /* otherwise let's try to initialize it. */ - - else { - - /* try to init the plugin. If fails, delete it and return error. */ - - if (!p->init(&PluginHost::HostCallback)) { - delete p; - return 0; - } - - /* plugin setup */ - - p->setup(G_Conf.samplerate, kernelAudio::realBufsize); - - /* try to add the new plugin until succeed */ - - int lockStatus; - while (true) { - lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); - if (lockStatus == 0) { - pStack->add(p); - pthread_mutex_unlock(&G_Mixer.mutex_plugins); - break; - } - } - - char name[256]; p->getName(name); - gLog("[pluginHost] plugin id=%d loaded (%s), stack type=%d, stack size=%d\n", p->getId(), name, stackType, pStack->size); - - /* p->resume() is suggested. Who knows... */ - - p->resume(); - - return 1; - } -} - - -/* ------------------------------------------------------------------ */ - - -void PluginHost::processStack(float *buffer, int stackType, Channel *ch) { - - gVector *pStack = getStack(stackType, ch); - - /* empty stack, stack not found or mixer not ready: do nothing */ - /// TODO - join evaluation - - if (!G_Mixer.ready) - return; - if (pStack == NULL) - return; - if (pStack->size == 0) - return; - - /* converting buffer from Giada to VST */ - - for (unsigned i=0; isize; i++) { - /// TODO - join evaluation - - if (pStack->at(i)->status != 1) - continue; - if (pStack->at(i)->suspended) - continue; - if (pStack->at(i)->bypass) - continue; - if (ch) { // process events if it's a channel stack - if (ch->type == CHANNEL_MIDI) { - ///gLog("events: %d\n", (((MidiChannel*)ch)->getVstEvents())->numEvents); - pStack->at(i)->processEvents(((MidiChannel*)ch)->getVstEvents()); - } - } - pStack->at(i)->processAudio(bufferI, bufferO, kernelAudio::realBufsize); - bufferI = bufferO; - } - - /* converting buffer from VST to Giada. A note for the future: if we - * overwrite (=) (as we do now) it's SEND, if we add (+) it's INSERT. */ - - for (unsigned i=0; i *pStack = getStack(stackType, ch); - for (unsigned i=0; isize; i++) { - if (pStack->at(i)->getId() == id) - return pStack->at(i); - } - return NULL; -} - - -/* ------------------------------------------------------------------ */ - - -Plugin *PluginHost::getPluginByIndex(int index, int stackType, Channel *ch) { - gVector *pStack = getStack(stackType, ch); - if (pStack->size == 0) - return NULL; - if ((unsigned) index >= pStack->size) - return NULL; - return pStack->at(index); -} - - -/* ------------------------------------------------------------------ */ - - -void PluginHost::freeStack(int stackType, Channel *ch) { - - gVector *pStack; - pStack = getStack(stackType, ch); - - if (pStack->size == 0) - return; - - int lockStatus; - while (true) { - lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); - if (lockStatus == 0) { - for (unsigned i=0; isize; i++) { - if (pStack->at(i)->status == 1) { // only if plugin is ok - pStack->at(i)->suspend(); - pStack->at(i)->close(); - } - delete pStack->at(i); - } - pStack->clear(); - pthread_mutex_unlock(&G_Mixer.mutex_plugins); - break; - } - } - -} - - -/* ------------------------------------------------------------------ */ - - -void PluginHost::freeAllStacks() { - freeStack(PluginHost::MASTER_OUT); - freeStack(PluginHost::MASTER_IN); - for (unsigned i=0; i *pStack; - pStack = getStack(stackType, ch); - - /* try to delete the plugin until succeed. G_Mixer has priority. */ - - for (unsigned i=0; isize; i++) - if (pStack->at(i)->getId() == id) { - - if (pStack->at(i)->status == 0) { // no frills if plugin is missing - delete pStack->at(i); - pStack->del(i); - return; - } - else { - int lockStatus; - while (true) { - lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); - if (lockStatus == 0) { - pStack->at(i)->suspend(); - pStack->at(i)->close(); - delete pStack->at(i); - pStack->del(i); - pthread_mutex_unlock(&G_Mixer.mutex_plugins); - gLog("[pluginHost] plugin id=%d removed\n", id); - return; - } - //else - //gLog("[pluginHost] waiting for mutex...\n"); - } - } - } - gLog("[pluginHost] plugin id=%d not found\n", id); -} - - -/* ------------------------------------------------------------------ */ - - -void PluginHost::swapPlugin(unsigned indexA, unsigned indexB, int stackType, Channel *ch) { - - gVector *pStack = getStack(stackType, ch); - - int lockStatus; - while (true) { - lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins); - if (lockStatus == 0) { - pStack->swap(indexA, indexB); - pthread_mutex_unlock(&G_Mixer.mutex_plugins); - gLog("[pluginHost] plugin at index %d and %d swapped\n", indexA, indexB); - return; - } - //else - //gLog("[pluginHost] waiting for mutex...\n"); - } -} - - -/* ------------------------------------------------------------------ */ - - -int PluginHost::getPluginIndex(int id, int stackType, Channel *ch) { - - gVector *pStack = getStack(stackType, ch); - - for (unsigned i=0; isize; i++) - if (pStack->at(i)->getId() == id) - return i; - return -1; -} - - -/* ------------------------------------------------------------------ */ - - -gVector *PluginHost::getStack(int stackType, Channel *ch) { - switch(stackType) { - case MASTER_OUT: - return &masterOut; - case MASTER_IN: - return &masterIn; - case CHANNEL: - return &ch->plugins; - default: - return NULL; - } -} - - -/* ------------------------------------------------------------------ */ - - -VstMidiEvent *PluginHost::createVstMidiEvent(uint32_t msg) -{ - VstMidiEvent *e = (VstMidiEvent*) malloc(sizeof(VstMidiEvent)); - - /* type = two types of events: MIDI event and MIDI system exclusive - * (aka sysex, not implemented). */ - - e->type = kVstMidiType; - e->byteSize = sizeof(VstMidiEvent); - - /* deltaFrames = sample frames related to the current block start - * sample position. */ - - e->deltaFrames = 0; - - /* flags = kVstMidiEventIsRealtime means that this event is played - * live (not in playback from a sequencer track). This allows the - * Plug-In to handle these flagged events with higher priority, - * especially when the Plug-In has a big latency */ - - e->flags = kVstMidiEventIsRealtime; - - /* midiData = 1 to 3 MIDI bytes; midiData[3] is reserved (zero) */ - - e->midiData[0] = kernelMidi::getB1(msg); // note on/off + channel - e->midiData[1] = kernelMidi::getB2(msg); // note number - e->midiData[2] = kernelMidi::getB3(msg); // velocity - e->midiData[3] = 0; - - /* noteLength = (in sample frames) of entire note, if available, - * else 0 */ - - e->noteLength = 0; - - /* noteOffset = offset (in sample frames) into note from note start - * if available, else 0 */ - - e->noteOffset = 0; - - /* noteOffVelocity = Note Off Velocity [0, 127]. */ - - e->noteOffVelocity = 0; - - return e; -} - - -/* ------------------------------------------------------------------ */ - - -unsigned PluginHost::countPlugins(int stackType, Channel *ch) { - gVector *pStack = getStack(stackType, ch); - return pStack->size; -} - - -#endif // #ifdef WITH_VST diff --git a/src/pluginHost.h b/src/pluginHost.h deleted file mode 100644 index 9b4db08..0000000 --- a/src/pluginHost.h +++ /dev/null @@ -1,132 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * pluginHost - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifdef WITH_VST - -#ifndef __PLUGIN_HOST_ -#define __PLUGIN_HOST_ - -#include "ge_window.h" -#include "plugin.h" -#include "utils.h" -#include "init.h" -#include "const.h" - - -class PluginHost { - -private: - - /* VSTs have a different buffer model: - * - * buffer[0] = channel left - * buffer[1] = channel right - * buffer[0][....] = all signals from left chan - * buffer[1][....] = all signals from right chan */ - - float **bufferI; - float **bufferO; - - /* VST struct containing infos on tempo (bpm, freq, smtpe, ...). */ - - VstTimeInfo vstTimeInfo; - -public: - - /* stack types. Use them together with getStack() in order to geta - * pointer to the right stack. */ - - enum stackType { - MASTER_OUT, - MASTER_IN, - CHANNEL - }; - - /* stack of Plugins */ - - gVector masterOut; - gVector masterIn; - - PluginHost(); - ~PluginHost(); - - int allocBuffers(); - - /* The plugin can ask the host if it supports a given capability, - * which is done through the HostCallback() function. - * - * Why static? This is a callback attached to each plugin in the stack - * and C++ callback functions need to be static when declared in class. - * - * OPCODE LIST: - * base version: vstsdk2.4/pluginterfaces/aeffect.h (vst 1.x) - * enhanced v. : vstsdk2.4/pluginterfaces/effectx.h (vst 2.x) */ - - static VstIntPtr VSTCALLBACK HostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt); - VstIntPtr gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt); - - int addPlugin(const char *fname, int stackType, class Channel *ch=NULL); - - void processEvents(float *buffer, class Channel *ch); - - /* processStack - * apply the fx list to the buffer. */ - - void processStack(float *buffer, int stackType, class Channel *ch=NULL); - - /* processStackOffline - * apply the fx list to a longer chunk of data */ - - void processStackOffline(float *buffer, int stackType, class Channel *ch, int size); - - /* createVstMidiEvent - * return a pointer to a new VstMidiEvent structure. */ - - VstMidiEvent *createVstMidiEvent(uint32_t msg); - - gVector *getStack(int stackType, class Channel *ch=NULL); - - Plugin *getPluginById(int id, int stackType, class Channel *ch=NULL); - - Plugin *getPluginByIndex(int index, int stackType, class Channel *ch=NULL); - - int getPluginIndex(int id, int stackType, class Channel *ch=NULL); - - unsigned countPlugins(int stackType, class Channel *ch=NULL); - - void freeStack(int stackType, class Channel *ch=NULL); - - void freeAllStacks(); - - void freePlugin(int id, int stackType, class Channel *ch=NULL); - - void swapPlugin(unsigned indexA, unsigned indexB, int stackType, class Channel *ch=NULL); -}; -#endif - -#endif // #ifdef WITH_VST diff --git a/src/recorder.cpp b/src/recorder.cpp deleted file mode 100644 index a0c8f0d..0000000 --- a/src/recorder.cpp +++ /dev/null @@ -1,698 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * recorder - * Action recorder. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "recorder.h" -#include "const.h" -#include "utils.h" -#include "mixer.h" -#include "mixerHandler.h" -#include "kernelAudio.h" -#include "pluginHost.h" -#include "kernelMidi.h" -#include "utils.h" -#include "patch.h" -#include "conf.h" -#include "channel.h" -#include "sampleChannel.h" -#include "log.h" - - -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -extern Mixer G_Mixer; -extern Patch f_patch; -extern Conf G_Conf; - - -namespace recorder -{ -gVector frames; -gVector< gVector > global; -gVector actions; - -bool active = false; -bool sortedActions = false; - -composite cmp; - - -/* ------------------------------------------------------------------ */ - - -void init() -{ - sortedActions = false; - active = false; - clearAll(); -} - - -/* ------------------------------------------------------------------ */ - - -bool canRec(Channel *ch) -{ - /* NO recording if: - * recorder is inactive - * mixer is not running - * mixer is recording a take in this channel ch - * channel is empty */ - - if (!active || !G_Mixer.running || G_Mixer.chanInput == ch || (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL)) - return 0; - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -void rec(int index, int type, int frame, uint32_t iValue, float fValue) -{ - /* make sure frame is even */ - - if (frame % 2 != 0) - frame++; - - /* allocating the action */ - - action *a = (action*) malloc(sizeof(action)); - a->chan = index; - a->type = type; - a->frame = frame; - a->iValue = iValue; - a->fValue = fValue; - - /* check if the frame exists in the stack. If it exists, we don't extend - * the stack, but we add (or push) a new action to it. */ - - int frameToExpand = frames.size; - for (int i=0; ichan == index && - ac->type == type && - ac->frame == frame && - ac->iValue == iValue && - ac->fValue == fValue) - return; - } - - global.at(frameToExpand).add(a); // expand array - } - - /* if WITH_VST create a new VST event and attach it to our action. - * Nota bene: the VST event occurs on localFrame=0: this is a - * user-generated event after all! */ - -#ifdef WITH_VST - if (type == ACTION_MIDI) - a->event = G_PluginHost.createVstMidiEvent(a->iValue); -#endif - - /* don't activate the channel (readActions == false), it's up to - * the other layers */ - - Channel *ch = G_Mixer.getChannelByIndex(index); - ch->hasActions = true; - - sortedActions = false; - - gLog("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", - a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue); - //print(); -} - - -/* ------------------------------------------------------------------ */ - - -void clearChan(int index) -{ - gLog("[REC] clearing chan %d...\n", index); - - for (unsigned i=0; ichan == index) { -#ifdef WITH_VST - if (a->type == ACTION_MIDI) - free(a->event); -#endif - free(a); - global.at(i).del(j); - } - else - j++; - } - } - - Channel *ch = G_Mixer.getChannelByIndex(index); - ch->hasActions = false; - optimize(); - //print(); -} - - -/* ------------------------------------------------------------------ */ - - -void clearAction(int index, char act) -{ - gLog("[REC] clearing action %d from chan %d...\n", act, index); - for (unsigned i=0; ichan == index && (act & a->type) == a->type) { // bitmask - free(a); - global.at(i).del(j); - } - else - j++; - } - } - Channel *ch = G_Mixer.getChannelByIndex(index); - ch->hasActions = false; /// FIXME - why this? Isn't it useless if we call chanHasActions? - optimize(); - chanHasActions(index); /// FIXME - //print(); -} - - -/* ------------------------------------------------------------------ */ - - -void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue, float fValue) -{ - /* make sure frame is even */ - - if (frame % 2 != 0) - frame++; - - /* find the frame 'frame' */ - - bool found = false; - for (unsigned i=0; ichan == chan && a->type == (type & a->type)); - if (checkValues) - doit &= (a->iValue == iValue && a->fValue == fValue); - - if (doit) { - int lockStatus = 0; - while (lockStatus == 0) { - lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_recs); - if (lockStatus == 0) { -#ifdef WITH_VST - if (type == ACTION_MIDI) - free(a->event); -#endif - free(a); - global.at(i).del(j); - pthread_mutex_unlock(&G_Mixer.mutex_recs); - found = true; - break; - } - else - gLog("[REC] delete action: waiting for mutex...\n"); - } - } - } - } - } - if (found) { - optimize(); - chanHasActions(chan); - gLog("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", - type, frame, chan, iValue, iValue, fValue); - } - else - gLog("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", - type, frame, chan, iValue, iValue, fValue); -} - - -/* ------------------------------------------------------------------ */ - - -void deleteActions(int chan, int frame_a, int frame_b, char type) -{ - sortActions(); - gVector dels; - - for (unsigned i=0; i frame_a && frames.at(i) < frame_b) - dels.add(frames.at(i)); - - for (unsigned i=0; i 0) { - for (unsigned i=0; itype == ACTION_MIDI) - free(global.at(i).at(k)->event); -#endif - free(global.at(i).at(k)); // free action - } - global.at(i).clear(); // free action container - global.del(i); - } - } - - for (unsigned i=0; ihasActions = false; - if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) - ((SampleChannel*)G_Mixer.channels.at(i))->readActions = false; - } - - global.clear(); - frames.clear(); -} - - -/* ------------------------------------------------------------------ */ - - -void optimize() -{ - /* do something until the i frame is empty. */ - - unsigned i = 0; - while (true) { - if (i == global.size) return; - if (global.at(i).size == 0) { - global.del(i); - frames.del(i); - } - else - i++; - } - - sortActions(); -} - - -/* ------------------------------------------------------------------ */ - - -void sortActions() -{ - if (sortedActions) - return; - for (unsigned i=0; i frames.at(i)) { - frames.swap(j, i); - global.swap(j, i); - } - sortedActions = true; - //print(); -} - - -/* ------------------------------------------------------------------ */ - - -void updateBpm(float oldval, float newval, int oldquanto) -{ - for (unsigned i=0; i 0 && scarto <= 6) - frames.at(i) = frames.at(i) + scarto; - } - - /* never ever have odd frames. */ - - if (frames.at(i) % 2 != 0) - frames.at(i)++; - } - - /* update structs */ - - for (unsigned i=0; iframe = frames.at(i); - } - } - - //print(); -} - - -/* ------------------------------------------------------------------ */ - - -void updateSamplerate(int systemRate, int patchRate) -{ - /* diff ratio: systemRate / patchRate - * e.g. 44100 / 96000 = 0.4... */ - - if (systemRate == patchRate) - return; - - gLog("[REC] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate); - - float ratio = systemRate / (float) patchRate; - for (unsigned i=0; iframe = frames.at(i); - } - } -} - - -/* ------------------------------------------------------------------ */ - - -void expand(int old_fpb, int new_fpb) -{ - /* this algorithm requires multiple passages if we expand from e.g. 2 - * to 16 beats, precisely 16 / 2 - 1 = 7 times (-1 is the first group, - * which exists yet). If we expand by a non-multiple, the result is zero, - * due to float->int implicit cast */ - - unsigned pass = (int) (new_fpb / old_fpb) - 1; - if (pass == 0) pass = 1; - - unsigned init_fs = frames.size; - - for (unsigned z=1; z<=pass; z++) { - for (unsigned i=0; ichan, a->type, newframe, a->iValue, a->fValue); - } - } - } - gLog("[REC] expanded recs\n"); - //print(); -} - - -/* ------------------------------------------------------------------ */ - - -void shrink(int new_fpb) -{ - /* easier than expand(): here we delete eveything beyond old_framesPerBars. */ - - unsigned i=0; - while (true) { - if (i == frames.size) break; - - if (frames.at(i) >= new_fpb) { - for (unsigned k=0; khasActions = false; - return; - } - for (unsigned i=0; ihasActions; i++) { - for (unsigned j=0; jhasActions; j++) { - if (global.at(i).at(j)->chan == index) - ch->hasActions = true; - } - } -} - - -/* ------------------------------------------------------------------ */ - - -int getNextAction(int chan, char type, int frame, action **out, uint32_t iValue) -{ - sortActions(); // mandatory - - unsigned i=0; - while (i < frames.size && frames.at(i) <= frame) i++; - - if (i == frames.size) // no further actions past 'frame' - return -1; - - for (; ichan == chan && (type & a->type) == a->type) { - if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) { - *out = global.at(i).at(j); - return 1; - } - } - } - - return -2; // no 'type' actions found -} - - -/* ------------------------------------------------------------------ */ - - -int getAction(int chan, char action, int frame, struct action **out) -{ - for (unsigned i=0; iframe && - action == global.at(i).at(j)->type && - chan == global.at(i).at(j)->chan) - { - *out = global.at(i).at(j); - return 1; - } - return 0; -} - - -/* ------------------------------------------------------------------ */ - - -void startOverdub(int index, char actionMask, int frame) -{ - /* prepare the composite struct */ - - if (actionMask == ACTION_KEYS) { - cmp.a1.type = ACTION_KEYPRESS; - cmp.a2.type = ACTION_KEYREL; - } - else { - cmp.a1.type = ACTION_MUTEON; - cmp.a2.type = ACTION_MUTEOFF; - } - cmp.a1.chan = index; - cmp.a2.chan = index; - cmp.a1.frame = frame; - // cmp.a2.frame doesn't exist yet - - /* avoid underlying action truncation: if action2.type == nextAction: - * you are in the middle of a composite action, truncation needed */ - - rec(index, cmp.a1.type, frame); - - action *act = NULL; - int res = getNextAction(index, cmp.a1.type | cmp.a2.type, cmp.a1.frame, &act); - if (res == 1) { - if (act->type == cmp.a2.type) { - int truncFrame = cmp.a1.frame-kernelAudio::realBufsize; - if (truncFrame < 0) - truncFrame = 0; - gLog("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type); - rec(index, cmp.a2.type, truncFrame); - } - } - - SampleChannel *ch = (SampleChannel*) G_Mixer.getChannelByIndex(index); - ch->readActions = false; // don't use disableRead() -} - - -/* ------------------------------------------------------------------ */ - - -void stopOverdub(int frame) -{ - cmp.a2.frame = frame; - bool ringLoop = false; - bool nullLoop = false; - - /* ring loop verification, i.e. a composite action with key_press at - * frame N and key_release at frame M, with M <= N */ - - if (cmp.a2.frame < cmp.a1.frame) { - ringLoop = true; - gLog("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame); - rec(cmp.a2.chan, cmp.a2.type, G_Mixer.totalFrames); // record at the end of the sequencer - } - else - if (cmp.a2.frame == cmp.a1.frame) { - nullLoop = true; - gLog("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame); - deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false); // false == don't check values - } - - SampleChannel *ch = (SampleChannel*) G_Mixer.getChannelByIndex(cmp.a2.chan); - ch->readActions = false; // don't use disableRead() - - /* remove any nested action between keypress----keyrel, then record */ - - if (!nullLoop) - deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type); - deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type); - - if (!ringLoop && !nullLoop) { - rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame); - - /* avoid underlying action truncation, if keyrel happens inside a - * composite action */ - - action *act = NULL; - int res = getNextAction(cmp.a2.chan, cmp.a1.type | cmp.a2.type, cmp.a2.frame, &act); - if (res == 1) { - if (act->type == cmp.a2.type) { - gLog("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type); - deleteAction(act->chan, act->frame, act->type, false); // false == don't check values - } - } - } -} - - -/* ------------------------------------------------------------------ */ - - -void print() -{ - gLog("[REC] ** print debug **\n"); - for (unsigned i=0; itype, global.at(i).at(j)->chan, global.at(i).at(j)->frame); - } - } -} - -} // namespace diff --git a/src/recorder.h b/src/recorder.h deleted file mode 100644 index 0fd378d..0000000 --- a/src/recorder.h +++ /dev/null @@ -1,192 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * recorder - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - -#ifndef RECORDER_H -#define RECORDER_H - -#include -#include -#include "utils.h" -#include "const.h" -#include "mixer.h" - -#ifdef WITH_VST - -/* before including aeffetx(x).h we must define __cdecl, otherwise VST - * headers can't be compiled correctly. In windows __cdecl is already - * defined. */ - - #ifdef __GNUC__ - #ifndef _WIN32 - #define __cdecl - #endif - #endif - #include "vst/aeffectx.h" -#endif - -/* - * [global0]-->[gVector<_action*>0]-->[a0][a1][a2] 0[frames1] - * [global1]-->[gVector<_action*>1]-->[a0][a1][a2] 1[frames2] - * [global2]-->[gVector<_action*>2]-->[a0][a1][a2] 2[frames3] - * [global3]-->[gVector<_action*>3]-->[a0][a1][a2] 3[frames4] - * */ - -namespace recorder { - -/* action - * struct containing fields to describe an atomic action. Note from - * VST sdk: parameter values, like all VST parameters, are declared as - * floats with an inclusive range of 0.0 to 1.0 (fValue). */ - -struct action { - int chan; // channel index, i.e. Channel->index - int type; - int frame; // redundant info, used by helper functions - float fValue; // used only for envelopes (volumes, vst params). - uint32_t iValue; // used only for MIDI events - - /* if VST store here a pointer to a vstEvent. */ - -#ifdef WITH_VST - VstMidiEvent *event; -#endif -}; - -/* composite - * a group of two actions (keypress+keyrel, muteon+muteoff) used during - * the overdub process */ - -struct composite { - action a1; - action a2; -}; - -extern gVector frames; // frame counter (sentinel) frames.size == global.size -extern gVector< gVector > global; // container of containers of actions -extern gVector actions; // container of actions - -extern bool active; -extern bool sortedActions; // are actions sorted via sortActions()? - -/* init - * everything starts from here. */ - -void init(); - -/* chanHasActions - * Check if the channel has at least one action recorded. If false, sets - * ch->hasActions = false. Used after an action deletion. */ - -void chanHasActions(int chan); - -/* canRec - * can a channel rec an action? Call this one BEFORE rec(). */ - -bool canRec(Channel *ch); - -/* rec - * record an action. */ - -void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f); - -/* clearChan - * clear all actions from a channel. */ - -void clearChan(int chan); - -/* clearAction - * clear the 'action' action type from a channel. */ - -void clearAction(int chan, char action); - -/* deleteAction - * delete ONE action. Useful in the action editor. 'type' can be a mask. */ - -void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue=0, float fValue=0.0); - -/* deleteActions - * delete A RANGE of actions from frame_a to frame_b in channel 'chan'. - * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */ - -void deleteActions(int chan, int frame_a, int frame_b, char type); - -/* clearAll - * delete everything. */ - -void clearAll(); - -/* optimize - * clear frames without actions. */ - -void optimize(); - -/* sortActions - * sorts actions by frame, asc mode. */ - -void sortActions(); - -/* updateBpm - * reassign frames by calculating the new bpm value. */ - -void updateBpm(float oldval, float newval, int oldquanto); - -/* updateSamplerate - * reassign frames taking in account the samplerate. If f_system == - * f_patch nothing changes, otherwise the conversion is mandatory. */ - -void updateSamplerate(int systemRate, int patchRate); - -void expand(int old_fpb, int new_fpb); -void shrink(int new_fpb); - -/* getNextAction - * return the nearest action in chan 'chan' of type 'action' starting - * from 'frame'. Action can be a bitmask. If iValue != -1 search for - * next action with iValue == iValue: useful for MIDI key_release. */ - -int getNextAction(int chan, char action, int frame, struct action **out, uint32_t iValue=0); - -/* getAction - * return a pointer to action in chan 'chan' of type 'action' at frame - * 'frame'. */ - -int getAction(int chan, char action, int frame, struct action **out); - -/* start/endOverdub */ - -void startOverdub(int chan, char action, int frame); -void stopOverdub(int frame); - -/* print - * debug of the frame stack. */ - -void print(); - -} // namespace - -#endif diff --git a/src/resource.h b/src/resource.h deleted file mode 100644 index d771ba8..0000000 --- a/src/resource.h +++ /dev/null @@ -1 +0,0 @@ -#define IDI_ICON1 101 \ No newline at end of file diff --git a/src/resource.rc b/src/resource.rc deleted file mode 100644 index fb0be40..0000000 --- a/src/resource.rc +++ /dev/null @@ -1,2 +0,0 @@ -#include "resource.h" -IDI_ICON1 ICON DISCARDABLE "giada.ico" \ No newline at end of file diff --git a/src/rtaudio-mod/Makefile.in b/src/rtaudio-mod/Makefile.in deleted file mode 100644 index 89cacdc..0000000 --- a/src/rtaudio-mod/Makefile.in +++ /dev/null @@ -1,75 +0,0 @@ -### Do not edit -- Generated by 'configure --with-whatever' from Makefile.in -### RtAudio library Makefile - -RM = /bin/rm -LN = /bin/ln - -OBJECTS = RtAudio.o @objects@ - -LIBNAME = librtaudio -STATIC = $(LIBNAME).a -SHARED = @sharedlib@ -RELEASE = 4.1.1 -MAJOR = 4 -LIBRARIES = $(STATIC) $(SHARED) - -CC = @CXX@ -AR = @AR@ -RANLIB = @RANLIB@ - -DEFS = @CPPFLAGS@ -CFLAGS = @CXXFLAGS@ -Iinclude -fPIC - -PREFIX = @prefix@ - -all : $(LIBRARIES) - -tests: - cd tests && $(MAKE) all - -$(LIBRARIES): $(OBJECTS) - $(AR) ruv $(STATIC) $(OBJECTS) - ranlib $(STATIC) - $(CC) -fPIC @libflags@ $(OBJECTS) @LIBS@ - $(LN) -sf @sharedname@ $(SHARED) - $(LN) -sf @sharedname@ $(SHARED).$(MAJOR) - -%.o : %.cpp - $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@ - -%.o : include/%.cpp - $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@ - -install: - install --mode=755 $(STATIC) $(PREFIX)/lib/ - install --mode=755 @sharedname@ $(PREFIX)/lib/ - $(LN) -sf @sharedname@ $(PREFIX)/lib/$(SHARED) - $(LN) -sf @sharedname@ $(PREFIX)/lib/$(SHARED).$(MAJOR) - install --mode=644 $(LIBNAME).pc $(PREFIX)/lib/pkgconfig - install --mode=644 RtAudio.h $(PREFIX)/include/ - install --mode=755 rtaudio-config $(PREFIX)/bin/ - -uninstall: - -@rm -vf $(patsubst %,$(PREFIX)/lib/%, $(LIBRARIES) $(SHARED).$(MAJOR) $(SHARED).$(RELEASE)) - -@rm -vf $(PREFIX)/lib/pkgconfig/$(LIBNAME).pc - -@rm -vf $(PREFIX)/bin/rtaudio-config - -clean : - $(RM) -f $(LIBRARIES) @sharedname@ $(SHARED)* - $(RM) -f $(OBJECTS) - $(RM) -f *~ - cd tests && $(MAKE) clean - -distclean: - $(RM) -f $(LIBRARIES) @sharedname@ $(SHARED)* - $(RM) -f $(OBJECTS) - $(RM) -f *~ - $(RM) -rf config.log config.status autom4te.cache Makefile rtaudio-config $(LIBNAME).pc - cd tests && $(MAKE) distclean - -strip : - strip $(LIBRARIES) - ranlib $(LIBRARIES) - cd tests && $(MAKE) strip - -.PHONY: clean distclean strip install uninstall diff --git a/src/rtaudio-mod/RtAudio.cpp b/src/rtaudio-mod/RtAudio.cpp deleted file mode 100644 index 5716c59..0000000 --- a/src/rtaudio-mod/RtAudio.cpp +++ /dev/null @@ -1,10145 +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 WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ - - RtAudio: realtime audio i/o C++ classes - Copyright (c) 2001-2014 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 4.1.1 - -#include "RtAudio.h" -#include -#include -#include -#include - -// 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) -#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 ) throw() -{ - return RTAUDIO_VERSION; -} - -void RtAudio :: getCompiledApi( std::vector &apis ) throw() -{ - apis.clear(); - - // The order here will control the order of RtAudio's API search in - // the constructor. -#if defined(__UNIX_JACK__) - apis.push_back( UNIX_JACK ); -#endif -#if defined(__LINUX_ALSA__) - apis.push_back( LINUX_ALSA ); -#endif -#if defined(__LINUX_PULSE__) - apis.push_back( LINUX_PULSE ); -#endif -#if defined(__LINUX_OSS__) - apis.push_back( LINUX_OSS ); -#endif -#if defined(__WINDOWS_ASIO__) - apis.push_back( WINDOWS_ASIO ); -#endif -#if defined(__WINDOWS_WASAPI__) - apis.push_back( WINDOWS_WASAPI ); -#endif -#if defined(__WINDOWS_DS__) - apis.push_back( WINDOWS_DS ); -#endif -#if defined(__MACOSX_CORE__) - apis.push_back( MACOSX_CORE ); -#endif -#if defined(__RTAUDIO_DUMMY__) - apis.push_back( RTAUDIO_DUMMY ); -#endif -} - -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; igetDeviceCount() ) 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() throw() -{ - 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; -} - -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 ) { - 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; imBuffers[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; imBuffers[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 minimumRate ) minimumRate = rangeList[i].mMinimum; - if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; - } - } - - if ( haveValueRange ) { - for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) - info.sampleRates.push_back( 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; ixrun[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; iStreammBuffers[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; iStreammBuffers[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; iStreammBuffers[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 > physicalFormats; - formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger; - physicalFormats.push_back( std::pair( 32, formatFlags ) ); - formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; - physicalFormats.push_back( std::pair( 32, formatFlags ) ); - physicalFormats.push_back( std::pair( 24, formatFlags ) ); // 24-bit packed - formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh ); - physicalFormats.push_back( std::pair( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low - formatFlags |= kAudioFormatFlagIsAlignedHigh; - physicalFormats.push_back( std::pair( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high - formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; - physicalFormats.push_back( std::pair( 16, formatFlags ) ); - physicalFormats.push_back( std::pair( 8, formatFlags ) ); - - bool setPhysicalFormat = false; - for( unsigned int i=0; iflags & 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 ( stream_.state == STREAM_RUNNING ) - AudioDeviceStop( handle->id[0], callbackHandler ); -#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) - AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] ); -#else - // deprecated in favor of AudioDeviceDestroyIOProcID() - AudioDeviceRemoveIOProc( handle->id[0], callbackHandler ); -#endif - } - - if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { - if ( stream_.state == STREAM_RUNNING ) - AudioDeviceStop( handle->id[1], callbackHandler ); -#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) - AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] ); -#else - // deprecated in favor of AudioDeviceDestroyIOProcID() - 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; - } - - OSStatus result = noErr; - CoreHandle *handle = (CoreHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - result = AudioDeviceStart( handle->id[0], callbackHandler ); - 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] ) ) { - - result = AudioDeviceStart( handle->id[1], callbackHandler ); - 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 - } - - result = AudioDeviceStop( handle->id[0], callbackHandler ); - 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] ) ) { - - result = AudioDeviceStop( handle->id[1], callbackHandler ); - 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; inStreams[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; imBuffers[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; inStreams[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; idrainCounter ) { - 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; imBuffers[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; inStreams[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 -#include -#include - -// 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; } -}; - -/* --- Monocasual hack ---------------------------------------------- */ -#ifdef __linux__ -void *RtApi :: __HACK__getJackClient() { - JackHandle *handle = (JackHandle *) stream_.apiHandle; - return (void*) handle->client; -} -#endif -/* ------------------------------------------------------------------ */ - -static void jackSilentError( const char * ) {}; - -RtApiJack :: RtApiJack() -{ - // 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, NULL, 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, NULL, 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.sampleRates.push_back( jack_get_sample_rate( client ) ); - - // 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(), NULL, 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(), NULL, 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, NULL, 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; - } - - // Count the available ports containing the client name as device - // channels. Jack "input ports" equal RtAudio output channels. - unsigned int nChannels = 0; - unsigned long flag = JackPortIsInput; - if ( mode == INPUT ) flag = JackPortIsOutput; - ports = jack_get_ports( client, deviceName.c_str(), NULL, 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(), NULL, 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 *) &handle ); - jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); - } - - // Register our ports. - char label[64]; - if ( mode == OUTPUT ) { - for ( unsigned int i=0; iports[0][i] = jack_port_register( handle->client, (const char *)label, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); - } - } - else { - for ( unsigned int i=0; iports[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 ); - - 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; - } - - 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 ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - result = 1; - ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, 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; iclient, 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 ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - result = 1; - ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, 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; iclient, 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; iports[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; iports[0][i], (jack_nframes_t) nframes ); - memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); - } - } - else { // no buffer conversion - for ( unsigned int i=0; iports[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; iports[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; iports[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 - -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 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; isaveDeviceInfo(); - - 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; - } - } - - // Check the device channel count. - long inputChannels, outputChannels; - result = ASIOGetChannels( &inputChannels, &outputChannels ); - if ( result != ASE_OK ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) || - ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - 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 ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Get the current sample rate - ASIOSampleRate currentRate; - result = ASIOGetSampleRate( ¤tRate ); - if ( result != ASE_OK ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Set the sample rate only if necessary - if ( currentRate != sampleRate ) { - result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); - if ( result != ASE_OK ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // 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 ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // 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 ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // 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 ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - 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; - } - - if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) { - drivers.removeCurrentDriver(); - errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!"; - return FAILURE; - } - - 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. - AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - if ( handle == 0 ) { - try { - handle = new AsioHandle; - } - catch ( std::bad_alloc& ) { - //if ( handle == NULL ) { - drivers.removeCurrentDriver(); - errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory."; - return FAILURE; - } - 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. - long inputLatency, outputLatency; - if ( mode == INPUT && stream_.mode == OUTPUT ) { - ASIODisposeBuffers(); - if ( handle->bufferInfos ) free( handle->bufferInfos ); - } - - // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. - bool buffersAllocated = false; - 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; iisInput = ASIOFalse; - infos->channelNum = i + stream_.channelOffset[0]; - infos->buffers[0] = infos->buffers[1] = 0; - } - for ( i=0; iisInput = ASIOTrue; - infos->channelNum = i + stream_.channelOffset[1]; - infos->buffers[0] = infos->buffers[1] = 0; - } - - // 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 ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers."; - errorText_ = errorStream_.str(); - goto error; - } - buffersAllocated = true; - - // 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 ( 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_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - stream_.sampleRate = sampleRate; - stream_.device[mode] = device; - stream_.state = STREAM_STOPPED; - asioCallbackInfo = &stream_.callbackInfo; - stream_.callbackInfo.object = (void *) this; - if ( stream_.mode == OUTPUT && mode == INPUT ) - // We had already set up an output stream. - stream_.mode = DUPLEX; - else - stream_.mode = mode; - - // Determine device latencies - 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 ( buffersAllocated ) - ASIODisposeBuffers(); - drivers.removeCurrentDriver(); - - 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; - } - - 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; - } - - 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; ibufferInfos[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; ibufferInfos[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; ibufferInfos[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; ibufferInfos[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; ibufferInfos[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 , 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 -#include -#include -#include - -//============================================================================= - -#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() { - delete buffer_; - } - - // sets the length of the internal ring buffer - void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) { - delete 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_; - } - - // "in" index can end on the "out" index but cannot begin at it - 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_; - } - - // "out" index can begin at and end on 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 convertBufferWasapi function is used to perform this conversion -// between HwIn->UserIn and UserOut->HwOut during the stream callback loop. -// This sample rate converter favors speed over quality, and works best with conversions between -// one rate and its multiple. -void convertBufferWasapi( char* outBuffer, - const char* inBuffer, - const unsigned int& channelCount, - const unsigned int& inSampleRate, - const unsigned int& outSampleRate, - const unsigned int& inSampleCount, - unsigned int& outSampleCount, - const RtAudioFormat& format ) -{ - // calculate the new outSampleCount and relative sampleStep - float sampleRatio = ( float ) outSampleRate / inSampleRate; - float sampleStep = 1.0f / sampleRatio; - float inSampleFraction = 0.0f; - - outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio ); - - // frame-by-frame, copy each relative input sample into it's corresponding output sample - for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ ) - { - unsigned int inSample = ( unsigned int ) inSampleFraction; - - switch ( format ) - { - case RTAUDIO_SINT8: - memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) ); - break; - case RTAUDIO_SINT16: - memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) ); - break; - case RTAUDIO_SINT24: - memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) ); - break; - case RTAUDIO_SINT32: - memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) ); - break; - case RTAUDIO_FLOAT32: - memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) ); - break; - case RTAUDIO_FLOAT64: - memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) ); - break; - } - - // jump to next in sample - inSampleFraction += sampleStep; - } -} - -//----------------------------------------------------------------------------- - -// 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 ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator"; - error( RtAudioError::DRIVER_ERROR ); - } -} - -//----------------------------------------------------------------------------- - -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; - - // 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::wstring deviceName; - 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; - } - - deviceName = defaultDeviceNameProp.pwszVal; - defaultDeviceName = std::string( deviceName.begin(), deviceName.end() ); - - // 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; - } - - deviceName = deviceNameProp.pwszVal; - info.name = std::string( deviceName.begin(), deviceName.end() ); - - // 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] ); - } - - // 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; - } - - // 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 ); - - // stop capture client if applicable - if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) { - HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop(); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream."; - error( RtAudioError::DRIVER_ERROR ); - return; - } - } - - // stop render client if applicable - if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) { - HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop(); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream."; - error( RtAudioError::DRIVER_ERROR ); - return; - } - } - - // 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 ); - } - - // stop capture client if applicable - if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) { - HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop(); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream."; - error( RtAudioError::DRIVER_ERROR ); - return; - } - } - - // stop render client if applicable - if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) { - HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop(); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream."; - error( RtAudioError::DRIVER_ERROR ); - return; - } - } - - // 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; - } - - // determine whether index falls within capture or render 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 device audio client."; - goto Exit; - } - - hr = captureAudioClient->GetMixFormat( &deviceFormat ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format."; - goto Exit; - } - - stream_.nDeviceChannels[mode] = deviceFormat->nChannels; - captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); - } - else { - if ( mode != OUTPUT ) { - errorType = RtAudioError::INVALID_USE; - errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device."; - goto Exit; - } - - // retrieve renderAudioClient from devicePtr - IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; - - 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 device audio client."; - goto Exit; - } - - hr = renderAudioClient->GetMixFormat( &deviceFormat ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve 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 != stream_.nDeviceChannels ) - 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, 0 ); - - // 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; - - // declare local stream variables - RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback; - BYTE* streamBuffer = NULL; - unsigned long captureFlags = 0; - unsigned int bufferFrameCount = 0; - unsigned int numFramesPadding = 0; - unsigned int convBufferSize = 0; - bool callbackPushed = false; - 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; - - errorText_.clear(); - 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 ) 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; - } - - captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate ); - - // initialize capture stream according to desire buffer size - float desiredBufferSize = stream_.bufferSize * captureSrRatio; - REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec ); - - if ( !captureClient ) { - hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - desiredBufferPeriod, - desiredBufferPeriod, - 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; - } - - // 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 )->captureClient = captureClient; - ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent; - } - - 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 ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT]; - inBufferSize *= stream_.nDeviceChannels[INPUT]; - - // set captureBuffer size - captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) ); - - // 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; - } - } - - // 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; - } - - renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate ); - - // initialize render stream according to desire buffer size - float desiredBufferSize = stream_.bufferSize * renderSrRatio; - REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec ); - - if ( !renderClient ) { - hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - desiredBufferPeriod, - desiredBufferPeriod, - 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; - } - - 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 ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT]; - outBufferSize *= stream_.nDeviceChannels[OUTPUT]; - - // set renderBuffer size - renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) ); - - // 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; - } - } - - if ( stream_.mode == INPUT ) { - convBuffSize = ( size_t ) ( 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 ) ( 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 ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), - ( size_t ) ( 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] ) ); - } - - convBuffer = ( char* ) malloc( convBuffSize ); - stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize ); - 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 ) { - // Pull callback buffer from inputBuffer - callbackPulled = captureBuffer.pullBuffer( convBuffer, - ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT], - stream_.deviceFormat[INPUT] ); - - if ( callbackPulled ) { - // Convert callback buffer to user sample rate - convertBufferWasapi( stream_.deviceBuffer, - convBuffer, - stream_.nDeviceChannels[INPUT], - captureFormat->nSamplesPerSec, - stream_.sampleRate, - ( unsigned int ) ( stream_.bufferSize * captureSrRatio ), - convBufferSize, - stream_.deviceFormat[INPUT] ); - - 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 ); - - // 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 ( stream_.doConvertBuffer[OUTPUT] ) { - // Convert callback buffer to stream format - convertBuffer( stream_.deviceBuffer, - stream_.userBuffer[OUTPUT], - stream_.convertInfo[OUTPUT] ); - - } - - // Convert callback buffer to stream sample rate - convertBufferWasapi( convBuffer, - stream_.deviceBuffer, - stream_.nDeviceChannels[OUTPUT], - stream_.sampleRate, - renderFormat->nSamplesPerSec, - stream_.bufferSize, - convBufferSize, - stream_.deviceFormat[OUTPUT] ); - - // 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( 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 ) { - callbackPulled = false; - } - - // tick stream time - RtApi::tickStreamTime(); - } - -Exit: - // clean up - CoTaskMemFree( captureFormat ); - CoTaskMemFree( renderFormat ); - - free ( convBuffer ); - - CoUninitialize(); - - // update stream state - stream_.state = STREAM_STOPPED; - - if ( errorText_.empty() ) - return; - else - 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 -#include -#include - -#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* 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 ( coInitialized_ ) CoUninitialize(); // balanced call. - if ( stream_.state != STREAM_CLOSED ) closeStream(); -} - -// 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 indices; - for ( unsigned int i=0; i(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= (unsigned int) outCaps.dwMinSecondarySampleRate && - SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) - info.sampleRates.push_back( 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 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 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; - } - - 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(); - 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(); - 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(); - 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(); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break; - Sleep( 1 ); - } - - //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); - - handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; - if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; - handle->bufferPointer[1] = safeReadPointer; - } - else if ( stream_.mode == OUTPUT ) { - - // Set the proper nextWritePosition after initial startup. - LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; - errorText_ = errorStream_.str(); - 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; idsBufferSize[0]; - nextWritePointer = handle->bufferPointer[0]; - - DWORD endWrite, leadPointer; - while ( true ) { - // Find out where the read and "safe write" pointers are. - result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; - errorText_ = errorStream_.str(); - 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(); - 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(); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; - handle->bufferPointer[0] = nextWritePointer; - } - - // Don't bother draining input - if ( handle->drainCounter ) { - handle->drainCounter++; - goto unlock; - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - - // Setup parameters. - if ( stream_.doConvertBuffer[1] ) { - buffer = stream_.deviceBuffer; - bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1]; - bufferBytes *= formatBytes( stream_.deviceFormat[1] ); - } - else { - buffer = stream_.userBuffer[1]; - bufferBytes = stream_.bufferSize * stream_.nUserChannels[1]; - bufferBytes *= formatBytes( stream_.userFormat ); - } - - LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - long nextReadPointer = handle->bufferPointer[1]; - DWORD dsBufferSize = handle->dsBufferSize[1]; - - // Find out where the write and "safe read" pointers are. - result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; - errorText_ = errorStream_.str(); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - - if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset - DWORD endRead = nextReadPointer + bufferBytes; - - // Handling depends on whether we are INPUT or DUPLEX. - // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode, - // then a wait here will drag the write pointers into the forbidden zone. - // - // In DUPLEX mode, rather than wait, we will back off the read pointer until - // it's in a safe position. This causes dropouts, but it seems to be the only - // practical way to sync up the read and write pointers reliably, given the - // the very complex relationship between phase and increment of the read and write - // pointers. - // - // In order to minimize audible dropouts in DUPLEX mode, we will - // provide a pre-roll period of 0.5 seconds in which we return - // zeros from the read buffer while the pointers sync up. - - if ( stream_.mode == DUPLEX ) { - if ( safeReadPointer < endRead ) { - if ( duplexPrerollBytes <= 0 ) { - // Pre-roll time over. Be more agressive. - int adjustment = endRead-safeReadPointer; - - handle->xrun[1] = true; - // Two cases: - // - large adjustments: we've probably run out of CPU cycles, so just resync exactly, - // and perform fine adjustments later. - // - small adjustments: back off by twice as much. - if ( adjustment >= 2*bufferBytes ) - nextReadPointer = safeReadPointer-2*bufferBytes; - else - nextReadPointer = safeReadPointer-bufferBytes-adjustment; - - if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; - - } - else { - // In pre=roll time. Just do it. - nextReadPointer = safeReadPointer - bufferBytes; - while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; - } - endRead = nextReadPointer + bufferBytes; - } - } - else { // mode == INPUT - while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) { - // See comments for playback. - double millis = (endRead - safeReadPointer) * 1000.0; - millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate); - if ( millis < 1.0 ) millis = 1.0; - Sleep( (DWORD) millis ); - - // Wake up and find out where we are now. - result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; - errorText_ = errorStream_.str(); - 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(); - 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(); - 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; jobject; - bool* isRunning = &info->isRunning; - - while ( *isRunning == true ) { - object->callbackEvent(); - } - - _endthreadex( 0 ); - return 0; -} - -#include "tchar.h" - -static std::string convertTChar( LPCTSTR name ) -{ -#if defined( UNICODE ) || defined( _UNICODE ) - int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL); - std::string s( length-1, '\0' ); - WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL); -#else - std::string s( name ); -#endif - - return s; -} - -static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, - LPCTSTR description, - LPCTSTR /*module*/, - LPVOID lpContext ) -{ - struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext; - std::vector& 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 = convertTChar( 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 -#include - - // 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; - - // 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 ) { - 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: - 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; - - // 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::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: - snd_ctl_close( chandle ); - snd_card_next( &card ); - } - - result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); - if ( result == 0 ) { - if ( nDevices == device ) { - strcpy( name, "default" ); - goto foundDevice; - } - nDevices++; - } - - if ( nDevices == 0 ) { - errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - if ( device >= nDevices ) { - errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - foundDevice: - - // If a stream is already open, we cannot probe the stream devices. - // Thus, use the saved results. - if ( stream_.state != STREAM_CLOSED && - ( stream_.device[0] == device || stream_.device[1] == device ) ) { - snd_ctl_close( chandle ); - if ( device >= devices_.size() ) { - errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened."; - error( RtAudioError::WARNING ); - return info; - } - return devices_[ device ]; - } - - int openMode = SND_PCM_ASYNC; - snd_pcm_stream_t stream; - snd_pcm_info_t *pcminfo; - snd_pcm_info_alloca( &pcminfo ); - snd_pcm_t *phandle; - snd_pcm_hw_params_t *params; - snd_pcm_hw_params_alloca( ¶ms ); - - // First try for playback unless default device (which has subdev -1) - stream = SND_PCM_STREAM_PLAYBACK; - snd_pcm_info_set_stream( pcminfo, stream ); - if ( subdevice != -1 ) { - snd_pcm_info_set_device( pcminfo, subdevice ); - snd_pcm_info_set_subdevice( pcminfo, 0 ); - - result = snd_ctl_pcm_info( chandle, pcminfo ); - if ( result < 0 ) { - // Device probably doesn't support playback. - goto captureProbe; - } - } - - result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto captureProbe; - } - - // The device is open ... fill the parameter structure. - result = snd_pcm_hw_params_any( phandle, params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto captureProbe; - } - - // Get output channel information. - unsigned int value; - result = snd_pcm_hw_params_get_channels_max( params, &value ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto captureProbe; - } - info.outputChannels = value; - snd_pcm_close( phandle ); - - captureProbe: - stream = SND_PCM_STREAM_CAPTURE; - snd_pcm_info_set_stream( pcminfo, stream ); - - // Now try for capture unless default device (with subdev = -1) - if ( subdevice != -1 ) { - result = snd_ctl_pcm_info( chandle, pcminfo ); - snd_ctl_close( chandle ); - if ( result < 0 ) { - // Device probably doesn't support capture. - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - } - else - snd_ctl_close( chandle ); - - result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - - // The device is open ... fill the parameter structure. - result = snd_pcm_hw_params_any( phandle, params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - - result = snd_pcm_hw_params_get_channels_max( params, &value ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - info.inputChannels = value; - snd_pcm_close( phandle ); - - // If device opens for both playback and capture, we determine the channels. - if ( info.outputChannels > 0 && info.inputChannels > 0 ) - info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - - // ALSA doesn't provide default devices so we'll use the first available one. - if ( device == 0 && info.outputChannels > 0 ) - info.isDefaultOutput = true; - if ( device == 0 && info.inputChannels > 0 ) - info.isDefaultInput = true; - - probeParameters: - // At this point, we just need to figure out the supported data - // formats and sample rates. We'll proceed by opening the device in - // the direction with the maximum number of channels, or playback if - // they are equal. This might limit our sample rate options, but so - // be it. - - if ( info.outputChannels >= info.inputChannels ) - stream = SND_PCM_STREAM_PLAYBACK; - else - stream = SND_PCM_STREAM_CAPTURE; - snd_pcm_info_set_stream( pcminfo, stream ); - - result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // The device is open ... fill the parameter structure. - result = snd_pcm_hw_params_any( phandle, params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Test our discrete set of sample rate values. - info.sampleRates.clear(); - for ( unsigned int i=0; i= 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; iflags & 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" ); - goto foundDevice; - } - nDevices++; - } - - 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 (eg: NetBSD 1.6.x with GNU Pthread) - if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { - // We previously attempted to increase the audio callback priority - // to SCHED_RR here via the attributes. However, while no errors - // were reported in doing so, it did not work. So, now this is - // done in the alsaCallbackHandler function. - stream_.callbackInfo.doRealtime = true; - 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; - stream_.callbackInfo.priority = priority; - } -#endif - - stream_.callbackInfo.isRunning = true; - result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo ); - pthread_attr_destroy( &attr ); - 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 ); - - 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; ixrun[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; ixrun[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 { - 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 (eg: NetBSD 1.6.x with GNU Pthread) - if ( &info->doRealtime ) { - pthread_t tID = pthread_self(); // ID of this thread - sched_param prio = { info->priority }; // scheduling priority of thread - pthread_setschedparam( tID, SCHED_RR, &prio ); - } -#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 -#include -#include - -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.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32; - - return info; -} - -static void *pulseaudio_callback( void * user ) -{ - CallbackInfo *cbi = static_cast( user ); - RtApiPulse *context = static_cast( cbi->object ); - volatile bool *isRunning = &cbi->isRunning; - - while ( *isRunning ) { - pthread_testcancel(); - context->callbackEvent(); - } - - pthread_exit( NULL ); -} - -void RtApiPulse::closeStream( void ) -{ - PulseAudioHandle *pah = static_cast( 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( 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( 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 ); - - 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( 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->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( 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->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; - - // 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( stream_.apiHandle ); - - int error; - if ( !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, "RtAudio", 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_.callbackInfo.isRunning = true; - if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread."; - goto error; - } - } - - stream_.state = STREAM_STOPPED; - return true; - - 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; - } - - return FAILURE; -} - -//******************** End of __LINUX_PULSE__ *********************// -#endif - -#if defined(__LINUX_OSS__) - -#include -#include -#include -#include -#include -#include -#include - -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; - if ( mask & AFMT_FLOAT ) - info.nativeFormats |= RTAUDIO_FLOAT32; - 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= (int) SAMPLE_RATES[k] ) - info.sampleRates.push_back( 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 - 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 (eg: NetBSD 1.6.x with GNU Pthread) - if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { - 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; - pthread_attr_setschedparam( &attr, ¶m ); - pthread_attr_setschedpolicy( &attr, SCHED_RR ); - } - 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 ) { - 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; - } - - 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 ); - - 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; iid[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; - - 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 0 ) { - if ( stream_.deviceInterleaved[mode] ) { - if ( mode == OUTPUT ) { - for ( int k=0; k> 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> 8); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_SINT32) { - Int32 *in = (Int32 *)inBuffer; - for (unsigned int i=0; i> 16) & 0x0000ffff); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_FLOAT32) { - Float32 *in = (Float32 *)inBuffer; - for (unsigned int i=0; i> 8) & 0x00ff); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_SINT24) { - Int24 *in = (Int24 *)inBuffer; - for (unsigned int i=0; i> 16); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_SINT32) { - Int32 *in = (Int32 *)inBuffer; - for (unsigned int i=0; i> 24) & 0x000000ff); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_FLOAT32) { - Float32 *in = (Float32 *)inBuffer; - for (unsigned int i=0; i>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 ) -{ - register char val; - register char *ptr; - - ptr = buffer; - if ( format == RTAUDIO_SINT16 ) { - for ( unsigned int i=0; i -#include - -/* --- Monocasual hack ---------------------------------------------- */ -#if defined(__linux__) -#include -#endif -/* ------------------------------------------------------------------ */ - -#include -#include - -/*! \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). - - 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 Direct Sound, 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. -*/ -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). - -/*! \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). - - 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 RtAudioError : public std::exception -{ - 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 ) throw() : message_(message), type_(type) {} - - //! The destructor. - virtual ~RtAudioError( void ) throw() {} - - //! Prints thrown error message to stderr. - virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } - - //! Returns the thrown error message type. - virtual const Type& getType(void) const throw() { return type_; } - - //! Returns the thrown error message string. - virtual const std::string& getMessage(void) const throw() { return message_; } - - //! Returns the thrown error message as a c-style string. - virtual const char* what( void ) const throw() { return message_.c_str(); } - - protected: - std::string message_; - 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 -{ - 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 Direct Sound API. */ - RTAUDIO_DUMMY /*!< A compilable but non-functional API. */ - }; - - //! 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 sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ - RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ - - // Default constructor. - DeviceInfo() - :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), - isDefaultOutput(false), isDefaultInput(false), 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 Direct Sound, 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 ) throw(); - - //! 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 &apis ) throw(); - - //! 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() throw(); - - //! Returns the audio API specifier for the current instance of RtAudio. - RtAudio::Api getCurrentApi( void ) throw(); - - //! 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 ) throw(); - - //! 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 ) throw(); - - //! 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 ) throw(); - - //! 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 ) throw(); - - //! 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 throw(); - - //! Returns true if the stream is running and false if it is stopped or not open. - bool isStreamRunning( void ) const throw(); - - //! 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 ) throw(); - -/* --- Monocasual hack ---------------------------------------------- */ - //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 - #include - - 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 - - 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) {} -}; - -// **************************************************************** // -// -// 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 S24& v ) { *this = v; } - 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 -#endif - -#include - -class RtApi -{ -public: - -/* --- Monocasual hack ---------------------------------------------- */ -#ifdef __linux__ - void *__HACK__getJackClient(); -#endif -/* ------------------------------------------------------------------ */ - - 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 inOffset; - std::vector 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 ) throw() { return rtapi_->getCurrentApi(); } -inline unsigned int RtAudio :: getDeviceCount( void ) throw() { return rtapi_->getDeviceCount(); } -inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); } -inline unsigned int RtAudio :: getDefaultInputDevice( void ) throw() { return rtapi_->getDefaultInputDevice(); } -inline unsigned int RtAudio :: getDefaultOutputDevice( void ) throw() { return rtapi_->getDefaultOutputDevice(); } -inline void RtAudio :: closeStream( void ) throw() { 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 throw() { return rtapi_->isStreamOpen(); } -inline bool RtAudio :: isStreamRunning( void ) const throw() { 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 ) throw() { rtapi_->showWarnings( value ); } - -// RtApi Subclass prototypes. - -#if defined(__MACOSX_CORE__) - -#include - -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 ); - long getStreamLatency( 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 ); - long getStreamLatency( 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 ); -}; - -#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 ); - long getStreamLatency( 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 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 ); - long getStreamLatency( 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 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(); - ~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 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 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/rtaudio-mod/config/config.guess b/src/rtaudio-mod/config/config.guess deleted file mode 100644 index 313be34..0000000 --- a/src/rtaudio-mod/config/config.guess +++ /dev/null @@ -1,1371 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 -# Free Software Foundation, Inc. - -timestamp='2004-02-26' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Written by Per Bothner . -# Please send patches to . -# -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. -# -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 99, 2000 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit 0 ;; - --version | -v ) - echo "$version" ; exit 0 ;; - --help | --h* | -h ) - echo "$usage"; exit 0 ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - - -dummy=dummy-$$ -trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int dummy(){}" > $dummy.c - for c in cc gcc c89 ; do - ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 - if test $? = 0 ; then - CC_FOR_BUILD="$c"; break - fi - done - rm -f $dummy.c $dummy.o $dummy.rel - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 8/24/94.) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # Netbsd (nbsd) targets should (where applicable) match one or - # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # Determine the machine/vendor (is the vendor relevant). - case "${UNAME_MACHINE}" in - amiga) machine=m68k-unknown ;; - arm32) machine=arm-unknown ;; - atari*) machine=m68k-atari ;; - sun3*) machine=m68k-sun ;; - mac68k) machine=m68k-apple ;; - macppc) machine=powerpc-apple ;; - hp3[0-9][05]) machine=m68k-hp ;; - ibmrt|romp-ibm) machine=romp-ibm ;; - *) machine=${UNAME_MACHINE}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE}" in - i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # The OS release - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit 0 ;; - alpha:OSF1:*:*) - if test $UNAME_RELEASE = "V4.0"; then - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - fi - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - cat <$dummy.s - .data -\$Lformat: - .byte 37,100,45,37,120,10,0 # "%d-%x\n" - - .text - .globl main - .align 4 - .ent main -main: - .frame \$30,16,\$26,0 - ldgp \$29,0(\$27) - .prologue 1 - .long 0x47e03d80 # implver \$0 - lda \$2,-1 - .long 0x47e20c21 # amask \$2,\$1 - lda \$16,\$Lformat - mov \$0,\$17 - not \$1,\$18 - jsr \$26,printf - ldgp \$29,0(\$26) - mov 0,\$16 - jsr \$26,exit - .end main -EOF - $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null - if test "$?" = 0 ; then - case `./$dummy` in - 0-0) - UNAME_MACHINE="alpha" - ;; - 1-0) - UNAME_MACHINE="alphaev5" - ;; - 1-1) - UNAME_MACHINE="alphaev56" - ;; - 1-101) - UNAME_MACHINE="alphapca56" - ;; - 2-303) - UNAME_MACHINE="alphaev6" - ;; - 2-307) - UNAME_MACHINE="alphaev67" - ;; - esac - fi - rm -f $dummy.s $dummy - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - exit 0 ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit 0 ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit 0 ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit 0;; - amiga:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit 0 ;; - arc64:OpenBSD:*:*) - echo mips64el-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - arc:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - hkmips:OpenBSD:*:*) - echo mips-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - pmax:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - sgi:OpenBSD:*:*) - echo mips-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - wgrisc:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit 0 ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit 0;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit 0;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit 0 ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit 0 ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - i86pc:SunOS:5.*:*) - echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit 0 ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit 0 ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit 0 ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit 0 ;; - atari*:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit 0 ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit 0 ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit 0 ;; - sun3*:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mac68k:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mvme68k:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mvme88k:OpenBSD:*:*) - echo m88k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit 0 ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit 0 ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit 0 ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit 0 ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit 0 ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD $dummy.c -o $dummy \ - && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ - && rm -f $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - echo mips-mips-riscos${UNAME_RELEASE} - exit 0 ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit 0 ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit 0 ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit 0 ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit 0 ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit 0 ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit 0 ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit 0 ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit 0 ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit 0 ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit 0 ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit 0 ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit 0 ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit 0 ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - echo rs6000-ibm-aix3.2.5 - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit 0 ;; - *:AIX:*:[45]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit 0 ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit 0 ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit 0 ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit 0 ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit 0 ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit 0 ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit 0 ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit 0 ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - case "${HPUX_REV}" in - 11.[0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - esac ;; - esac - fi ;; - esac - if [ "${HP_ARCH}" = "" ]; then - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` - if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi - rm -f $dummy.c $dummy - fi ;; - esac - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit 0 ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit 0 ;; - 3050*:HI-UX:*:*) - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - echo unknown-hitachi-hiuxwe2 - exit 0 ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit 0 ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit 0 ;; - *9??*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit 0 ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit 0 ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit 0 ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit 0 ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit 0 ;; - hppa*:OpenBSD:*:*) - echo hppa-unknown-openbsd - exit 0 ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit 0 ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit 0 ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit 0 ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit 0 ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit 0 ;; - CRAY*X-MP:*:*:*) - echo xmp-cray-unicos - exit 0 ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} - exit 0 ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ - exit 0 ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*T3D:*:*:*) - echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY-2:*:*:*) - echo cray2-cray-unicos - exit 0 ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit 0 ;; - hp300:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit 0 ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit 0 ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit 0 ;; - *:FreeBSD:*:*) - echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit 0 ;; - *:OpenBSD:*:*) - echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - exit 0 ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit 0 ;; - i*:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit 0 ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit 0 ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i386-pc-interix - exit 0 ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit 0 ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit 0 ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - *:GNU:*:*) - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit 0 ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit 0 ;; - arm*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux - exit 0 ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - mips:Linux:*:*) - cat >$dummy.c < /* for printf() prototype */ -int main (int argc, char *argv[]) { -#else -int main (argc, argv) int argc; char *argv[]; { -#endif -#ifdef __MIPSEB__ - printf ("%s-unknown-linux-gnu\n", argv[1]); -#endif -#ifdef __MIPSEL__ - printf ("%sel-unknown-linux-gnu\n", argv[1]); -#endif - return 0; -} -EOF - $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - ;; - ppc:Linux:*:*) - # Determine Lib Version - cat >$dummy.c < -#if defined(__GLIBC__) -extern char __libc_version[]; -extern char __libc_release[]; -#endif -main(argc, argv) - int argc; - char *argv[]; -{ -#if defined(__GLIBC__) - printf("%s %s\n", __libc_version, __libc_release); -#else - printf("unknown\n"); -#endif - return 0; -} -EOF - LIBC="" - $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null - if test "$?" = 0 ; then - ./$dummy | grep 1\.99 > /dev/null - if test "$?" = 0 ; then LIBC="libc1" ; fi - fi - rm -f $dummy.c $dummy - echo powerpc-unknown-linux-gnu${LIBC} - exit 0 ;; - alpha:Linux:*:*) - cat <$dummy.s - .data - \$Lformat: - .byte 37,100,45,37,120,10,0 # "%d-%x\n" - .text - .globl main - .align 4 - .ent main - main: - .frame \$30,16,\$26,0 - ldgp \$29,0(\$27) - .prologue 1 - .long 0x47e03d80 # implver \$0 - lda \$2,-1 - .long 0x47e20c21 # amask \$2,\$1 - lda \$16,\$Lformat - mov \$0,\$17 - not \$1,\$18 - jsr \$26,printf - ldgp \$29,0(\$26) - mov 0,\$16 - jsr \$26,exit - .end main -EOF - LIBC="" - $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null - if test "$?" = 0 ; then - case `./$dummy` in - 0-0) UNAME_MACHINE="alpha" ;; - 1-0) UNAME_MACHINE="alphaev5" ;; - 1-1) UNAME_MACHINE="alphaev56" ;; - 1-101) UNAME_MACHINE="alphapca56" ;; - 2-303) UNAME_MACHINE="alphaev6" ;; - 2-307) UNAME_MACHINE="alphaev67" ;; - esac - objdump --private-headers $dummy | \ - grep ld.so.1 > /dev/null - if test "$?" = 0 ; then - LIBC="libc1" - fi - fi - rm -f $dummy.s $dummy - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit 0 ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; - esac - exit 0 ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu - exit 0 ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux - exit 0 ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - x86_64:Linux:*:*) - echo x86_64-unknown-linux-gnu - exit 0 ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - ld_supported_emulations=`cd /; ld --help 2>&1 \ - | sed -ne '/supported emulations:/!d - s/[ ][ ]*/ /g - s/.*supported emulations: *// - s/ .*// - p'` - case "$ld_supported_emulations" in - i*86linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit 0 - ;; - elf_i*86) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - i*86coff) - echo "${UNAME_MACHINE}-pc-linux-gnucoff" - exit 0 - ;; - esac - # Either a pre-BFD a.out linker (linux-gnuoldld) - # or one that does not give us useful --help. - # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout. - # If ld does not provide *any* "supported emulations:" - # that means it is gnuoldld. - test -z "$ld_supported_emulations" && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0 - case "${UNAME_MACHINE}" in - i*86) - VENDOR=pc; - ;; - *) - VENDOR=unknown; - ;; - esac - # Determine whether the default compiler is a.out or elf - cat >$dummy.c < -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif -#ifdef __ELF__ -# ifdef __GLIBC__ -# if __GLIBC__ >= 2 - printf ("%s-${VENDOR}-linux-gnu\n", argv[1]); -# else - printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); -# endif -# else - printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); -# endif -#else - printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]); -#endif - return 0; -} -EOF - $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 - ;; -# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions -# are messed up and put the nodename in both sysname and nodename. - i*86:DYNIX/ptx:4*:*) - echo i386-sequent-sysv4 - exit 0 ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit 0 ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit 0 ;; - i*86:*:5:7*) - # Fixed at (any) Pentium or better - UNAME_MACHINE=i586 - if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then - echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} - fi - exit 0 ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` - (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit 0 ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit 0 ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i386. - echo i386-pc-msdosdjgpp - exit 0 ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit 0 ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit 0 ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit 0 ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit 0 ;; - M68*:*:R3V[567]*:*) - test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; - 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && echo i486-ncr-sysv4.3${OS_REL} && exit 0 - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && echo i486-ncr-sysv4 && exit 0 ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit 0 ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit 0 ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit 0 ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit 0 ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit 0 ;; - PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit 0 ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit 0 ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit 0 ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit 0 ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit 0 ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit 0 ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit 0 ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit 0 ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit 0 ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit 0 ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit 0 ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit 0 ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit 0 ;; - *:Darwin:*:*) - echo `uname -p`-apple-darwin${UNAME_RELEASE} - exit 0 ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - if test "${UNAME_MACHINE}" = "x86pc"; then - UNAME_MACHINE=pc - fi - echo `uname -p`-${UNAME_MACHINE}-nto-qnx - exit 0 ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit 0 ;; - NSR-[KW]:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit 0 ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit 0 ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit 0 ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit 0 ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = "386"; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit 0 ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit 0 ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit 0 ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit 0 ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit 0 ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit 0 ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit 0 ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit 0 ;; -esac - -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 -rm -f $dummy.c $dummy - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit 0 ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit 0 ;; - c34*) - echo c34-convex-bsd - exit 0 ;; - c38*) - echo c38-convex-bsd - exit 0 ;; - c4*) - echo c4-convex-bsd - exit 0 ;; - esac -fi - -cat >&2 < in order to provide the needed -information to handle your system. - -config.guess timestamp = $timestamp - -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/src/rtaudio-mod/config/config.sub b/src/rtaudio-mod/config/config.sub deleted file mode 100755 index 9a7d59a..0000000 --- a/src/rtaudio-mod/config/config.sub +++ /dev/null @@ -1,1366 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 -# Free Software Foundation, Inc. - -timestamp='2012-11-19' - -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Please send patches to . -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit 0 ;; - --version | -v ) - echo "$version" ; exit 0 ;; - --help | --h* | -h ) - echo "$usage"; exit 0 ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit 0;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | storm-chaos* | os2-emx*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis) - os= - basic_machine=$1 - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \ - | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \ - | pyramid | mn10200 | mn10300 | tron | a29k \ - | 580 | i960 | h8300 \ - | x86 | ppcbe | mipsbe | mipsle | shbe | shle \ - | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \ - | hppa64 \ - | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \ - | alphaev6[78] \ - | we32k | ns16k | clipper | i370 | sh | sh[34] \ - | powerpc | powerpc64 | powerpcle \ - | 1750a | dsp16xx | pdp10 | pdp11 \ - | mips16 | mips64 | mipsel | mips64el \ - | mips64orion | mips64orionel | mipstx39 | mipstx39el \ - | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \ - | mips64vr5000 | miprs64vr5000el | mcore | s390 | s390x \ - | sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \ - | v850 | c4x \ - | thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \ - | pj | pjl | h8500) - basic_machine=$basic_machine-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12) - # Motorola 68HC11/12. - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | w65) - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - # FIXME: clean up the formatting here. - vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \ - | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \ - | arm-* | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \ - | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \ - | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \ - | xmp-* | ymp-* \ - | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \ - | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \ - | hppa2.0n-* | hppa64-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \ - | alphaev6[78]-* \ - | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \ - | clipper-* | orion-* \ - | sparclite-* | pdp10-* | pdp11-* | sh-* | powerpc-* | powerpc64-* | powerpcle-* \ - | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \ - | mips16-* | mips64-* | mipsel-* \ - | mips64el-* | mips64orion-* | mips64orionel-* \ - | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \ - | mipstx39-* | mipstx39el-* | mcore-* \ - | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \ - | [cjt]90-* \ - | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \ - | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \ - | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*) - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | ymp) - basic_machine=ymp-cray - os=-unicos - ;; - cray2) - basic_machine=cray2-cray - os=-unicos - ;; - [cjt]90) - basic_machine=${basic_machine}-cray - os=-unicos - ;; - crds | unos) - basic_machine=m68k-crds - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; -# I'm not sure what "Sysv32" means. Should this be sysv3.2? - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mipsel*-linux*) - basic_machine=mipsel-unknown - os=-linux-gnu - ;; - mips*-linux*) - basic_machine=mips-unknown - os=-linux-gnu - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - mmix*) - basic_machine=mmix-knuth - os=-mmixware - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pentium | p5 | k5 | k6 | nexgen) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon) - basic_machine=i686-pc - ;; - pentiumii | pentium2) - basic_machine=i686-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc) basic_machine=powerpc-unknown - ;; - ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sparclite-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=t3e-cray - os=-unicos - ;; - tic54x | c54x*) - basic_machine=tic54x-unknown - os=-coff - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xmp) - basic_machine=xmp-cray - os=-unicos - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - mips) - if [ x$os = x-linux-gnu ]; then - basic_machine=mips-unknown - else - basic_machine=mips-mips - fi - ;; - romp) - basic_machine=romp-ibm - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh3 | sh4) - basic_machine=sh-unknown - ;; - sparc | sparcv9 | sparcv9b) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - c4x*) - basic_machine=c4x-none - os=-coff - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ - | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto*) - os=-nto-qnx - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - # This also exists in the configure program, but was not the - # default. - # os=-sunos4 - ;; - m68*-cisco) - os=-aout - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-ibm) - os=-aix - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -vxsim* | -vxworks*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit 0 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/src/rtaudio-mod/config/install.sh b/src/rtaudio-mod/config/install.sh deleted file mode 100755 index e69de29..0000000 diff --git a/src/rtaudio-mod/configure b/src/rtaudio-mod/configure deleted file mode 100755 index 9f3d3f8..0000000 --- a/src/rtaudio-mod/configure +++ /dev/null @@ -1,5888 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for RtAudio 4.1. -# -# Report bugs to . -# -# -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: gary@music.mcgill.ca about your system, including any -$0: error possibly output before this message. Then install -$0: a modern shell, or manually run the script under such a -$0: shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME='RtAudio' -PACKAGE_TARNAME='rtaudio' -PACKAGE_VERSION='4.1' -PACKAGE_STRING='RtAudio 4.1' -PACKAGE_BUGREPORT='gary@music.mcgill.ca' -PACKAGE_URL='' - -ac_unique_file="RtAudio.cpp" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_subst_vars='LTLIBOBJS -LIBOBJS -objects -PULSE_LIBS -PULSE_CFLAGS -req -api -libflags -sharedname -sharedlib -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -object_path -cxxflag -cppflag -EGREP -GREP -CPP -ac_ct_CC -CFLAGS -CC -AR -RANLIB -OBJEXT -EXEEXT -ac_ct_CXX -CPPFLAGS -LDFLAGS -CXXFLAGS -CXX -PKG_CONFIG_LIBDIR -PKG_CONFIG_PATH -PKG_CONFIG -GXX -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -enable_debug -with_jack -with_alsa -with_pulse -with_oss -with_core -with_asio -with_ds -with_wasapi -' - ac_precious_vars='build_alias -host_alias -target_alias -PKG_CONFIG -PKG_CONFIG_PATH -PKG_CONFIG_LIBDIR -CXX -CXXFLAGS -LDFLAGS -LIBS -CPPFLAGS -CCC -CC -CFLAGS -CPP -PULSE_CFLAGS -PULSE_LIBS' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures RtAudio 4.1 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/rtaudio] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of RtAudio 4.1:";; - esac - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-debug = enable various debug output - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-jack = choose JACK server support (mac and linux only) - --with-alsa = choose native ALSA API support (linux only) - --with-pulse = choose PulseAudio API support (linux only) - --with-oss = choose OSS API support (linux only) - --with-jack = choose JACK server support (unix only) - --with-core = choose CoreAudio API support (mac only) - --with-asio = choose ASIO API support (windoze only) - --with-ds = choose DirectSound API support (windoze only) - --with-wasapi = choose Windows Audio Session API support (windoze only) - -Some influential environment variables: - PKG_CONFIG path to pkg-config utility - PKG_CONFIG_PATH - directories to add to pkg-config's search path - PKG_CONFIG_LIBDIR - path overriding pkg-config's built-in search path - CXX C++ compiler command - CXXFLAGS C++ compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CC C compiler command - CFLAGS C compiler flags - CPP C preprocessor - PULSE_CFLAGS - C compiler flags for PULSE, overriding pkg-config - PULSE_LIBS linker flags for PULSE, overriding pkg-config - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -RtAudio configure 4.1 -generated by GNU Autoconf 2.69 - -Copyright (C) 2012 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_cxx_try_compile LINENO -# ---------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_compile - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - -# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_c_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ----------------------------------- ## -## Report this to gary@music.mcgill.ca ## -## ----------------------------------- ##" - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_mongrel - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile - -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - -# ac_fn_c_check_func LINENO FUNC VAR -# ---------------------------------- -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_c_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main () -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_func -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by RtAudio $as_me 4.1, which was -generated by GNU Autoconf 2.69. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -ac_aux_dir= -for ac_dir in config "$srcdir"/config; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in config \"$srcdir\"/config" "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - - -ac_config_files="$ac_config_files rtaudio-config librtaudio.pc Makefile tests/Makefile" - - -# Fill GXX with something before test. -GXX="no" - - - - - - - - - - - -if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. -set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PKG_CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $PKG_CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -PKG_CONFIG=$ac_cv_path_PKG_CONFIG -if test -n "$PKG_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 -$as_echo "$PKG_CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_path_PKG_CONFIG"; then - ac_pt_PKG_CONFIG=$PKG_CONFIG - # Extract the first word of "pkg-config", so it can be a program name with args. -set dummy pkg-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $ac_pt_PKG_CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG -if test -n "$ac_pt_PKG_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 -$as_echo "$ac_pt_PKG_CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_pt_PKG_CONFIG" = x; then - PKG_CONFIG="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - PKG_CONFIG=$ac_pt_PKG_CONFIG - fi -else - PKG_CONFIG="$ac_cv_path_PKG_CONFIG" -fi - -fi -if test -n "$PKG_CONFIG"; then - _pkg_min_version=0.9.0 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 -$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } - if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - PKG_CONFIG="" - fi -fi - - - -# Checks for programs. -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -if test -z "$CXX"; then - if test -n "$CCC"; then - CXX=$CCC - else - if test -n "$ac_tool_prefix"; then - for ac_prog in g++ CC c++ cxx - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 -$as_echo "$CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CXX" && break - done -fi -if test -z "$CXX"; then - ac_ct_CXX=$CXX - for ac_prog in g++ CC c++ cxx -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CXX"; then - ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CXX="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CXX=$ac_cv_prog_ac_ct_CXX -if test -n "$ac_ct_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 -$as_echo "$ac_ct_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CXX" && break -done - - if test "x$ac_ct_CXX" = x; then - CXX="g++" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CXX=$ac_ct_CXX - fi -fi - - fi -fi -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 -$as_echo_n "checking whether the C++ compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C++ compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 -$as_echo_n "checking for C++ compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C++ compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 -$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } -if ${ac_cv_cxx_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 -$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GXX=yes -else - GXX= -fi -ac_test_CXXFLAGS=${CXXFLAGS+set} -ac_save_CXXFLAGS=$CXXFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 -$as_echo_n "checking whether $CXX accepts -g... " >&6; } -if ${ac_cv_prog_cxx_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_cxx_werror_flag=$ac_cxx_werror_flag - ac_cxx_werror_flag=yes - ac_cv_prog_cxx_g=no - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -else - CXXFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -else - ac_cxx_werror_flag=$ac_save_cxx_werror_flag - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cxx_werror_flag=$ac_save_cxx_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 -$as_echo "$ac_cv_prog_cxx_g" >&6; } -if test "$ac_test_CXXFLAGS" = set; then - CXXFLAGS=$ac_save_CXXFLAGS -elif test $ac_cv_prog_cxx_g = yes; then - if test "$GXX" = yes; then - CXXFLAGS="-g -O2" - else - CXXFLAGS="-g" - fi -else - if test "$GXX" = yes; then - CXXFLAGS="-O2" - else - CXXFLAGS= - fi -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -$as_echo "$RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -$as_echo "$ac_ct_RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -else - RANLIB="$ac_cv_prog_RANLIB" -fi - -# Extract the first word of "ar", so it can be a program name with args. -set dummy ar; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $AR in - [\\/]* | ?:[\\/]*) - ac_cv_path_AR="$AR" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - test -z "$ac_cv_path_AR" && ac_cv_path_AR="no" - ;; -esac -fi -AR=$ac_cv_path_AR -if test -n "$AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 -$as_echo "$AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -if [ $AR = "no" ] ; then - as_fn_error $? "\"Could not find ar - needed to create a library\"" "$LINENO" 5; -fi - -# Checks for header files. -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -for ac_header in sys/ioctl.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -# Check for debug -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to compile debug version" >&5 -$as_echo_n "checking whether to compile debug version... " >&6; } -# Check whether --enable-debug was given. -if test "${enable_debug+set}" = set; then : - enableval=$enable_debug; cppflag=-D__RTAUDIO_DEBUG__ - cxxflag=-g - object_path=Debug - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - cppflag= - cxxflag=-O2 - object_path=Release - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - -# Checks for functions -ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" -if test "x$ac_cv_func_gettimeofday" = xyes; then : - cppflag="$cppflag -DHAVE_GETTIMEOFDAY" -fi - - -# Set paths if prefix is defined -if test "x$prefix" != "x" && test "x$prefix" != "xNONE"; then - LIBS="$LIBS -L$prefix/lib" - CPPFLAGS="$CPPFLAGS -I$prefix/include" -fi - -# For -I and -D flags -CPPFLAGS="$CPPFLAGS $cppflag" - -# For debugging and optimization ... overwrite default because it has both -g and -O2 -#CXXFLAGS="$CXXFLAGS $cxxflag" -CXXFLAGS="$cxxflag" - -# Check compiler and use -Wall if gnu. -if test $GXX = "yes" ; then - cxxflag="-Wall -Wextra" - -fi - -CXXFLAGS="$CXXFLAGS $cxxflag" - -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - - -sharedlib="librtaudio.so" - -sharedname="librtaudio.so.\$(RELEASE)" - -libflags="-shared -Wl,-soname,\$(SHARED).\$(MAJOR) -o \$(SHARED).\$(RELEASE)" - -case $host in - *-apple*) - sharedlib="librtaudio.dylib" - - sharedname="librtaudio.\$(RELEASE).dylib" - - libflags="-dynamiclib -o librtaudio.\$(RELEASE).dylib" - -esac - -# Checks for package options and external software -api="" - -req="" - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for audio API" >&5 -$as_echo_n "checking for audio API... " >&6; } -case $host in - *-*-netbsd*) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using OSS" >&5 -$as_echo "using OSS" >&6; } - api="$api -D__LINUX_OSS__" - LIBS="$LIBS -lossaudio" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 -$as_echo_n "checking for pthread_create in -lpthread... " >&6; } -if ${ac_cv_lib_pthread_pthread_create+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lpthread $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char pthread_create (); -int -main () -{ -return pthread_create (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_pthread_pthread_create=yes -else - ac_cv_lib_pthread_pthread_create=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 -$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } -if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBPTHREAD 1 -_ACEOF - - LIBS="-lpthread $LIBS" - -else - as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5 -fi - - ;; - - *-*-linux*) - -# Check whether --with-jack was given. -if test "${with_jack+set}" = set; then : - withval=$with_jack; - api="$api -D__UNIX_JACK__" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using JACK" >&5 -$as_echo "using JACK" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for jack_client_open in -ljack" >&5 -$as_echo_n "checking for jack_client_open in -ljack... " >&6; } -if ${ac_cv_lib_jack_jack_client_open+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ljack $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char jack_client_open (); -int -main () -{ -return jack_client_open (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_jack_jack_client_open=yes -else - ac_cv_lib_jack_jack_client_open=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jack_jack_client_open" >&5 -$as_echo "$ac_cv_lib_jack_jack_client_open" >&6; } -if test "x$ac_cv_lib_jack_jack_client_open" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBJACK 1 -_ACEOF - - LIBS="-ljack $LIBS" - -else - as_fn_error $? "JACK support requires the jack library!" "$LINENO" 5 -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5 -$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; } -if ${ac_cv_lib_asound_snd_pcm_open+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lasound $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char snd_pcm_open (); -int -main () -{ -return snd_pcm_open (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_asound_snd_pcm_open=yes -else - ac_cv_lib_asound_snd_pcm_open=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5 -$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; } -if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBASOUND 1 -_ACEOF - - LIBS="-lasound $LIBS" - -else - as_fn_error $? "Jack support also requires the asound library!" "$LINENO" 5 -fi - -fi - - - # Look for ALSA flag - -# Check whether --with-alsa was given. -if test "${with_alsa+set}" = set; then : - withval=$with_alsa; - api="$api -D__LINUX_ALSA__" - req="$req alsa" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ALSA" >&5 -$as_echo "using ALSA" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5 -$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; } -if ${ac_cv_lib_asound_snd_pcm_open+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lasound $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char snd_pcm_open (); -int -main () -{ -return snd_pcm_open (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_asound_snd_pcm_open=yes -else - ac_cv_lib_asound_snd_pcm_open=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5 -$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; } -if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBASOUND 1 -_ACEOF - - LIBS="-lasound $LIBS" - -else - as_fn_error $? "ALSA support requires the asound library!" "$LINENO" 5 -fi - -fi - - - # Look for PULSE flag - -# Check whether --with-pulse was given. -if test "${with_pulse+set}" = set; then : - withval=$with_pulse; - api="$api -D__LINUX_PULSE__" - req="$req libpulse-simple" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using PulseAudio" >&5 -$as_echo "using PulseAudio" >&6; } - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PULSE" >&5 -$as_echo_n "checking for PULSE... " >&6; } - -if test -n "$PULSE_CFLAGS"; then - pkg_cv_PULSE_CFLAGS="$PULSE_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse-simple\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libpulse-simple") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_PULSE_CFLAGS=`$PKG_CONFIG --cflags "libpulse-simple" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$PULSE_LIBS"; then - pkg_cv_PULSE_LIBS="$PULSE_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse-simple\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libpulse-simple") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_PULSE_LIBS=`$PKG_CONFIG --libs "libpulse-simple" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - PULSE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libpulse-simple" 2>&1` - else - PULSE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libpulse-simple" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$PULSE_PKG_ERRORS" >&5 - - as_fn_error $? "PulseAudio support requires the pulse-simple library!" "$LINENO" 5 -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - as_fn_error $? "PulseAudio support requires the pulse-simple library!" "$LINENO" 5 -else - PULSE_CFLAGS=$pkg_cv_PULSE_CFLAGS - PULSE_LIBS=$pkg_cv_PULSE_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -fi - LIBS="$LIBS `pkg-config --libs libpulse-simple`" -fi - - - # Look for OSS flag - -# Check whether --with-oss was given. -if test "${with_oss+set}" = set; then : - withval=$with_oss; - api="$api -D__LINUX_OSS__" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using OSS" >&5 -$as_echo "using OSS" >&6; } -fi - - - # If no audio api flags specified, use ALSA - if test "$api" == ""; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ALSA" >&5 -$as_echo "using ALSA" >&6; } - api=-D__LINUX_ALSA__ - - req="$req alsa" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5 -$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; } -if ${ac_cv_lib_asound_snd_pcm_open+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lasound $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char snd_pcm_open (); -int -main () -{ -return snd_pcm_open (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_asound_snd_pcm_open=yes -else - ac_cv_lib_asound_snd_pcm_open=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5 -$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; } -if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBASOUND 1 -_ACEOF - - LIBS="-lasound $LIBS" - -else - as_fn_error $? "ALSA support requires the asound library!" "$LINENO" 5 -fi - - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 -$as_echo_n "checking for pthread_create in -lpthread... " >&6; } -if ${ac_cv_lib_pthread_pthread_create+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lpthread $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char pthread_create (); -int -main () -{ -return pthread_create (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_pthread_pthread_create=yes -else - ac_cv_lib_pthread_pthread_create=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 -$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } -if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBPTHREAD 1 -_ACEOF - - LIBS="-lpthread $LIBS" - -else - as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5 -fi - - ;; - - *-apple*) - -# Check whether --with-jack was given. -if test "${with_jack+set}" = set; then : - withval=$with_jack; - api="$api -D__UNIX_JACK__" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using JACK" >&5 -$as_echo "using JACK" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for jack_client_open in -ljack" >&5 -$as_echo_n "checking for jack_client_open in -ljack... " >&6; } -if ${ac_cv_lib_jack_jack_client_open+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ljack $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char jack_client_open (); -int -main () -{ -return jack_client_open (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_jack_jack_client_open=yes -else - ac_cv_lib_jack_jack_client_open=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jack_jack_client_open" >&5 -$as_echo "$ac_cv_lib_jack_jack_client_open" >&6; } -if test "x$ac_cv_lib_jack_jack_client_open" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBJACK 1 -_ACEOF - - LIBS="-ljack $LIBS" - -else - as_fn_error $? "JACK support requires the jack library!" "$LINENO" 5 -fi - -fi - - -# AC_CHECK_HEADER(jack/jack.h, [], [AC_MSG_ERROR(Jack header file not found!)] ) -# LIBS="$LIBS -framework jackmp" ], ) - - - # Look for Core flag - -# Check whether --with-core was given. -if test "${with_core+set}" = set; then : - withval=$with_core; - api="$api -D__MACOSX_CORE__" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using CoreAudio" >&5 -$as_echo "using CoreAudio" >&6; } - ac_fn_c_check_header_mongrel "$LINENO" "CoreAudio/CoreAudio.h" "ac_cv_header_CoreAudio_CoreAudio_h" "$ac_includes_default" -if test "x$ac_cv_header_CoreAudio_CoreAudio_h" = xyes; then : - -else - as_fn_error $? "CoreAudio header files not found!" "$LINENO" 5 -fi - - - LIBS="$LIBS -framework CoreAudio -framework CoreFoundation" -fi - - - # If no audio api flags specified, use CoreAudio - if test "$api" == ""; then - api=-D__MACOSX_CORE__ - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using CoreAudio" >&5 -$as_echo "using CoreAudio" >&6; } - ac_fn_c_check_header_mongrel "$LINENO" "CoreAudio/CoreAudio.h" "ac_cv_header_CoreAudio_CoreAudio_h" "$ac_includes_default" -if test "x$ac_cv_header_CoreAudio_CoreAudio_h" = xyes; then : - -else - as_fn_error $? "CoreAudio header files not found!" "$LINENO" 5 -fi - - - LIBS="-framework CoreAudio -framework CoreFoundation" - - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 -$as_echo_n "checking for pthread_create in -lpthread... " >&6; } -if ${ac_cv_lib_pthread_pthread_create+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lpthread $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char pthread_create (); -int -main () -{ -return pthread_create (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_pthread_pthread_create=yes -else - ac_cv_lib_pthread_pthread_create=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 -$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } -if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBPTHREAD 1 -_ACEOF - - LIBS="-lpthread $LIBS" - -else - as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5 -fi - - ;; - - *-mingw32*) - -# Check whether --with-asio was given. -if test "${with_asio+set}" = set; then : - withval=$with_asio; - api="$api -D__WINDOWS_ASIO__" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ASIO" >&5 -$as_echo "using ASIO" >&6; } - objects="asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o" - -fi - - - # Look for DirectSound flag - -# Check whether --with-ds was given. -if test "${with_ds+set}" = set; then : - withval=$with_ds; - api="$api -D__WINDOWS_DS__" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using DirectSound" >&5 -$as_echo "using DirectSound" >&6; } - LIBS="-ldsound -lwinmm $LIBS" -fi - - - # Look for WASAPI flag - -# Check whether --with-wasapi was given. -if test "${with_wasapi+set}" = set; then : - withval=$with_wasapi; - api="$api -D__WINDOWS_WASAPI__" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using WASAPI" >&5 -$as_echo "using WASAPI" >&6; } - LIBS="-lwinmm -luuid -lksuser $LIBS" -fi - - - # If no audio api flags specified, use DS - if test "$api" == ""; then - api=-D__WINDOWS_DS__ - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: using DirectSound" >&5 -$as_echo "using DirectSound" >&6; } - LIBS="-ldsound -lwinmm $LIBS" - fi - - LIBS="-lole32 $LIBS" - ;; - - *) - # Default case for unknown realtime systems. - as_fn_error $? "Unknown system type for realtime support!" "$LINENO" 5 - ;; -esac - -CPPFLAGS="$CPPFLAGS $api" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -# Transform confdefs.h into DEFS. -# Protect against shell expansion while executing Makefile rules. -# Protect against Makefile macro expansion. -# -# If the first sed substitution is executed (which looks for macros that -# take arguments), then branch to the quote section. Otherwise, -# look for a macro that doesn't take arguments. -ac_script=' -:mline -/\\$/{ - N - s,\\\n,, - b mline -} -t clear -:clear -s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g -t quote -s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g -t quote -b any -:quote -s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g -s/\[/\\&/g -s/\]/\\&/g -s/\$/$$/g -H -:any -${ - g - s/^\n// - s/\n/ /g - p -} -' -DEFS=`sed -n "$ac_script" confdefs.h` - - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by RtAudio $as_me 4.1, which was -generated by GNU Autoconf 2.69. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - -Configuration files: -$config_files - -Report bugs to ." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -RtAudio config.status 4.1 -configured by $0, generated by GNU Autoconf 2.69, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2012 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h | --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "rtaudio-config") CONFIG_FILES="$CONFIG_FILES rtaudio-config" ;; - "librtaudio.pc") CONFIG_FILES="$CONFIG_FILES librtaudio.pc" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - - -eval set X " :F $CONFIG_FILES " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - - - - esac - -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - - -chmod oug+x rtaudio-config diff --git a/src/rtaudio-mod/configure.ac b/src/rtaudio-mod/configure.ac deleted file mode 100644 index fa45967..0000000 --- a/src/rtaudio-mod/configure.ac +++ /dev/null @@ -1,197 +0,0 @@ -# Process this file with autoconf to produce a configure script. -AC_INIT(RtAudio, 4.1, gary@music.mcgill.ca, rtaudio) -AC_CONFIG_AUX_DIR(config) -AC_CONFIG_SRCDIR(RtAudio.cpp) -AC_CONFIG_FILES([rtaudio-config librtaudio.pc Makefile tests/Makefile]) - -# Fill GXX with something before test. -AC_SUBST( GXX, ["no"] ) - -dnl Check for pkg-config program, used for configuring some libraries. -m4_define_default([PKG_PROG_PKG_CONFIG], -[AC_MSG_CHECKING([pkg-config]) -AC_MSG_RESULT([no])]) - -PKG_PROG_PKG_CONFIG - -dnl If the pkg-config autoconf support isn't installed, define its -dnl autoconf macro to disable any packages depending on it. -m4_define_default([PKG_CHECK_MODULES], -[AC_MSG_CHECKING([$1]) -AC_MSG_RESULT([no]) -$4]) - -# Checks for programs. -AC_PROG_CXX(g++ CC c++ cxx) -AC_PROG_RANLIB -AC_PATH_PROG(AR, ar, no) -if [[ $AR = "no" ]] ; then - AC_MSG_ERROR("Could not find ar - needed to create a library"); -fi - -# Checks for header files. -AC_HEADER_STDC -AC_CHECK_HEADERS(sys/ioctl.h unistd.h) - -# Check for debug -AC_MSG_CHECKING(whether to compile debug version) -AC_ARG_ENABLE(debug, - [ --enable-debug = enable various debug output], - [AC_SUBST( cppflag, [-D__RTAUDIO_DEBUG__] ) AC_SUBST( cxxflag, [-g] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)], - [AC_SUBST( cppflag, [] ) AC_SUBST( cxxflag, [-O2] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)]) - - -# Checks for functions -AC_CHECK_FUNC(gettimeofday, [cppflag="$cppflag -DHAVE_GETTIMEOFDAY"], ) - -# Set paths if prefix is defined -if test "x$prefix" != "x" && test "x$prefix" != "xNONE"; then - LIBS="$LIBS -L$prefix/lib" - CPPFLAGS="$CPPFLAGS -I$prefix/include" -fi - -# For -I and -D flags -CPPFLAGS="$CPPFLAGS $cppflag" - -# For debugging and optimization ... overwrite default because it has both -g and -O2 -#CXXFLAGS="$CXXFLAGS $cxxflag" -CXXFLAGS="$cxxflag" - -# Check compiler and use -Wall if gnu. -if [test $GXX = "yes" ;] then - AC_SUBST( cxxflag, ["-Wall -Wextra"] ) -fi - -CXXFLAGS="$CXXFLAGS $cxxflag" - -AC_CANONICAL_HOST - -AC_SUBST( sharedlib, ["librtaudio.so"] ) -AC_SUBST( sharedname, ["librtaudio.so.\$(RELEASE)"] ) -AC_SUBST( libflags, ["-shared -Wl,-soname,\$(SHARED).\$(MAJOR) -o \$(SHARED).\$(RELEASE)"] ) -case $host in - *-apple*) - AC_SUBST( sharedlib, ["librtaudio.dylib"] ) - AC_SUBST( sharedname, ["librtaudio.\$(RELEASE).dylib"] ) - AC_SUBST( libflags, ["-dynamiclib -o librtaudio.\$(RELEASE).dylib"] ) -esac - -# Checks for package options and external software -AC_SUBST( api, [""] ) -AC_SUBST( req, [""] ) -AC_MSG_CHECKING(for audio API) -case $host in - *-*-netbsd*) - AC_MSG_RESULT(using OSS) - api="$api -D__LINUX_OSS__" - LIBS="$LIBS -lossaudio" - AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!)) - ;; - - *-*-linux*) - AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (mac and linux only)], [ - api="$api -D__UNIX_JACK__" - AC_MSG_RESULT(using JACK) - AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!)) - AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(Jack support also requires the asound library!))], ) - - # Look for ALSA flag - AC_ARG_WITH(alsa, [ --with-alsa = choose native ALSA API support (linux only)], [ - api="$api -D__LINUX_ALSA__" - req="$req alsa" - AC_MSG_RESULT(using ALSA) - AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))], ) - - # Look for PULSE flag - AC_ARG_WITH(pulse, [ --with-pulse = choose PulseAudio API support (linux only)], [ - api="$api -D__LINUX_PULSE__" - req="$req libpulse-simple" - AC_MSG_RESULT(using PulseAudio) - PKG_CHECK_MODULES([PULSE], [libpulse-simple], , AC_MSG_ERROR(PulseAudio support requires the pulse-simple library!)) - LIBS="$LIBS `pkg-config --libs libpulse-simple`" ], ) - - # Look for OSS flag - AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [ - api="$api -D__LINUX_OSS__" - AC_MSG_RESULT(using OSS)], ) - - # If no audio api flags specified, use ALSA - if [test "$api" == "";] then - AC_MSG_RESULT(using ALSA) - AC_SUBST( api, [-D__LINUX_ALSA__] ) - req="$req alsa" - AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!)) - fi - - AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!)) - ;; - - *-apple*) - AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (unix only)], [ - api="$api -D__UNIX_JACK__" - AC_MSG_RESULT(using JACK) - AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!))], ) - -# AC_CHECK_HEADER(jack/jack.h, [], [AC_MSG_ERROR(Jack header file not found!)] ) -# LIBS="$LIBS -framework jackmp" ], ) - - - # Look for Core flag - AC_ARG_WITH(core, [ --with-core = choose CoreAudio API support (mac only)], [ - api="$api -D__MACOSX_CORE__" - AC_MSG_RESULT(using CoreAudio) - AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] ) - LIBS="$LIBS -framework CoreAudio -framework CoreFoundation" ], ) - - # If no audio api flags specified, use CoreAudio - if [test "$api" == ""; ] then - AC_SUBST( api, [-D__MACOSX_CORE__] ) - AC_MSG_RESULT(using CoreAudio) - AC_CHECK_HEADER(CoreAudio/CoreAudio.h, - [], - [AC_MSG_ERROR(CoreAudio header files not found!)] ) - AC_SUBST( LIBS, ["-framework CoreAudio -framework CoreFoundation"] ) - fi - - AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!)) - ;; - - *-mingw32*) - AC_ARG_WITH(asio, [ --with-asio = choose ASIO API support (windoze only)], [ - api="$api -D__WINDOWS_ASIO__" - AC_MSG_RESULT(using ASIO) - AC_SUBST( objects, ["asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o"] ) ], ) - - # Look for DirectSound flag - AC_ARG_WITH(ds, [ --with-ds = choose DirectSound API support (windoze only)], [ - api="$api -D__WINDOWS_DS__" - AC_MSG_RESULT(using DirectSound) - LIBS="-ldsound -lwinmm $LIBS" ], ) - - # Look for WASAPI flag - AC_ARG_WITH(wasapi, [ --with-wasapi = choose Windows Audio Session API support (windoze only)], [ - api="$api -D__WINDOWS_WASAPI__" - AC_MSG_RESULT(using WASAPI) - LIBS="-lwinmm -luuid -lksuser $LIBS" ], ) - - # If no audio api flags specified, use DS - if [test "$api" == "";] then - AC_SUBST( api, [-D__WINDOWS_DS__] ) - AC_MSG_RESULT(using DirectSound) - LIBS="-ldsound -lwinmm $LIBS" - fi - - LIBS="-lole32 $LIBS" - ;; - - *) - # Default case for unknown realtime systems. - AC_MSG_ERROR(Unknown system type for realtime support!) - ;; -esac - -CPPFLAGS="$CPPFLAGS $api" - -AC_OUTPUT - -chmod oug+x rtaudio-config diff --git a/src/rtaudio-mod/librtaudio.pc.in b/src/rtaudio-mod/librtaudio.pc.in deleted file mode 100644 index 45c3f4e..0000000 --- a/src/rtaudio-mod/librtaudio.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include - -Name: librtaudio -Description: RtAudio - a set of C++ classes that provide a common API for realtime audio input/output -Version: 4.1.1 -Requires: @req@ -Libs: -L${libdir} -lrtaudio -Libs.private: -lpthread -Cflags: -pthread -I${includedir} @CPPFLAGS@ \ No newline at end of file diff --git a/src/rtaudio-mod/rtaudio-config.in b/src/rtaudio-mod/rtaudio-config.in deleted file mode 100644 index 5f83d51..0000000 --- a/src/rtaudio-mod/rtaudio-config.in +++ /dev/null @@ -1,19 +0,0 @@ -#! /bin/sh -if (test "x$#" != "x1") ; then - echo "Usage: $0 [--libs | --cxxflags | --cppflags]" - exit; -fi - -LIBRARY="@LIBS@" -CXXFLAGS="@CXXFLAGS@" -CPPFLAGS="@CPPFLAGS@" - -if (test "x$1" = "x--libs") ; then - echo "$LIBRARY -lrtaudio" -elif (test "x$1" = "x--cxxflags") ; then - echo "$CXXFLAGS" -elif (test "x$1" = "x--cppflags") ; then - echo "$CPPFLAGS" -else - echo "Unknown option: $1" -fi diff --git a/src/sampleChannel.cpp b/src/sampleChannel.cpp deleted file mode 100644 index 1b9cb29..0000000 --- a/src/sampleChannel.cpp +++ /dev/null @@ -1,1036 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * channel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#include "sampleChannel.h" -#include "patch.h" -#include "conf.h" -#include "wave.h" -#include "pluginHost.h" -#include "waveFx.h" -#include "mixerHandler.h" -#include "kernelMidi.h" -#include "log.h" - - -extern Patch G_Patch; -extern Mixer G_Mixer; -extern Conf G_Conf; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -SampleChannel::SampleChannel(int bufferSize) - : Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize), - frameRewind (-1), - wave (NULL), - tracker (0), - begin (0), - end (0), - pitch (gDEFAULT_PITCH), - boost (1.0f), - mode (DEFAULT_CHANMODE), - qWait (false), - fadeinOn (false), - fadeinVol (1.0f), - fadeoutOn (false), - fadeoutVol (1.0f), - fadeoutTracker (0), - fadeoutStep (DEFAULT_FADEOUT_STEP), - readActions (true), - midiInReadActions(0x0), - midiInPitch (0x0) -{ - rsmp_state = src_new(SRC_LINEAR, 2, NULL); - pChan = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float)); -} - - -/* -------------------------------------------------------------------------- */ - - -SampleChannel::~SampleChannel() -{ - if (wave) - delete wave; - src_delete(rsmp_state); - free(pChan); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::clear() -{ - /** TODO - these memsets can be done only if status PLAY (if below), - * but it would require extra clearPChan calls when samples stop */ - - memset(vChan, 0, sizeof(float) * bufferSize); - memset(pChan, 0, sizeof(float) * bufferSize); - - if (status & (STATUS_PLAY | STATUS_ENDING)) { - tracker = fillChan(vChan, tracker, 0); - if (fadeoutOn && fadeoutType == XFADE) { - gLog("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker); - fadeoutTracker = fillChan(pChan, fadeoutTracker, 0); - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::calcVolumeEnv(int frame) -{ - /* method: check this frame && next frame, then calculate delta */ - - recorder::action *a0 = NULL; - recorder::action *a1 = NULL; - int res; - - /* get this action on frame 'frame'. It's unlikely that the action - * is not found. */ - - res = recorder::getAction(index, ACTION_VOLUME, frame, &a0); - if (res == 0) - return; - - /* get the action next to this one. - * res == -1: a1 not found, this is the last one. Rewind the search - * and use action at frame number 0 (actions[0]). - * res == -2 ACTION_VOLUME not found. This should never happen */ - - res = recorder::getNextAction(index, ACTION_VOLUME, frame, &a1); - - if (res == -1) - res = recorder::getAction(index, ACTION_VOLUME, 0, &a1); - - volume_i = a0->fValue; - volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::hardStop(int frame) -{ - if (frame != 0) // clear data in range [frame, bufferSize-1] - clearChan(vChan, frame); - status = STATUS_OFF; - sendMidiLplay(); - reset(frame); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::onBar(int frame) -{ - ///if (mode == LOOP_REPEAT && status == STATUS_PLAY) - /// //setXFade(frame); - /// reset(frame); - - if (mode == LOOP_REPEAT) { - if (status == STATUS_PLAY) - //setXFade(frame); - reset(frame); - } - else - if (mode == LOOP_ONCE_BAR) { - if (status == STATUS_WAIT) { - status = STATUS_PLAY; - tracker = fillChan(vChan, tracker, frame); - sendMidiLplay(); - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -int SampleChannel::save(const char *path) -{ - return wave->writeData(path); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setBegin(unsigned v) -{ - begin = v; - tracker = begin; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setEnd(unsigned v) -{ - end = v; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setPitch(float v) -{ - pitch = v; - rsmp_data.src_ratio = 1/pitch; - - /* if status is off don't slide between frequencies */ - - if (status & (STATUS_OFF | STATUS_WAIT)) - src_set_ratio(rsmp_state, 1/pitch); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::rewind() -{ - /* rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode */ - - if (wave != NULL) { - if ((mode & LOOP_ANY) || (recStatus == REC_READING && (mode & SINGLE_ANY))) - reset(0); // rewind is user-generated events, always on frame 0 - } -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::parseAction(recorder::action *a, int localFrame, int globalFrame) -{ - if (readActions == false) - return; - - switch (a->type) { - case ACTION_KEYPRESS: - if (mode & SINGLE_ANY) - start(localFrame, false); - break; - case ACTION_KEYREL: - if (mode & SINGLE_ANY) - stop(); - break; - case ACTION_KILLCHAN: - if (mode & SINGLE_ANY) - kill(localFrame); - break; - case ACTION_MUTEON: - setMute(true); // internal mute - break; - case ACTION_MUTEOFF: - unsetMute(true); // internal mute - break; - case ACTION_VOLUME: - calcVolumeEnv(globalFrame); - break; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::sum(int frame, bool running) -{ - if (wave == NULL || status & ~(STATUS_PLAY | STATUS_ENDING)) - return; - - if (frame != frameRewind) { - - /* volume envelope, only if seq is running */ - - if (running) { - volume_i += volume_d; - if (volume_i < 0.0f) - volume_i = 0.0f; - else - if (volume_i > 1.0f) - volume_i = 1.0f; - } - - /* fadein or fadeout processes. If mute, delete any signal. */ - - /** TODO - big issue: fade[in/out]Vol * internal_volume might be a - * bad choice: it causes glitches when muting on and off during a - * volume envelope. */ - - if (mute || mute_i) { - vChan[frame] = 0.0f; - vChan[frame+1] = 0.0f; - } - else - if (fadeinOn) { - if (fadeinVol < 1.0f) { - vChan[frame] *= fadeinVol * volume_i; - vChan[frame+1] *= fadeinVol * volume_i; - fadeinVol += 0.01f; - } - else { - fadeinOn = false; - fadeinVol = 0.0f; - } - } - else - if (fadeoutOn) { - if (fadeoutVol > 0.0f) { // fadeout ongoing - if (fadeoutType == XFADE) { - vChan[frame] *= volume_i; - vChan[frame+1] *= volume_i; - vChan[frame] = pChan[frame] * fadeoutVol * volume_i; - vChan[frame+1] = pChan[frame+1] * fadeoutVol * volume_i; - } - else { - vChan[frame] *= fadeoutVol * volume_i; - vChan[frame+1] *= fadeoutVol * volume_i; - } - fadeoutVol -= fadeoutStep; - } - else { // fadeout end - fadeoutOn = false; - fadeoutVol = 1.0f; - - /* QWait ends with the end of the xfade */ - - if (fadeoutType == XFADE) { - qWait = false; - } - else { - if (fadeoutEnd == DO_MUTE) - mute = true; - else - if (fadeoutEnd == DO_MUTE_I) - mute_i = true; - else // DO_STOP - hardStop(frame); - } - } - } - else { - vChan[frame] *= volume_i; - vChan[frame+1] *= volume_i; - } - } - else { - - if (mode & (SINGLE_BASIC | SINGLE_PRESS | SINGLE_RETRIG) || - (mode == SINGLE_ENDLESS && status == STATUS_ENDING) || - (mode & LOOP_ANY && !running)) // stop loops when the seq is off - { - status = STATUS_OFF; - sendMidiLplay(); - } - - /* temporary stop LOOP_ONCE not in ENDING status, otherwise they - * would return in wait, losing the ENDING status */ - - //if (mode == LOOP_ONCE && status != STATUS_ENDING) - if ((mode & (LOOP_ONCE | LOOP_ONCE_BAR)) && status != STATUS_ENDING) { - status = STATUS_WAIT; - sendMidiLplay(); - } - - /* check for end of samples. SINGLE_ENDLESS runs forever unless - * it's in ENDING mode. */ - - reset(frame); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::onZero(int frame) -{ - if (wave == NULL) - return; - - if (mode & LOOP_ANY) { - - /* do a crossfade if the sample is playing. Regular chanReset - * instead if it's muted, otherwise a click occurs */ - - if (status == STATUS_PLAY) { - /* - if (mute || mute_i) - reset(frame); - else - setXFade(frame); - */ - reset(frame); - } - else - if (status == STATUS_ENDING) - hardStop(frame); - } - - if (status == STATUS_WAIT) { /// FIXME - should be inside previous if! - status = STATUS_PLAY; - sendMidiLplay(); - tracker = fillChan(vChan, tracker, frame); - } - - if (recStatus == REC_ENDING) { - recStatus = REC_STOPPED; - setReadActions(false); // rec stop - } - else - if (recStatus == REC_WAITING) { - recStatus = REC_READING; - setReadActions(true); // rec start - } -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::quantize(int index, int localFrame, int globalFrame) -{ - /* skip if LOOP_ANY or not in quantizer-wait mode */ - - if ((mode & LOOP_ANY) || !qWait) - return; - - /* no fadeout if the sample starts for the first time (from a - * STATUS_OFF), it would be meaningless. */ - - if (status == STATUS_OFF) { - status = STATUS_PLAY; - sendMidiLplay(); - qWait = false; - tracker = fillChan(vChan, tracker, localFrame); /// FIXME: ??? - } - else - //setXFade(localFrame); - reset(localFrame); - - /* this is the moment in which we record the keypress, if the - * quantizer is on. SINGLE_PRESS needs overdub */ - - if (recorder::canRec(this)) { - if (mode == SINGLE_PRESS) - recorder::startOverdub(index, ACTION_KEYS, globalFrame); - else - recorder::rec(index, ACTION_KEYPRESS, globalFrame); - } -} - - -/* -------------------------------------------------------------------------- */ - - -int SampleChannel::getPosition() -{ - if (status & ~(STATUS_EMPTY | STATUS_MISSING | STATUS_OFF)) // if is not (...) - return tracker - begin; - else - return -1; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setMute(bool internal) -{ - if (internal) { - - /* global mute is on? don't waste time with fadeout, just mute it - * internally */ - - if (mute) - mute_i = true; - else { - if (isPlaying()) - setFadeOut(DO_MUTE_I); - else - mute_i = true; - } - } - else { - - /* internal mute is on? don't waste time with fadeout, just mute it - * globally */ - - if (mute_i) - mute = true; - else { - - /* sample in play? fadeout needed. Else, just mute it globally */ - - if (isPlaying()) - setFadeOut(DO_MUTE); - else - mute = true; - } - } - - sendMidiLmute(); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::unsetMute(bool internal) -{ - if (internal) { - if (mute) - mute_i = false; - else { - if (isPlaying()) - setFadeIn(internal); - else - mute_i = false; - } - } - else { - if (mute_i) - mute = false; - else { - if (isPlaying()) - setFadeIn(internal); - else - mute = false; - } - } - - sendMidiLmute(); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::calcFadeoutStep() -{ - if (end - tracker < (1 / DEFAULT_FADEOUT_STEP) * 2) - fadeoutStep = ceil((end - tracker) / volume) * 2; /// or volume_i ??? - else - fadeoutStep = DEFAULT_FADEOUT_STEP; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setReadActions(bool v) -{ - if (v) - readActions = true; - else { - readActions = false; - if (G_Conf.recsStopOnChanHalt) - kill(0); /// FIXME - wrong frame value - } -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setFadeIn(bool internal) -{ - if (internal) mute_i = false; // remove mute before fading in - else mute = false; - fadeinOn = true; - fadeinVol = 0.0f; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setFadeOut(int actionPostFadeout) -{ - calcFadeoutStep(); - fadeoutOn = true; - fadeoutVol = 1.0f; - fadeoutType = FADEOUT; - fadeoutEnd = actionPostFadeout; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::setXFade(int frame) -{ - gLog("[xFade] frame=%d tracker=%d\n", frame, tracker); - - calcFadeoutStep(); - fadeoutOn = true; - fadeoutVol = 1.0f; - fadeoutType = XFADE; - fadeoutTracker = fillChan(pChan, tracker, 0, false); - reset(frame); -} - - -/* -------------------------------------------------------------------------- */ - - -/* on reset, if frame > 0 and in play, fill again pChan to create - * something like this: - * - * |abcdefabcdefab*abcdefabcde| - * [old data-----]*[new data--] - * - * */ - -void SampleChannel::reset(int frame) -{ - //fadeoutTracker = tracker; // store old frame number for xfade - tracker = begin; - mute_i = false; - if (frame > 0 && status & (STATUS_PLAY | STATUS_ENDING)) - tracker = fillChan(vChan, tracker, frame); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::empty() -{ - status = STATUS_OFF; - if (wave) { - delete wave; - wave = NULL; - } - status = STATUS_EMPTY; - sendMidiLplay(); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::pushWave(Wave *w) -{ - wave = w; - status = STATUS_OFF; - sendMidiLplay(); - begin = 0; - end = wave->size; -} - - -/* -------------------------------------------------------------------------- */ - - -bool SampleChannel::allocEmpty(int frames, int takeId) -{ - Wave *w = new Wave(); - if (!w->allocEmpty(frames)) - return false; - - char wname[32]; - sprintf(wname, "TAKE-%d", takeId); - - w->pathfile = gGetCurrentPath()+"/"+wname; // FIXME - use gGetSlash() in utils.h - w->name = wname; - wave = w; - status = STATUS_OFF; - begin = 0; - end = wave->size; - - sendMidiLplay(); - - return true; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::process(float *buffer) -{ -#ifdef WITH_VST - G_PluginHost.processStack(vChan, PluginHost::CHANNEL, this); -#endif - - for (int j=0; j FILENAME_MAX) - return SAMPLE_PATH_TOO_LONG; - - Wave *w = new Wave(); - - if (!w->open(file)) { - gLog("[SampleChannel] %s: read error\n", file); - delete w; - return SAMPLE_READ_ERROR; - } - - if (w->channels() > 2) { - gLog("[SampleChannel] %s: unsupported multichannel wave\n", file); - delete w; - return SAMPLE_MULTICHANNEL; - } - - if (!w->readData()) { - delete w; - return SAMPLE_READ_ERROR; - } - - if (w->channels() == 1) /** FIXME: error checking */ - wfx_monoToStereo(w); - - if (w->rate() != G_Conf.samplerate) { - gLog("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n", - w->rate(), G_Conf.samplerate); - w->resample(G_Conf.rsmpQuality, G_Conf.samplerate); - } - - pushWave(w); - - /* sample name must be unique. Start from k = 1, zero is too nerdy */ - - std::string oldName = wave->name; - int k = 1; - while (!mh_uniqueSamplename(this, wave->name.c_str())) { - wave->updateName((oldName + "-" + gItoa(k)).c_str()); - k++; - } - - gLog("[SampleChannel] %s loaded in channel %d\n", file, index); - return SAMPLE_LOADED_OK; -} - - -/* -------------------------------------------------------------------------- */ - - -int SampleChannel::loadByPatch(const char *f, int i) -{ - int res = load(f); - - volume = G_Patch.getVol(i); - key = G_Patch.getKey(i); - index = G_Patch.getIndex(i); - mode = G_Patch.getMode(i); - mute = G_Patch.getMute(i); - mute_s = G_Patch.getMute_s(i); - solo = G_Patch.getSolo(i); - boost = G_Patch.getBoost(i); - panLeft = G_Patch.getPanLeft(i); - panRight = G_Patch.getPanRight(i); - readActions = G_Patch.getRecActive(i); - recStatus = readActions ? REC_READING : REC_STOPPED; - - readPatchMidiIn(i); - midiInReadActions = G_Patch.getMidiValue(i, "InReadActions"); - midiInPitch = G_Patch.getMidiValue(i, "InPitch"); - readPatchMidiOut(i); - - if (res == SAMPLE_LOADED_OK) { - setBegin(G_Patch.getBegin(i)); - setEnd (G_Patch.getEnd(i, wave->size)); - setPitch(G_Patch.getPitch(i)); - } - else { - // volume = DEFAULT_VOL; - // mode = DEFAULT_CHANMODE; - // status = STATUS_WRONG; - // key = 0; - - if (res == SAMPLE_LEFT_EMPTY) - status = STATUS_EMPTY; - else - if (res == SAMPLE_READ_ERROR) - status = STATUS_MISSING; - sendMidiLplay(); - } - - return res; -} - - -/* -------------------------------------------------------------------------- */ - - -bool SampleChannel::canInputRec() - { - return wave == NULL; -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::start(int frame, bool doQuantize) -{ - switch (status) { - case STATUS_EMPTY: - case STATUS_MISSING: - case STATUS_WRONG: - { - return; - } - - case STATUS_OFF: - { - if (mode & LOOP_ANY) { - status = STATUS_WAIT; - sendMidiLplay(); - } - else { - if (G_Mixer.quantize > 0 && G_Mixer.running && doQuantize) - qWait = true; - else { - - /* fillChan only if frame != 0. If you call fillChan on frame == 0 - * a duplicate call to fillChan occurs with loss of data. */ - - status = STATUS_PLAY; - sendMidiLplay(); - if (frame != 0) - tracker = fillChan(vChan, tracker, frame); - } - } - break; - } - - case STATUS_PLAY: - { - if (mode == SINGLE_BASIC) - setFadeOut(DO_STOP); - else - if (mode == SINGLE_RETRIG) { - if (G_Mixer.quantize > 0 && G_Mixer.running && doQuantize) - qWait = true; - else - reset(frame); - } - else - if (mode & (LOOP_ANY | SINGLE_ENDLESS)) { - status = STATUS_ENDING; - sendMidiLplay(); - } - break; - } - - case STATUS_WAIT: - { - status = STATUS_OFF; - sendMidiLplay(); - break; - } - - case STATUS_ENDING: - { - status = STATUS_PLAY; - sendMidiLplay(); - break; - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::writePatch(FILE *fp, int i, bool isProject) -{ - Channel::writePatch(fp, i, isProject); - - const char *path = ""; - if (wave != NULL) { - path = wave->pathfile.c_str(); - if (isProject) - path = gBasename(path).c_str(); // make it portable - } - - fprintf(fp, "samplepath%d=%s\n", i, path); - fprintf(fp, "chanKey%d=%d\n", i, key); - //fprintf(fp, "columnIndex%d=%d\n", i, index); - fprintf(fp, "chanmode%d=%d\n", i, mode); - fprintf(fp, "chanBegin%d=%d\n", i, begin); - fprintf(fp, "chanend%d=%d\n", i, end); - fprintf(fp, "chanBoost%d=%f\n", i, boost); - fprintf(fp, "chanRecActive%d=%d\n", i, readActions); - fprintf(fp, "chanPitch%d=%f\n", i, pitch); - - fprintf(fp, "chanMidiInReadActions%d=%u\n", i, midiInReadActions); - fprintf(fp, "chanMidiInPitch%d=%u\n", i, midiInPitch); -} - - -/* -------------------------------------------------------------------------- */ - - -void SampleChannel::clearChan(float *dest, int start) -{ - memset(dest+start, 0, sizeof(float)*(bufferSize-start)); -} - - -/* -------------------------------------------------------------------------- */ - - -int SampleChannel::fillChan(float *dest, int start, int offset, bool rewind) -{ - int position; // return value: the new position - - if (pitch == 1.0f) { - - /* case 1: 'dest' lies within the original sample boundaries (start- - * end) */ - - if (start+bufferSize-offset <= end) { - memcpy(dest+offset, wave->data+start, (bufferSize-offset)*sizeof(float)); - position = start+bufferSize-offset; - if (rewind) - frameRewind = -1; - } - - /* case2: 'dest' lies outside the end of the sample, OR the sample - * is smaller than 'dest' */ - - else { - memcpy(dest+offset, wave->data+start, (end-start)*sizeof(float)); - position = end; - if (rewind) - frameRewind = end-start+offset; - } - } - else { - - rsmp_data.data_in = wave->data+start; // source data - rsmp_data.input_frames = (end-start)/2; // how many readable bytes - rsmp_data.data_out = dest+offset; // destination (processed data) - rsmp_data.output_frames = (bufferSize-offset)/2; // how many bytes to process - rsmp_data.end_of_input = false; - - src_process(rsmp_state, &rsmp_data); - int gen = rsmp_data.output_frames_gen*2; // frames generated by this call - - position = start + rsmp_data.input_frames_used*2; // position goes forward of frames_used (i.e. read from wave) - - if (rewind) { - if (gen == bufferSize-offset) - frameRewind = -1; - else - frameRewind = gen+offset; - } - } - return position; -} diff --git a/src/sampleChannel.h b/src/sampleChannel.h deleted file mode 100644 index 736063a..0000000 --- a/src/sampleChannel.h +++ /dev/null @@ -1,210 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * sampleChannel - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef SAMPLE_CHANNEL_H -#define SAMPLE_CHANNEL_H - - -#include -#include "channel.h" - - -class SampleChannel : public Channel { - -private: - - /* rsmp_state, rsmp_data - * structs from libsamplerate */ - - SRC_STATE *rsmp_state; - SRC_DATA rsmp_data; - - /* pChan - * extra virtual channel for processing resampled data. */ - - float *pChan; - - /* frameRewind - * exact frame in which a rewind occurs */ - - int frameRewind; - - /* fillChan - * copy from wave to *dest and resample data from wave, if necessary. - * Start to fill pChan from byte 'offset'. If rewind=false don't - * rewind internal tracker. Returns new sample position, in frames */ - - int fillChan(float *dest, int start, int offset, bool rewind=true); - - /* clearChan - * set data to zero from start to bufferSize-1. */ - - void clearChan(float *dest, int start); - - /* calcFadeoutStep - * how many frames are left before the end of the sample? Is there - * enough room for a complete fadeout? Should we shorten it? */ - - void calcFadeoutStep(); - - /* calcVolumeEnv - * compute any changes in volume done via envelope tool */ - - void calcVolumeEnv(int frame); - -public: - - SampleChannel(int bufferSize); - ~SampleChannel(); - - void clear (); - void process (float *buffer); - void start (int frame, bool doQuantize); - void kill (int frame); - void empty (); - void stopBySeq (); - void stop (); - void rewind (); - void setMute (bool internal); - void unsetMute (bool internal); - void reset (int frame); - int load (const char *file); - int loadByPatch(const char *file, int i); - void writePatch (FILE *fp, int i, bool isProject); - void quantize (int index, int localFrame, int globalFrame); - void onZero (int frame); - void onBar (int frame); - void parseAction(recorder::action *a, int localFrame, int globalFrame); - - /* fade methods - * prepare channel for fade, mixer will take care of the process - * during master play. */ - - void setFadeIn (bool internal); - void setFadeOut (int actionPostFadeout); - void setXFade (int frame); - - /* pushWave - * add a new wave to an existing channel. */ - - void pushWave(class Wave *w); - - /* getPosition - * returns the position of an active sample. If EMPTY o MISSING - * returns -1. */ - - int getPosition(); - - /* sum - * add sample frames to virtual channel. Frame = processed frame in - * Mixer. Running = is Mixer in play? */ - - void sum(int frame, bool running); - - /* setPitch - * updates the pitch value and chanStart+chanEnd accordingly. */ - - void setPitch(float v); - - /* setStart/end - * change begin/end read points in sample. */ - - void setBegin(unsigned v); - void setEnd (unsigned v); - - /* save - * save sample to file. */ - - int save(const char *path); - - /* hardStop - * stop the channel immediately, no further checks. */ - - void hardStop(int frame); - - /* allocEmpty - * alloc an empty wave used in input recordings. */ - - bool allocEmpty(int frames, int takeId); - - /* canInputRec - * true if channel can host a new wave from input recording. */ - - bool canInputRec(); - - /* setReadActions - * if enabled, recorder will read actions from this channel */ - - void setReadActions(bool v); - - /* ---------------------------------------------------------------- */ - - class Wave *wave; - int tracker; // chan position - int begin; - int end; - float pitch; - float boost; - int mode; // mode: see const.h - bool qWait; // quantizer wait - bool fadeinOn; - float fadeinVol; - bool fadeoutOn; - float fadeoutVol; // fadeout volume - int fadeoutTracker; // tracker fadeout, xfade only - float fadeoutStep; // fadeout decrease - int fadeoutType; // xfade or fadeout - int fadeoutEnd; // what to do when fadeout ends - - /* recorder:: stuff */ - - bool readActions; // read actions or not - - /* midi stuff */ - - uint32_t midiInReadActions; - uint32_t midiInPitch; - - /* const - what to do when a fadeout ends */ - - enum { - DO_STOP = 0x01, - DO_MUTE = 0x02, - DO_MUTE_I = 0x04 - }; - - /* const - fade types */ - - enum { - FADEOUT = 0x01, - XFADE = 0x02 - }; -}; - -#endif diff --git a/src/utils.cpp b/src/utils.cpp deleted file mode 100644 index e7b2e8c..0000000 --- a/src/utils.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * utils - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#include "utils.h" -#if defined(_WIN32) // getcwd (unix) or __getcwd (win) - #include - #include -#else - #include -#endif - -#include -#include // stat (gDirExists) -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) - #include // basename unix - #include // getpwuid -#endif - - - -bool gFileExists(const char *filename) { - FILE *fh = fopen(filename, "rb"); - if (!fh) { - return 0; - } - else { - fclose(fh); - return 1; - } -} - - -/* -------------------------------------------------------------------------- */ - - -bool gIsDir(const char *path) -{ - bool ret; - -#if defined(__linux__) - - struct stat s1; - stat(path, &s1); - ret = S_ISDIR(s1.st_mode); - -#elif defined(__APPLE__) - - if (strcmp(path, "")==0) - ret = false; - else { - struct stat s1; - stat(path, &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) { - std::string tmp = path; - tmp += "/Contents/Info.plist"; - if (gFileExists(tmp.c_str())) - ret = false; - } - } - -#elif defined(__WIN32) - - unsigned dwAttrib = GetFileAttributes(path); - ret = (dwAttrib != INVALID_FILE_ATTRIBUTES && - (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); -#endif - - return ret & !gIsProject(path); -} - - -/* -------------------------------------------------------------------------- */ - - -bool gDirExists(const char *path) -{ - struct stat st; - if (stat(path, &st) != 0 && errno == ENOENT) - return false; - return true; -} - - -/* -------------------------------------------------------------------------- */ - - -bool gMkdir(const char *path) -{ -#if defined(__linux__) || defined(__APPLE__) - if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0) -#else - if (_mkdir(path) == 0) -#endif - return true; - return false; -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gBasename(const char *path) -{ - std::string out = path; - out.erase(0, out.find_last_of(gGetSlash().c_str())+1); - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gDirname(const char *path) -{ - std::string out = path; - out.erase(out.find_last_of(gGetSlash().c_str())); - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gGetCurrentPath() -{ - char buf[PATH_MAX]; -#if defined(__WIN32) - if (_getcwd(buf, PATH_MAX) != NULL) -#else - if (getcwd(buf, PATH_MAX) != NULL) -#endif - return buf; - else - return ""; -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gGetExt(const char *file) -{ - int len = strlen(file); - 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); -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gStripExt(const char *file) -{ - int len = strlen(file); - int pos = -1; - for (int i=0; i=0) { /// TODO - use gGetSlash() -#if defined(__linux__) || defined(__APPLE__) - if (out[i] == '/') -#elif defined(_WIN32) - if (out[i] == '\\') -#endif - break; - i--; - } - - out.erase(0, i+1); // includes the '/' (or '\' on windows) - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gGetSlash() // TODO - create SLASH macro or constant -{ -#if defined(_WIN32) - return "\\"; -#else - return "/"; -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gItoa(int i) -{ - std::stringstream out; - out << i; - return out.str(); -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gTrim(const char *f) -{ - std::string out = f; - return gTrim(out); -} - - -std::string gTrim(const std::string &s) -{ - std::size_t first = s.find_first_not_of(" \n\t"); - std::size_t last = s.find_last_not_of(" \n\t"); - return s.substr(first, last-first+1); -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gReplace(std::string in, const std::string& search, const std::string& replace) -{ - size_t pos = 0; - while ((pos = in.find(search, pos)) != std::string::npos) { - in.replace(pos, search.length(), replace); - pos += replace.length(); - } - return in; -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gStripFileUrl(const char *f) -{ - std::string out = f; - out = gReplace(out, "file://", ""); - out = gReplace(out, "%20", " "); - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -std::string gGetHomePath() -{ - char path[PATH_MAX]; - -#if defined(__linux__) - - snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME")); - -#elif defined(_WIN32) - - snprintf(path, PATH_MAX, "."); - -#elif defined(__APPLE__) - - struct passwd *p = getpwuid(getuid()); - if (p == NULL) { - gLog("[gGetHomePath] unable to fetch user infos\n"); - return ""; - } - else { - const char *home = p->pw_dir; - snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home); - } - -#endif - - return std::string(path); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSplit(std::string in, std::string sep, gVector *v) -{ - std::string full = in; - std::string token = ""; - size_t curr = 0; - size_t next = -1; - do { - curr = next + 1; - next = full.find_first_of(sep, curr); - token = full.substr(curr, next - curr); - if (token != "") - v->add(token); - } - while (next != std::string::npos); -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 0c1a2d0..0000000 --- a/src/utils.h +++ /dev/null @@ -1,190 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * utils - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef UTILS_H -#define UTILS_H - - -#include -#include -#include "log.h" - - -/* gVector - * lightweight template class. */ - -template class gVector -{ -public: - - /* gVector() - * default constructor, no parameters */ - - gVector() : size(0), s(NULL) {} - - /* gVector(const &) - * copy-constructor, when gVector a = b (where b is gVector). - * Default constructor doesn't copy referenced ojbects, so we need - * to re-allocate the internal stack for the copied object */ - - gVector(const gVector &other) - { - s = new T[other.size]; - for (unsigned i=0; i size-1) gLog("[vector] del() outside! requested=%d, size=%d\n", p, size); - T *tmp = new T[size-1]; - unsigned i=0; - unsigned j=0; - while (i 0) { - delete [] s; - s = NULL; - size = 0; - } - } - - - void swap(unsigned x, unsigned y) - { - T tmp = s[x]; - s[x] = s[y]; - s[y] = tmp; - } - - - T &at(unsigned p) - { - if (p > size-1) gLog("[vector] at() outside! requested=%d, size=%d\n", p, size); - return s[p]; - } - - - T &last() - { - return s[size-1]; - } - - - unsigned size; - T *s; // stack (array of T) -}; - - -/* ------------------------------------------------------------------ */ - - -bool gFileExists(const char *path); - -bool gDirExists(const char *path); - -bool gIsDir(const char *path); - -bool gIsProject(const char *path); - -bool gIsPatch(const char *path); - -bool gMkdir(const char *path); - -std::string gBasename(const char *path); - -std::string gReplace(std::string in, const std::string& search, const std::string& replace); - -std::string gDirname(const char *path); - -std::string gTrim(const char *path); -std::string gTrim(const std::string &s); - -std::string gGetCurrentPath(); - -std::string gGetHomePath(); - -std::string gStripFileUrl(const char *path); - -std::string gGetExt(const char *path); - -std::string gStripExt(const char *path); - -std::string gGetProjectName(const char *path); - -std::string gGetSlash(); - -std::string gItoa(int i); - -void gSplit(std::string in, std::string sep, gVector *v); - -#endif diff --git a/src/utils/gui_utils.cpp b/src/utils/gui_utils.cpp new file mode 100644 index 0000000..7625576 --- /dev/null +++ b/src/utils/gui_utils.cpp @@ -0,0 +1,210 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gui_utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../core/mixer.h" +#include "../core/patch.h" +#include "../core/recorder.h" +#include "../core/wave.h" +#include "../core/pluginHost.h" +#include "../core/channel.h" +#include "../core/conf.h" +#include "../core/graphics.h" +#include "../gui/dialogs/gd_warnings.h" +#include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/gd_actionEditor.h" +#include "../gui/elems/ge_keyboard.h" +#include "../gui/elems/ge_window.h" +#include "../gui/elems/ge_channel.h" +#include "gui_utils.h" +#include "log.h" + + +extern Mixer G_Mixer; +extern unsigned G_beats; +extern bool G_audio_status; +extern Patch G_patch; +extern Conf G_conf; +extern uint32_t G_time; +extern gdMainWindow *mainWin; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +static int blinker = 0; + + +void gu_refresh() +{ + Fl::lock(); + + /* update dynamic elements: in and out meters, beat meter and + * each channel */ + + mainWin->inOut->refresh(); + mainWin->beatMeter->redraw(); + mainWin->keyboard->refreshColumns(); + + /* compute timer for blinker */ + + blinker++; + if (blinker > 12) + blinker = 0; + + /* redraw GUI */ + + Fl::unlock(); + Fl::awake(); +} + + +/* -------------------------------------------------------------------------- */ + + +int gu_getBlinker() +{ + return blinker; +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_updateControls() +{ + for (unsigned i=0; iguiChannel->update(); + + mainWin->inOut->setOutVol(G_Mixer.outVol); + mainWin->inOut->setInVol(G_Mixer.inVol); +#ifdef WITH_VST + mainWin->inOut->setMasterFxOutFull(G_PluginHost.masterOut.size > 0); + mainWin->inOut->setMasterFxInFull(G_PluginHost.masterIn.size > 0); +#endif + + mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); + mainWin->timing->setBpm(G_Mixer.bpm); + + /* if you reset to init state while the seq is in play: it's better to + * update the button status */ + + mainWin->controller->updatePlay(G_Mixer.running); + mainWin->controller->updateMetronome(0); +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_update_win_label(const char *c) +{ + std::string out = VERSIONE_STR; + out += " - "; + out += c; + mainWin->copy_label(out.c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_setFavicon(Fl_Window *w) +{ +#if defined(__linux__) + fl_open_display(); + Pixmap p, mask; + XpmCreatePixmapFromData( + fl_display, + DefaultRootWindow(fl_display), + (char **)giada_icon, + &p, + &mask, + NULL); + w->icon((char *)p); +#elif defined(_WIN32) + w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1))); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_openSubWindow(gWindow *parent, gWindow *child, int id) +{ + if (parent->hasWindow(id)) { + gLog("[GU] parent has subwindow with id=%d, deleting\n", id); + parent->delSubWindow(id); + } + child->setId(id); + parent->addSubWindow(child); +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_refreshActionEditor() +{ + /** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */ + + gdActionEditor *aeditor = (gdActionEditor*) mainWin->getChild(WID_ACTION_EDITOR); + if (aeditor) { + Channel *chan = aeditor->chan; + mainWin->delSubWindow(WID_ACTION_EDITOR); + gu_openSubWindow(mainWin, new gdActionEditor(chan), WID_ACTION_EDITOR); + } +} + + +/* -------------------------------------------------------------------------- */ + + +gWindow *gu_getSubwindow(gWindow *parent, int id) +{ + if (parent->hasWindow(id)) + return parent->getChild(id); + else + return NULL; +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_closeAllSubwindows() +{ + /* don't close WID_FILE_BROWSER, because it's the caller of this + * function */ + + mainWin->delSubWindow(WID_ACTION_EDITOR); + mainWin->delSubWindow(WID_SAMPLE_EDITOR); + mainWin->delSubWindow(WID_FX_LIST); + mainWin->delSubWindow(WID_FX); +} diff --git a/src/utils/gui_utils.h b/src/utils/gui_utils.h new file mode 100644 index 0000000..66156ce --- /dev/null +++ b/src/utils/gui_utils.h @@ -0,0 +1,93 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gui_utils + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + +#ifndef GUI_UTILS_H +#define GUI_UTILS_H + +#include +#include +#include +#include +#ifdef __APPLE__ + #include // in osx, for basename() (but linux?) +#endif + +/* including stuff for the favicon */ + +#if defined(_WIN32) + #include "../ext/resource.h" +#elif defined(__linux__) + #include +#endif + + +/* refresh + * refresh all GUI elements. */ + +void gu_refresh(); + +/* getBlinker +* return blinker value, used to make widgets blink. */ + +int gu_getBlinker(); + +/* updateControls + * update attributes of control elements (sample names, volumes, ...). + * Useful when loading a new patch. */ + +void gu_updateControls(); + +/* update_win_label + * update the name of the main window */ + +void gu_update_win_label(const char *c); + +void gu_setFavicon(Fl_Window *w); + +void gu_openSubWindow(class gWindow *parent, gWindow *child, int id); + +/* refreshActionEditor + * reload the action editor window by closing and reopening it. It's used + * when you delete some actions from the mainWindow and the action editor + * window is open. */ + +void gu_refreshActionEditor(); + + +/* closeAllSubwindows + * close all subwindows attached to mainWin. */ + +void gu_closeAllSubwindows(); + + +/* getSubwindow + * return a pointer to an open subwindow, otherwise NULL. */ + +gWindow *gu_getSubwindow(class gWindow *parent, int id); + +#endif diff --git a/src/utils/log.cpp b/src/utils/log.cpp new file mode 100644 index 0000000..9b2182d --- /dev/null +++ b/src/utils/log.cpp @@ -0,0 +1,80 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * log + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#include +#include +#include +#include "../utils/utils.h" +#include "../core/const.h" +#include "log.h" + + +static FILE *f; +static int mode; +static bool stat; + + +int gLog_init(int m) { + mode = m; + stat = true; + if (mode == LOG_MODE_FILE) { + std::string fpath = gGetHomePath() + "/giada.log"; + f = fopen(fpath.c_str(), "a"); + if (!f) { + stat = false; + return 0; + } + } + return 1; +} + + +/* ------------------------------------------------------------------ */ + + +void gLog_close() { + if (mode == LOG_MODE_FILE) + fclose(f); +} + + +/* ------------------------------------------------------------------ */ + + +void gLog(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); + else + vprintf(format, args); + va_end(args); +} diff --git a/src/utils/log.h b/src/utils/log.h new file mode 100644 index 0000000..c53f9c7 --- /dev/null +++ b/src/utils/log.h @@ -0,0 +1,45 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * log + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * ------------------------------------------------------------------ */ + + +#ifndef __LOG_H__ +#define __LOG_H__ + + +/* init + * init logger. Mode defines where to write the output: LOG_MODE_STDOUT, + * LOG_MODE_FILE and LOG_MODE_MUTE. */ + +int gLog_init (int mode); + +void gLog_close(); + +void gLog(const char *format, ...); + + +#endif diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp new file mode 100644 index 0000000..e7b2e8c --- /dev/null +++ b/src/utils/utils.cpp @@ -0,0 +1,379 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "utils.h" +#if defined(_WIN32) // getcwd (unix) or __getcwd (win) + #include + #include +#else + #include +#endif + +#include +#include // stat (gDirExists) +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) + #include // basename unix + #include // getpwuid +#endif + + + +bool gFileExists(const char *filename) { + FILE *fh = fopen(filename, "rb"); + if (!fh) { + return 0; + } + else { + fclose(fh); + return 1; + } +} + + +/* -------------------------------------------------------------------------- */ + + +bool gIsDir(const char *path) +{ + bool ret; + +#if defined(__linux__) + + struct stat s1; + stat(path, &s1); + ret = S_ISDIR(s1.st_mode); + +#elif defined(__APPLE__) + + if (strcmp(path, "")==0) + ret = false; + else { + struct stat s1; + stat(path, &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) { + std::string tmp = path; + tmp += "/Contents/Info.plist"; + if (gFileExists(tmp.c_str())) + ret = false; + } + } + +#elif defined(__WIN32) + + unsigned dwAttrib = GetFileAttributes(path); + ret = (dwAttrib != INVALID_FILE_ATTRIBUTES && + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#endif + + return ret & !gIsProject(path); +} + + +/* -------------------------------------------------------------------------- */ + + +bool gDirExists(const char *path) +{ + struct stat st; + if (stat(path, &st) != 0 && errno == ENOENT) + return false; + return true; +} + + +/* -------------------------------------------------------------------------- */ + + +bool gMkdir(const char *path) +{ +#if defined(__linux__) || defined(__APPLE__) + if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0) +#else + if (_mkdir(path) == 0) +#endif + return true; + return false; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gBasename(const char *path) +{ + std::string out = path; + out.erase(0, out.find_last_of(gGetSlash().c_str())+1); + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gDirname(const char *path) +{ + std::string out = path; + out.erase(out.find_last_of(gGetSlash().c_str())); + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gGetCurrentPath() +{ + char buf[PATH_MAX]; +#if defined(__WIN32) + if (_getcwd(buf, PATH_MAX) != NULL) +#else + if (getcwd(buf, PATH_MAX) != NULL) +#endif + return buf; + else + return ""; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gGetExt(const char *file) +{ + int len = strlen(file); + 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); +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gStripExt(const char *file) +{ + int len = strlen(file); + int pos = -1; + for (int i=0; i=0) { /// TODO - use gGetSlash() +#if defined(__linux__) || defined(__APPLE__) + if (out[i] == '/') +#elif defined(_WIN32) + if (out[i] == '\\') +#endif + break; + i--; + } + + out.erase(0, i+1); // includes the '/' (or '\' on windows) + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gGetSlash() // TODO - create SLASH macro or constant +{ +#if defined(_WIN32) + return "\\"; +#else + return "/"; +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gItoa(int i) +{ + std::stringstream out; + out << i; + return out.str(); +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gTrim(const char *f) +{ + std::string out = f; + return gTrim(out); +} + + +std::string gTrim(const std::string &s) +{ + std::size_t first = s.find_first_not_of(" \n\t"); + std::size_t last = s.find_last_not_of(" \n\t"); + return s.substr(first, last-first+1); +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gReplace(std::string in, const std::string& search, const std::string& replace) +{ + size_t pos = 0; + while ((pos = in.find(search, pos)) != std::string::npos) { + in.replace(pos, search.length(), replace); + pos += replace.length(); + } + return in; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gStripFileUrl(const char *f) +{ + std::string out = f; + out = gReplace(out, "file://", ""); + out = gReplace(out, "%20", " "); + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string gGetHomePath() +{ + char path[PATH_MAX]; + +#if defined(__linux__) + + snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME")); + +#elif defined(_WIN32) + + snprintf(path, PATH_MAX, "."); + +#elif defined(__APPLE__) + + struct passwd *p = getpwuid(getuid()); + if (p == NULL) { + gLog("[gGetHomePath] unable to fetch user infos\n"); + return ""; + } + else { + const char *home = p->pw_dir; + snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home); + } + +#endif + + return std::string(path); +} + + +/* -------------------------------------------------------------------------- */ + + +void gSplit(std::string in, std::string sep, gVector *v) +{ + std::string full = in; + std::string token = ""; + size_t curr = 0; + size_t next = -1; + do { + curr = next + 1; + next = full.find_first_of(sep, curr); + token = full.substr(curr, next - curr); + if (token != "") + v->add(token); + } + while (next != std::string::npos); +} diff --git a/src/utils/utils.h b/src/utils/utils.h new file mode 100644 index 0000000..0c1a2d0 --- /dev/null +++ b/src/utils/utils.h @@ -0,0 +1,190 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef UTILS_H +#define UTILS_H + + +#include +#include +#include "log.h" + + +/* gVector + * lightweight template class. */ + +template class gVector +{ +public: + + /* gVector() + * default constructor, no parameters */ + + gVector() : size(0), s(NULL) {} + + /* gVector(const &) + * copy-constructor, when gVector a = b (where b is gVector). + * Default constructor doesn't copy referenced ojbects, so we need + * to re-allocate the internal stack for the copied object */ + + gVector(const gVector &other) + { + s = new T[other.size]; + for (unsigned i=0; i size-1) gLog("[vector] del() outside! requested=%d, size=%d\n", p, size); + T *tmp = new T[size-1]; + unsigned i=0; + unsigned j=0; + while (i 0) { + delete [] s; + s = NULL; + size = 0; + } + } + + + void swap(unsigned x, unsigned y) + { + T tmp = s[x]; + s[x] = s[y]; + s[y] = tmp; + } + + + T &at(unsigned p) + { + if (p > size-1) gLog("[vector] at() outside! requested=%d, size=%d\n", p, size); + return s[p]; + } + + + T &last() + { + return s[size-1]; + } + + + unsigned size; + T *s; // stack (array of T) +}; + + +/* ------------------------------------------------------------------ */ + + +bool gFileExists(const char *path); + +bool gDirExists(const char *path); + +bool gIsDir(const char *path); + +bool gIsProject(const char *path); + +bool gIsPatch(const char *path); + +bool gMkdir(const char *path); + +std::string gBasename(const char *path); + +std::string gReplace(std::string in, const std::string& search, const std::string& replace); + +std::string gDirname(const char *path); + +std::string gTrim(const char *path); +std::string gTrim(const std::string &s); + +std::string gGetCurrentPath(); + +std::string gGetHomePath(); + +std::string gStripFileUrl(const char *path); + +std::string gGetExt(const char *path); + +std::string gStripExt(const char *path); + +std::string gGetProjectName(const char *path); + +std::string gGetSlash(); + +std::string gItoa(int i); + +void gSplit(std::string in, std::string sep, gVector *v); + +#endif diff --git a/src/wave.cpp b/src/wave.cpp deleted file mode 100644 index edaef7b..0000000 --- a/src/wave.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * wave - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include -#include // memcpy -#include -#include "wave.h" -#include "utils.h" -#include "conf.h" -#include "init.h" -#include "log.h" - - -extern Conf G_Conf; - - -Wave::Wave() - : data (NULL), - size (0), - isLogical(0), - isEdited (0) {} - - -/* ------------------------------------------------------------------ */ - - -Wave::~Wave() { - clear(); -} - - -/* ------------------------------------------------------------------ */ - - -int Wave::open(const char *f) { - - pathfile = f; - name = gStripExt(gBasename(f).c_str()); - fileIn = sf_open(f, SFM_READ, &inHeader); - - if (fileIn == NULL) { - gLog("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn)); - pathfile = ""; - name = ""; - return 0; - } - - isLogical = false; - isEdited = false; - - return 1; -} - - -/* ------------------------------------------------------------------ */ - -/* how to read and write with libsndfile: - * - * a frame consists of all items (samples) that belong to the same - * point in time. So in each frame there are as many items as there - * are channels. - * - * Quindi: - * frame = [item, item, ...] - * In pratica: - * frame1 = [itemLeft, itemRight] - * frame2 = [itemLeft, itemRight] - * ... - */ - -int Wave::readData() { - size = inHeader.frames * inHeader.channels; - data = (float *) malloc(size * sizeof(float)); - if (data == NULL) { - gLog("[wave] unable to allocate memory\n"); - return 0; - } - - if (sf_read_float(fileIn, data, size) != size) - gLog("[wave] warning: incomplete read!\n"); - - sf_close(fileIn); - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -int Wave::writeData(const char *f) { - - /* prepare the header for output file */ - - outHeader.samplerate = inHeader.samplerate; - outHeader.channels = inHeader.channels; - outHeader.format = inHeader.format; - - fileOut = sf_open(f, SFM_WRITE, &outHeader); - if (fileOut == NULL) { - gLog("[wave] unable to open %s for exporting\n", f); - return 0; - } - - int out = sf_write_float(fileOut, data, size); - if (out != (int) size) { - gLog("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut)); - return 0; - } - - isLogical = false; - isEdited = false; - sf_close(fileOut); - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -void Wave::clear() { - if (data != NULL) { - free(data); - data = NULL; - pathfile = ""; - size = 0; - } -} - - -/* ------------------------------------------------------------------ */ - - -int Wave::allocEmpty(unsigned __size) { - - /* the caller must pass a __size for stereo values */ - - /// FIXME - this way if malloc fails size becomes wrong - size = __size; - data = (float *) malloc(size * sizeof(float)); - if (data == NULL) { - gLog("[wave] unable to allocate memory\n"); - return 0; - } - - memset(data, 0, sizeof(float) * size); /// FIXME - is it useful? - - inHeader.samplerate = G_Conf.samplerate; - inHeader.channels = 2; - inHeader.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; // wave only - - isLogical = true; - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -int Wave::resample(int quality, int newRate) { - - float ratio = newRate / (float) inHeader.samplerate; - int newSize = ceil(size * ratio); - if (newSize % 2 != 0) // libsndfile goes crazy with odd size in case of saving - newSize++; - - float *tmp = (float *) malloc(newSize * sizeof(float)); - if (!tmp) { - gLog("[wave] unable to allocate memory for resampling\n"); - return -1; - } - - SRC_DATA src_data; - src_data.data_in = data; - src_data.input_frames = size/2; // in frames, i.e. /2 (stereo) - src_data.data_out = tmp; - src_data.output_frames = newSize/2; // in frames, i.e. /2 (stereo) - src_data.src_ratio = ratio; - - gLog("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2); - - int ret = src_simple(&src_data, quality, 2); - if (ret != 0) { - gLog("[wave] resampling error: %s\n", src_strerror(ret)); - return 0; - } - - free(data); - data = tmp; - size = newSize; - inHeader.samplerate = newRate; - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -std::string Wave::basename() { - return gStripExt(gBasename(pathfile.c_str()).c_str()); -} - -std::string Wave::extension() { - return gGetExt(pathfile.c_str()); -} - - -/* ------------------------------------------------------------------ */ - - -void Wave::updateName(const char *n) { - std::string ext = gGetExt(pathfile.c_str()); - name = gStripExt(gBasename(n).c_str()); - pathfile = gDirname(pathfile.c_str()) + gGetSlash() + name + "." + ext; - isLogical = true; - - /* a wave with updated name must become logical, since the underlying - * file does not exist yet. */ -} diff --git a/src/wave.h b/src/wave.h deleted file mode 100644 index 9ce81df..0000000 --- a/src/wave.h +++ /dev/null @@ -1,88 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * wave - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef WAVE_H -#define WAVE_H - - -#include -#include -#include - - -class Wave { - -private: - - SNDFILE *fileIn; - SNDFILE *fileOut; - SF_INFO inHeader; - SF_INFO outHeader; - -public: - - Wave(); - ~Wave(); - - std::string pathfile; // full path + sample name - std::string name; // sample name (changeable) - - float *data; - int size; // wave size (size in stereo: size / 2) - bool isLogical; // memory only (a take) - bool isEdited; // edited via editor - - inline int rate () { return inHeader.samplerate; } - inline int channels() { return inHeader.channels; } - inline int frames () { return inHeader.frames; } - inline void rate (int v) { inHeader.samplerate = v; } - inline void channels(int v) { inHeader.channels = v; } - inline void frames (int v) { inHeader.frames = v; } - - std::string basename (); - std::string extension(); - - void updateName(const char *n); - int open (const char *f); - int readData (); - int writeData (const char *f); - void clear (); - - /* allocEmpty - * alloc an empty waveform. */ - - int allocEmpty(unsigned size); - - /* resample - * simple algorithm for one-shot resampling. */ - - int resample(int quality, int newRate); -}; - -#endif diff --git a/src/waveFx.cpp b/src/waveFx.cpp deleted file mode 100644 index 25e0812..0000000 --- a/src/waveFx.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * waveFx - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#include -#include "waveFx.h" -#include "channel.h" -#include "mixer.h" -#include "wave.h" -#include "log.h" - - -extern Mixer G_Mixer; - - -float wfx_normalizeSoft(Wave *w) { - float peak = 0.0f; - float abs = 0.0f; - for (int i=0; isize; i++) { // i++: both L and R samples - abs = fabs(w->data[i]); - if (abs > peak) - peak = abs; - } - - /* peak == 0.0f: don't normalize the silence - * peak > 1.0f: don't reduce the amplitude, just leave it alone */ - - if (peak == 0.0f || peak > 1.0f) - return 1.0f; - - return 1.0f / peak; -} - - -/* ------------------------------------------------------------------ */ - - -bool wfx_monoToStereo(Wave *w) { - - unsigned newSize = w->size * 2; - float *dataNew = (float *) malloc(newSize * sizeof(float)); - if (dataNew == NULL) { - gLog("[wfx] unable to allocate memory for mono>stereo conversion\n"); - return 0; - } - - for (int i=0, j=0; isize; i++) { - dataNew[j] = w->data[i]; - dataNew[j+1] = w->data[i]; - j+=2; - } - - free(w->data); - w->data = dataNew; - w->size = newSize; - w->frames(w->frames()*2); - w->channels(2); - - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -void wfx_silence(Wave *w, int a, int b) { - - /* stereo values */ - a = a * 2; - b = b * 2; - - gLog("[wfx] silencing from %d to %d\n", a, b); - - for (int i=a; idata[i] = 0.0f; - w->data[i+1] = 0.0f; - } - - w->isEdited = true; - - return; -} - - -/* ------------------------------------------------------------------ */ - - -int wfx_cut(Wave *w, int a, int b) { - a = a * 2; - b = b * 2; - - if (a < 0) a = 0; - if (b > w->size) b = w->size; - - /* create a new temp wave and copy there the original one, skipping - * the a-b range */ - - unsigned newSize = w->size-(b-a); - float *temp = (float *) malloc(newSize * sizeof(float)); - if (temp == NULL) { - gLog("[wfx] unable to allocate memory for cutting\n"); - return 0; - } - - gLog("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", a, b, newSize, newSize/2); - - for (int i=0, k=0; isize; i++) { - if (i < a || i >= b) { // left margin always included, in order to keep - temp[k] = w->data[i]; // the stereo pair - k++; - } - } - - free(w->data); - w->data = temp; - w->size = newSize; - //w->inHeader.frames -= b-a; - w->frames(w->frames() - b - a); - w->isEdited = true; - - gLog("[wfx] cutting done\n"); - - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -int wfx_trim(Wave *w, int a, int b) { - a = a * 2; - b = b * 2; - - if (a < 0) a = 0; - if (b > w->size) b = w->size; - - int newSize = b - a; - float *temp = (float *) malloc(newSize * sizeof(float)); - if (temp == NULL) { - gLog("[wfx] unable to allocate memory for trimming\n"); - return 0; - } - - gLog("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a); - - for (int i=a, k=0; idata[i]; - - free(w->data); - w->data = temp; - w->size = newSize; - //w->inHeader.frames = b-a; - w->frames(b - a); - w->isEdited = true; - - return 1; -} - - -/* ------------------------------------------------------------------ */ - - -void wfx_fade(Wave *w, int a, int b, int type) { - - float m = type == 0 ? 0.0f : 1.0f; - float d = 1.0f/(float)(b-a); - if (type == 1) - d = -d; - - a *= 2; - b *= 2; - - for (int i=a; idata[i] *= m; - w->data[i+1] *= m; - m += d; - } -} - - -/* ------------------------------------------------------------------ */ - - -void wfx_smooth(Wave *w, int a, int b) { - - int d = 32; // 64 if stereo data - - /* do nothing if fade edges (both of 32 samples) are > than selected - * portion of wave. d*2 => count both edges, (b-a)*2 => stereo - * values. */ - - if (d*2 > (b-a)*2) { - gLog("[WFX] selection is too small, nothing to do\n"); - return; - } - - wfx_fade(w, a, a+d, 0); - wfx_fade(w, b-d, b, 1); -} diff --git a/src/waveFx.h b/src/waveFx.h deleted file mode 100644 index ebdef4a..0000000 --- a/src/waveFx.h +++ /dev/null @@ -1,59 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * waveFx - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual - * - * This file is part of Giada - Your Hardcore Loopmachine. - * - * Giada - Your Hardcore Loopmachine is free software: you can - * redistribute it and/or modify it under the terms of the GNU General - * Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * Giada - Your Hardcore Loopmachine is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Giada - Your Hardcore Loopmachine. If not, see - * . - * - * ------------------------------------------------------------------ */ - - -#ifndef WAVEFX_H -#define WAVEFX_H - - -/* normalizeSoft - * normalize the wave by returning the dB value for the boost volume. It - * doesn't deal with data in memory. */ - -float wfx_normalizeSoft(class Wave *w); - -bool wfx_monoToStereo(class Wave *w); - -void wfx_silence(class Wave *w, int a, int b); - -int wfx_cut(class Wave *w, int a, int b); - -int wfx_trim(class Wave *w, int a, int b); - -/* fade - * fade in or fade out selection. Fade In = type 0, Fade Out = type 1 */ - -void wfx_fade(class Wave *w, int a, int b, int type); - -/* smooth - * smooth edges of selection. */ - -void wfx_smooth(class Wave *w, int a, int b); - - -#endif