Repository: mtytel/cursynth Branch: master Commit: 578b43cd5d2f Files: 161 Total size: 1.6 MB Directory structure: gitextract_mpjqhbob/ ├── .gitignore ├── ABOUT-NLS ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── README ├── cJSON/ │ ├── LICENSE │ ├── Makefile.am │ ├── cJSON.c │ └── cJSON.h ├── configure.ac ├── doc/ │ ├── .gitignore │ ├── Makefile.am │ ├── cursynth.texi │ └── fdl.texi ├── mopo/ │ ├── .gitignore │ ├── AUTHORS │ ├── COPYING │ ├── ChangeLog │ ├── INSTALL │ ├── Makefile.am │ ├── NEWS │ ├── README │ ├── configure.ac │ └── src/ │ ├── Makefile.am │ ├── delay.cpp │ ├── delay.h │ ├── envelope.cpp │ ├── envelope.h │ ├── feedback.cpp │ ├── feedback.h │ ├── filter.cpp │ ├── filter.h │ ├── linear_slope.cpp │ ├── linear_slope.h │ ├── memory.h │ ├── midi_lookup.cpp │ ├── midi_lookup.h │ ├── mono_panner.cpp │ ├── mono_panner.h │ ├── mopo.h │ ├── operators.cpp │ ├── operators.h │ ├── oscillator.cpp │ ├── oscillator.h │ ├── processor.cpp │ ├── processor.h │ ├── processor_router.cpp │ ├── processor_router.h │ ├── send_receive.cpp │ ├── send_receive.h │ ├── smooth_filter.cpp │ ├── smooth_filter.h │ ├── smooth_value.cpp │ ├── smooth_value.h │ ├── step_generator.cpp │ ├── step_generator.h │ ├── tick_router.h │ ├── trigger_operators.cpp │ ├── trigger_operators.h │ ├── utils.h │ ├── value.cpp │ ├── value.h │ ├── voice_handler.cpp │ ├── voice_handler.h │ ├── wave.cpp │ └── wave.h ├── patches/ │ ├── bit_sin.mite │ ├── bwang.mite │ ├── default.mite │ ├── loaded_room.mite │ ├── test_patch1.mite │ ├── test_patch2.mite │ ├── water_wobble.mite │ ├── whistle.mite │ ├── whoooo.mite │ └── womp.mite ├── po/ │ ├── .gitignore │ ├── Makevars │ └── POTFILES.in ├── rtaudio/ │ ├── .gitignore │ ├── Makefile.am │ ├── RtAudio.cpp │ ├── RtAudio.h │ ├── RtError.h │ ├── configure.ac │ ├── contrib/ │ │ └── python/ │ │ └── pyrtaudio/ │ │ ├── PyRtAudioTest.py │ │ ├── readme │ │ ├── rtaudiomodule.cpp │ │ └── setup.py │ ├── include/ │ │ ├── asio.cpp │ │ ├── asio.h │ │ ├── asiodrivers.cpp │ │ ├── asiodrivers.h │ │ ├── asiodrvr.h │ │ ├── asiolist.cpp │ │ ├── asiolist.h │ │ ├── asiosys.h │ │ ├── dsound.h │ │ ├── ginclude.h │ │ ├── iasiodrv.h │ │ ├── iasiothiscallresolver.cpp │ │ ├── iasiothiscallresolver.h │ │ └── soundcard.h │ ├── install │ ├── librtaudio.pc.in │ ├── readme │ ├── rtaudio-config.in │ └── tests/ │ ├── Windows/ │ │ ├── audioprobe.dsp │ │ ├── duplex.dsp │ │ ├── playraw.dsp │ │ ├── playsaw.dsp │ │ ├── record.dsp │ │ ├── rtaudio.dsw │ │ ├── testall.dsp │ │ └── teststops.dsp │ ├── audioprobe.cpp │ ├── duplex.cpp │ ├── playraw.cpp │ ├── playsaw.cpp │ ├── record.cpp │ ├── testall.cpp │ └── teststops.cpp ├── rtmidi/ │ ├── .gitignore │ ├── Makefile.am │ ├── RtError.h │ ├── RtMidi.cpp │ ├── RtMidi.h │ ├── configure.ac │ ├── include/ │ │ ├── ks.h │ │ └── ksmedia.h │ ├── msw/ │ │ ├── readme │ │ ├── rtmidilib.sln │ │ └── rtmidilib.vcproj │ ├── readme │ ├── rtmidi-config.in │ └── tests/ │ ├── RtMidi.dsw │ ├── cmidiin.cpp │ ├── cmidiin.dsp │ ├── midiout.cpp │ ├── midiout.dsp │ ├── midiprobe.cpp │ ├── midiprobe.dsp │ ├── qmidiin.cpp │ ├── qmidiin.dsp │ ├── sysextest.cpp │ └── sysextest.dsp └── src/ ├── Makefile.am ├── cursynth.cpp ├── cursynth.h ├── cursynth_common.h ├── cursynth_engine.cpp ├── cursynth_engine.h ├── cursynth_gui.cpp ├── cursynth_gui.h ├── cursynth_strings.cpp ├── cursynth_strings.h └── main.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store # Compiled Object files *.slo *.lo *.o debug # Compiled Dynamic libraries *.so *.dylib # Compiled Static libraries *.lai *.la *.a # Executables cursynth a.out # Autotools Makefile.in ar-lib config.guess config.rpath config.sub config.h.in *.la *.lo *~ .deps/ .libs/ Makefile aclocal.m4 autom4te.cache autoscan.log compile config.h config.log config.status configure depcomp install-sh libtool ltmain.sh m4/ missing stamp* stdin.info *.tar.gz stdin.info ================================================ FILE: ABOUT-NLS ================================================ 1 Notes on the Free Translation Project *************************************** Free software is going international! The Free Translation Project is a way to get maintainers of free software, translators, and users all together, so that free software will gradually become able to speak many languages. A few packages already provide translations for their messages. If you found this `ABOUT-NLS' file inside a distribution, you may assume that the distributed package does use GNU `gettext' internally, itself available at your nearest GNU archive site. But you do _not_ need to install GNU `gettext' prior to configuring, installing or using this package with messages translated. Installers will find here some useful hints. These notes also explain how users should proceed for getting the programs to use the available translations. They tell how people wanting to contribute and work on translations can contact the appropriate team. 1.1 INSTALL Matters =================== Some packages are "localizable" when properly installed; the programs they contain can be made to speak your own native language. Most such packages use GNU `gettext'. Other packages have their own ways to internationalization, predating GNU `gettext'. By default, this package will be installed to allow translation of messages. It will automatically detect whether the system already provides the GNU `gettext' functions. Installers may use special options at configuration time for changing the default behaviour. The command: ./configure --disable-nls will _totally_ disable translation of messages. When you already have GNU `gettext' installed on your system and run configure without an option for your new package, `configure' will probably detect the previously built and installed `libintl' library and will decide to use it. If not, you may have to to use the `--with-libintl-prefix' option to tell `configure' where to look for it. Internationalized packages usually have many `po/LL.po' files, where LL gives an ISO 639 two-letter code identifying the language. Unless translations have been forbidden at `configure' time by using the `--disable-nls' switch, all available translations are installed together with the package. However, the environment variable `LINGUAS' may be set, prior to configuration, to limit the installed set. `LINGUAS' should then contain a space separated list of two-letter codes, stating which languages are allowed. 1.2 Using This Package ====================== As a user, if your language has been installed for this package, you only have to set the `LANG' environment variable to the appropriate `LL_CC' combination. If you happen to have the `LC_ALL' or some other `LC_xxx' environment variables set, you should unset them before setting `LANG', otherwise the setting of `LANG' will not have the desired effect. Here `LL' is an ISO 639 two-letter language code, and `CC' is an ISO 3166 two-letter country code. For example, let's suppose that you speak German and live in Germany. At the shell prompt, merely execute `setenv LANG de_DE' (in `csh'), `export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). This can be done from your `.login' or `.profile' file, once and for all. You might think that the country code specification is redundant. But in fact, some languages have dialects in different countries. For example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The country code serves to distinguish the dialects. The locale naming convention of `LL_CC', with `LL' denoting the language and `CC' denoting the country, is the one use on systems based on GNU libc. On other systems, some variations of this scheme are used, such as `LL' or `LL_CC.ENCODING'. You can get the list of locales supported by your system for your language by running the command `locale -a | grep '^LL''. Not all programs have translations for all languages. By default, an English message is shown in place of a nonexistent translation. If you understand other languages, you can set up a priority list of languages. This is done through a different environment variable, called `LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' for the purpose of message handling, but you still need to have `LANG' set to the primary language; this is required by other parts of the system libraries. For example, some Swedish users who would rather read translations in German than English for when Swedish is not available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. Special advice for Norwegian users: The language code for Norwegian bokma*l changed from `no' to `nb' recently (in 2003). During the transition period, while some message catalogs for this language are installed under `nb' and some older ones under `no', it's recommended for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and older translations are used. In the `LANGUAGE' environment variable, but not in the `LANG' environment variable, `LL_CC' combinations can be abbreviated as `LL' to denote the language's main dialect. For example, `de' is equivalent to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' (Portuguese as spoken in Portugal) in this context. 1.3 Translating Teams ===================== For the Free Translation Project to be a success, we need interested people who like their own language and write it well, and who are also able to synergize with other translators speaking the same language. Each translation team has its own mailing list. The up-to-date list of teams can be found at the Free Translation Project's homepage, `http://translationproject.org/', in the "Teams" area. If you'd like to volunteer to _work_ at translating messages, you should become a member of the translating team for your own language. The subscribing address is _not_ the same as the list itself, it has `-request' appended. For example, speakers of Swedish can send a message to `sv-request@li.org', having this message body: subscribe Keep in mind that team members are expected to participate _actively_ in translations, or at solving translational difficulties, rather than merely lurking around. If your team does not exist yet and you want to start one, or if you are unsure about what to do or how to get started, please write to `coordinator@translationproject.org' to reach the coordinator for all translator teams. The English team is special. It works at improving and uniformizing the terminology in use. Proven linguistic skills are praised more than programming skills, here. 1.4 Available Packages ====================== Languages are not equally supported in all packages. The following matrix shows the current state of internationalization, as of June 2010. The matrix shows, in regard of each package, for which languages PO files have been submitted to translation coordination, with a translation percentage of at least 50%. Ready PO files af am an ar as ast az be be@latin bg bn_IN bs ca +--------------------------------------------------+ a2ps | [] [] | aegis | | ant-phone | | anubis | | aspell | [] [] | bash | | bfd | | bibshelf | [] | binutils | | bison | | bison-runtime | [] | bluez-pin | [] [] | bombono-dvd | | buzztard | | cflow | | clisp | | coreutils | [] [] | cpio | | cppi | | cpplib | [] | cryptsetup | | dfarc | | dialog | [] [] | dico | | diffutils | [] | dink | | doodle | | e2fsprogs | [] | enscript | [] | exif | | fetchmail | [] | findutils | [] | flex | [] | freedink | | gas | | gawk | [] [] | gcal | [] | gcc | | gettext-examples | [] [] [] [] | gettext-runtime | [] [] | gettext-tools | [] [] | gip | [] | gjay | | gliv | [] | glunarclock | [] [] | gnubiff | | gnucash | [] | gnuedu | | gnulib | | gnunet | | gnunet-gtk | | gnutls | | gold | | gpe-aerial | | gpe-beam | | gpe-bluetooth | | gpe-calendar | | gpe-clock | [] | gpe-conf | | gpe-contacts | | gpe-edit | | gpe-filemanager | | gpe-go | | gpe-login | | gpe-ownerinfo | [] | gpe-package | | gpe-sketchbook | | gpe-su | [] | gpe-taskmanager | [] | gpe-timesheet | [] | gpe-today | [] | gpe-todo | | gphoto2 | | gprof | [] | gpsdrive | | gramadoir | | grep | | grub | [] [] | gsasl | | gss | | gst-plugins-bad | [] | gst-plugins-base | [] | gst-plugins-good | [] | gst-plugins-ugly | [] | gstreamer | [] [] [] | gtick | | gtkam | [] | gtkorphan | [] | gtkspell | [] [] [] | gutenprint | | hello | [] | help2man | | hylafax | | idutils | | indent | [] [] | iso_15924 | | iso_3166 | [] [] [] [] [] [] [] | iso_3166_2 | | iso_4217 | | iso_639 | [] [] [] [] | iso_639_3 | | jwhois | | kbd | | keytouch | [] | keytouch-editor | | keytouch-keyboa... | [] | klavaro | [] | latrine | | ld | [] | leafpad | [] [] | libc | [] [] | libexif | () | libextractor | | libgnutls | | libgpewidget | | libgpg-error | | libgphoto2 | | libgphoto2_port | | libgsasl | | libiconv | [] | libidn | | lifelines | | liferea | [] [] | lilypond | | linkdr | [] | lordsawar | | lprng | | lynx | [] | m4 | | mailfromd | | mailutils | | make | | man-db | | man-db-manpages | | minicom | | mkisofs | | myserver | | nano | [] [] | opcodes | | parted | | pies | | popt | | psmisc | | pspp | [] | pwdutils | | radius | [] | recode | [] [] | rosegarden | | rpm | | rush | | sarg | | screem | | scrollkeeper | [] [] [] | sed | [] [] | sharutils | [] [] | shishi | | skencil | | solfege | | solfege-manual | | soundtracker | | sp | | sysstat | | tar | [] | texinfo | | tin | | unicode-han-tra... | | unicode-transla... | | util-linux-ng | [] | vice | | vmm | | vorbis-tools | | wastesedge | | wdiff | | wget | [] [] | wyslij-po | | xchat | [] [] [] [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] | +--------------------------------------------------+ af am an ar as ast az be be@latin bg bn_IN bs ca 6 0 1 2 3 19 1 10 3 28 3 1 38 crh cs da de el en en_GB en_ZA eo es et eu fa +-------------------------------------------------+ a2ps | [] [] [] [] [] [] [] | aegis | [] [] [] | ant-phone | [] () | anubis | [] [] | aspell | [] [] [] [] [] | bash | [] [] [] | bfd | [] | bibshelf | [] [] [] | binutils | [] | bison | [] [] | bison-runtime | [] [] [] [] | bluez-pin | [] [] [] [] [] [] | bombono-dvd | [] | buzztard | [] [] [] | cflow | [] [] | clisp | [] [] [] [] | coreutils | [] [] [] [] | cpio | | cppi | | cpplib | [] [] [] | cryptsetup | [] | dfarc | [] [] [] | dialog | [] [] [] [] [] | dico | | diffutils | [] [] [] [] [] [] | dink | [] [] [] | doodle | [] | e2fsprogs | [] [] [] | enscript | [] [] [] | exif | () [] [] | fetchmail | [] [] () [] [] [] | findutils | [] [] [] | flex | [] [] | freedink | [] [] [] | gas | [] | gawk | [] [] [] | gcal | [] | gcc | [] [] | gettext-examples | [] [] [] [] | gettext-runtime | [] [] [] [] | gettext-tools | [] [] [] | gip | [] [] [] [] | gjay | [] | gliv | [] [] [] | glunarclock | [] [] | gnubiff | () | gnucash | [] () () () () | gnuedu | [] [] | gnulib | [] [] | gnunet | | gnunet-gtk | [] | gnutls | [] [] | gold | [] | gpe-aerial | [] [] [] [] | gpe-beam | [] [] [] [] | gpe-bluetooth | [] [] | gpe-calendar | [] | gpe-clock | [] [] [] [] | gpe-conf | [] [] [] | gpe-contacts | [] [] [] | gpe-edit | [] [] | gpe-filemanager | [] [] [] | gpe-go | [] [] [] [] | gpe-login | [] [] | gpe-ownerinfo | [] [] [] [] | gpe-package | [] [] [] | gpe-sketchbook | [] [] [] [] | gpe-su | [] [] [] [] | gpe-taskmanager | [] [] [] [] | gpe-timesheet | [] [] [] [] | gpe-today | [] [] [] [] | gpe-todo | [] [] [] | gphoto2 | [] [] () [] [] [] | gprof | [] [] [] | gpsdrive | [] [] [] | gramadoir | [] [] [] | grep | [] | grub | [] [] | gsasl | [] | gss | | gst-plugins-bad | [] [] [] [] [] | gst-plugins-base | [] [] [] [] [] | gst-plugins-good | [] [] [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] [] | gstreamer | [] [] [] [] [] | gtick | [] () [] | gtkam | [] [] () [] [] | gtkorphan | [] [] [] [] | gtkspell | [] [] [] [] [] [] [] | gutenprint | [] [] [] | hello | [] [] [] [] | help2man | [] | hylafax | [] [] | idutils | [] [] | indent | [] [] [] [] [] [] [] | iso_15924 | [] () [] [] | iso_3166 | [] [] [] [] () [] [] [] () | iso_3166_2 | () | iso_4217 | [] [] [] () [] [] | iso_639 | [] [] [] [] () [] [] | iso_639_3 | [] | jwhois | [] | kbd | [] [] [] [] [] | keytouch | [] [] | keytouch-editor | [] [] | keytouch-keyboa... | [] | klavaro | [] [] [] [] | latrine | [] () | ld | [] [] | leafpad | [] [] [] [] [] [] | libc | [] [] [] [] | libexif | [] [] () | libextractor | | libgnutls | [] | libgpewidget | [] [] | libgpg-error | [] [] | libgphoto2 | [] () | libgphoto2_port | [] () [] | libgsasl | | libiconv | [] [] [] [] [] | libidn | [] [] [] | lifelines | [] () | liferea | [] [] [] [] [] | lilypond | [] [] [] | linkdr | [] [] [] | lordsawar | [] | lprng | | lynx | [] [] [] [] | m4 | [] [] [] [] | mailfromd | | mailutils | [] | make | [] [] [] | man-db | | man-db-manpages | | minicom | [] [] [] [] | mkisofs | | myserver | | nano | [] [] [] | opcodes | [] [] | parted | [] [] | pies | | popt | [] [] [] [] [] | psmisc | [] [] [] | pspp | [] | pwdutils | [] | radius | [] | recode | [] [] [] [] [] [] | rosegarden | () () () | rpm | [] [] [] | rush | | sarg | | screem | | scrollkeeper | [] [] [] [] [] | sed | [] [] [] [] [] [] | sharutils | [] [] [] [] | shishi | | skencil | [] () [] | solfege | [] [] [] | solfege-manual | [] [] | soundtracker | [] [] [] | sp | [] | sysstat | [] [] [] | tar | [] [] [] [] | texinfo | [] [] [] | tin | [] [] | unicode-han-tra... | | unicode-transla... | | util-linux-ng | [] [] [] [] | vice | () () | vmm | [] | vorbis-tools | [] [] | wastesedge | [] | wdiff | [] [] | wget | [] [] [] | wyslij-po | | xchat | [] [] [] [] [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] [] [] [] | +-------------------------------------------------+ crh cs da de el en en_GB en_ZA eo es et eu fa 5 64 105 117 18 1 8 0 28 89 18 19 0 fi fr ga gl gu he hi hr hu hy id is it ja ka kn +----------------------------------------------------+ a2ps | [] [] [] [] | aegis | [] [] | ant-phone | [] [] | anubis | [] [] [] [] | aspell | [] [] [] [] | bash | [] [] [] [] | bfd | [] [] [] | bibshelf | [] [] [] [] [] | binutils | [] [] [] | bison | [] [] [] [] | bison-runtime | [] [] [] [] [] [] | bluez-pin | [] [] [] [] [] [] [] [] | bombono-dvd | [] | buzztard | [] | cflow | [] [] [] | clisp | [] | coreutils | [] [] [] [] [] | cpio | [] [] [] [] | cppi | [] [] | cpplib | [] [] [] | cryptsetup | [] [] [] | dfarc | [] [] [] | dialog | [] [] [] [] [] [] [] | dico | | diffutils | [] [] [] [] [] [] [] [] [] | dink | [] | doodle | [] [] | e2fsprogs | [] [] | enscript | [] [] [] [] | exif | [] [] [] [] [] [] | fetchmail | [] [] [] [] | findutils | [] [] [] [] [] [] | flex | [] [] [] | freedink | [] [] [] | gas | [] [] | gawk | [] [] [] [] () [] | gcal | [] | gcc | [] | gettext-examples | [] [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] | gettext-tools | [] [] [] [] | gip | [] [] [] [] [] [] | gjay | [] | gliv | [] () | glunarclock | [] [] [] [] | gnubiff | () [] () | gnucash | () () () () () [] | gnuedu | [] [] | gnulib | [] [] [] [] [] [] | gnunet | | gnunet-gtk | [] | gnutls | [] [] | gold | [] [] | gpe-aerial | [] [] [] | gpe-beam | [] [] [] [] | gpe-bluetooth | [] [] [] [] | gpe-calendar | [] [] | gpe-clock | [] [] [] [] [] | gpe-conf | [] [] [] [] | gpe-contacts | [] [] [] [] | gpe-edit | [] [] [] | gpe-filemanager | [] [] [] [] | gpe-go | [] [] [] [] [] | gpe-login | [] [] [] | gpe-ownerinfo | [] [] [] [] [] | gpe-package | [] [] [] | gpe-sketchbook | [] [] [] [] | gpe-su | [] [] [] [] [] [] | gpe-taskmanager | [] [] [] [] [] | gpe-timesheet | [] [] [] [] [] | gpe-today | [] [] [] [] [] [] [] | gpe-todo | [] [] [] | gphoto2 | [] [] [] [] [] [] | gprof | [] [] [] [] | gpsdrive | [] [] [] | gramadoir | [] [] [] | grep | [] [] | grub | [] [] [] [] | gsasl | [] [] [] [] [] | gss | [] [] [] [] [] | gst-plugins-bad | [] [] [] [] [] [] | gst-plugins-base | [] [] [] [] [] [] | gst-plugins-good | [] [] [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] [] | gstreamer | [] [] [] [] [] | gtick | [] [] [] [] [] | gtkam | [] [] [] [] [] | gtkorphan | [] [] [] | gtkspell | [] [] [] [] [] [] [] [] [] | gutenprint | [] [] [] [] | hello | [] [] [] | help2man | [] [] | hylafax | [] | idutils | [] [] [] [] [] [] | indent | [] [] [] [] [] [] [] [] | iso_15924 | [] () [] [] | iso_3166 | [] () [] [] [] [] [] [] [] [] [] [] | iso_3166_2 | () [] [] [] | iso_4217 | [] () [] [] [] [] | iso_639 | [] () [] [] [] [] [] [] [] | iso_639_3 | () [] [] | jwhois | [] [] [] [] [] | kbd | [] [] | keytouch | [] [] [] [] [] [] | keytouch-editor | [] [] [] [] [] | keytouch-keyboa... | [] [] [] [] [] | klavaro | [] [] | latrine | [] [] [] | ld | [] [] [] [] | leafpad | [] [] [] [] [] [] [] () | libc | [] [] [] [] [] | libexif | [] | libextractor | | libgnutls | [] [] | libgpewidget | [] [] [] [] | libgpg-error | [] [] | libgphoto2 | [] [] [] | libgphoto2_port | [] [] [] | libgsasl | [] [] [] [] [] | libiconv | [] [] [] [] [] [] | libidn | [] [] [] [] | lifelines | () | liferea | [] [] [] [] | lilypond | [] [] | linkdr | [] [] [] [] [] | lordsawar | | lprng | [] | lynx | [] [] [] [] [] | m4 | [] [] [] [] [] [] | mailfromd | | mailutils | [] [] | make | [] [] [] [] [] [] [] [] [] | man-db | [] [] | man-db-manpages | [] | minicom | [] [] [] [] [] | mkisofs | [] [] [] [] | myserver | | nano | [] [] [] [] [] [] | opcodes | [] [] [] [] | parted | [] [] [] [] | pies | | popt | [] [] [] [] [] [] [] [] [] | psmisc | [] [] [] | pspp | | pwdutils | [] [] | radius | [] [] | recode | [] [] [] [] [] [] [] [] | rosegarden | () () () () () | rpm | [] [] | rush | | sarg | [] | screem | [] [] | scrollkeeper | [] [] [] [] | sed | [] [] [] [] [] [] [] [] | sharutils | [] [] [] [] [] [] [] | shishi | [] | skencil | [] | solfege | [] [] [] [] | solfege-manual | [] [] | soundtracker | [] [] | sp | [] () | sysstat | [] [] [] [] [] | tar | [] [] [] [] [] [] [] | texinfo | [] [] [] [] | tin | [] | unicode-han-tra... | | unicode-transla... | [] [] | util-linux-ng | [] [] [] [] [] [] | vice | () () () | vmm | [] | vorbis-tools | [] | wastesedge | () () | wdiff | [] | wget | [] [] [] [] [] [] [] [] | wyslij-po | [] [] [] | xchat | [] [] [] [] [] [] [] [] [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] [] [] | +----------------------------------------------------+ fi fr ga gl gu he hi hr hu hy id is it ja ka kn 105 121 53 20 4 8 3 5 53 2 120 5 84 67 0 4 ko ku ky lg lt lv mk ml mn mr ms mt nb nds ne +-----------------------------------------------+ a2ps | [] | aegis | | ant-phone | | anubis | [] [] | aspell | [] | bash | | bfd | | bibshelf | [] [] | binutils | | bison | [] | bison-runtime | [] [] [] [] [] | bluez-pin | [] [] [] [] [] | bombono-dvd | | buzztard | | cflow | | clisp | | coreutils | [] | cpio | | cppi | | cpplib | | cryptsetup | | dfarc | [] | dialog | [] [] [] [] [] | dico | | diffutils | [] [] | dink | | doodle | | e2fsprogs | | enscript | | exif | [] | fetchmail | | findutils | | flex | | freedink | [] | gas | | gawk | | gcal | | gcc | | gettext-examples | [] [] [] [] | gettext-runtime | [] | gettext-tools | [] | gip | [] [] | gjay | | gliv | | glunarclock | [] | gnubiff | | gnucash | () () () () | gnuedu | | gnulib | | gnunet | | gnunet-gtk | | gnutls | [] | gold | | gpe-aerial | [] | gpe-beam | [] | gpe-bluetooth | [] [] | gpe-calendar | [] | gpe-clock | [] [] [] [] [] | gpe-conf | [] [] | gpe-contacts | [] [] | gpe-edit | [] | gpe-filemanager | [] [] | gpe-go | [] [] [] | gpe-login | [] | gpe-ownerinfo | [] [] | gpe-package | [] [] | gpe-sketchbook | [] [] | gpe-su | [] [] [] [] [] [] | gpe-taskmanager | [] [] [] [] [] [] | gpe-timesheet | [] [] | gpe-today | [] [] [] [] | gpe-todo | [] [] | gphoto2 | | gprof | [] | gpsdrive | | gramadoir | | grep | | grub | | gsasl | | gss | | gst-plugins-bad | [] [] [] [] | gst-plugins-base | [] [] | gst-plugins-good | [] [] | gst-plugins-ugly | [] [] [] [] [] | gstreamer | | gtick | | gtkam | [] | gtkorphan | [] [] | gtkspell | [] [] [] [] [] [] [] | gutenprint | | hello | [] [] [] | help2man | | hylafax | | idutils | | indent | | iso_15924 | [] [] | iso_3166 | [] [] () [] [] [] [] [] | iso_3166_2 | | iso_4217 | [] [] | iso_639 | [] [] | iso_639_3 | [] | jwhois | [] | kbd | | keytouch | [] | keytouch-editor | [] | keytouch-keyboa... | [] | klavaro | [] | latrine | [] | ld | | leafpad | [] [] [] | libc | [] | libexif | | libextractor | | libgnutls | [] | libgpewidget | [] [] | libgpg-error | | libgphoto2 | | libgphoto2_port | | libgsasl | | libiconv | | libidn | | lifelines | | liferea | | lilypond | | linkdr | | lordsawar | | lprng | | lynx | | m4 | | mailfromd | | mailutils | | make | [] | man-db | | man-db-manpages | | minicom | [] | mkisofs | | myserver | | nano | [] [] | opcodes | | parted | | pies | | popt | [] [] [] | psmisc | | pspp | | pwdutils | | radius | | recode | | rosegarden | | rpm | | rush | | sarg | | screem | | scrollkeeper | [] [] | sed | | sharutils | | shishi | | skencil | | solfege | [] | solfege-manual | | soundtracker | | sp | | sysstat | [] | tar | [] | texinfo | [] | tin | | unicode-han-tra... | | unicode-transla... | | util-linux-ng | | vice | | vmm | | vorbis-tools | | wastesedge | | wdiff | | wget | [] | wyslij-po | | xchat | [] [] [] | xdg-user-dirs | [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] | +-----------------------------------------------+ ko ku ky lg lt lv mk ml mn mr ms mt nb nds ne 20 5 10 1 13 48 4 2 2 4 24 10 20 3 1 nl nn or pa pl ps pt pt_BR ro ru rw sk sl sq sr +---------------------------------------------------+ a2ps | [] [] [] [] [] [] [] [] | aegis | [] [] [] | ant-phone | [] [] | anubis | [] [] [] | aspell | [] [] [] [] [] | bash | [] [] | bfd | [] | bibshelf | [] [] | binutils | [] [] | bison | [] [] [] | bison-runtime | [] [] [] [] [] [] [] | bluez-pin | [] [] [] [] [] [] [] [] | bombono-dvd | [] () | buzztard | [] [] | cflow | [] | clisp | [] [] | coreutils | [] [] [] [] [] [] | cpio | [] [] [] | cppi | [] | cpplib | [] | cryptsetup | [] | dfarc | [] | dialog | [] [] [] [] | dico | [] | diffutils | [] [] [] [] [] [] | dink | () | doodle | [] [] | e2fsprogs | [] [] | enscript | [] [] [] [] [] | exif | [] [] [] () [] | fetchmail | [] [] [] [] | findutils | [] [] [] [] [] | flex | [] [] [] [] [] | freedink | [] [] | gas | | gawk | [] [] [] [] | gcal | | gcc | [] | gettext-examples | [] [] [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] [] [] [] | gettext-tools | [] [] [] [] [] [] | gip | [] [] [] [] [] | gjay | | gliv | [] [] [] [] [] [] | glunarclock | [] [] [] [] [] | gnubiff | [] () | gnucash | [] () () () | gnuedu | [] | gnulib | [] [] [] [] | gnunet | | gnunet-gtk | | gnutls | [] [] | gold | | gpe-aerial | [] [] [] [] [] [] [] | gpe-beam | [] [] [] [] [] [] [] | gpe-bluetooth | [] [] | gpe-calendar | [] [] [] [] | gpe-clock | [] [] [] [] [] [] [] [] | gpe-conf | [] [] [] [] [] [] [] | gpe-contacts | [] [] [] [] [] | gpe-edit | [] [] [] | gpe-filemanager | [] [] [] | gpe-go | [] [] [] [] [] [] [] [] | gpe-login | [] [] | gpe-ownerinfo | [] [] [] [] [] [] [] [] | gpe-package | [] [] | gpe-sketchbook | [] [] [] [] [] [] [] | gpe-su | [] [] [] [] [] [] [] [] | gpe-taskmanager | [] [] [] [] [] [] [] [] | gpe-timesheet | [] [] [] [] [] [] [] [] | gpe-today | [] [] [] [] [] [] [] [] | gpe-todo | [] [] [] [] [] | gphoto2 | [] [] [] [] [] [] [] [] | gprof | [] [] [] | gpsdrive | [] [] | gramadoir | [] [] | grep | [] [] [] [] | grub | [] [] [] | gsasl | [] [] [] [] | gss | [] [] [] | gst-plugins-bad | [] [] [] [] [] [] | gst-plugins-base | [] [] [] [] [] | gst-plugins-good | [] [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] [] | gstreamer | [] [] [] [] [] | gtick | [] [] [] | gtkam | [] [] [] [] [] [] | gtkorphan | [] | gtkspell | [] [] [] [] [] [] [] [] [] [] | gutenprint | [] [] | hello | [] [] [] [] | help2man | [] [] | hylafax | [] | idutils | [] [] [] [] [] | indent | [] [] [] [] [] [] [] | iso_15924 | [] [] [] [] | iso_3166 | [] [] [] [] [] () [] [] [] [] [] [] [] [] | iso_3166_2 | [] [] [] | iso_4217 | [] [] [] [] [] [] [] [] | iso_639 | [] [] [] [] [] [] [] [] [] | iso_639_3 | [] [] | jwhois | [] [] [] [] | kbd | [] [] [] | keytouch | [] [] [] | keytouch-editor | [] [] [] | keytouch-keyboa... | [] [] [] | klavaro | [] [] | latrine | [] [] | ld | | leafpad | [] [] [] [] [] [] [] [] [] | libc | [] [] [] [] | libexif | [] [] () [] | libextractor | | libgnutls | [] [] | libgpewidget | [] [] [] | libgpg-error | [] [] | libgphoto2 | [] [] | libgphoto2_port | [] [] [] [] [] | libgsasl | [] [] [] [] [] | libiconv | [] [] [] [] [] | libidn | [] [] | lifelines | [] [] | liferea | [] [] [] [] [] () () [] | lilypond | [] | linkdr | [] [] [] | lordsawar | | lprng | [] | lynx | [] [] [] | m4 | [] [] [] [] [] | mailfromd | [] | mailutils | [] | make | [] [] [] [] | man-db | [] [] [] | man-db-manpages | [] [] [] | minicom | [] [] [] [] | mkisofs | [] [] [] | myserver | | nano | [] [] [] [] | opcodes | [] [] | parted | [] [] [] [] | pies | [] | popt | [] [] [] [] | psmisc | [] [] [] | pspp | [] [] | pwdutils | [] | radius | [] [] [] | recode | [] [] [] [] [] [] [] [] | rosegarden | () () | rpm | [] [] [] | rush | [] [] | sarg | | screem | | scrollkeeper | [] [] [] [] [] [] [] [] | sed | [] [] [] [] [] [] [] [] [] | sharutils | [] [] [] [] | shishi | [] | skencil | [] [] | solfege | [] [] [] [] | solfege-manual | [] [] [] | soundtracker | [] | sp | | sysstat | [] [] [] [] | tar | [] [] [] [] | texinfo | [] [] [] [] | tin | [] | unicode-han-tra... | | unicode-transla... | | util-linux-ng | [] [] [] [] [] | vice | [] | vmm | [] | vorbis-tools | [] [] | wastesedge | [] | wdiff | [] [] | wget | [] [] [] [] [] [] [] | wyslij-po | [] [] [] | xchat | [] [] [] [] [] [] [] [] [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] | +---------------------------------------------------+ nl nn or pa pl ps pt pt_BR ro ru rw sk sl sq sr 135 10 4 7 105 1 29 62 47 91 3 54 46 9 37 sv sw ta te tg th tr uk vi wa zh_CN zh_HK zh_TW +---------------------------------------------------+ a2ps | [] [] [] [] [] | 27 aegis | [] | 9 ant-phone | [] [] [] [] | 9 anubis | [] [] [] [] | 15 aspell | [] [] [] | 20 bash | [] [] [] | 12 bfd | [] | 6 bibshelf | [] [] [] | 16 binutils | [] [] | 8 bison | [] [] | 12 bison-runtime | [] [] [] [] [] [] | 29 bluez-pin | [] [] [] [] [] [] [] [] | 37 bombono-dvd | [] | 4 buzztard | [] | 7 cflow | [] [] [] | 9 clisp | | 10 coreutils | [] [] [] [] | 22 cpio | [] [] [] [] [] [] | 13 cppi | [] [] | 5 cpplib | [] [] [] [] [] [] | 14 cryptsetup | [] [] | 7 dfarc | [] | 9 dialog | [] [] [] [] [] [] [] | 30 dico | [] | 2 diffutils | [] [] [] [] [] [] | 30 dink | | 4 doodle | [] [] | 7 e2fsprogs | [] [] [] | 11 enscript | [] [] [] [] | 17 exif | [] [] [] | 16 fetchmail | [] [] [] | 17 findutils | [] [] [] [] [] | 20 flex | [] [] [] [] | 15 freedink | [] | 10 gas | [] | 4 gawk | [] [] [] [] | 18 gcal | [] [] | 5 gcc | [] [] [] | 7 gettext-examples | [] [] [] [] [] [] [] | 34 gettext-runtime | [] [] [] [] [] [] [] | 29 gettext-tools | [] [] [] [] [] [] | 22 gip | [] [] [] [] | 22 gjay | [] | 3 gliv | [] [] [] | 14 glunarclock | [] [] [] [] [] | 19 gnubiff | [] [] | 4 gnucash | () [] () [] () | 10 gnuedu | [] [] | 7 gnulib | [] [] [] [] | 16 gnunet | [] | 1 gnunet-gtk | [] [] [] | 5 gnutls | [] [] [] | 10 gold | [] | 4 gpe-aerial | [] [] [] | 18 gpe-beam | [] [] [] | 19 gpe-bluetooth | [] [] [] | 13 gpe-calendar | [] [] [] [] | 12 gpe-clock | [] [] [] [] [] | 28 gpe-conf | [] [] [] [] | 20 gpe-contacts | [] [] [] | 17 gpe-edit | [] [] [] | 12 gpe-filemanager | [] [] [] [] | 16 gpe-go | [] [] [] [] [] | 25 gpe-login | [] [] [] | 11 gpe-ownerinfo | [] [] [] [] [] | 25 gpe-package | [] [] [] | 13 gpe-sketchbook | [] [] [] | 20 gpe-su | [] [] [] [] [] | 30 gpe-taskmanager | [] [] [] [] [] | 29 gpe-timesheet | [] [] [] [] [] | 25 gpe-today | [] [] [] [] [] [] | 30 gpe-todo | [] [] [] [] | 17 gphoto2 | [] [] [] [] [] | 24 gprof | [] [] [] | 15 gpsdrive | [] [] [] | 11 gramadoir | [] [] [] | 11 grep | [] [] [] | 10 grub | [] [] [] | 14 gsasl | [] [] [] [] | 14 gss | [] [] [] | 11 gst-plugins-bad | [] [] [] [] | 26 gst-plugins-base | [] [] [] [] [] | 24 gst-plugins-good | [] [] [] [] | 24 gst-plugins-ugly | [] [] [] [] [] | 29 gstreamer | [] [] [] [] | 22 gtick | [] [] [] | 13 gtkam | [] [] [] | 20 gtkorphan | [] [] [] | 14 gtkspell | [] [] [] [] [] [] [] [] [] | 45 gutenprint | [] | 10 hello | [] [] [] [] [] [] | 21 help2man | [] [] | 7 hylafax | [] | 5 idutils | [] [] [] [] | 17 indent | [] [] [] [] [] [] | 30 iso_15924 | () [] () [] [] | 16 iso_3166 | [] [] () [] [] () [] [] [] () | 53 iso_3166_2 | () [] () [] | 9 iso_4217 | [] () [] [] () [] [] | 26 iso_639 | [] [] [] () [] () [] [] [] [] | 38 iso_639_3 | [] () | 8 jwhois | [] [] [] [] [] | 16 kbd | [] [] [] [] [] | 15 keytouch | [] [] [] | 16 keytouch-editor | [] [] [] | 14 keytouch-keyboa... | [] [] [] | 14 klavaro | [] | 11 latrine | [] [] [] | 10 ld | [] [] [] [] | 11 leafpad | [] [] [] [] [] [] | 33 libc | [] [] [] [] [] | 21 libexif | [] () | 7 libextractor | [] | 1 libgnutls | [] [] [] | 9 libgpewidget | [] [] [] | 14 libgpg-error | [] [] [] | 9 libgphoto2 | [] [] | 8 libgphoto2_port | [] [] [] [] | 14 libgsasl | [] [] [] | 13 libiconv | [] [] [] [] | 21 libidn | () [] [] | 11 lifelines | [] | 4 liferea | [] [] [] | 21 lilypond | [] | 7 linkdr | [] [] [] [] [] | 17 lordsawar | | 1 lprng | [] | 3 lynx | [] [] [] [] | 17 m4 | [] [] [] [] | 19 mailfromd | [] [] | 3 mailutils | [] | 5 make | [] [] [] [] | 21 man-db | [] [] [] | 8 man-db-manpages | | 4 minicom | [] [] | 16 mkisofs | [] [] | 9 myserver | | 0 nano | [] [] [] [] | 21 opcodes | [] [] [] | 11 parted | [] [] [] [] [] | 15 pies | [] [] | 3 popt | [] [] [] [] [] [] | 27 psmisc | [] [] | 11 pspp | | 4 pwdutils | [] [] | 6 radius | [] [] | 9 recode | [] [] [] [] | 28 rosegarden | () | 0 rpm | [] [] [] | 11 rush | [] [] | 4 sarg | | 1 screem | [] | 3 scrollkeeper | [] [] [] [] [] | 27 sed | [] [] [] [] [] | 30 sharutils | [] [] [] [] [] | 22 shishi | [] | 3 skencil | [] [] | 7 solfege | [] [] [] [] | 16 solfege-manual | [] | 8 soundtracker | [] [] [] | 9 sp | [] | 3 sysstat | [] [] | 15 tar | [] [] [] [] [] [] | 23 texinfo | [] [] [] [] [] | 17 tin | | 4 unicode-han-tra... | | 0 unicode-transla... | | 2 util-linux-ng | [] [] [] [] | 20 vice | () () | 1 vmm | [] | 4 vorbis-tools | [] | 6 wastesedge | | 2 wdiff | [] [] | 7 wget | [] [] [] [] [] | 26 wyslij-po | [] [] | 8 xchat | [] [] [] [] [] [] | 36 xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] | 63 xkeyboard-config | [] [] [] | 22 +---------------------------------------------------+ 85 teams sv sw ta te tg th tr uk vi wa zh_CN zh_HK zh_TW 178 domains 119 1 3 3 0 10 65 51 155 17 98 7 41 2618 Some counters in the preceding matrix are higher than the number of visible blocks let us expect. This is because a few extra PO files are used for implementing regional variants of languages, or language dialects. For a PO file in the matrix above to be effective, the package to which it applies should also have been internationalized and distributed as such by its maintainer. There might be an observable lag between the mere existence a PO file and its wide availability in a distribution. If June 2010 seems to be old, you may fetch a more recent copy of this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date matrix with full percentage details can be found at `http://translationproject.org/extra/matrix.html'. 1.5 Using `gettext' in new packages =================================== If you are writing a freely available program and want to internationalize it you are welcome to use GNU `gettext' in your package. Of course you have to respect the GNU Library General Public License which covers the use of the GNU `gettext' library. This means in particular that even non-free programs can use `libintl' as a shared library, whereas only free software can use `libintl' as a static library or use modified versions of `libintl'. Once the sources are changed appropriately and the setup can handle the use of `gettext' the only thing missing are the translations. The Free Translation Project is also available for packages which are not developed inside the GNU project. Therefore the information given above applies also for every other Free Software Project. Contact `coordinator@translationproject.org' to make the `.pot' files available to the translation teams. ================================================ FILE: AUTHORS ================================================ Little IO Matt Tytel ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: ChangeLog ================================================ 2014-03-15 Matt Tytel * patches/*: Edited levels of all patches. * configure.ac: Bumped version to 1.4 * src/cursynth_gui.cpp: Updated website to gnu.org/software/cursynth 2014-01-22 gettextize * Makefile.am (ACLOCAL_AMFLAGS): New variable. (EXTRA_DIST): Add config.rpath, m4/ChangeLog. * configure.ac (AC_CONFIG_FILES): Add po/Makefile.in. (AM_GNU_GETTEXT_VERSION): Bump to 0.18.3. Copyright 2013-2015 Matt Tytel Copying and distribution of this file, with or without modification, are promitted provided the copyright notice and this notice are preserved. ================================================ FILE: INSTALL ================================================ Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, these shell commands should configure, build and install this package: $ ./configure $ make $ sudo make install The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX `make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as `configure' are involved. Use GNU `make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. ================================================ FILE: Makefile.am ================================================ SUBDIRS = cJSON rtaudio rtmidi mopo src po doc EXTRA_DIST = config.rpath patches patchesdir = $(pkgdatadir)/patches patches_DATA = $(top_srcdir)/patches/*.mite ACLOCAL_AMFLAGS = -I m4 LDFLAGS += -L/usr/local/opt/gettext/lib CPPFLAGS += -I/usr/local/opt/gettext/include ================================================ FILE: NEWS ================================================ No news is good news. ================================================ FILE: README ================================================ cursynth - a GNU program ------------ cursynth is a polyphonic, MIDI enabled, subtractive synth that runs in your terminal with an ascii interface. cursynth will connect to all MIDI devices and send output to the default audio device on your system. ### Building If you just want to build Cursynth, check out [ftp://ftp.gnu.org/gnu/cursynth] for latest source releases that don't require as much to build. If you want to develop Cursynth, this source is the right place. Run these commands to build the project from source: $ autoreconf -i $ ./configure $ make And then if you want to install, next run: $ sudo make install ### Usage cursynth [--buffer-size OR -b preferred-buffer-size] [--sample-rate OR -s preferred-sample-rate] [--version OR -V] ### Controls * awsedftgyhujkolp;' - a playable keyboard (no key up events) * \`1234567890 - a slider for the current selected control * up/down - previous/next control * left/right - decrement/increment control * F1 (or [shift] + H) - help/controls * [shift] + L - browse/load patches * [shift] + S - save patch * m - arm midi learn * c - erase midi learn ### Requirements: * OS: Mac OSX or GNU/Linux * Terminal: a color enabled terminal with minimum 120x44 ascii characters * If you want key up events, you have to use a MIDI keyboard ### TODO: * Make a LV2 plugin version of cursynth * More modulation sources and destinations * Routable Envelopes * On startup select audio output device * On startup select MIDI input device * Lot's more... Questions? Feature requests? Bugs? email matthewtytel@gmail.com Copyright 2013-2015 Matt Tytel ================================================ FILE: cJSON/LICENSE ================================================ Copyright (c) 2009 Dave Gamble 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. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: cJSON/Makefile.am ================================================ noinst_LIBRARIES = libcJSON.a libcJSON_a_SOURCES = cJSON.c cJSON.h ================================================ FILE: cJSON/cJSON.c ================================================ /* Copyright (c) 2009 Dave Gamble 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. 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. */ /* cJSON */ /* JSON parser in C. */ #include #include #include #include #include #include #include #include "cJSON.h" static const char *ep; const char *cJSON_GetErrorPtr(void) {return ep;} static int cJSON_strcasecmp(const char *s1,const char *s2) { if (!s1) return (s1==s2)?0:1;if (!s2) return 1; for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); } static void *(*cJSON_malloc)(size_t sz) = malloc; static void (*cJSON_free)(void *ptr) = free; static char* cJSON_strdup(const char* str) { size_t len; char* copy; len = strlen(str) + 1; if (!(copy = (char*)cJSON_malloc(len))) return 0; memcpy(copy,str,len); return copy; } void cJSON_InitHooks(cJSON_Hooks* hooks) { if (!hooks) { /* Reset hooks */ cJSON_malloc = malloc; cJSON_free = free; return; } cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; cJSON_free = (hooks->free_fn)?hooks->free_fn:free; } /* Internal constructor. */ static cJSON *cJSON_New_Item(void) { cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); if (node) memset(node,0,sizeof(cJSON)); return node; } /* Delete a cJSON structure. */ void cJSON_Delete(cJSON *c) { cJSON *next; while (c) { next=c->next; if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); if (c->string) cJSON_free(c->string); cJSON_free(c); c=next; } } /* Parse the input text to generate a number, and populate the result into item. */ static const char *parse_number(cJSON *item,const char *num) { double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; if (*num=='-') sign=-1,num++; /* Has sign? */ if (*num=='0') num++; /* is zero */ if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ if (*num=='e' || *num=='E') /* Exponent? */ { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ } n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ item->valuedouble=n; item->valueint=(int)n; item->type=cJSON_Number; return num; } /* Render the number nicely from the given item into a string. */ static char *print_number(cJSON *item) { char *str; double d=item->valuedouble; if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) { str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ if (str) sprintf(str,"%d",item->valueint); } else { str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ if (str) { if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); else sprintf(str,"%f",d); } } return str; } static unsigned parse_hex4(const char *str) { unsigned h=0; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=(h&15)<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=(h&15)<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=(h&15)<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; return h; } /* Parse the input text into an unescaped cstring, and populate item. */ static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; static const char *parse_string(cJSON *item,const char *str) { const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; if (*str!='\"') {ep=str;return 0;} /* not a string! */ while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ if (!out) return 0; ptr=str+1;ptr2=out; while (*ptr!='\"' && *ptr) { if (*ptr!='\\') *ptr2++=*ptr++; else { ptr++; switch (*ptr) { case 'b': *ptr2++='\b'; break; case 'f': *ptr2++='\f'; break; case 'n': *ptr2++='\n'; break; case 'r': *ptr2++='\r'; break; case 't': *ptr2++='\t'; break; case 'u': /* transcode utf16 to utf8. */ uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ { if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ uc2=parse_hex4(ptr+3);ptr+=6; if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); } len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; switch (len) { case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 1: *--ptr2 =(uc | firstByteMark[len]); } ptr2+=len; break; default: *ptr2++=*ptr; break; } ptr++; } } *ptr2=0; if (*ptr=='\"') ptr++; item->valuestring=out; item->type=cJSON_String; return ptr; } /* Render the cstring provided to an escaped version that can be printed. */ static char *print_string_ptr(const char *str) { const char *ptr;char *ptr2,*out;int len=0;unsigned char token; if (!str) return cJSON_strdup(""); ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} out=(char*)cJSON_malloc(len+3); if (!out) return 0; ptr2=out;ptr=str; *ptr2++='\"'; while (*ptr) { if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; else { *ptr2++='\\'; switch (token=*ptr++) { case '\\': *ptr2++='\\'; break; case '\"': *ptr2++='\"'; break; case '\b': *ptr2++='b'; break; case '\f': *ptr2++='f'; break; case '\n': *ptr2++='n'; break; case '\r': *ptr2++='r'; break; case '\t': *ptr2++='t'; break; default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ } } } *ptr2++='\"';*ptr2++=0; return out; } /* Invote print_string_ptr (which is useful) on an item. */ static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} /* Predeclare these prototypes. */ static const char *parse_value(cJSON *item,const char *value); static char *print_value(cJSON *item,int depth,int fmt); static const char *parse_array(cJSON *item,const char *value); static char *print_array(cJSON *item,int depth,int fmt); static const char *parse_object(cJSON *item,const char *value); static char *print_object(cJSON *item,int depth,int fmt); /* Utility to jump whitespace and cr/lf */ static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} /* Parse an object - create a new root, and populate. */ cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) { const char *end=0; cJSON *c=cJSON_New_Item(); ep=0; if (!c) return 0; /* memory fail */ end=parse_value(c,skip(value)); if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} if (return_parse_end) *return_parse_end=end; return c; } /* Default options for cJSON_Parse */ cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} /* Render a cJSON item/entity/structure to text. */ char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} /* Parser core - when encountering text, process appropriately. */ static const char *parse_value(cJSON *item,const char *value) { if (!value) return 0; /* Fail on null. */ if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } if (*value=='\"') { return parse_string(item,value); } if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } if (*value=='[') { return parse_array(item,value); } if (*value=='{') { return parse_object(item,value); } ep=value;return 0; /* failure. */ } /* Render a value to text. */ static char *print_value(cJSON *item,int depth,int fmt) { char *out=0; if (!item) return 0; switch ((item->type)&255) { case cJSON_NULL: out=cJSON_strdup("null"); break; case cJSON_False: out=cJSON_strdup("false");break; case cJSON_True: out=cJSON_strdup("true"); break; case cJSON_Number: out=print_number(item);break; case cJSON_String: out=print_string(item);break; case cJSON_Array: out=print_array(item,depth,fmt);break; case cJSON_Object: out=print_object(item,depth,fmt);break; } return out; } /* Build an array from input text. */ static const char *parse_array(cJSON *item,const char *value) { cJSON *child; if (*value!='[') {ep=value;return 0;} /* not an array! */ item->type=cJSON_Array; value=skip(value+1); if (*value==']') return value+1; /* empty array. */ item->child=child=cJSON_New_Item(); if (!item->child) return 0; /* memory fail */ value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_value(child,skip(value+1))); if (!value) return 0; /* memory fail */ } if (*value==']') return value+1; /* end of array */ ep=value;return 0; /* malformed. */ } /* Render an array to text */ static char *print_array(cJSON *item,int depth,int fmt) { char **entries; char *out=0,*ptr,*ret;int len=5; cJSON *child=item->child; int numentries=0,i=0,fail=0; /* How many entries in the array? */ while (child) numentries++,child=child->next; /* Explicitly handle numentries==0 */ if (!numentries) { out=(char*)cJSON_malloc(3); if (out) strcpy(out,"[]"); return out; } /* Allocate an array to hold the values for each */ entries=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!entries) return 0; memset(entries,0,numentries*sizeof(char*)); /* Retrieve all the results: */ child=item->child; while (child && !fail) { ret=print_value(child,depth+1,fmt); entries[i++]=ret; if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; child=child->next; } /* If we didn't fail, try to malloc the output string */ if (!fail) out=(char*)cJSON_malloc(len); /* If that fails, we fail. */ if (!out) fail=1; /* Handle failure. */ if (fail) { for (i=0;itype=cJSON_Object; value=skip(value+1); if (*value=='}') return value+1; /* empty array. */ item->child=child=cJSON_New_Item(); if (!item->child) return 0; value=skip(parse_string(child,skip(value))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') {ep=value;return 0;} /* fail! */ value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_string(child,skip(value+1))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') {ep=value;return 0;} /* fail! */ value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ if (!value) return 0; } if (*value=='}') return value+1; /* end of array */ ep=value;return 0; /* malformed. */ } /* Render an object to text. */ static char *print_object(cJSON *item,int depth,int fmt) { char **entries=0,**names=0; char *out=0,*ptr,*ret,*str;int len=7,i=0,j; cJSON *child=item->child; int numentries=0,fail=0; /* Count the number of entries. */ while (child) numentries++,child=child->next; /* Explicitly handle empty object case */ if (!numentries) { out=(char*)cJSON_malloc(fmt?depth+4:3); if (!out) return 0; ptr=out;*ptr++='{'; if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; while (child) { names[i]=str=print_string_ptr(child->string); entries[i++]=ret=print_value(child,depth,fmt); if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; child=child->next; } /* Try to allocate the output string */ if (!fail) out=(char*)cJSON_malloc(len); if (!out) fail=1; /* Handle failure */ if (fail) { for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} /* Utility for array list handling. */ static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} /* Utility for handling references. */ static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} /* Add item to array/object. */ void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} /* Replace array/object items with new ones. */ void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} /* Create basic types: */ cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} /* Create Arrays: */ cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} /* Duplication */ cJSON *cJSON_Duplicate(cJSON *item,int recurse) { cJSON *newitem,*cptr,*nptr=0,*newchild; /* Bail on bad ptr */ if (!item) return 0; /* Create new item */ newitem=cJSON_New_Item(); if (!newitem) return 0; /* Copy over all vars */ newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} /* If non-recursive, then we're done! */ if (!recurse) return newitem; /* Walk the ->next chain for the child. */ cptr=item->child; while (cptr) { newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ if (!newchild) {cJSON_Delete(newitem);return 0;} if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ cptr=cptr->next; } return newitem; } void cJSON_Minify(char *json) { char *into=json; while (*json) { if (*json==' ') json++; else if (*json=='\t') json++; // Whitespace characters. else if (*json=='\r') json++; else if (*json=='\n') json++; else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line. else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments. else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive. else *into++=*json++; // All other characters. } *into=0; // and null-terminate. } ================================================ FILE: cJSON/cJSON.h ================================================ /* Copyright (c) 2009 Dave Gamble 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. 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. */ #ifndef cJSON__h #define cJSON__h #ifdef __cplusplus extern "C" { #endif /* cJSON Types: */ #define cJSON_False 0 #define cJSON_True 1 #define cJSON_NULL 2 #define cJSON_Number 3 #define cJSON_String 4 #define cJSON_Array 5 #define cJSON_Object 6 #define cJSON_IsReference 256 /* The cJSON structure: */ typedef struct cJSON { struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ int type; /* The type of the item, as above. */ char *valuestring; /* The item's string, if type==cJSON_String */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's number, if type==cJSON_Number */ char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ } cJSON; typedef struct cJSON_Hooks { void *(*malloc_fn)(size_t sz); void (*free_fn)(void *ptr); } cJSON_Hooks; /* Supply malloc, realloc and free functions to cJSON */ extern void cJSON_InitHooks(cJSON_Hooks* hooks); /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ extern cJSON *cJSON_Parse(const char *value); /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ extern char *cJSON_Print(cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ extern char *cJSON_PrintUnformatted(cJSON *item); /* Delete a cJSON entity and all subentities. */ extern void cJSON_Delete(cJSON *c); /* Returns the number of items in an array (or object). */ extern int cJSON_GetArraySize(cJSON *array); /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); /* Get item "string" from object. Case insensitive. */ extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ extern const char *cJSON_GetErrorPtr(void); /* These calls create a cJSON item of the appropriate type. */ extern cJSON *cJSON_CreateNull(void); extern cJSON *cJSON_CreateTrue(void); extern cJSON *cJSON_CreateFalse(void); extern cJSON *cJSON_CreateBool(int b); extern cJSON *cJSON_CreateNumber(double num); extern cJSON *cJSON_CreateString(const char *string); extern cJSON *cJSON_CreateArray(void); extern cJSON *cJSON_CreateObject(void); /* These utilities create an Array of count items. */ extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); extern cJSON *cJSON_CreateStringArray(const char **strings,int count); /* Append item to the specified array/object. */ extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); /* Remove/Detatch items from Arrays/Objects. */ extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); extern void cJSON_DeleteItemFromArray(cJSON *array,int which); extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); /* Update array items. */ extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will need to be released. With recurse!=0, it will duplicate any children connected to the item. The item->next and ->prev pointers are always zero on return from Duplicate. */ /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); extern void cJSON_Minify(char *json); /* Macros for creating things quickly. */ #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) #ifdef __cplusplus } #endif #endif ================================================ FILE: configure.ac ================================================ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.68]) AC_INIT([cursynth], [1.6], [littleioaudio@gmail.com]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_SILENT_RULES([yes]) AC_CONFIG_SRCDIR([src/cursynth.cpp]) AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CXX([c++ g++ cxx]) AC_PROG_CC AC_PROG_INSTALL AC_PROG_RANLIB AM_PROG_CC_C_O AM_PROG_AR AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.18.3]) # Checks for libraries. AC_CHECK_LIB([intl], [gettext]) AC_CHECK_LIB([ncurses], [curs_set]) AC_CHECK_LIB([pthread], [pthread_create]) # Checks for header files. AC_CHECK_HEADERS([fcntl.h float.h libintl.h limits.h locale.h math.h ncurses.h stddef.h stdlib.h string.h strings.h sys/ioctl.h sys/time.h unistd.h]) # Get flags for rtaudio and rtmidi. AC_CANONICAL_HOST 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(cursynth requires the pthread library!)) ;; *-*-linux*) AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (Mac OSX and GNU/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 (GNU/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 (GNU/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 (GNU/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(cursynth 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!))], ) # Look for Core flag AC_ARG_WITH(core, [ --with-core = choose CoreAudio API support (MAC OSX 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 CoreMIDI -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!)] ) LIBS="$LIBS -framework CoreAudio -framework CoreMIDI -framework CoreFoundation" fi AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(cursynth requires the pthread library!)) ;; *-mingw32*) AC_ARG_WITH(asio, [ --with-asio = choose ASIO API support (Windows 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 (Windows only)], [ api="$api -D__WINDOWS_DS__" AC_MSG_RESULT(using DirectSound) LIBS="-ldsound -lwinmm $LIBS" ], ) # If no audio api flags specified, use DirectSound 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 # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_TYPE_PID_T AC_C_RESTRICT AC_TYPE_SIZE_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_CHECK_TYPES([ptrdiff_t]) # Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_ERROR_AT_LINE AC_FUNC_FORK AC_FUNC_MALLOC AC_CHECK_FUNCS([dup2 floor gettimeofday memset modf pow rmdir strcasecmp strchr strdup strerror]) AC_CONFIG_SUBDIRS([mopo rtaudio rtmidi]) AC_CONFIG_FILES([Makefile po/Makefile.in src/Makefile cJSON/Makefile doc/Makefile]) AC_OUTPUT ================================================ FILE: doc/.gitignore ================================================ # Documentation mdate-sh cursynth.info texinfo.tex version.texi stdin.info *.html ================================================ FILE: doc/Makefile.am ================================================ info_TEXINFOS = cursynth.texi cursynth_TEXINFOS = cursynth.texi ================================================ FILE: doc/cursynth.texi ================================================ \input texinfo @c -*-texinfo-*- @comment $Id@w{$} @comment %**start of header @setfilename cursynth.info @include version.texi @settitle GNU Sample @value{VERSION} @syncodeindex pg cp @comment %**end of header @copying This manual is for GNU Sample (version @value{VERSION}, @value{UPDATED}), which is an example in the Texinfo documentation. Copyright @copyright{} 2013 Free Software Foundation, Inc. @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end quotation @end copying @dircategory Texinfo documentation system @direntry * cursynth: (cursynth)Invoking cursynth. @end direntry @titlepage @title GNU Sample @subtitle for version @value{VERSION}, @value{UPDATED} @author Matt Tytel (@email{matthewtytel@@gmail.com}) @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top GNU Sample This manual is for cursynth (version @value{VERSION}, @value{UPDATED}). cursynth is a music synthesizer. It's polyphonic and MIDI enabled and it runs graphically within a terminal session. @end ifnottex @menu * Invoking cursynth:: * Controls:: * Synthesis:: * Oscillators:: * Filter:: * Amplifier:: * Modulation Matrix:: * Effects:: * Articulation:: * Patches:: * GNU Free Documentation License:: * Index:: @end menu @node Invoking cursynth @chapter Invoking cursynth @pindex cursynth @cindex invoking @command{cursynth} To start cursynth, run: $ cursynth cursynth will take over the current terminal session and show you all the synthesis settings as sliders and text selections on the screen. Start typing on your keyboard's home row to hear cursynth for the first time. Typing F1 or 'H' will show help and information. @node Controls @chapter Controls @pindex cursynth @cindex controls cursynth is MIDI enabled so if you have a working MIDI keyboard or controller, cursynth will respond to note/velocity/etc events and can learn what parameters to change when a knob or slider is moved through MIDI learn. @itemize @bullet @item awsedftgyhujkolp;' - a playable keyboard (no key up events) @item \`1234567890 - a slider for the current selected control @item up/down - previous/next control @item left/right - decrement/increment control @item F1 (or [shift] + H) - help/controls @item [shift] + L - browse/load patches @item [shift] + S - save patch @item m - arm MIDI learn @item c - erase MIDI learn @end itemize @node Synthesis @chapter Synthesis @pindex cursynth @cindex synthesis cursynth uses a very common form of synthesis called *Subtractive Synthesis*. There are three major components in cursynth's synthesis: @itemize @bullet @item Oscillators @item Filter @item Amplifier @end itemize Synthesis starts with the two oscillators that create the initial sound. The output of the two oscillators are mixed together and fed into the envelope controlled filter. The output of the filter is fed into the envelope controlled amplifier. Since cursynth is a polyphonic synthesizer, there is a copy of each component (oscillator, filter, amplifier) for each voice. The output of all the voices are combined into a single output that is fed into the delay line. @node Oscillators @chapter Oscillators @pindex cursynth @cindex oscillators The initial sound creation in cursynth happens in the oscillators. There are two oscillators and you can choose the wave type for each from the 13 waveforms: @itemize @bullet @item sin @item triangle @item square @item down saw @item up saw @item three step @item four step @item eight step @item three pyramid @item five pyramid @item nine pyramid @item white noise @end itemize You can control the second oscillator's transpose relative to the given note and fine tune the frequency using the tune parameter. The two oscillators are hooked into a cross frequency modulation cycle. That is, oscillator 1 modifies the frequency of oscillator 2 and oscillator 2 in turn modifies the frequency of oscillator 1. Cross modulation greatly expands the types of sounds you can get from the oscillators. The output of the two oscillators are mixed together using a ratio definted by the 'oscillator mix' parameter. A value of 0.5 mixes the oscillators equally. A value of 0.0 is only the first oscillator, A value of 1.0 is only oscillator 2. @node Filter @chapter Filter @pindex cursynth @cindex filter cursynth has a resonant filter for each voice. This filter can either be a low-pass or high-pass filter. You can positively or negatively keytrack the filter so that higher notes will have a higher cutoff and lower notes will have a lower cutoff. The filter also has an ADSR envelope that controls the filter cutoff. It can also be routed to other things using the modulation matrix. @node Amplifier @chapter Amplifier @pindex cursynth @cindex amplifier cursynth has an amplifier that is controlled by an ADSR amplifier. You can also velocity track the notes so the faster you hit a note on a MIDI keyboard, the bigger the amplitude of the voice will be. @node Modulation Matrix @chapter Modulation Matrix @pindex cursynth @cindex matrix cursynth has a modulation matrix so you can add custom modulation to cursynth's routing. For example, you can set an LFO as the source and the pitch as a destination with a small modulation scale to create a vibrato. There are five fields for five separate modulation settings. @node Effects @chapter Effects @pindex cursynth @cindex effects cursynth currently has a single delay effect. The audio of all the synth voices are combined into one signal that is passed into the delay effect. You can control the feedback amount and delay time. @node Articulation @chapter Articulation @pindex cursynth @cindex articulation cursynth has a number of controls that change how it acts as a synthesizer. @itemize @bullet @item polyphony @item legato @item portamento @item portamento type @item pitch bend range @end itemize polyphony controls the maximum number of active voices at any time. legato controls what happens when a note overrides a currently active note because we had too many active voices. 'off' will cause the note to retrigger, restarting all oscillators, envelopes and LFOs. 'on' will not retrigger any oscillators, envelopes or LFOs but will set the frequency to the new note. portamento controls how long it takes to slide the frequency from one note to another note. cursynth has a linear (in semitones) slide measured in seconds-per-octave. portamento type controls when the notes slide. When 'off' notes never slide their frequency. When 'on' notes always slide their frequency. When 'auto' notes slide only when the note that is being overriden is still held down on the keyboard, that is will jump to the new frequency immediately if the overriden note was already released. pitch bend range controls how much the pitch wheel affects the pitch of all the voices, measured in semitones. @node Patches @chapter Patches @pindex cursynth @cindex patches cursynth is a synthesizer that can make a variety of different sounds through different settings. You can save to, and load these settings from cursynth patches. To load, press 'L' (shift + 'l'), then select from the patch list. You can test the patch by playing on your keyboard. When you decide on a patch, press enter to finish loading. To save, press 'S' (shift + 's'), then name your patch and hit enter. The patch will save to $HOME/.cursynth/patches/ All cursynth patches have a .mite extensions and there are two locations where cursynth looks for them: @itemize @bullet @item a system data share directory, (usually /usr/local/share/cursynth/patches/) @item $HOME/.cursynth/patches/ @end itemize Patches are also human readable so you can edit them with your text editor if you wish. @node GNU Free Documentation License @appendix GNU Free Documentation License @include fdl.texi @node Index @unnumbered Index @printindex cp @bye ================================================ FILE: doc/fdl.texi ================================================ @cindex FDL, GNU Free Documentation License @center Version 1.2, November 2002 @display Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @end display @enumerate 0 @item PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document @dfn{free} in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of ``copyleft'', which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. @item APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The ``Document'', below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as ``you''. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A ``Modified Version'' of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A ``Secondary Section'' is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The ``Invariant Sections'' are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The ``Cover Texts'' are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A ``Transparent'' copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not ``Transparent'' is called ``Opaque''. Examples of suitable formats for Transparent copies include plain @sc{ascii} without markup, Texinfo input format, La@TeX{} input format, @acronym{SGML} or @acronym{XML} using a publicly available @acronym{DTD}, and standard-conforming simple @acronym{HTML}, PostScript or @acronym{PDF} designed for human modification. Examples of transparent image formats include @acronym{PNG}, @acronym{XCF} and @acronym{JPG}. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, @acronym{SGML} or @acronym{XML} for which the @acronym{DTD} and/or processing tools are not generally available, and the machine-generated @acronym{HTML}, PostScript or @acronym{PDF} produced by some word processors for output purposes only. The ``Title Page'' means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, ``Title Page'' means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. A section ``Entitled XYZ'' means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as ``Acknowledgements'', ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' of such a section when you modify the Document means that it remains a section ``Entitled XYZ'' according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. @item VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. @item COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. @item MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: @enumerate A @item Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. @item List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. @item State on the Title page the name of the publisher of the Modified Version, as the publisher. @item Preserve all the copyright notices of the Document. @item Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. @item Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. @item Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. @item Include an unaltered copy of this License. @item Preserve the section Entitled ``History'', Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled ``History'' in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. @item Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the ``History'' section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. @item For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. @item Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. @item Delete any section Entitled ``Endorsements''. Such a section may not be included in the Modified Version. @item Do not retitle any existing section to be Entitled ``Endorsements'' or to conflict in title with any Invariant Section. @item Preserve any Warranty Disclaimers. @end enumerate If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled ``Endorsements'', provided it contains nothing but endorsements of your Modified Version by various parties---for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. @item COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled ``History'' in the various original documents, forming one section Entitled ``History''; likewise combine any sections Entitled ``Acknowledgements'', and any sections Entitled ``Dedications''. You must delete all sections Entitled ``Endorsements.'' @item COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. @item AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an ``aggregate'' if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. @item TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled ``Acknowledgements'', ``Dedications'', or ``History'', the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. @item TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. @item FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See @uref{http://www.gnu.org/copyleft/}. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License ``or any later version'' applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. @end enumerate @page @appendixsec ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: @smallexample @group Copyright (C) @var{year} @var{your name}. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end group @end smallexample If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the ``with...Texts.'' line with this: @smallexample @group with the Invariant Sections being @var{list their titles}, with the Front-Cover Texts being @var{list}, and with the Back-Cover Texts being @var{list}. @end group @end smallexample If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. @c Local Variables: @c ispell-local-pdict: "ispell-dict" @c End: ================================================ FILE: mopo/.gitignore ================================================ # Compiled Object files *.slo *.lo *.o # Compiled Dynamic libraries *.so *.dylib # Compiled Static libraries *.lai *.la *.a # Executables mopo_test a.out # Autotools ar-lib *.in *.la *.lo *~ .deps/ .libs/ Makefile aclocal.m4 autom4te.cache autoscan.log compile config.guess config.h config.log config.status config.sub configure depcomp install-sh libtool ltmain.sh m4/ missing stamp* *.tar.gz ================================================ FILE: mopo/AUTHORS ================================================ Little IO Matt Tytel ================================================ FILE: mopo/COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: mopo/ChangeLog ================================================ ================================================ FILE: mopo/INSTALL ================================================ Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX `make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as `configure' are involved. Use GNU `make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. ================================================ FILE: mopo/Makefile.am ================================================ SUBDIRS = src ================================================ FILE: mopo/NEWS ================================================ ================================================ FILE: mopo/README ================================================ mopo: MOdular and POlyphonic synthesis library. ----------------------------- mopo is an audio synthesis library aimed at creating modular polyphonic synthesizers. ================================================ FILE: mopo/configure.ac ================================================ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.68]) AC_INIT([mopo], [0.1], [littleioaudio@gmail.com]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_SILENT_RULES(yes) AC_CONFIG_SRCDIR([src/delay.cpp]) # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_RANLIB AM_PROG_AR # Checks for libraries. AC_CHECK_LIB([pthread], [pthread_create]) # Checks for header files. AC_CHECK_HEADERS([limits.h stddef.h stdlib.h string.h strings.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_C_RESTRICT AC_TYPE_SIZE_T AC_CHECK_TYPES([ptrdiff_t]) # Checks for library functions. AC_FUNC_FORK AC_FUNC_MALLOC AC_CHECK_FUNCS([dup2 memset modf pow rmdir strcasecmp strchr strdup strerror]) AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT ================================================ FILE: mopo/src/Makefile.am ================================================ noinst_LIBRARIES = libmopo.a libmopo_a_SOURCES = delay.cpp \ delay.h \ envelope.cpp \ envelope.h \ feedback.cpp \ feedback.h \ filter.cpp \ filter.h \ linear_slope.cpp \ linear_slope.h \ memory.h \ midi_lookup.cpp \ midi_lookup.h \ mono_panner.cpp \ mono_panner.h \ mopo.h \ operators.cpp \ operators.h \ oscillator.cpp \ oscillator.h \ processor.cpp \ processor.h \ processor_router.cpp \ processor_router.h \ send_receive.cpp \ send_receive.h \ smooth_filter.cpp \ smooth_filter.h \ smooth_value.cpp \ smooth_value.h \ step_generator.cpp \ step_generator.h \ tick_router.h \ trigger_operators.cpp \ trigger_operators.h \ value.cpp \ value.h \ utils.h \ voice_handler.cpp \ voice_handler.h \ wave.cpp \ wave.h ================================================ FILE: mopo/src/delay.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "delay.h" namespace mopo { Delay::Delay() : Processor(Delay::kNumInputs, 1) { } void Delay::process() { for (int i = 0; i < buffer_size_; ++i) outputs_[0]->buffer[i] = tick(i); } } // namespace mopo ================================================ FILE: mopo/src/delay.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef DELAY_H #define DELAY_H #include "memory.h" #include "processor.h" namespace mopo { // A signal delay processor with wet/dry, delay time and feedback controls. // Handles fractional delay amounts through interpolation. class Delay : public Processor { public: enum Inputs { kAudio, kWet, kDelayTime, kFeedback, kNumInputs }; Delay(); virtual Processor* clone() const { return new Delay(*this); } virtual void process(); protected: mopo_float tick(int i) { mopo_float input = inputs_[kAudio]->at(i); mopo_float wet = inputs_[kWet]->at(i); mopo_float period = inputs_[kDelayTime]->at(i) * sample_rate_; mopo_float feedback = inputs_[kFeedback]->at(i); mopo_float read = memory_.get(period); memory_.push(input + read * feedback); return INTERPOLATE(input, read, wet); } Memory memory_; }; } // namespace mopo #endif // DELAY_H ================================================ FILE: mopo/src/envelope.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "envelope.h" #include #define KILL_TIME 0.02 namespace mopo { Envelope::Envelope() : Processor(kNumInputs, kNumOutputs), state_(kReleasing), current_value_(0), decay_decay_(0), release_decay_(0) { } void Envelope::trigger(mopo_float event, int offset) { if (event == kVoiceOn) state_ = kKilling; else if (event == kVoiceOff) { state_ = kReleasing; outputs_[kFinished]->trigger(kVoiceOff, offset); } else if (event == kVoiceReset) { state_ = kAttacking; current_value_ = 0.0; } } void Envelope::process() { outputs_[kFinished]->clearTrigger(); // Only update decay and release rate once per buffer. mopo_float decay_samples = sample_rate_ * inputs_[kDecay]->at(buffer_size_ - 1); decay_decay_ = pow(CLOSE_ENOUGH, 1.0 / decay_samples); mopo_float release_samples = sample_rate_ * inputs_[kRelease]->at(buffer_size_ - 1); release_decay_ = pow(CLOSE_ENOUGH, 1.0 / release_samples); int i = 0; if (inputs_[kTrigger]->source->triggered) { int trigger_offset = inputs_[kRelease]->source->trigger_offset; for (; i < trigger_offset; ++i) outputs_[kValue]->buffer[i] = tick(i); trigger(inputs_[kTrigger]->source->trigger_value, trigger_offset); } for (; i < buffer_size_; ++i) outputs_[kValue]->buffer[i] = tick(i); } inline mopo_float Envelope::tick(int i) { if (state_ == kAttacking) { if (inputs_[kAttack]->at(i) <= 0) current_value_ = 1; else { mopo_float change = 1.0 / (sample_rate_ * inputs_[kAttack]->at(i)); current_value_ = CLAMP(current_value_ + change, 0, 1); } if (current_value_ >= 1) state_ = kDecaying; } else if (state_ == kDecaying) { current_value_ = INTERPOLATE(inputs_[kSustain]->at(i), current_value_, decay_decay_); } else if (state_ == kKilling) { current_value_ -= CLAMP(1 / (KILL_TIME * sample_rate_), 0, 1); if (current_value_ <= 0) { outputs_[kFinished]->trigger(kVoiceReset, i); state_ = kAttacking; } } else if (state_ == kReleasing) current_value_ *= release_decay_; return current_value_; } } // namespace mopo ================================================ FILE: mopo/src/envelope.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef ENVELOPE_H #define ENVELOPE_H #include "processor.h" #define CLOSE_ENOUGH (1.0 / 256.0) namespace mopo { // An ADSR (Attack, Decay, Sustain, Release) Envelope. // The attack is a linear scale and completes in the exact amount of time. // The decay and release are exponential and get down to the _CLOSE_ENOUGH_ // level in the specified amount of time. // // The reason for this is that technically the decay and release // take an extremely long time to finish because they are exponential. But // users are used to specifying the amount of time the decay or release take // so we make this compromise of _CLOSE_ENOUGH_. class Envelope : public Processor { public: enum Inputs { kAttack, kDecay, kSustain, kRelease, kTrigger, kNumInputs }; enum Outputs { kValue, kFinished, kNumOutputs }; enum State { kAttacking, kDecaying, kReleasing, kKilling, }; Envelope(); virtual Processor* clone() const { return new Envelope(*this); } void process(); void trigger(mopo_float event, int offset); private: mopo_float tick(int i); State state_; mopo_float current_value_; mopo_float decay_decay_; mopo_float release_decay_; }; } // namespace mopo #endif // ENVELOPE_H ================================================ FILE: mopo/src/feedback.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "feedback.h" #include "processor_router.h" namespace mopo { void Feedback::process() { memcpy(buffer_, inputs_[0]->source->buffer, buffer_size_ * sizeof(mopo_float)); refreshOutput(); } void Feedback::refreshOutput() { memcpy(outputs_[0]->buffer, buffer_, MAX_BUFFER_SIZE * sizeof(mopo_float)); } } // namespace mopo ================================================ FILE: mopo/src/feedback.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef FEEDBACK_H #define FEEDBACK_H #include "processor.h" namespace mopo { // A special processor for the purpose of feedback loops in the signal flow. // Feedback can be used for batch buffer feedback processing or sample by // sample feedback processing. class Feedback : public Processor { public: Feedback() : Processor(1, 1) { } virtual Processor* clone() const { return new Feedback(*this); } virtual void process(); virtual void refreshOutput(); inline void tick(int i) { buffer_[i] = inputs_[0]->source->buffer[i]; } inline void tickBeginRefreshOutput() { outputs_[0]->buffer[0] = buffer_[buffer_size_ - 1]; } inline void tickRefreshOutput(int i) { MOPO_ASSERT(i > 0 && i < buffer_size_); outputs_[0]->buffer[i] = buffer_[i - 1]; } protected: mopo_float buffer_[MAX_BUFFER_SIZE]; }; } // namespace mopo #endif // FEEDBACK_H ================================================ FILE: mopo/src/filter.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "filter.h" #include namespace mopo { Filter::Filter() : Processor(Filter::kNumInputs, 1) { current_type_ = kNumTypes; current_cutoff_ = 0.0; current_resonance_ = 0.0; in_0_ = in_1_ = in_2_ = 0.0; out_0_ = out_1_ = 0.0; past_in_1_ = past_in_2_ = past_out_1_ = past_out_2_ = 0.0; } void Filter::process() { current_type_ = static_cast(inputs_[kType]->at(0)); computeCoefficients(current_type_, inputs_[kCutoff]->at(0), inputs_[kResonance]->at(0)); int i = 0; if (inputs_[kReset]->source->triggered && inputs_[kReset]->source->trigger_value == kVoiceReset) { int trigger_offset = inputs_[kReset]->source->trigger_offset; for (; i < trigger_offset; ++i) outputs_[0]->buffer[i] = tick(i); reset(); } for (; i < buffer_size_; ++i) outputs_[0]->buffer[i] = tick(i); } inline mopo_float Filter::tick(int i) { mopo_float input = inputs_[kAudio]->at(i); mopo_float cutoff = inputs_[kCutoff]->at(i); mopo_float resonance = inputs_[kResonance]->at(i); if (cutoff != current_cutoff_ || resonance != current_resonance_) computeCoefficients(current_type_, cutoff, resonance); mopo_float out = input * in_0_ + past_in_1_ * in_1_ + past_in_2_ * in_2_ - past_out_1_ * out_0_ - past_out_2_ * out_1_; past_in_2_ = past_in_1_; past_in_1_ = input; past_out_2_ = past_out_1_; past_out_1_ = out; return out; } inline void Filter::reset() { past_in_1_ = past_in_2_ = past_out_1_ = past_out_2_ = 0; } inline void Filter::computeCoefficients(Type type, mopo_float cutoff, mopo_float resonance) { mopo_float sf = 1.0 / tan(PI * cutoff / sample_rate_); mopo_float sf_squared = sf * sf; mopo_float norm = 1.0 + 1.0 / resonance * sf + sf_squared; switch(type) { case kLP12: { in_2_ = in_0_ = 1.0 / norm; in_1_ = 2.0 / norm; out_0_ = 2.0 * (1.0 - sf_squared) / norm; out_1_ = (1.0 - 1.0 / resonance * sf + sf_squared) / norm; break; } case kHP12: { in_2_ = in_0_ = sf_squared / norm; in_1_ = -2.0 * sf_squared / norm; out_0_ = 2.0 * (1.0 - sf_squared) / norm; out_1_ = (1.0 - 1.0 / resonance * sf + sf_squared) / norm; break; } case kBP12: { in_2_ = in_0_ = sf / norm; in_1_ = 0.0; out_0_ = 2.0 * (1.0 - sf_squared) / norm; out_1_ = (1.0 - 1.0 / resonance * sf + sf_squared) / norm; break; } case kAP12: { in_0_ = 1.0 / norm; in_1_ = -2.0 * (1.0 - sf_squared) / norm; in_2_ = (1.0 - 1.0 / resonance * sf + sf_squared) / norm; out_0_ = -in_1_; out_1_ = in_2_; break; } default: in_2_ = in_1_ = in_0_ = out_1_ = out_0_ = 0.0; } current_cutoff_ = cutoff; current_resonance_ = resonance; } } // namespace mopo ================================================ FILE: mopo/src/filter.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef FILTER_H #define FILTER_H #include "processor.h" namespace mopo { // 12 dB per octave filters. There is a low pass filter, high pass filter, // band pass filter, and all pass filter. class Filter : public Processor { public: enum Inputs { kAudio, kType, kCutoff, kResonance, kReset, kNumInputs }; enum Type { kLP12, kHP12, kBP12, kAP12, kNumTypes, }; Filter(); virtual Processor* clone() const { return new Filter(*this); } virtual void process(); private: mopo_float tick(int i); void reset(); void computeCoefficients(Type type, mopo_float cutoff, mopo_float resonance); Type current_type_; mopo_float current_cutoff_, current_resonance_; mopo_float in_0_, in_1_, in_2_; mopo_float out_0_, out_1_; mopo_float past_in_1_, past_in_2_; mopo_float past_out_1_, past_out_2_; }; } // namespace mopo #endif // FILTER_H ================================================ FILE: mopo/src/linear_slope.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "linear_slope.h" #include "utils.h" #include namespace mopo { LinearSlope::LinearSlope() : Processor(kNumInputs, 1) { last_value_ = 0.0; } void LinearSlope::process() { int i = 0; if (inputs_[kTriggerJump]->source->triggered) { int trigger_offset = inputs_[kTriggerJump]->source->trigger_offset; for (; i < trigger_offset; ++i) { last_value_ = tick(i); outputs_[0]->buffer[i] = last_value_; } last_value_ = inputs_[kTarget]->at(i); } for (; i < buffer_size_; ++i) { last_value_ = tick(i); outputs_[0]->buffer[i] = last_value_; } } inline mopo_float LinearSlope::tick(int i) { mopo_float target = inputs_[kTarget]->at(i); if (utils::closeToZero(inputs_[kRunSeconds]->at(i))) return inputs_[kTarget]->at(i); mopo_float increment = 1.0 / (sample_rate_ * inputs_[kRunSeconds]->at(0)); if (target <= last_value_) return CLAMP(last_value_ - increment, target, last_value_); return CLAMP(last_value_ + increment, last_value_, target); } } // namespace mopo ================================================ FILE: mopo/src/linear_slope.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef LINEAR_SLOPE_H #define LINEAR_SLOPE_H #include "value.h" namespace mopo { // A processor that will slope to the target value over a given amount of // time. Useful for portamento or smoothing out values. class LinearSlope : public Processor { public: enum Inputs { kTarget, kRunSeconds, kTriggerJump, kNumInputs }; LinearSlope(); virtual Processor* clone() const { return new LinearSlope(*this); } virtual void process(); private: mopo_float tick(int i); mopo_float last_value_; }; } // namespace mopo #endif // LINEAR_SLOPE_H ================================================ FILE: mopo/src/memory.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef MEMORY_H #define MEMORY_H #include "mopo.h" #include #include #include // MAX_MEMORY must be a power of 2. #define MAX_MEMORY 1048576 #define MEMORY_BITMASK 1048575 namespace mopo { // A processor utility to store a stream of data for later lookup. class Memory { public: Memory() : offset_(0) { memset(memory_, 0, MAX_MEMORY * sizeof(mopo_float)); } inline void push(mopo_float sample) { offset_ = (offset_ + 1) & MEMORY_BITMASK; memory_[offset_] = sample; } inline mopo_float getIndex(int index) const { return memory_[(offset_ - index) & MEMORY_BITMASK]; } inline mopo_float get(mopo_float past) const { double float_index; mopo_float sample_fraction = modf(past, &float_index); int index = std::max(float_index, 1); // TODO(mtytel): Quadratic or all-pass interpolation is better. mopo_float from = getIndex(index - 1); mopo_float to = getIndex(index); return INTERPOLATE(from, to, sample_fraction); } protected: mopo_float memory_[MAX_MEMORY]; unsigned int offset_; }; } // namespace mopo #endif // MEMORY_H ================================================ FILE: mopo/src/midi_lookup.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "midi_lookup.h" namespace mopo { const MidiLookupSingleton MidiLookup::lookup_; } // namespace mopo ================================================ FILE: mopo/src/midi_lookup.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef MIDI_LOOKUP_H #define MIDI_LOOKUP_H #include "mopo.h" #include #define MIDI_0_FREQUENCY 8.1757989156 #define NOTES_PER_OCTAVE 12 #define CENTS_PER_NOTE 100 #define MAX_CENTS (MIDI_SIZE * CENTS_PER_NOTE) namespace mopo { class MidiLookupSingleton { public: MidiLookupSingleton() { mopo_float cents_per_octave = CENTS_PER_NOTE * NOTES_PER_OCTAVE; for (int i = 0; i <= MAX_CENTS; ++i) { frequency_lookup_[i] = MIDI_0_FREQUENCY * pow(2, i / cents_per_octave); } } mopo_float centsLookup(mopo_float cents_from_0) const { if (cents_from_0 >= MAX_CENTS) return frequency_lookup_[MAX_CENTS]; if (cents_from_0 <= 0) return frequency_lookup_[0]; int full_cents = cents_from_0; mopo_float fraction_cents = cents_from_0 - full_cents; return INTERPOLATE(frequency_lookup_[full_cents], frequency_lookup_[full_cents + 1], fraction_cents); } private: mopo_float frequency_lookup_[MAX_CENTS + 1]; }; class MidiLookup { public: static mopo_float centsLookup(mopo_float cents_from_0) { return lookup_.centsLookup(cents_from_0); } private: static const MidiLookupSingleton lookup_; }; } // namespace mopo #endif // MIDI_LOOKUP_H ================================================ FILE: mopo/src/mono_panner.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "mono_panner.h" #include "mopo.h" #include "wave.h" #include #define LEFT_ROTATION 100.325 #define RIGHT_ROTATION 100.125 namespace mopo { MonoPanner::MonoPanner() : Processor(MonoPanner::kNumInputs, MonoPanner::kNumOutputs) { } void MonoPanner::process() { double integral; for (int i = 0; i < buffer_size_; ++i) { mopo_float audio = inputs_[kAudio]->at(i); mopo_float pan = inputs_[kPan]->at(i); mopo_float left_gain = Wave::fullsin(modf(pan + LEFT_ROTATION, &integral)); mopo_float right_gain = Wave::fullsin(modf(pan + RIGHT_ROTATION, &integral)); outputs_[kLeft]->buffer[i] = audio * left_gain; outputs_[kRight]->buffer[i] = audio * right_gain; } } } // namespace mopo ================================================ FILE: mopo/src/mono_panner.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef MONO_PANNER_H #define MONO_PANNER_H #include "processor.h" namespace mopo { // A processor that will split a single signal into two signals, left and // right. The amplitude of the two outputs is set by the pan amount, ranging // from all the way left, all the way right, or equally split. class MonoPanner : public Processor { public: enum Inputs { kAudio, kPan, kNumInputs }; enum Outputs { kLeft, kRight, kNumOutputs }; MonoPanner(); virtual Processor* clone() const { return new MonoPanner(*this); } virtual void process(); }; } // namespace mopo #endif // MONO_PANNER_H ================================================ FILE: mopo/src/mopo.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef MOPO_H #define MOPO_H // Utilities. #define UNUSED(x) (void)(x) #define CLAMP(v, s, b) ((v) < (s) ? (s) : ((v) > (b) ? (b) : (v))) #define INTERPOLATE(s, e, f) ((s) + (f) * ((e) - (s))) // Debugging. #define DEBUG #ifdef DEBUG #include #define MOPO_ASSERT(x) assert(x) #else #define MOPO_ASSERT(x) (void)(x) #endif // DEBUG namespace mopo { const double PI = 3.1415926535897932384626433832795; const int MAX_BUFFER_SIZE = 4096; const int DEFAULT_BUFFER_SIZE = 64; const int DEFAULT_SAMPLE_RATE = 44100; const int MIDI_SIZE = 128; const int PPQ = 15360; // Pulses per quarter note. typedef double mopo_float; // Common types of events across different Processors. enum VoiceEvent { kVoiceOff, // Stop. (e.g. release in an envelope) kVoiceOn, // Start. (e.g. start attack in an envelope) kVoiceReset, // Reset. Immediately reset to initial state. }; } // namespace mopo #endif // MOPO_H ================================================ FILE: mopo/src/operators.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "operators.h" namespace mopo { void Operator::process() { for (int i = 0; i < buffer_size_; ++i) tick(i); } void VariableAdd::process() { memset(outputs_[0]->buffer, 0, buffer_size_ * sizeof(mopo_float)); int num_inputs = inputs_.size(); for (int i = 0; i < num_inputs; ++i) { if (inputs_[i]->source != &Processor::null_source_) { for (int s = 0; s < buffer_size_; ++s) outputs_[0]->buffer[s] += inputs_[i]->at(s); } } } } // namespace mopo ================================================ FILE: mopo/src/operators.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef OPERATORS_H #define OPERATORS_H #include "midi_lookup.h" #include "processor.h" namespace mopo { // A base class for arithmetic operators. class Operator : public Processor { public: Operator(int num_inputs, int num_outputs) : Processor(num_inputs, num_outputs) { } virtual void process(); virtual void tick(int i) = 0; }; // A processor that will clamp a signal output to a given window. class Clamp : public Operator { public: Clamp(mopo_float min = -1, mopo_float max = 1) : Operator(1, 1), min_(min), max_(max) { } virtual Processor* clone() const { return new Clamp(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = CLAMP(inputs_[0]->at(i), min_, max_); } private: mopo_float min_, max_; }; // A processor that will invert a signal. class Negate : public Operator { public: Negate() : Operator(1, 1) { } virtual Processor* clone() const { return new Negate(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = -inputs_[0]->at(i); } }; // A processor that will scale a signal by a given scalar. class LinearScale : public Operator { public: LinearScale(mopo_float scale = 1) : Operator(1, 1), scale_(scale) { } virtual Processor* clone() const { return new LinearScale(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = scale_ * inputs_[0]->at(i); } private: mopo_float scale_; }; // A processor that will convert a stream of midi to a stream of frequencies. class MidiScale : public Operator { public: MidiScale() : Operator(1, 1) { } virtual Processor* clone() const { return new MidiScale(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = MidiLookup::centsLookup(CENTS_PER_NOTE * inputs_[0]->at(i)); } }; // A processor that will add two streams together. class Add : public Operator { public: Add() : Operator(2, 1) { } virtual Processor* clone() const { return new Add(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = inputs_[0]->at(i) + inputs_[1]->at(i); } }; // A processor that will add a variable number of strings together. class VariableAdd : public Operator { public: VariableAdd(int num_inputs) : Operator(num_inputs, 1) { } virtual Processor* clone() const { return new VariableAdd(*this); } void process(); inline void tick(int i) { int num_inputs = inputs_.size(); outputs_[0]->buffer[i] = 0.0; for (int input = 0; input < num_inputs; ++input) outputs_[0]->buffer[i] += inputs_[input]->at(i); } }; // A processor that will subtract one stream from another. class Subtract : public Operator { public: Subtract() : Operator(2, 1) { } virtual Processor* clone() const { return new Subtract(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = inputs_[0]->at(i) - inputs_[1]->at(i); } }; // A processor that will multiply to streams together. class Multiply : public Operator { public: Multiply() : Operator(2, 1) { } virtual Processor* clone() const { return new Multiply(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = inputs_[0]->at(i) * inputs_[1]->at(i); } }; // A processor that will interpolate two streams by an interpolation stream. class Interpolate : public Operator { public: enum Inputs { kFrom, kTo, kFractional, kNumInputs }; Interpolate() : Operator(kNumInputs, 1) { } virtual Processor* clone() const { return new Interpolate(*this); } inline void tick(int i) { outputs_[0]->buffer[i] = INTERPOLATE(inputs_[kFrom]->at(i), inputs_[kTo]->at(i), inputs_[kFractional]->at(i)); } }; } // namespace mopo #endif // OPERATORS_H ================================================ FILE: mopo/src/oscillator.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "oscillator.h" #include namespace mopo { Oscillator::Oscillator() : Processor(kNumInputs, 1), offset_(0.0), waveform_(Wave::kSin) { } void Oscillator::preprocess() { waveform_ = static_cast(inputs_[kWaveform]->at(0)); } void Oscillator::process() { preprocess(); int i = 0; if (inputs_[kReset]->source->triggered && inputs_[kReset]->source->trigger_value == kVoiceReset) { int trigger_offset = inputs_[kReset]->source->trigger_offset; for (; i < trigger_offset; ++i) tick(i); offset_ = 0.0; } for (; i < buffer_size_; ++i) tick(i); } } // namespace mopo ================================================ FILE: mopo/src/oscillator.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef OSCILLATOR_H #define OSCILLATOR_H #include "processor.h" #include "wave.h" namespace mopo { // A processor that produces an oscillation stream based on the input // frequency, phase, and waveform. You can reset the waveform stream using // the reset input. class Oscillator : public Processor { public: enum Inputs { kFrequency, kPhase, kWaveform, kReset, kNumInputs }; Oscillator(); virtual Processor* clone() const { return new Oscillator(*this); } void preprocess(); void process(); inline void tick(int i) { mopo_float frequency = inputs_[kFrequency]->at(i); mopo_float phase = inputs_[kPhase]->at(i); offset_ += frequency / sample_rate_; double integral; offset_ = modf(offset_, &integral); outputs_[0]->buffer[i] = Wave::blwave(waveform_, offset_ + phase, frequency); } protected: mopo_float offset_; Wave::Type waveform_; }; } // namespace mopo #endif // OSCILLATOR_H ================================================ FILE: mopo/src/processor.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "processor.h" #include "processor_router.h" namespace mopo { const Processor::Output Processor::null_source_; Processor::Processor(int num_inputs, int num_outputs) : sample_rate_(DEFAULT_SAMPLE_RATE), buffer_size_(DEFAULT_BUFFER_SIZE), router_(0) { for (int i = 0; i < num_inputs; ++i) { Input* input = new Input(); // All inputs start off with null input. input->source = &Processor::null_source_; registerInput(input); } for (int i = 0; i < num_outputs; ++i) { Output* output = new Output(); // All outputs are owned by this Processor. output->owner = this; registerOutput(output); } } void Processor::plug(const Output* source) { plug(source, 0); } void Processor::plug(const Output* source, unsigned int input_index) { MOPO_ASSERT(input_index < inputs_.size()); MOPO_ASSERT(source); inputs_[input_index]->source = source; if (router_) router_->connect(this, source, input_index); } void Processor::plug(const Processor* source) { plug(source, 0); } void Processor::plug(const Processor* source, unsigned int input_index) { plug(source->output(), input_index); } void Processor::plugNext(const Output* source) { for (size_t i = 0; i < inputs_.size(); ++i) { if (inputs_[i]->source == &Processor::null_source_) { plug(source, i); return; } } } void Processor::plugNext(const Processor* source) { plugNext(source->output()); } void Processor::unplugIndex(unsigned int input_index) { inputs_[input_index]->source = &Processor::null_source_; } void Processor::unplug(const Output* source) { for (unsigned int i = 0; i < inputs_.size(); ++i) { if (inputs_[i]->source == source) inputs_[i]->source = &Processor::null_source_; } } void Processor::unplug(const Processor* source) { for (unsigned int i = 0; i < inputs_.size(); ++i) { if (inputs_[i]->source->owner == source) inputs_[i]->source = &Processor::null_source_; } } void Processor::registerInput(Input* input) { inputs_.push_back(input); if (router_ && input->source != &Processor::null_source_) router_->connect(this, input->source, inputs_.size() - 1); } void Processor::registerOutput(Output* output) { outputs_.push_back(output); } Processor::Input* Processor::input(unsigned int index) const { MOPO_ASSERT(index < inputs_.size()); return inputs_[index]; } Processor::Output* Processor::output(unsigned int index) const { MOPO_ASSERT(index < outputs_.size()); return outputs_[index]; } } // namespace mopo ================================================ FILE: mopo/src/processor.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef PROCESSOR_H #define PROCESSOR_H #include "mopo.h" #include #include namespace mopo { class ProcessorRouter; class Processor { public: // An output port from the Processor. struct Output { Output() { owner = 0; clearBuffer(); clearTrigger(); } void trigger(mopo_float value, int offset = 0) { triggered = true; trigger_offset = offset; trigger_value = value; } void clearTrigger() { triggered = false; trigger_offset = 0; trigger_value = 0.0; } void clearBuffer() { memset(buffer, 0, MAX_BUFFER_SIZE * sizeof(mopo_float)); } const Processor* owner; mopo_float buffer[MAX_BUFFER_SIZE]; bool triggered; int trigger_offset; mopo_float trigger_value; }; // An input port to the Processor. You can plug an Output into on of // these inputs. struct Input { Input() { source = 0; } const Output* source; mopo_float at(int i) const { return source->buffer[i]; } }; Processor(int num_inputs, int num_outputs); // Currently need to override this boiler plate clone. // TODO(mtytel): Should probably make a macro for this. virtual Processor* clone() const = 0; // Subclasses override this for main processing code. virtual void process() = 0; // Subclasses should override this if they need to adjust for change in // sample rate. virtual void setSampleRate(int sample_rate) { sample_rate_ = sample_rate; } virtual void setBufferSize(int buffer_size) { buffer_size_ = buffer_size; } // Attaches an output to an input in this processor. void plug(const Output* source); void plug(const Output* source, unsigned int input_index); void plug(const Processor* source); void plug(const Processor* source, unsigned int input_index); // Attaches an output to the first available input in this processor. void plugNext(const Output* source); void plugNext(const Processor* source); // Remove a connection between two processors. virtual void unplugIndex(unsigned int input_index); virtual void unplug(const Output* source); virtual void unplug(const Processor* source); // Sets the ProcessorRouter that will own this Processor. void router(ProcessorRouter* router) { router_ = router; } // Returns the ProcessorRouter that owns this Processor. const ProcessorRouter* router() const { return router_; } virtual void registerInput(Input* input); virtual void registerOutput(Output* output); virtual int numInputs() const { return inputs_.size(); } virtual int numOutputs() const { return outputs_.size(); } // Returns the Input port corresponding to the passed in index. Input* input(unsigned int index = 0) const; // Returns the Output port corresponding to the passed in index. Output* output(unsigned int index = 0) const; protected: int sample_rate_; int buffer_size_; std::vector inputs_; std::vector outputs_; ProcessorRouter* router_; static const Output null_source_; }; } // namespace mopo #endif // PROCESSOR_H ================================================ FILE: mopo/src/processor_router.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "processor_router.h" #include "feedback.h" #include "mopo.h" #include #include namespace mopo { ProcessorRouter::ProcessorRouter(int num_inputs, int num_outputs) : Processor(num_inputs, num_outputs) { order_ = new std::vector(); feedback_order_ = new std::vector(); } ProcessorRouter::ProcessorRouter(const ProcessorRouter& original) : Processor(original), order_(original.order_), feedback_order_(original.feedback_order_) { size_t num_processors = order_->size(); for (size_t i = 0; i < num_processors; ++i) { const Processor* next = order_->at(i); processors_[next] = next->clone(); } size_t num_feedbacks = feedback_order_->size(); for (size_t i = 0; i < num_feedbacks; ++i) { const Feedback* next = feedback_order_->at(i); feedback_processors_[next] = new Feedback(*next); } } void ProcessorRouter::process() { updateAllProcessors(); // First make sure all the Feedback loops are ready to be read. int num_feedbacks = feedback_order_->size(); for (int i = 0; i < num_feedbacks; ++i) feedback_processors_[feedback_order_->at(i)]->refreshOutput(); // Run all the main processors. int num_processors = order_->size(); for (int i = 0; i < num_processors; ++i) processors_[order_->at(i)]->process(); // Store the outputs into the Feedback objects for next time. for (int i = 0; i < num_feedbacks; ++i) feedback_processors_[feedback_order_->at(i)]->process(); MOPO_ASSERT(num_processors != 0); } void ProcessorRouter::setSampleRate(int sample_rate) { Processor::setSampleRate(sample_rate); updateAllProcessors(); int num_processors = order_->size(); for (int i = 0; i < num_processors; ++i) processors_[order_->at(i)]->setSampleRate(sample_rate); int num_feedbacks = feedback_order_->size(); for (int i = 0; i < num_feedbacks; ++i) feedback_processors_[feedback_order_->at(i)]->setSampleRate(sample_rate); } void ProcessorRouter::setBufferSize(int buffer_size) { Processor::setBufferSize(buffer_size); updateAllProcessors(); int num_processors = order_->size(); for (int i = 0; i < num_processors; ++i) processors_[order_->at(i)]->setBufferSize(buffer_size); int num_feedbacks = feedback_order_->size(); for (int i = 0; i < num_feedbacks; ++i) feedback_processors_[feedback_order_->at(i)]->setBufferSize(buffer_size); } void ProcessorRouter::addProcessor(Processor* processor) { MOPO_ASSERT(processor->router() == NULL || processor->router() == this); processor->router(this); order_->push_back(processor); processors_[processor] = processor; for (int i = 0; i < processor->numInputs(); ++i) connect(processor, processor->input(i)->source, i); } void ProcessorRouter::removeProcessor(const Processor* processor) { MOPO_ASSERT(processor->router() == this); std::vector::iterator pos = std::find(order_->begin(), order_->end(), processor); MOPO_ASSERT(pos != order_->end()); order_->erase(pos, pos + 1); processors_.erase(processor); } void ProcessorRouter::connect(Processor* destination, const Output* source, int index) { if (isDownstream(destination, source->owner)) { // We are introducing a cycle so insert a Feedback node. Feedback* feedback = new Feedback(); feedback->plug(source); destination->plug(feedback, index); addFeedback(feedback); } else { // Not introducing a cycle so just make sure _destination_ is in order. reorder(destination); } } void ProcessorRouter::reorder(Processor* processor) { // Get all the dependencies inside this router. std::set dependencies = getDependencies(processor); // Stably reorder putting dependencies first. std::vector new_order; new_order.reserve(order_->size()); int num_processors = processors_.size(); // First put the dependencies. for (int i = 0; i < num_processors; ++i) { if (order_->at(i) != processor && dependencies.find(order_->at(i)) != dependencies.end()) { new_order.push_back(order_->at(i)); } } // Then the processor if it is in this router. if (processors_.find(processor) != processors_.end()) new_order.push_back(processor); // Then the remaining processors. for (int i = 0; i < num_processors; ++i) { if (order_->at(i) != processor && dependencies.find(order_->at(i)) == dependencies.end()) { new_order.push_back(order_->at(i)); } } MOPO_ASSERT(new_order.size() == processors_.size()); (*order_) = new_order; // Make sure our parent is ordered as well. if (router_) router_->reorder(processor); } bool ProcessorRouter::isDownstream(const Processor* first, const Processor* second) { std::set dependencies = getDependencies(second); return dependencies.find(first) != dependencies.end(); } bool ProcessorRouter::areOrdered(const Processor* first, const Processor* second) { const Processor* first_context = getContext(first); const Processor* second_context = getContext(second); if (first_context && second_context) { size_t num_processors = order_->size(); for (size_t i = 0; i < num_processors; ++i) { if (order_->at(i) == first_context) return true; else if (order_->at(i) == second_context) return false; } } else if (router_) return router_->areOrdered(first, second); return true; } void ProcessorRouter::addFeedback(Feedback* feedback) { feedback->router(this); feedback_order_->push_back(feedback); feedback_processors_[feedback] = feedback; } void ProcessorRouter::updateAllProcessors() { size_t num_processors = order_->size(); for (int i = 0; i < num_processors; ++i) { const Processor* next = order_->at(i); if (processors_.find(next) == processors_.end()) processors_[next] = next->clone(); } size_t num_feedbacks = feedback_order_->size(); for (int i = 0; i < num_feedbacks; ++i) { const Feedback* next = feedback_order_->at(i); if (feedback_processors_.find(next) == feedback_processors_.end()) feedback_processors_[next] = new Feedback(*next); } } const Processor* ProcessorRouter::getContext(const Processor* processor) { const Processor* context = processor; while (context && processors_.find(context) == processors_.end()) context = context->router(); return context; } std::set ProcessorRouter::getDependencies( const Processor* processor) { std::vector inputs; std::set visited; std::set dependencies; const Processor* context = getContext(processor); inputs.push_back(processor); for (size_t i = 0; i < inputs.size(); ++i) { // Find the parent that is inside this router. const Processor* dependency = getContext(inputs[i]); // If _inputs[i]_ has an ancestor in this context, then it is a // dependency. If it is outside this router context, we don't need to // check its inputs. if (dependency) { dependencies.insert(dependency); for (int j = 0; j < inputs[i]->numInputs(); ++j) { const Input* input = inputs[i]->input(j); if (input->source && input->source->owner && visited.find(input->source->owner) == visited.end()) { inputs.push_back(input->source->owner); visited.insert(input->source->owner); } } } } // Make sure our context isn't listed as a dependency. dependencies.erase(context); return dependencies; } } // namespace mopo ================================================ FILE: mopo/src/processor_router.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef PROCESSOR_ROUTER_H #define PROCESSOR_ROUTER_H #include "processor.h" #include #include #include namespace mopo { class Feedback; class ProcessorRouter : public Processor { public: ProcessorRouter(int num_inputs = 0, int num_outputs = 0); ProcessorRouter(const ProcessorRouter& original); virtual Processor* clone() const { return new ProcessorRouter(*this); } virtual void process(); virtual void setSampleRate(int sample_rate); virtual void setBufferSize(int buffer_size); virtual void addProcessor(Processor* processor); virtual void removeProcessor(const Processor* processor); // Any time new dependencies are added into the ProcessorRouter graph, we // should call _connect_ on the destination Processor and source Output. void connect(Processor* destination, const Output* source, int index); bool isDownstream(const Processor* first, const Processor* second); bool areOrdered(const Processor* first, const Processor* second); protected: // When we create a cycle into the ProcessorRouter graph, we must insert // a Feedback node and add it here. virtual void addFeedback(Feedback* feedback); // Makes sure _processor_ runs in a topologically sorted order in // relation to all other Processors in _this_. void reorder(Processor* processor); // Ensures we have all copies of all processors and feedback processors. virtual void updateAllProcessors(); // Returns the ancestor of _processor_ which is a child of _this_. // Returns NULL if _processor_ is not a descendant of _this_. const Processor* getContext(const Processor* processor); std::set getDependencies(const Processor* processor); std::vector* order_; std::map processors_; std::vector* feedback_order_; std::map feedback_processors_; }; } // namespace mopo #endif // PROCESSOR_ROUTER_H ================================================ FILE: mopo/src/send_receive.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "send_receive.h" #include "processor_router.h" namespace mopo { Send::Send() : Processor(1, 1) { memory_output_ = new MemoryOutput(); memory_output_->owner = this; memory_output_->memory = &memory_; } void Send::process() { for (int i = 0; i < buffer_size_; ++i) memory_.push(inputs_[0]->at(i)); memory_output_->memory = &memory_; } Receive::Receive() : Processor(kNumInputs, 1) { memory_input_ = new MemoryInput(); memory_input_->owner = this; } void Receive::process() { mopo_float adjust = buffer_size_; if (router_ && !router_->areOrdered(memory_input_->source->owner, memory_input_->owner)) { adjust = 0; } for (int i = 0; i < buffer_size_; ++i) { mopo_float time = inputs_[kDelayTime]->at(i) * sample_rate_ + adjust; outputs_[0]->buffer[i] = memory_input_->get(time); adjust -= 1.0; } } void Receive::setSend(const Send* send, bool dependent) { memory_input_->source = send->memory_output(); if (dependent) plug(send, kDependent); } } // namespace mopo ================================================ FILE: mopo/src/send_receive.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef SEND_RECEIVE_H #define SEND_RECEIVE_H #include "memory.h" #include "processor.h" // TODO(mtytel): This classes don't work with polyphony. Fix that. namespace mopo { class Send; class Receive; struct MemoryOutput { MemoryOutput() : owner(0), memory(0) { } const Send* owner; Memory* memory; inline mopo_float get(mopo_float past) const { return memory->get(past); } }; struct MemoryInput { MemoryInput() : owner(0), source(0) { } const Receive* owner; const MemoryOutput* source; inline mopo_float get(mopo_float past) const { return source->get(past); } }; class Send : public Processor { public: Send(); virtual Processor* clone() const { return new Send(*this); } virtual void process(); inline mopo_float get(mopo_float past) const { return memory_.get(past); } const MemoryOutput* memory_output() const { return memory_output_; } protected: Memory memory_; MemoryOutput* memory_output_; }; class Receive : public Processor { public: enum Inputs { kDependent, kDelayTime, kNumInputs }; Receive(); virtual Processor* clone() const { return new Receive(*this); } virtual void process(); void setSend(const Send* send, bool dependent = true); protected: MemoryInput* memory_input_; }; } // namespace mopo #endif // SEND_RECEIVE_H ================================================ FILE: mopo/src/smooth_filter.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "smooth_filter.h" namespace mopo { SmoothFilter::SmoothFilter() : Processor(SmoothFilter::kNumInputs, 1) { last_value_ = 0.0; } void SmoothFilter::process() { mopo_float decay = inputs_[kDecay]->at(0); for (int i = 0; i < buffer_size_; ++i) { mopo_float target = inputs_[kTarget]->at(i); last_value_ = INTERPOLATE(last_value_, target, decay); outputs_[0]->buffer[i] = last_value_; } } } // namespace mopo ================================================ FILE: mopo/src/smooth_filter.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef SMOOTH_FILTER_H #define SMOOTH_FILTER_H #include "processor.h" namespace mopo { class SmoothFilter : public Processor { public: enum Inputs { kTarget, kDecay, kTriggerJump, kNumInputs }; SmoothFilter(); virtual Processor* clone() const { return new SmoothFilter(*this); } virtual void process(); private: mopo_float last_value_; }; } // namespace mopo #endif // FILTER_H ================================================ FILE: mopo/src/smooth_value.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "smooth_value.h" #include #define SMOOTH_CUTOFF 3.0 namespace mopo { SmoothValue::SmoothValue(mopo_float value) : Value(value), target_value_(value) { } void SmoothValue::setSampleRate(int sample_rate) { sample_rate_ = sample_rate; decay_ = 1 - exp(-2.0 * PI * SMOOTH_CUTOFF / sample_rate_); } void SmoothValue::process() { for (int i = 0; i < buffer_size_; ++i) outputs_[0]->buffer[i] = tick(); } inline mopo_float SmoothValue::tick() { value_ = INTERPOLATE(value_, target_value_, decay_); return value_; } } // namespace mopo ================================================ FILE: mopo/src/smooth_value.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef SMOOTH_VALUE_H #define SMOOTH_VALUE_H #include "value.h" namespace mopo { class SmoothValue : public Value { public: SmoothValue(mopo_float value = 0.0); virtual Processor* clone() const { return new SmoothValue(*this); } virtual void process(); virtual void setSampleRate(int sample_rate); void set(mopo_float value) { target_value_ = value; } void setHard(mopo_float value) { Value::set(value); target_value_ = value; } private: mopo_float tick(); mopo_float target_value_; mopo_float decay_; }; } // namespace mopo #endif // SMOOTH_VALUE_H ================================================ FILE: mopo/src/step_generator.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "step_generator.h" #include namespace mopo { StepGenerator::StepGenerator(int max_steps) : Processor(kNumInputs + max_steps, 1), offset_(0.0), current_step_(0) { } void StepGenerator::process() { unsigned int num_steps = static_cast(inputs_[kNumSteps]->at(0)); int i = 0; if (inputs_[kReset]->source->triggered && inputs_[kReset]->source->trigger_value == kVoiceReset) i = inputs_[kReset]->source->trigger_offset; mopo_float total = 0.0; for (; i < buffer_size_; ++i) total += inputs_[kFrequency]->at(i); total /= sample_rate_; double integral; offset_ = modf(offset_ + total, &integral); current_step_ = (current_step_ + num_steps) % num_steps; for (i = 0; i < buffer_size_; ++i) outputs_[0]->buffer[i] = inputs_[kSteps + current_step_]->at(i); } } // namespace mopo ================================================ FILE: mopo/src/step_generator.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef STEP_GENERATOR_H #define STEP_GENERATOR_H #include "processor.h" #define DEFAULT_MAX_STEPS 128 namespace mopo { class StepGenerator : public Processor { public: enum Inputs { kFrequency, kNumSteps, kReset, kSteps, kNumInputs }; StepGenerator(int max_steps = DEFAULT_MAX_STEPS); virtual Processor* clone() const { return new StepGenerator(*this); } void process(); protected: mopo_float offset_; unsigned int current_step_; }; } // namespace mopo #endif // STEP_GENERATOR_H ================================================ FILE: mopo/src/tick_router.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef TICK_ROUTER_H #define TICK_ROUTER_H #include "processor_router.h" namespace mopo { class TickRouter : public ProcessorRouter { public: TickRouter(int num_inputs = 0, int num_outputs = 0) : ProcessorRouter(num_inputs, num_outputs) { } virtual void process() = 0; virtual void tick(int i) = 0; }; } // namespace mopo #endif // PROCESSOR_ROUTER_H ================================================ FILE: mopo/src/trigger_operators.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "trigger_operators.h" namespace mopo { TriggerCombiner::TriggerCombiner() : Processor(2, 1) { } void TriggerCombiner::process() { outputs_[0]->clearTrigger(); if (inputs_[0]->source->triggered) { outputs_[0]->trigger(inputs_[0]->source->trigger_value, inputs_[0]->source->trigger_offset); } else if (inputs_[1]->source->triggered) { outputs_[0]->trigger(inputs_[1]->source->trigger_value, inputs_[1]->source->trigger_offset); } } TriggerWait::TriggerWait() : Processor(kNumInputs, 1) { } void TriggerWait::waitTrigger(mopo_float trigger_value) { waiting_ = true; trigger_value_ = trigger_value; } void TriggerWait::sendTrigger(int trigger_offset) { if (waiting_) outputs_[0]->trigger(trigger_value_, trigger_offset); waiting_ = false; } void TriggerWait::process() { outputs_[0]->clearTrigger(); if (inputs_[kWait]->source->triggered && inputs_[kTrigger]->source->triggered) { if (inputs_[kWait]->source->trigger_offset <= inputs_[kTrigger]->source->trigger_offset) { waitTrigger(inputs_[kWait]->source->trigger_value); sendTrigger(inputs_[kTrigger]->source->trigger_offset); } else { sendTrigger(inputs_[kTrigger]->source->trigger_offset); waitTrigger(inputs_[kWait]->source->trigger_value); } } else if (inputs_[kWait]->source->triggered) waitTrigger(inputs_[kWait]->source->trigger_value); else if (inputs_[kTrigger]->source->triggered) sendTrigger(inputs_[kTrigger]->source->trigger_offset); } LegatoFilter::LegatoFilter() : Processor(kNumInputs, kNumOutputs), last_value_(kVoiceOff) { } void LegatoFilter::process() { outputs_[kRetrigger]->clearTrigger(); outputs_[kRemain]->clearTrigger(); if (!inputs_[kTrigger]->source->triggered) return; if (inputs_[kTrigger]->source->trigger_value == kVoiceOn && last_value_ == kVoiceOn && inputs_[kLegato]->at(0)) { outputs_[kRemain]->trigger(inputs_[kTrigger]->source->trigger_value, inputs_[kTrigger]->source->trigger_offset); } else { outputs_[kRetrigger]->trigger(inputs_[kTrigger]->source->trigger_value, inputs_[kTrigger]->source->trigger_offset); } last_value_ = inputs_[kTrigger]->source->trigger_value; } PortamentoFilter::PortamentoFilter() : Processor(kNumInputs, 1), last_value_(kVoiceOff) { } void PortamentoFilter::process() { outputs_[0]->clearTrigger(); if (!inputs_[kTrigger]->source->triggered) return; int state = static_cast(inputs_[kPortamento]->at(0)); if (inputs_[kTrigger]->source->trigger_value != kVoiceOff) { if (state == kPortamentoOff || (state == kPortamentoAuto && last_value_ == kVoiceOff)) { outputs_[0]->trigger(inputs_[kTrigger]->source->trigger_value, inputs_[kTrigger]->source->trigger_offset); } } last_value_ = inputs_[kTrigger]->source->trigger_value; } } // namespace mopo ================================================ FILE: mopo/src/trigger_operators.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef TRIGGER_OPERATORS_H #define TRIGGER_OPERATORS_H #include "processor.h" namespace mopo { class TriggerCombiner : public Processor { public: TriggerCombiner(); virtual Processor* clone() const { return new TriggerCombiner(*this); } void process(); }; class TriggerWait : public Processor { public: enum Inputs { kWait, kTrigger, kNumInputs }; TriggerWait(); virtual Processor* clone() const { return new TriggerWait(*this); } void process(); private: void waitTrigger(mopo_float trigger_value); void sendTrigger(int trigger_offset); bool waiting_; mopo_float trigger_value_; }; class LegatoFilter : public Processor { public: enum Inputs { kLegato, kTrigger, kNumInputs }; enum Outputs { kRetrigger, kRemain, kNumOutputs }; LegatoFilter(); virtual Processor* clone() const { return new LegatoFilter(*this); } void process(); private: mopo_float last_value_; }; class PortamentoFilter : public Processor { public: enum Inputs { kPortamento, kTrigger, kNumInputs }; enum State { kPortamentoOff, kPortamentoAuto, kPortamentoOn, kNumPortamentoStates, }; PortamentoFilter(); virtual Processor* clone() const { return new PortamentoFilter(*this); } void process(); private: mopo_float last_value_; }; } // namespace mopo #endif // TRIGGER_OPERATORS_H ================================================ FILE: mopo/src/utils.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef UTILS_H #define UTILS_H #include "mopo.h" #define EPSILON 0.000000000001 namespace mopo { namespace utils { inline bool closeToZero(mopo_float value) { return value <= EPSILON && value >= -EPSILON; } inline bool isSilent(const mopo_float* buffer, int length) { for (int i = 0; i < length; ++i) { if (!closeToZero(buffer[i])) return false; } return true; } } // namespace utils } // namespace mopo #endif // UTILS_H ================================================ FILE: mopo/src/value.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "value.h" namespace mopo { Value::Value(mopo_float value) : Processor(kNumInputs, 1), value_(value) { for (int i = 0; i < MAX_BUFFER_SIZE; ++i) outputs_[0]->buffer[i] = value_; } void Value::process() { if (outputs_[0]->buffer[0] == value_ && outputs_[0]->buffer[buffer_size_ - 1] == value_ && !inputs_[kSet]->source->triggered) { return; } int i = 0; if (inputs_[kSet]->source->triggered) { int trigger_offset = inputs_[kSet]->source->trigger_offset; for (; i < trigger_offset; ++i) outputs_[0]->buffer[i] = value_; value_ = inputs_[kSet]->source->trigger_value; } for (; i < buffer_size_; ++i) outputs_[0]->buffer[i] = value_; } void Value::set(mopo_float value) { value_ = value; for (int i = 0; i < buffer_size_; ++i) outputs_[0]->buffer[i] = value_; } } // namespace mopo ================================================ FILE: mopo/src/value.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef VALUE_H #define VALUE_H #include "processor.h" namespace mopo { class Value : public Processor { public: enum Inputs { kSet, kNumInputs }; Value(mopo_float value = 0.0); virtual Processor* clone() const { return new Value(*this); } virtual void process(); mopo_float value() const { return value_; } virtual void set(mopo_float value); protected: mopo_float value_; }; } // namespace mopo #endif // VALUE_H ================================================ FILE: mopo/src/voice_handler.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "voice_handler.h" #include "utils.h" namespace mopo { Voice::Voice(Processor* processor) : processor_(processor) { } VoiceHandler::VoiceHandler(size_t polyphony) : Processor(kNumInputs, 1), polyphony_(0), sustain_(false), voice_output_(0), voice_killer_(0) { setPolyphony(polyphony); } void VoiceHandler::prepareVoiceTriggers(Voice* voice) { note_.clearTrigger(); velocity_.clearTrigger(); voice_event_.clearTrigger(); if (voice->hasNewEvent()) { voice_event_.trigger(voice->state()->event); if (voice->state()->event == kVoiceOn) { note_.trigger(voice->state()->note); velocity_.trigger(voice->state()->velocity); } voice->clearEvent(); } } void VoiceHandler::processVoice(Voice* voice) { voice->processor()->process(); for (int i = 0; i < buffer_size_; ++i) outputs_[0]->buffer[i] += voice_output_->buffer[i]; } void VoiceHandler::process() { global_router_.process(); size_t polyphony = static_cast(inputs_[kPolyphony]->at(0)); setPolyphony(CLAMP(polyphony, 1, polyphony)); memset(outputs_[0]->buffer, 0, buffer_size_ * sizeof(mopo_float)); std::list::iterator iter = active_voices_.begin(); while (iter != active_voices_.end()) { Voice* voice = *iter; prepareVoiceTriggers(voice); processVoice(voice); // Remove voice if the right processor has a full silent buffer. if (voice_killer_ && voice->state()->event != kVoiceOn && utils::isSilent(voice_killer_->buffer, buffer_size_)) { free_voices_.push_back(voice); iter = active_voices_.erase(iter); } else iter++; } } void VoiceHandler::setSampleRate(int sample_rate) { Processor::setSampleRate(sample_rate); voice_router_.setSampleRate(sample_rate); global_router_.setSampleRate(sample_rate); std::set::iterator iter = all_voices_.begin(); for (; iter != all_voices_.end(); ++iter) (*iter)->processor()->setSampleRate(sample_rate); } void VoiceHandler::setBufferSize(int buffer_size) { Processor::setBufferSize(buffer_size); voice_router_.setBufferSize(buffer_size); global_router_.setBufferSize(buffer_size); std::set::iterator iter = all_voices_.begin(); for (; iter != all_voices_.end(); ++iter) (*iter)->processor()->setBufferSize(buffer_size); } void VoiceHandler::sustainOn() { sustain_ = true; } void VoiceHandler::sustainOff() { sustain_ = false; std::list::iterator iter = sustained_voices_.begin(); for (; iter != sustained_voices_.end(); ++iter) { Voice* voice = *iter; voice->deactivate(); } sustained_voices_.clear(); } void VoiceHandler::noteOn(mopo_float note, mopo_float velocity) { Voice* voice = 0; pressed_notes_.push_back(note); if (free_voices_.size() && active_voices_.size() < polyphony_) { voice = free_voices_.front(); free_voices_.pop_front(); } else { voice = active_voices_.front(); active_voices_.pop_front(); } voice->activate(note, velocity); active_voices_.push_back(voice); } void VoiceHandler::noteOff(mopo_float note) { std::list::iterator note_iter = pressed_notes_.begin(); while (note_iter != pressed_notes_.end()) { if (*note_iter == note) note_iter = pressed_notes_.erase(note_iter); else note_iter++; } std::list::iterator iter = active_voices_.begin(); for (; iter != active_voices_.end(); ++iter) { Voice* voice = *iter; if (voice->state()->note == note) { if (sustain_) sustained_voices_.push_back(voice); else { if (polyphony_ == 1 && pressed_notes_.size()) voice->activate(pressed_notes_.back(), voice->state()->velocity); else voice->deactivate(); } } } } void VoiceHandler::setPolyphony(size_t polyphony) { while (all_voices_.size() < polyphony) { Voice* new_voice = createVoice(); all_voices_.insert(new_voice); free_voices_.push_back(new_voice); } while (active_voices_.size() > polyphony) { active_voices_.front()->deactivate(); active_voices_.pop_front(); } polyphony_ = polyphony; } void VoiceHandler::addProcessor(Processor* processor) { voice_router_.addProcessor(processor); } void VoiceHandler::addGlobalProcessor(Processor* processor) { global_router_.addProcessor(processor); } Voice* VoiceHandler::createVoice() { return new Voice(voice_router_.clone()); } } // namespace mopo ================================================ FILE: mopo/src/voice_handler.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef VOICE_HANDLER_H #define VOICE_HANDLER_H #include "processor_router.h" #include "value.h" #include #include namespace mopo { struct VoiceState { VoiceEvent event; mopo_float note; mopo_float velocity; }; class Voice { public: Voice(Processor* voice); Processor* processor() { return processor_; } const VoiceState* state() { return &state_; } void activate(mopo_float note, mopo_float velocity) { new_event_ = true; state_.event = kVoiceOn; state_.note = note; state_.velocity = velocity; } void deactivate() { new_event_ = true; state_.event = kVoiceOff; } bool hasNewEvent() { return new_event_; } void clearEvent() { new_event_ = false; } private: bool new_event_; VoiceState state_; Processor* processor_; }; class VoiceHandler : public Processor { public: enum Inputs { kPolyphony, kNumInputs }; VoiceHandler(size_t polyphony = 1); virtual Processor* clone() const { MOPO_ASSERT(false); return NULL; } virtual void process(); virtual void setSampleRate(int sample_rate); virtual void setBufferSize(int buffer_size); void noteOn(mopo_float note, mopo_float velocity = 1); void noteOff(mopo_float note); void sustainOn(); void sustainOff(); Output* voice_event() { return &voice_event_; } Output* note() { return ¬e_; } Output* velocity() { return &velocity_; } void addProcessor(Processor* processor); void addGlobalProcessor(Processor* processor); void setPolyphony(size_t polyphony); void setVoiceOutput(const Output* output) { voice_output_ = output; } void setVoiceOutput(const Processor* output) { setVoiceOutput(output->output()); } void setVoiceKiller(const Output* killer) { voice_killer_ = killer; } void setVoiceKiller(const Processor* killer) { setVoiceKiller(killer->output()); } private: Voice* createVoice(); void prepareVoiceTriggers(Voice* voice); void processVoice(Voice* voice); size_t polyphony_; bool sustain_; const Output* voice_output_; const Output* voice_killer_; Output voice_event_; Output note_; Output velocity_; std::list pressed_notes_; std::set all_voices_; std::list free_voices_; std::list sustained_voices_; std::list active_voices_; ProcessorRouter voice_router_; ProcessorRouter global_router_; }; } // namespace mopo #endif // VOICE_HANDLER_H ================================================ FILE: mopo/src/wave.cpp ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #include "wave.h" namespace mopo { const WaveLookup Wave::lookup_; } // namespace mopo ================================================ FILE: mopo/src/wave.h ================================================ /* Copyright 2013-2015 Matt Tytel * * mopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mopo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mopo. If not, see . */ #pragma once #ifndef WAVE_H #define WAVE_H #include "mopo.h" #include #include #define LOOKUP_SIZE 2048 #define HIGH_FREQUENCY 20000 #define MAX_HARMONICS 100 namespace mopo { class WaveLookup { public: WaveLookup() { // Sin lookup table. for (int i = 0; i < LOOKUP_SIZE + 1; ++i) sin_[i] = sin((2 * PI * i) / LOOKUP_SIZE); // Square lookup table. for (int i = 0; i < LOOKUP_SIZE + 1; ++i) { int p = i; mopo_float scale = 4.0 / PI; square_[0][i] = scale * sin_[p]; for (int h = 1; h < MAX_HARMONICS; ++h) { p = (p + i) % LOOKUP_SIZE; square_[h][i] = square_[h - 1][i]; if (h % 2 == 0) square_[h][i] += scale * sin_[p] / (h + 1); } } // Saw lookup table. for (int i = 0; i < LOOKUP_SIZE + 1; ++i) { int index = (i + (LOOKUP_SIZE / 2)) % LOOKUP_SIZE; int p = i; mopo_float scale = 2.0 / PI; saw_[0][index] = scale * sin_[p]; for (int h = 1; h < MAX_HARMONICS; ++h) { p = (p + i) % LOOKUP_SIZE; mopo_float harmonic = scale * sin_[p] / (h + 1); if (h % 2 == 0) saw_[h][index] = saw_[h - 1][index] + harmonic; else saw_[h][index] = saw_[h - 1][index] - harmonic; } } // Triangle lookup table. for (int i = 0; i < LOOKUP_SIZE + 1; ++i) { int p = i; mopo_float scale = 8.0 / (PI * PI); triangle_[0][i] = scale * sin_[p]; for (int h = 1; h < MAX_HARMONICS; ++h) { p = (p + i) % LOOKUP_SIZE; triangle_[h][i] = triangle_[h - 1][i]; mopo_float harmonic = scale * sin_[p] / ((h + 1) * (h + 1)); if (h % 4 == 0) triangle_[h][i] += harmonic; else if (h % 2 == 0) triangle_[h][i] -= harmonic; } } } inline mopo_float fullsin(mopo_float t) const { double integral; mopo_float fractional = modf(t * LOOKUP_SIZE, &integral); int index = integral; return INTERPOLATE(sin_[index], sin_[index + 1], fractional); } inline mopo_float square(mopo_float t, int harmonics) const { double integral; mopo_float fractional = modf(t * LOOKUP_SIZE, &integral); int index = integral; return INTERPOLATE(square_[harmonics][index], square_[harmonics][index + 1], fractional); } inline mopo_float upsaw(mopo_float t, int harmonics) const { double integral; mopo_float fractional = modf(t * LOOKUP_SIZE, &integral); int index = integral; return INTERPOLATE(saw_[harmonics][index], saw_[harmonics][index + 1], fractional); } inline mopo_float downsaw(mopo_float t, int harmonics) const { return -upsaw(t, harmonics); } inline mopo_float triangle(mopo_float t, int harmonics) const { double integral; mopo_float fractional = modf(t * LOOKUP_SIZE, &integral); int index = integral; return INTERPOLATE(triangle_[harmonics][index], triangle_[harmonics][index + 1], fractional); } template inline mopo_float step(mopo_float t, int harmonics) const { return (1.0 * steps) / (steps - 1) * (upsaw(t, harmonics) + downsaw(steps * t, harmonics / steps) / steps); } template inline mopo_float pyramid(mopo_float t, int harmonics) const { size_t squares = steps - 1; mopo_float phase_increment = 1.0 / (2.0 * squares); mopo_float phase = 0.5 + t; mopo_float out = 0.0; double integral; for (size_t i = 0; i < squares; ++i) { out += square(modf(phase, &integral), harmonics); phase += phase_increment; } out /= squares; return out; } private: // Make them 1 larger for wrapping. mopo_float sin_[LOOKUP_SIZE + 1]; mopo_float square_[MAX_HARMONICS][LOOKUP_SIZE + 1]; mopo_float saw_[MAX_HARMONICS][LOOKUP_SIZE + 1]; mopo_float triangle_[MAX_HARMONICS][LOOKUP_SIZE + 1]; }; class Wave { public: enum Type { kSin, kTriangle, kSquare, kDownSaw, kUpSaw, kThreeStep, kFourStep, kEightStep, kThreePyramid, kFivePyramid, kNinePyramid, kWhiteNoise, kNumWaveforms }; static inline mopo_float blwave(Type waveform, mopo_float t, mopo_float frequency) { if (fabs(frequency) < 1) return wave(waveform, t); int harmonics = HIGH_FREQUENCY / fabs(frequency) - 1; if (harmonics >= MAX_HARMONICS) return wave(waveform, t); switch (waveform) { case kSin: return lookup_.fullsin(t); case kTriangle: return lookup_.triangle(t, harmonics); case kSquare: return lookup_.square(t, harmonics); case kDownSaw: return lookup_.downsaw(t, harmonics); case kUpSaw: return lookup_.upsaw(t, harmonics); case kThreeStep: return lookup_.step<3>(t, harmonics); case kFourStep: return lookup_.step<4>(t, harmonics); case kEightStep: return lookup_.step<8>(t, harmonics); case kThreePyramid: return lookup_.pyramid<3>(t, harmonics); case kFivePyramid: return lookup_.pyramid<5>(t, harmonics); case kNinePyramid: return lookup_.pyramid<9>(t, harmonics); default: return wave(waveform, t); } } static inline mopo_float wave(Type waveform, mopo_float t) { switch (waveform) { case kSin: return fullsin(t); case kSquare: return square(t); case kTriangle: return triangle(t); case kDownSaw: return downsaw(t); case kUpSaw: return upsaw(t); case kThreeStep: return step<3>(t); case kFourStep: return step<4>(t); case kEightStep: return step<8>(t); case kThreePyramid: return pyramid<3>(t); case kFivePyramid: return pyramid<5>(t); case kNinePyramid: return pyramid<9>(t); case kWhiteNoise: return whitenoise(); default: return 0; } } static inline mopo_float nullwave() { return 0; } static inline mopo_float whitenoise() { return (2.0 * rand()) / RAND_MAX - 1; } static inline mopo_float fullsin(mopo_float t) { return lookup_.fullsin(t); } static inline mopo_float square(mopo_float t) { return t < 0.5 ? 1 : -1; } static inline mopo_float triangle(mopo_float t) { double integral; return fabsf(2.0f - 4.0f * modf(t + 0.75f, &integral)) - 1; } static inline mopo_float downsaw(mopo_float t) { return -upsaw(t); } static inline mopo_float upsaw(mopo_float t) { return t * 2 - 1; } static inline mopo_float hannwave(mopo_float t) { return 0.5f * (1.0f - cosf(2.0f * PI * t)); } template static inline mopo_float step(mopo_float t) { mopo_float section = (int)(steps * t); return 2 * section / (steps - 1) - 1; } template static inline mopo_float pyramid(mopo_float t) { size_t squares = steps - 1; mopo_float phase_increment = 1.0 / (2.0 * squares); mopo_float phase = 0.5 + t; mopo_float out = 0.0; double integral; for (size_t i = 0; i < squares; ++i) { out += square(modf(phase, &integral)); phase += phase_increment; } out /= squares; return out; } protected: static const WaveLookup lookup_; }; } // namespace mopo #endif // WAVE_H ================================================ FILE: patches/bit_sin.mite ================================================ { "amp attack": 0.890625, "amp decay": 0.679688, "amp release": 0.534376, "amp sustain": 0, "cross modulation": 0, "cutoff": 81.171875, "delay dry/wet": 0.421875, "delay feedback": -0.518750, "delay time": 0.706094, "fil attack": 0, "fil decay": 0.093750, "fil env depth": 0, "fil release": 3, "fil sustain": 0.601562, "filter type": 0, "keytrack": 1, "legato": 0, "lfo 1 frequency": 10, "lfo 1 waveform": 2, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 4, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.500000, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 3, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 0, "osc 2 transpose": 24, "osc 2 tune": 0.345625, "osc 2 waveform": 0, "osc mix": 0.445313, "pitch bend range": 4, "polyphony": 12, "portamento": 0.027188, "portamento type": 0, "resonance": 0.500000, "velocity track": 0.300000, "volume": 0.554688 } ================================================ FILE: patches/bwang.mite ================================================ { "amp attack": 0, "amp decay": 2.153125, "amp release": 0.300000, "amp sustain": 0, "cross modulation": 0, "cutoff": 46.367188, "delay dry/wet": 0.182812, "delay feedback": -0.253125, "delay time": 0.137344, "fil attack": 0, "fil decay": 2.175000, "fil env depth": 42, "fil release": 0.159375, "fil sustain": 0.281250, "filter type": 0, "keytrack": 0.609375, "legato": 0, "lfo 1 frequency": 2, "lfo 1 waveform": 0, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 0, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.010000, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 0, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 5, "osc 2 transpose": -12, "osc 2 tune": 0.173750, "osc 2 waveform": 7, "osc mix": 0.484375, "pitch bend range": 2, "polyphony": 17, "portamento": 0.010000, "portamento type": 0, "resonance": 7.984375, "velocity track": 0.300000, "volume": 0.232813 } ================================================ FILE: patches/default.mite ================================================ { "amp attack": 0.010000, "amp decay": 0.700000, "amp release": 0.300000, "amp sustain": 0, "cross modulation": 0.000000, "cutoff": 92, "delay dry/wet": 0.300000, "delay feedback": -0.300000, "delay time": 0.060000, "fil attack": 0, "fil decay": 0.300000, "fil env depth": 12, "fil release": 0.300000, "fil sustain": 0, "filter type": 0, "keytrack": 0, "legato": 0, "lfo 1 frequency": 2, "lfo 1 waveform": 0, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 0, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.010000, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 0, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 3, "osc 2 transpose": -12, "osc 2 tune": 0.080000, "osc 2 waveform": 3, "osc mix": 0.500000, "pitch bend range": 2, "polyphony": 1, "portamento": 0.010000, "portamento type": 0, "resonance": 3, "velocity track": 0.300000, "volume": 0.600000 } ================================================ FILE: patches/loaded_room.mite ================================================ { "amp attack": 0.010000, "amp decay": 2.437500, "amp release": 0.300000, "amp sustain": 0, "cross modulation": 0.189063, "cutoff": 80.593750, "delay dry/wet": 0.500000, "delay feedback": -0.503125, "delay time": 0.149219, "fil attack": 0, "fil decay": 2.835938, "fil env depth": 48, "fil release": 0.300000, "fil sustain": 0, "filter type": 0, "keytrack": 0, "legato": 0, "lfo 1 frequency": 8.953125, "lfo 1 waveform": 0, "lfo 2 frequency": 2.031250, "lfo 2 waveform": 2, "mod destination 1": 3, "mod destination 2": 2, "mod destination 3": 4, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.718750, "mod scale 2": 0.135000, "mod scale 3": 0.500000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 3, "mod source 2": 3, "mod source 3": 4, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 6, "osc 2 transpose": -12, "osc 2 tune": 0.080000, "osc 2 waveform": 7, "osc mix": 0.375000, "pitch bend range": 2, "polyphony": 10, "portamento": 0.010000, "portamento type": 0, "resonance": 3.445312, "velocity track": 0.300000, "volume": 0.279688 } ================================================ FILE: patches/test_patch1.mite ================================================ { "amp attack": 0.408438, "amp decay": 0.723437, "amp release": 0.229688, "amp sustain": 0, "cross modulation": 0, "cutoff": 63.382812, "delay dry/wet": 0.300000, "delay feedback": -0.409375, "delay time": 0.060000, "fil attack": 0.468750, "fil decay": 0.300000, "fil env depth": 46, "fil release": 0.300000, "fil sustain": 0, "filter type": 0, "keytrack": 0, "legato": 0, "lfo 1 frequency": 2, "lfo 1 waveform": 0, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 0, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.010000, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 0, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 4, "osc 2 transpose": -12, "osc 2 tune": 0.080000, "osc 2 waveform": 4, "osc mix": 0.500000, "pitch bend range": 2, "polyphony": 12, "portamento": 0.010000, "portamento type": 0, "resonance": 4.359375, "velocity track": 0.300000, "volume": 0.404688 } ================================================ FILE: patches/test_patch2.mite ================================================ { "amp attack": 0, "amp decay": 0.160937, "amp release": 0.229688, "amp sustain": 0, "cross modulation": 0, "cutoff": 63.382812, "delay dry/wet": 0.675000, "delay feedback": -0.596875, "delay time": 0.353906, "fil attack": 0, "fil decay": 0.159375, "fil env depth": 46, "fil release": 0.300000, "fil sustain": 0, "filter type": 0, "keytrack": 0, "legato": 0, "lfo 1 frequency": 2, "lfo 1 waveform": 0, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 0, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.010000, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 0, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 4, "osc 2 transpose": 12, "osc 2 tune": 0.345625, "osc 2 waveform": 4, "osc mix": 0.500000, "pitch bend range": 2, "polyphony": 12, "portamento": 0.010000, "portamento type": 0, "resonance": 4.359375, "velocity track": 0.300000, "volume": 0.584375 } ================================================ FILE: patches/water_wobble.mite ================================================ { "amp attack": 0.281250, "amp decay": 1.262500, "amp release": 0.300000, "amp sustain": 0, "cross modulation": 0, "cutoff": 56.421875, "delay dry/wet": 0.401562, "delay feedback": -0.612500, "delay time": 0.060000, "fil attack": 0, "fil decay": 2.175000, "fil env depth": 34, "fil release": 0.159375, "fil sustain": 0.281250, "filter type": 0, "keytrack": 0.390625, "legato": 0, "lfo 1 frequency": 8.015625, "lfo 1 waveform": 0, "lfo 2 frequency": 6.328125, "lfo 2 waveform": 0, "mod destination 1": 2, "mod destination 2": 5, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.166250, "mod scale 2": 0.437500, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 3, "mod source 2": 4, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 4, "osc 2 transpose": 7, "osc 2 tune": 0.298750, "osc 2 waveform": 6, "osc mix": 0.484375, "pitch bend range": 2, "polyphony": 17, "portamento": 0.010000, "portamento type": 0, "resonance": 7.531250, "velocity track": 0.300000, "volume": 0.154688 } ================================================ FILE: patches/whistle.mite ================================================ { "amp attack": 0.117188, "amp decay": 0.957812, "amp release": 0.534376, "amp sustain": 0, "cross modulation": 0, "cutoff": 76.531250, "delay dry/wet": 1.416312e-12, "delay feedback": -0.596875, "delay time": 0.168281, "fil attack": 0, "fil decay": 0.093750, "fil env depth": 0, "fil release": 3, "fil sustain": 0.601562, "filter type": 0, "keytrack": 1, "legato": 0, "lfo 1 frequency": 5.359375, "lfo 1 waveform": 0, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 1, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.010000, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 3, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 11, "osc 2 transpose": 0, "osc 2 tune": 0.345625, "osc 2 waveform": 11, "osc mix": 0.242188, "pitch bend range": 2, "polyphony": 13, "portamento": 0.027188, "portamento type": 0, "resonance": 15, "velocity track": 0.300000, "volume": 0.576563 } ================================================ FILE: patches/whoooo.mite ================================================ { "amp attack": 0.164063, "amp decay": 2.320312, "amp release": 0.534376, "amp sustain": 0, "cross modulation": 0, "cutoff": 81.171875, "delay dry/wet": 1.416332e-12, "delay feedback": -0.596875, "delay time": 0.404453, "fil attack": 0, "fil decay": 0.093750, "fil env depth": 0, "fil release": 3, "fil sustain": 0.601562, "filter type": 0, "keytrack": 1, "legato": 0, "lfo 1 frequency": 5.359375, "lfo 1 waveform": 0, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 4, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.056875, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 3, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 0, "osc 2 transpose": 24, "osc 2 tune": 0.345625, "osc 2 waveform": 0, "osc mix": 0.445313, "pitch bend range": 4, "polyphony": 12, "portamento": 0.027188, "portamento type": 0, "resonance": 0.500000, "velocity track": 0.300000, "volume": 0.515625 } ================================================ FILE: patches/womp.mite ================================================ { "amp attack": 0.197500, "amp decay": 0.700000, "amp release": 0.300000, "amp sustain": 0, "cross modulation": 0, "cutoff": 57.968750, "delay dry/wet": 0.401562, "delay feedback": -0.612500, "delay time": 0.137344, "fil attack": 0.445312, "fil decay": 2.175000, "fil env depth": 34, "fil release": 0.159375, "fil sustain": 0.281250, "filter type": 0, "keytrack": 0, "legato": 0, "lfo 1 frequency": 2, "lfo 1 waveform": 0, "lfo 2 frequency": 2, "lfo 2 waveform": 0, "mod destination 1": 0, "mod destination 2": 0, "mod destination 3": 0, "mod destination 4": 0, "mod destination 5": 0, "mod scale 1": 0.010000, "mod scale 2": 0.010000, "mod scale 3": 0.010000, "mod scale 4": 0.010000, "mod scale 5": 0.010000, "mod source 1": 0, "mod source 2": 0, "mod source 3": 0, "mod source 4": 0, "mod source 5": 0, "osc 1 waveform": 5, "osc 2 transpose": 19, "osc 2 tune": 0.173750, "osc 2 waveform": 5, "osc mix": 0.484375, "pitch bend range": 2, "polyphony": 17, "portamento": 0.010000, "portamento type": 0, "resonance": 3, "velocity track": 0.300000, "volume": 0.271875 } ================================================ FILE: po/.gitignore ================================================ # Ignore everything in this directory * # Except these files !.gitignore !ChangeLog !LINGUAS !POTFILES.in !Makevars !*.po ================================================ FILE: po/Makevars ================================================ # Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. DOMAIN = $(PACKAGE) # These two variables depend on the location of this directory. subdir = po top_builddir = .. # These options get passed to xgettext. XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = Matt Tytel # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = matthewtytel@gmail.com # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' # context. Possible values are "yes" and "no". Set this to yes if the # package uses functions taking also a message context, like pgettext(), or # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. USE_MSGCTXT = no # These options get passed to msgmerge. # Useful options are in particular: # --previous to keep previous msgids of translated messages, # --quiet to reduce the verbosity. MSGMERGE_OPTIONS = ================================================ FILE: po/POTFILES.in ================================================ # List of source files which contain translatable strings. src/cursynth_gui.cpp ================================================ FILE: rtaudio/.gitignore ================================================ librtaudio.so* librtaudio.dylib.4 rtaudio-config librtaudio.pc ================================================ FILE: rtaudio/Makefile.am ================================================ noinst_LIBRARIES = librtaudio.a librtaudio_a_SOURCES = RtAudio.cpp RtAudio.h RtError.h ================================================ FILE: rtaudio/RtAudio.cpp ================================================ /************************************************************************/ /*! \class RtAudio \brief Realtime audio i/o C++ classes. RtAudio provides a common API (Application Programming Interface) for realtime audio input/output across GNU/Linux (native ALSA, Jack, and OSS), Macintosh OS X (CoreAudio and Jack), and Windows (DirectSound and ASIO) operating systems. RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes Copyright (c) 2001-2013 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.0.12 #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__) #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. // // *************************************************** // 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_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_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 ) throw() { 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 print out an error message. std::cerr << "\nRtAudio: no compiled API support found ... critical error!!\n\n"; } RtAudio :: ~RtAudio() throw() { 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; } 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( RtError::INVALID_USE ); return; } if ( oParams && oParams->nChannels < 1 ) { errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one."; error( RtError::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( RtError::INVALID_USE ); return; } if ( oParams == NULL && iParams == NULL ) { errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!"; error( RtError::INVALID_USE ); return; } if ( formatBytes(format) == 0 ) { errorText_ = "RtApi::openStream: 'format' parameter value is undefined."; error( RtError::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( RtError::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( RtError::INVALID_USE ); return; } } clearStreamInfo(); bool result; if ( oChannels > 0 ) { result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel, sampleRate, format, bufferFrames, options ); if ( result == false ) { error( RtError::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( RtError::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 } 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( RtError::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( RtError::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( RtError::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( RtError::WARNING ); return 0; } for ( unsigned int i=0; i= nDevices ) { errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!"; error( RtError::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( RtError::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( RtError::WARNING ); return info; } //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); int length = CFStringGetLength(cfname); char *mname = (char *)malloc(length * 3 + 1); CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding()); 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( RtError::WARNING ); return info; } //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); length = CFStringGetLength(cfname); char *name = (char *)malloc(length * 3 + 1); CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding()); 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( RtError::WARNING ); return info; } // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList."; error( RtError::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( RtError::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( RtError::WARNING ); return info; } // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList."; error( RtError::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( RtError::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( RtError::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( RtError::WARNING ); return info; } Float64 minimumRate = 100000000.0, maximumRate = 0.0; for ( UInt32 i=0; i maximumRate ) maximumRate = rangeList[i].mMaximum; } info.sampleRates.clear(); for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) info.sampleRates.push_back( SAMPLE_RATES[k] ); } if ( info.sampleRates.size() == 0 ) { errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtError::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) { 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 ) { 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; 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( RtError::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( RtError::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( RtError::SYSTEM_ERROR ); } void RtApiCore :: stopStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiCore::stopStream(): the stream is already stopped!"; error( RtError::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( RtError::SYSTEM_ERROR ); } void RtApiCore :: abortStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiCore::abortStream(): the stream is already stopped!"; error( RtError::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( RtError::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; } }; 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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::SYSTEM_ERROR ); } void RtApiJack :: stopStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiJack::stopStream(): the stream is already stopped!"; error( RtError::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( RtError::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( RtError::WARNING ); return FAILURE; } if ( stream_.bufferSize != nframes ) { errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!"; error( RtError::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 ); } } 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 GNU/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( RtError::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( RtError::INVALID_USE ); return info; } if ( device >= nDevices ) { errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!"; error( RtError::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( RtError::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( RtError::WARNING ); return info; } info.name = driverName; if ( !drivers.loadDriver( driverName ) ) { errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ")."; errorText_ = errorStream_.str(); error( RtError::WARNING ); return info; } result = ASIOInit( &driverInfo ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ")."; errorText_ = errorStream_.str(); error( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::SYSTEM_ERROR ); } void RtApiAsio :: stopStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!"; error( RtError::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( RtError::SYSTEM_ERROR ); } void RtApiAsio :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!"; error( RtError::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( RtError::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 ); } } 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 ( RtError &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_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() ) { errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!"; error( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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; } unsigned int 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( RtError::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( RtError::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( RtError::SYSTEM_ERROR ); } void RtApiDs :: stopStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiDs::stopStream(): the stream is already stopped!"; error( RtError::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; // 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; 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. if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR ); } void RtApiDs :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiDs::abortStream(): the stream is already stopped!"; error( RtError::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( RtError::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; 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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::SYSTEM_ERROR ); return; } nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; handle->bufferPointer[0] = nextWritePointer; 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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::INVALID_USE ); return info; } if ( device >= nDevices ) { errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!"; error( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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; } } 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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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 ); 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( RtError::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 GNU/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( RtError::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( RtError::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 ) { 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( RtError::SYSTEM_ERROR ); } void RtApiAlsa :: stopStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!"; error( RtError::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: MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; error( RtError::SYSTEM_ERROR ); } void RtApiAlsa :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!"; error( RtError::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: MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; error( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::INVALID_USE ); return; } if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiPulse::startStream(): the stream is already running!"; error( RtError::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( RtError::INVALID_USE ); return; } if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!"; error( RtError::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( RtError::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( RtError::INVALID_USE ); return; } if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!"; error( RtError::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( RtError::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; ss.format = sf->pa_format; break; } } if ( !sf_found ) { errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample format."; return false; } // Set interleaving 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_.doConvertBuffer[mode] = channels > 1 && !stream_.userInterleaved; stream_.deviceFormat[mode] = stream_.userFormat; stream_.nUserChannels[mode] = channels; stream_.nDeviceChannels[mode] = channels + firstChannel; stream_.channelOffset[mode] = 0; // 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; switch ( mode ) { case INPUT: pah->s_rec = pa_simple_new( NULL, "RtAudio", PA_STREAM_RECORD, NULL, "Record", &ss, NULL, NULL, &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 "soundcard.h" #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( RtError::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( RtError::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( RtError::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( RtError::WARNING ); return info; } unsigned nDevices = sysinfo.numaudios; if ( nDevices == 0 ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: no devices found!"; error( RtError::INVALID_USE ); return info; } if ( device >= nDevices ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!"; error( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::SYSTEM_ERROR ); } void RtApiOss :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiOss::abortStream(): the stream is already stopped!"; error( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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. static bool firstErrorOccured = false; if ( firstErrorOccured ) return; firstErrorOccured = true; const std::string errorMessage = errorText_; if ( type != RtError::WARNING && stream_.state != STREAM_STOPPED) { stream_.callbackInfo.isRunning = false; // exit from the thread abortStream(); } errorCallback( type, errorMessage ); firstErrorOccured = false; return; } if ( type == RtError::WARNING && showWarnings_ == true ) std::cerr << '\n' << errorText_ << "\n\n"; else if ( type != RtError::WARNING ) throw( RtError( errorText_, type ) ); } void RtApi :: verifyStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApi:: a stream is not open!"; error( RtError::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( RtError::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 #include "RtError.h" // RtAudio version static const std::string RT_AUDIO_VERSION( "4.0.12" ); /*! \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 ); //! RtAudio error callback function prototype. /*! \param type Type of error. \param errorText Error description. */ typedef void (*RtAudioErrorCallback)( RtError::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 GNU/Linux Sound Architecture API. */ LINUX_PULSE, /*!< The GNU/Linux PulseAudio API. */ LINUX_OSS, /*!< The GNU/Linux Open Sound System API. */ UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */ MACOSX_CORE, /*!< Macintosh OS-X Core Audio 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, GNU/Linux OSS, and GNU/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 ) { return RT_AUDIO_VERSION; } //! 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. No exceptions can be thrown. If no API argument is specified and multiple API support has been compiled, the default order of use is JACK, ALSA, OSS (GNU/Linux systems) and ASIO, DS (Windows systems). */ RtAudio( RtAudio::Api api=UNSPECIFIED ) throw(); //! 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 RtError (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 RtError (type = SYSTEM_ERROR) is thrown if a stream cannot be opened with the specified parameters or an error occurs during processing. An RtError (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 RtError (type = SYSTEM_ERROR) is thrown if an error occurs during processing. An RtError (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 RtError (type = SYSTEM_ERROR) is thrown if an error occurs during processing. An RtError (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 RtError (type = SYSTEM_ERROR) is thrown if an error occurs during processing. An RtError (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 RtError (type = INVALID_USE) will be thrown. */ double getStreamTime( void ); //! 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 RtError (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 RtError (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(); protected: void openRtApi( RtAudio::Api api ); RtApi *rtapi_; }; // Operating system dependent thread functionality. #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) #include #include typedef unsigned long 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: 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 ); 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_; /*! 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 RtError (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( RtError::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 :: 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(__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( RtError::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 ================================================ FILE: rtaudio/RtError.h ================================================ /************************************************************************/ /*! \class RtError \brief Exception handling class for RtAudio & RtMidi. The RtError class is quite simple but it does allow errors to be "caught" by RtError::Type. See the RtAudio and RtMidi documentation to know which methods can throw an RtError. */ /************************************************************************/ #ifndef RTERROR_H #define RTERROR_H #include #include #include class RtError : public std::exception { public: //! Defined RtError 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. RtError( const std::string& message, Type type = RtError::UNSPECIFIED ) throw() : message_(message), type_(type) {} //! The destructor. virtual ~RtError( 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_; }; #endif ================================================ FILE: rtaudio/configure.ac ================================================ # Process this file with autoconf to produce a configure script. AC_INIT(RtAudio, 4.0, 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]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_SILENT_RULES([yes]) # 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 AM_PROG_AR 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(c++ g++ CC 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"], ) # 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] ) 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 OSX and GNU/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 (GNU/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 (GNU/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 (GNU/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 OSX 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" ], ) # If no audio api flags specified, use DirectSound 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 ================================================ FILE: rtaudio/contrib/python/pyrtaudio/PyRtAudioTest.py ================================================ import rtaudio as rt from math import cos import struct class audio_generator: def __init__(self): self.idx = -1 self.freq = 440. def __call__(self): self.idx += 1 if self.idx%48000 == 0: self.freq *= 2**(1/12.) return 0.5*cos(2.*3.1416*self.freq*self.idx/48000.) class callback: def __init__(self, gen): self.gen = gen self.i = 0 def __call__(self,playback, capture): [struct.pack_into("f", playback, 4*o, self.gen()) for o in xrange(256)] self.i = self.i + 256 if self.i > 48000*10: print '.' return 1 dac = rt.RtAudio() n = dac.getDeviceCount() print 'Number of devices available: ', n for i in range(n): try: print dac.getDeviceInfo(i) except rt.RtError as e: print e print 'Default output device: ', dac.getDefaultOutputDevice() print 'Default input device: ', dac.getDefaultInputDevice() print 'is stream open: ', dac.isStreamOpen() print 'is stream running: ', dac.isStreamRunning() oParams = {'deviceId': 1, 'nChannels': 1, 'firstChannel': 0} iParams = {'deviceId': 1, 'nChannels': 1, 'firstChannel': 0} try: dac.openStream(oParams,oParams,48000,256,callback(audio_generator()) ) except rt.RtError as e: print e else: dac.startStream() import time print 'latency: ', dac.getStreamLatency() while (dac.isStreamRunning()): time.sleep(0.1) print dac.getStreamTime() dac.stopStream() dac.abortStream() dac.closeStream() ================================================ FILE: rtaudio/contrib/python/pyrtaudio/readme ================================================ PyRtAudio - a python wrapper around RtAudio that allows to perform audio i/o operations in real-time from the python language. By Antoine Lefebvre, 2011 This software is in the development stage. Do not expect compatibility with future versions. Comments, suggestions, new features, bug fixes, etc. are welcome. This distribution of PyRtAudio contains the following: - rtaudiomodule.cpp: the python wrapper code - setup.py: a setup script use to compile and install PyRtAudio - examples: a single PyRtAudioTest.py script INSTALLATION The compilation and installation of the PyRtAudio module is handled by the python Distribution Utilities ("Distutils"). Provided that your system has a C++ compiler and is properly configure, the following command should be sufficient: >> python setup.py install Please refer to the distutils documentation for installation problems: http://docs.python.org/distutils/index.html LEGAL AND ETHICAL: The PyRtAudio license is the same as the RtAudio license: PyRtAudio: a python wrapper around RtAudio Copyright (c)2011 Antoine Lefebvre Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is asked to send the modifications to the original developer so that they can be incorporated into the canonical version. This is, however, not a binding provision of this license. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: rtaudio/contrib/python/pyrtaudio/rtaudiomodule.cpp ================================================ /************************************************************************/ /* PyRtAudio: a python wrapper around RtAudio Copyright (c) 2011 Antoine Lefebvre 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. */ /************************************************************************/ // This software is in the development stage // Do not expect compatibility with future versions. // Comments, suggestions, new features, bug fixes, etc. are welcome #include #include "RtAudio.h" extern "C" { typedef struct { PyObject_HEAD; RtAudio *dac; RtAudioFormat _format; int _bufferSize; unsigned int inputChannels; PyObject *callback_func; } PyRtAudio; static PyObject *RtAudioError; static int callback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *data ) { PyRtAudio* self = (PyRtAudio*) data; if (status == RTAUDIO_OUTPUT_UNDERFLOW) printf("underflow.\n"); if (self == NULL) return -1; float* in = (float *) inputBuffer; float* out = (float *) outputBuffer; PyObject *py_callback_func = self->callback_func; int retval = 0; if (py_callback_func) { PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* iBuffer = PyBuffer_FromMemory(in, sizeof(float) * self->inputChannels * nBufferFrames); PyObject* oBuffer = PyBuffer_FromReadWriteMemory(out, sizeof(float) * nBufferFrames); PyObject *arglist = Py_BuildValue("(O,O)", oBuffer, iBuffer); if (arglist == NULL) { printf("error.\n"); PyErr_Print(); PyGILState_Release(gstate); return 2; } // Calling the callback PyObject *result = PyEval_CallObject(py_callback_func, arglist); if (PyErr_Occurred() != NULL) { PyErr_Print(); } else if PyInt_Check(result) { retval = PyInt_AsLong(result); } Py_DECREF(arglist); Py_DECREF(oBuffer); Py_DECREF(iBuffer); Py_XDECREF(result); PyGILState_Release(gstate); } return retval; } static void RtAudio_dealloc(PyRtAudio *self) { printf("RtAudio_dealloc.\n"); if (self == NULL) return; if (self->dac) { self->dac->closeStream(); Py_CLEAR(self->callback_func); delete self->dac; } self->ob_type->tp_free((PyObject *) self); } static PyObject* RtAudio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { printf("RtAudio_new.\n"); PyRtAudio *self; char *api = NULL; if(!PyArg_ParseTuple(args, "|s", &api)) return NULL; self = (PyRtAudio *) type->tp_alloc(type, 0); if(self == NULL) return NULL; self->dac = NULL; self->callback_func = NULL; try { if (api == NULL) self->dac = new RtAudio; else if(!strcmp(api, "jack")) self->dac = new RtAudio(RtAudio::UNIX_JACK); else if(!strcmp(api, "alsa")) self->dac = new RtAudio(RtAudio::LINUX_ALSA); else if(!strcmp(api, "oss")) self->dac = new RtAudio(RtAudio::LINUX_ALSA); else if(!strcmp(api, "core")) self->dac = new RtAudio(RtAudio::MACOSX_CORE); else if(!strcmp(api, "asio")) self->dac = new RtAudio(RtAudio::WINDOWS_ASIO); else if(!strcmp(api, "directsound")) self->dac = new RtAudio(RtAudio::WINDOWS_DS); } catch (RtError &error) { PyErr_SetString(RtAudioError, error.getMessage().c_str()); Py_INCREF(RtAudioError); return NULL; } self->dac->showWarnings(false); //Py_XINCREF(self); return (PyObject *) self; } static int RtAudio_init(PyRtAudio *self, PyObject *args, PyObject *kwds) { printf("RtAudio_init.\n"); //if (self == NULL) return 0; return 0; } // This functions does not yet support all the features of the RtAudio::openStream method. // Please send your patches if you improves this. static PyObject* RtAudio_openStream(PyRtAudio *self, PyObject *args) { if (self == NULL) return NULL; if (self->dac == NULL) { printf("the dac is null.\n"); Py_RETURN_NONE; } PyObject *oParamsObj; PyObject *iParamsObj; int fs; unsigned int bf; PyObject *pycallback; if (!PyArg_ParseTuple(args, "OOiiO", &oParamsObj, &iParamsObj, &fs, &bf, &pycallback)) return NULL; RtAudio::StreamParameters oParams; oParams.deviceId = 1; oParams.nChannels = 1; oParams.firstChannel = 0; if (PyDict_Check(oParamsObj)) { if (PyDict_Contains(oParamsObj, PyString_FromString("deviceId"))) { PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("deviceId")); oParams.deviceId = PyInt_AsLong(value); } if (PyDict_Contains(oParamsObj, PyString_FromString("nChannels"))) { PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("nChannels")); oParams.nChannels = PyInt_AsLong(value); } if (PyDict_Contains(oParamsObj, PyString_FromString("firstChannel"))) { PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("firstChannel")); oParams.firstChannel = PyInt_AsLong(value); } } else { printf("First argument must be a dictionary. Default values will be used.\n"); } RtAudio::StreamParameters iParams; iParams.deviceId = 1; iParams.nChannels = 2; iParams.firstChannel = 0; if (PyDict_Check(iParamsObj)) { if (PyDict_Contains(iParamsObj, PyString_FromString("deviceId"))) { PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("deviceId")); iParams.deviceId = PyInt_AsLong(value); } if (PyDict_Contains(iParamsObj, PyString_FromString("nChannels"))) { PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("nChannels")); iParams.nChannels = PyInt_AsLong(value); } if (PyDict_Contains(iParamsObj, PyString_FromString("firstChannel"))) { PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("firstChannel")); iParams.firstChannel = PyInt_AsLong(value); } } else { printf("Second argument must be a dictionary. Default values will be used.\n"); } if (!PyCallable_Check(pycallback)) { PyErr_SetString(PyExc_TypeError, "Need a callable object!"); Py_XINCREF(PyExc_TypeError); return NULL; } // sanity check the callback ? Py_INCREF(pycallback); /* Add a reference to new callback */ self->callback_func = pycallback; /*Remember new callback */ // add support for other format self->_format = RTAUDIO_FLOAT32; // add support for other options RtAudio::StreamOptions options; options.flags = RTAUDIO_NONINTERLEAVED; try { if (self->dac->isStreamOpen()) self->dac->closeStream(); self->dac->openStream(&oParams, &iParams, self->_format, fs, &bf, &callback, self, &options); } catch ( RtError& error ) { PyErr_SetString(RtAudioError, error.getMessage().c_str()); Py_INCREF(RtAudioError); return NULL; } self->inputChannels = iParams.nChannels; Py_RETURN_NONE; } static PyObject* RtAudio_closeStream(PyRtAudio *self, PyObject *args) { printf("RtAudio_closeStream.\n"); if (self == NULL || self->dac == NULL) return NULL; try { self->dac->closeStream(); Py_CLEAR(self->callback_func); } catch(RtError &error) { PyErr_SetString(RtAudioError, error.getMessage().c_str()); Py_INCREF(RtAudioError); return NULL; } Py_RETURN_NONE; } static PyObject* RtAudio_startStream(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; try { self->dac->startStream(); } catch(RtError &error) { PyErr_SetString(RtAudioError, error.getMessage().c_str()); Py_INCREF(RtAudioError); return NULL; } Py_RETURN_NONE; } static PyObject* RtAudio_stopStream(PyRtAudio *self, PyObject *args) { printf("RtAudio_stopStream.\n"); if (self == NULL || self->dac == NULL) return NULL; try { self->dac->stopStream(); } catch(RtError &error) { PyErr_SetString(RtAudioError, error.getMessage().c_str()); Py_INCREF(RtAudioError); return NULL; } Py_RETURN_NONE; } static PyObject* RtAudio_abortStream(PyRtAudio *self, PyObject *args) { printf("RtAudio_abortStream.\n"); if (self == NULL || self->dac == NULL) return NULL; try { self->dac->abortStream(); } catch(RtError &error) { PyErr_SetString(RtAudioError, error.getMessage().c_str()); Py_INCREF(RtAudioError); return NULL; } Py_RETURN_NONE; } static PyObject* RtAudio_isStreamRunning(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; if (self->dac == NULL) { Py_RETURN_FALSE; } if (self->dac->isStreamRunning()) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject* RtAudio_isStreamOpen(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; if (self->dac == NULL) { Py_RETURN_FALSE; } if (self->dac->isStreamOpen()) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject* RtAudio_getDeviceCount(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; return PyInt_FromLong(self->dac->getDeviceCount()); } static PyObject* RtAudio_getDeviceInfo(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; int device; if (!PyArg_ParseTuple(args, "i", &device)) return NULL; try { RtAudio::DeviceInfo info = self->dac->getDeviceInfo(device); PyObject* info_dict = PyDict_New(); if (info.probed) { Py_INCREF(Py_True); PyDict_SetItemString(info_dict, "probed", Py_True); } else { Py_INCREF(Py_False); PyDict_SetItemString(info_dict, "probed", Py_False); } PyObject* obj; obj = PyString_FromString(info.name.c_str()); PyDict_SetItemString(info_dict, "name", obj); obj = PyInt_FromLong(info.outputChannels); PyDict_SetItemString(info_dict, "outputChannels", obj); obj = PyInt_FromLong(info.inputChannels); PyDict_SetItemString(info_dict, "inputChannels", obj); obj = PyInt_FromLong(info.duplexChannels); PyDict_SetItemString(info_dict, "duplexChannels", obj); if (info.isDefaultOutput) { Py_INCREF(Py_True); PyDict_SetItemString(info_dict, "isDefaultOutput", Py_True); } else { Py_INCREF(Py_False); PyDict_SetItemString(info_dict, "isDefaultOutput", Py_False); } if (info.isDefaultInput) { Py_INCREF(Py_True); PyDict_SetItemString(info_dict, "isDefaultInput", Py_True); } else { Py_INCREF(Py_False); PyDict_SetItemString(info_dict, "isDefaultInput", Py_False); } return info_dict; } catch(RtError &error) { PyErr_SetString(RtAudioError, error.getMessage().c_str()); Py_INCREF(RtAudioError); return NULL; } } static PyObject* RtAudio_getDefaultOutputDevice(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; return PyInt_FromLong(self->dac->getDefaultOutputDevice()); } static PyObject* RtAudio_getDefaultInputDevice(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; return PyInt_FromLong(self->dac->getDefaultInputDevice()); } static PyObject* RtAudio_getStreamTime(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; return PyFloat_FromDouble( self->dac->getStreamTime() ); } static PyObject* RtAudio_getStreamLatency(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; return PyInt_FromLong( self->dac->getStreamLatency() ); } static PyObject* RtAudio_getStreamSampleRate(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; return PyInt_FromLong( self->dac->getStreamSampleRate() ); } static PyObject* RtAudio_showWarnings(PyRtAudio *self, PyObject *args) { if (self == NULL || self->dac == NULL) return NULL; PyObject *obj; if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; if (!PyBool_Check(obj)) return NULL; if (obj == Py_True) self->dac->showWarnings(true); else if (obj == Py_False) self->dac->showWarnings(false); else { printf("not true nor false\n"); } Py_RETURN_NONE; } static PyMethodDef RtAudio_methods[] = { // TO BE DONE: getCurrentApi(void) {"getDeviceCount", (PyCFunction) RtAudio_getDeviceCount, METH_NOARGS, "A public function that queries for the number of audio devices available."}, {"getDeviceInfo", (PyCFunction) RtAudio_getDeviceInfo, METH_VARARGS, "Return a dictionary with information for a specified device number."}, {"getDefaultOutputDevice", (PyCFunction) RtAudio_getDefaultOutputDevice, METH_NOARGS, "A function that returns the index of the default output device."}, {"getDefaultInputDevice", (PyCFunction) RtAudio_getDefaultInputDevice, METH_NOARGS, "A function that returns the index of the default input device."}, {"openStream", (PyCFunction) RtAudio_openStream, METH_VARARGS, "A public method for opening a stream with the specified parameters."}, {"closeStream", (PyCFunction) RtAudio_closeStream, METH_NOARGS, "A function that closes a stream and frees any associated stream memory. "}, {"startStream", (PyCFunction) RtAudio_startStream, METH_NOARGS, "A function that starts a stream. "}, {"stopStream", (PyCFunction) RtAudio_stopStream, METH_NOARGS, "Stop a stream, allowing any samples remaining in the output queue to be played. "}, {"abortStream", (PyCFunction) RtAudio_abortStream, METH_NOARGS, "Stop a stream, discarding any samples remaining in the input/output queue."}, {"isStreamOpen", (PyCFunction) RtAudio_isStreamOpen, METH_NOARGS, "Returns true if a stream is open and false if not."}, {"isStreamRunning", (PyCFunction) RtAudio_isStreamRunning, METH_NOARGS, "Returns true if the stream is running and false if it is stopped or not open."}, {"getStreamTime", (PyCFunction) RtAudio_getStreamTime, METH_NOARGS, "Returns the number of elapsed seconds since the stream was started."}, {"getStreamLatency", (PyCFunction) RtAudio_getStreamLatency, METH_NOARGS, "Returns the internal stream latency in sample frames."}, {"getStreamSampleRate", (PyCFunction) RtAudio_getStreamSampleRate, METH_NOARGS, "Returns actual sample rate in use by the stream."}, {"showWarnings", (PyCFunction) RtAudio_showWarnings, METH_VARARGS, "Specify whether warning messages should be printed to stderr."}, // TO BE DONE: getCompiledApi (std::vector< RtAudio::Api > &apis) throw () {NULL} }; static PyTypeObject RtAudio_type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "rtaudio.RtAudio", /*tp_name*/ sizeof(RtAudio), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor) RtAudio_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Audio input device", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ RtAudio_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)RtAudio_init, /* tp_init */ 0, /* tp_alloc */ RtAudio_new, /* tp_new */ 0, /* Low-level free-memory routine */ 0, /* For PyObject_IS_GC */ 0, // PyObject *tp_bases; 0, // PyObject *tp_mro; /* method resolution order */ 0, //PyObject *tp_cache; 0, //PyObject *tp_subclasses; 0, //PyObject *tp_weaklist; 0, //destructor tp_del; //0, /* Type attribute cache version tag. Added in version 2.6 */ }; #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC initrtaudio(void) { PyEval_InitThreads(); if (PyType_Ready(&RtAudio_type) < 0) return; PyObject* module = Py_InitModule3("rtaudio", NULL, "RtAudio wrapper."); if (module == NULL) return; Py_INCREF(&RtAudio_type); PyModule_AddObject(module, "RtAudio", (PyObject *)&RtAudio_type); RtAudioError = PyErr_NewException("rtaudio.RtError", NULL, NULL); Py_INCREF(RtAudioError); PyModule_AddObject(module, "RtError", RtAudioError); } } ================================================ FILE: rtaudio/contrib/python/pyrtaudio/setup.py ================================================ #!/bin/env python import os from distutils.core import setup, Extension if hasattr(os, 'uname'): OSNAME = os.uname()[0] else: OSNAME = 'Windows' define_macros = [] libraries = [] extra_link_args = [] extra_compile_args = ['-I../../../'] sources = ['rtaudiomodule.cpp', '../../../RtAudio.cpp'] if OSNAME == 'Linux': define_macros=[("__LINUX_ALSA__", ''), ('__LINUX_JACK__', ''), ('__LINUX_OSS__', '')] libraries = ['asound', 'jack', 'pthread'] elif OSNAME == 'Darwin': define_macros = [('__MACOSX_CORE__', '')] libraries = ['pthread', 'stdc++'] extra_link_args = ['-framework', 'CoreAudio'] elif OSNAME == 'Windows': define_macros = [('__WINDOWS_DS__', None), ('__WINDOWS_ASIO__', None), ('__LITTLE_ENDIAN__',None), ('WIN32',None)] libraries = ['winmm', 'dsound', 'Advapi32','Ole32','User32'] sources += ['../../../include/asio.cpp', '../../../include/asiodrivers.cpp', '../../../include/asiolist.cpp', '../../../include/iasiothiscallresolver.cpp'] extra_compile_args.append('-I../../../include/') extra_compile_args.append('-EHsc') audio = Extension('rtaudio', sources=sources, libraries=libraries, define_macros=define_macros, extra_compile_args = extra_compile_args, extra_link_args = extra_link_args, ) setup(name = 'rtaudio', version = '0.1', description = 'Python RtAudio interface', ext_modules = [audio]) ================================================ FILE: rtaudio/include/asio.cpp ================================================ /* 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 ================================================ FILE: rtaudio/include/asio.h ================================================ //--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- /* 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 ================================================ FILE: rtaudio/include/asiodrivers.cpp ================================================ #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 ================================================ FILE: rtaudio/include/asiodrivers.h ================================================ #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 ================================================ FILE: rtaudio/include/asiodrvr.h ================================================ /* 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 ================================================ FILE: rtaudio/include/asiolist.cpp ================================================ #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; } ================================================ FILE: rtaudio/include/asiolist.h ================================================ #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 ================================================ FILE: rtaudio/include/asiosys.h ================================================ #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 ================================================ FILE: rtaudio/include/dsound.h ================================================ /*==========================================================================; * * 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 ================================================ FILE: rtaudio/include/ginclude.h ================================================ #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__ ================================================ FILE: rtaudio/include/iasiodrv.h ================================================ #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; }; ================================================ FILE: rtaudio/include/iasiothiscallresolver.cpp ================================================ /* 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 */ ================================================ FILE: rtaudio/include/iasiothiscallresolver.h ================================================ // **************************************************************************** // // 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 */ ================================================ FILE: rtaudio/include/soundcard.h ================================================ /* * 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 # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=audioprobe - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "audioprobe.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "audioprobe.mak" CFG="audioprobe - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "audioprobe - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "audioprobe - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "audioprobe - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "audioprobe___Win32_Release" # PROP BASE Intermediate_Dir "audioprobe___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "../../" /I "../../include" /D "NDEBUG" /D "__WINDOW_DS__" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "audioprobe - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "audioprobe___Win32_Debug" # PROP BASE Intermediate_Dir "audioprobe___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../" /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "audioprobe - Win32 Release" # Name "audioprobe - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\include\asio.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiolist.cpp # End Source File # Begin Source File SOURCE=..\audioprobe.cpp # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.cpp # End Source File # Begin Source File SOURCE=..\..\RtAudio.cpp # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\include\asio.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrvr.h # End Source File # Begin Source File SOURCE=..\..\include\asiolist.h # End Source File # Begin Source File SOURCE=..\..\include\asiosys.h # End Source File # Begin Source File SOURCE=..\..\include\ginclude.h # End Source File # Begin Source File SOURCE=..\..\include\iasiodrv.h # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.h # End Source File # Begin Source File SOURCE=..\..\RtAudio.h # End Source File # Begin Source File SOURCE=..\..\RtError.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project ================================================ FILE: rtaudio/tests/Windows/duplex.dsp ================================================ # Microsoft Developer Studio Project File - Name="duplex" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=duplex - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "duplex.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "duplex.mak" CFG="duplex - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "duplex - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "duplex - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "duplex - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "duplex___Win32_Release" # PROP BASE Intermediate_Dir "duplex___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "../../" /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "duplex - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "duplex___Win32_Debug" # PROP BASE Intermediate_Dir "duplex___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../" /I "../../include" /D "_DEBUG" /D "__WINDOWS_ASIO__.__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "duplex - Win32 Release" # Name "duplex - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\include\asio.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiolist.cpp # End Source File # Begin Source File SOURCE=..\duplex.cpp # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.cpp # End Source File # Begin Source File SOURCE=..\..\RtAudio.cpp # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\include\asio.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrvr.h # End Source File # Begin Source File SOURCE=..\..\include\asiolist.h # End Source File # Begin Source File SOURCE=..\..\include\asiosys.h # End Source File # Begin Source File SOURCE=..\..\include\ginclude.h # End Source File # Begin Source File SOURCE=..\..\include\iasiodrv.h # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.h # End Source File # Begin Source File SOURCE=..\..\RtAudio.h # End Source File # Begin Source File SOURCE=..\..\RtError.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project ================================================ FILE: rtaudio/tests/Windows/playraw.dsp ================================================ # Microsoft Developer Studio Project File - Name="playraw" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=playraw - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "playraw.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "playraw.mak" CFG="playraw - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "playraw - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "playraw - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "playraw - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "playraw___Win32_Release" # PROP BASE Intermediate_Dir "playraw___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "../../" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "playraw - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "playraw___Win32_Debug" # PROP BASE Intermediate_Dir "playraw___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "playraw - Win32 Release" # Name "playraw - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\include\asio.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiolist.cpp # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.cpp # End Source File # Begin Source File SOURCE=..\playraw.cpp # End Source File # Begin Source File SOURCE=..\..\RtAudio.cpp # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\include\asio.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrvr.h # End Source File # Begin Source File SOURCE=..\..\include\asiolist.h # End Source File # Begin Source File SOURCE=..\..\include\asiosys.h # End Source File # Begin Source File SOURCE=..\..\include\ginclude.h # End Source File # Begin Source File SOURCE=..\..\include\iasiodrv.h # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.h # End Source File # Begin Source File SOURCE=..\..\RtAudio.h # End Source File # Begin Source File SOURCE=..\..\RtError.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project ================================================ FILE: rtaudio/tests/Windows/playsaw.dsp ================================================ # Microsoft Developer Studio Project File - Name="playsaw" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=playsaw - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "playsaw.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "playsaw.mak" CFG="playsaw - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "playsaw - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "playsaw - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "playsaw - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "playsaw___Win32_Release" # PROP BASE Intermediate_Dir "playsaw___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "../../" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "playsaw - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "playsaw___Win32_Debug" # PROP BASE Intermediate_Dir "playsaw___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "playsaw - Win32 Release" # Name "playsaw - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\include\asio.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiolist.cpp # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.cpp # End Source File # Begin Source File SOURCE=..\playsaw.cpp # End Source File # Begin Source File SOURCE=..\..\RtAudio.cpp # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\include\asio.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrvr.h # End Source File # Begin Source File SOURCE=..\..\include\asiolist.h # End Source File # Begin Source File SOURCE=..\..\include\asiosys.h # End Source File # Begin Source File SOURCE=..\..\include\ginclude.h # End Source File # Begin Source File SOURCE=..\..\include\iasiodrv.h # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.h # End Source File # Begin Source File SOURCE=..\..\RtAudio.h # End Source File # Begin Source File SOURCE=..\..\RtError.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project ================================================ FILE: rtaudio/tests/Windows/record.dsp ================================================ # Microsoft Developer Studio Project File - Name="record" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=record - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "record.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "record.mak" CFG="record - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "record - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "record - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "record - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "record___Win32_Release" # PROP BASE Intermediate_Dir "record___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "../../" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "record - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "record___Win32_Debug" # PROP BASE Intermediate_Dir "record___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "record - Win32 Release" # Name "record - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\include\asio.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiolist.cpp # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.cpp # End Source File # Begin Source File SOURCE=..\record.cpp # End Source File # Begin Source File SOURCE=..\..\RtAudio.cpp # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\include\asio.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrvr.h # End Source File # Begin Source File SOURCE=..\..\include\asiolist.h # End Source File # Begin Source File SOURCE=..\..\include\asiosys.h # End Source File # Begin Source File SOURCE=..\..\include\ginclude.h # End Source File # Begin Source File SOURCE=..\..\include\iasiodrv.h # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.h # End Source File # Begin Source File SOURCE=..\..\RtAudio.h # End Source File # Begin Source File SOURCE=..\..\RtError.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project ================================================ FILE: rtaudio/tests/Windows/rtaudio.dsw ================================================ Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "audioprobe"=.\audioprobe.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "duplex"=.\duplex.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "playraw"=.\playraw.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "playsaw"=.\playsaw.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "record"=.\record.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "testall"=.\testall.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "teststops"=.\teststops.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### ================================================ FILE: rtaudio/tests/Windows/testall.dsp ================================================ # Microsoft Developer Studio Project File - Name="testall" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=testall - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "testall.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "testall.mak" CFG="testall - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "testall - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "testall - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "testall - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "testall___Win32_Release" # PROP BASE Intermediate_Dir "testall___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "../../" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "testall - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "testall___Win32_Debug" # PROP BASE Intermediate_Dir "testall___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "testall - Win32 Release" # Name "testall - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\include\asio.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiolist.cpp # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.cpp # End Source File # Begin Source File SOURCE=..\..\RtAudio.cpp # End Source File # Begin Source File SOURCE=..\testall.cpp # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\include\asio.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrvr.h # End Source File # Begin Source File SOURCE=..\..\include\asiolist.h # End Source File # Begin Source File SOURCE=..\..\include\asiosys.h # End Source File # Begin Source File SOURCE=..\..\include\ginclude.h # End Source File # Begin Source File SOURCE=..\..\include\iasiodrv.h # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.h # End Source File # Begin Source File SOURCE=..\..\RtAudio.h # End Source File # Begin Source File SOURCE=..\..\RtError.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project ================================================ FILE: rtaudio/tests/Windows/teststops.dsp ================================================ # Microsoft Developer Studio Project File - Name="teststops" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=teststops - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "teststops.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "teststops.mak" CFG="teststops - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "teststops - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "teststops - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "teststops - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "teststops___Win32_Release" # PROP BASE Intermediate_Dir "teststops___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "../../" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "teststops - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "teststops___Win32_Debug" # PROP BASE Intermediate_Dir "teststops___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_ASIO__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "teststops - Win32 Release" # Name "teststops - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\include\asio.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.cpp # End Source File # Begin Source File SOURCE=..\..\include\asiolist.cpp # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.cpp # End Source File # Begin Source File SOURCE=..\..\RtAudio.cpp # End Source File # Begin Source File SOURCE=..\teststops.cpp # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\include\asio.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrivers.h # End Source File # Begin Source File SOURCE=..\..\include\asiodrvr.h # End Source File # Begin Source File SOURCE=..\..\include\asiolist.h # End Source File # Begin Source File SOURCE=..\..\include\asiosys.h # End Source File # Begin Source File SOURCE=..\..\include\ginclude.h # End Source File # Begin Source File SOURCE=..\..\include\iasiodrv.h # End Source File # Begin Source File SOURCE=..\..\include\iasiothiscallresolver.h # End Source File # Begin Source File SOURCE=..\..\RtAudio.h # End Source File # Begin Source File SOURCE=..\..\RtError.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project ================================================ FILE: rtaudio/tests/audioprobe.cpp ================================================ /******************************************/ /* audioprobe.cpp by Gary P. Scavone, 2001 Probe audio system and prints device info. */ /******************************************/ #include "RtAudio.h" #include #include int main() { // Create an api map. std::map apiMap; apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; apiMap[RtAudio::WINDOWS_DS] = "Windows Direct Sound"; apiMap[RtAudio::UNIX_JACK] = "Jack Client"; apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; std::vector< RtAudio::Api > apis; RtAudio :: getCompiledApi( apis ); std::cout << "\nRtAudio Version " << RtAudio::getVersion() << std::endl; std::cout << "\nCompiled APIs:\n"; for ( unsigned int i=0; i #include #include /* typedef char MY_TYPE; #define FORMAT RTAUDIO_SINT8 */ typedef signed short MY_TYPE; #define FORMAT RTAUDIO_SINT16 /* typedef S24 MY_TYPE; #define FORMAT RTAUDIO_SINT24 typedef signed long MY_TYPE; #define FORMAT RTAUDIO_SINT32 typedef float MY_TYPE; #define FORMAT RTAUDIO_FLOAT32 typedef double MY_TYPE; #define FORMAT RTAUDIO_FLOAT64 */ void usage( void ) { // Error function in case of incorrect command-line // argument specifications std::cout << "\nuseage: duplex N fs \n"; std::cout << " where N = number of channels,\n"; std::cout << " fs = the sample rate,\n"; std::cout << " iDevice = optional input device to use (default = 0),\n"; std::cout << " oDevice = optional output device to use (default = 0),\n"; std::cout << " iChannelOffset = an optional input channel offset (default = 0),\n"; std::cout << " and oChannelOffset = optional output channel offset (default = 0).\n\n"; exit( 0 ); } int inout( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *data ) { // Since the number of input and output channels is equal, we can do // a simple buffer copy operation here. if ( status ) std::cout << "Stream over/underflow detected." << std::endl; unsigned int *bytes = (unsigned int *) data; memcpy( outputBuffer, inputBuffer, *bytes ); return 0; } int main( int argc, char *argv[] ) { unsigned int channels, fs, bufferBytes, oDevice = 0, iDevice = 0, iOffset = 0, oOffset = 0; // Minimal command-line checking if (argc < 3 || argc > 7 ) usage(); RtAudio adac; if ( adac.getDeviceCount() < 1 ) { std::cout << "\nNo audio devices found!\n"; exit( 1 ); } channels = (unsigned int) atoi(argv[1]); fs = (unsigned int) atoi(argv[2]); if ( argc > 3 ) iDevice = (unsigned int) atoi(argv[3]); if ( argc > 4 ) oDevice = (unsigned int) atoi(argv[4]); if ( argc > 5 ) iOffset = (unsigned int) atoi(argv[5]); if ( argc > 6 ) oOffset = (unsigned int) atoi(argv[6]); // Let RtAudio print messages to stderr. adac.showWarnings( true ); // Set the same number of channels for both input and output. unsigned int bufferFrames = 512; RtAudio::StreamParameters iParams, oParams; iParams.deviceId = iDevice; iParams.nChannels = channels; iParams.firstChannel = iOffset; oParams.deviceId = oDevice; oParams.nChannels = channels; oParams.firstChannel = oOffset; RtAudio::StreamOptions options; //options.flags |= RTAUDIO_NONINTERLEAVED; try { adac.openStream( &oParams, &iParams, FORMAT, fs, &bufferFrames, &inout, (void *)&bufferBytes, &options ); } catch ( RtError& e ) { std::cout << '\n' << e.getMessage() << '\n' << std::endl; exit( 1 ); } bufferBytes = bufferFrames * channels * sizeof( MY_TYPE ); // Test RtAudio functionality for reporting latency. std::cout << "\nStream latency = " << adac.getStreamLatency() << " frames" << std::endl; try { adac.startStream(); char input; std::cout << "\nRunning ... press to quit (buffer frames = " << bufferFrames << ").\n"; std::cin.get(input); // Stop the stream. adac.stopStream(); } catch ( RtError& e ) { std::cout << '\n' << e.getMessage() << '\n' << std::endl; goto cleanup; } cleanup: if ( adac.isStreamOpen() ) adac.closeStream(); return 0; } ================================================ FILE: rtaudio/tests/playraw.cpp ================================================ /******************************************/ /* playraw.cpp by Gary P. Scavone, 2007 Play a specified raw file. It is necessary that the file be of the same data format as defined below. */ /******************************************/ #include "RtAudio.h" #include #include #include #include /* typedef char MY_TYPE; #define FORMAT RTAUDIO_SINT8 #define SCALE 127.0 */ typedef signed short MY_TYPE; #define FORMAT RTAUDIO_SINT16 #define SCALE 32767.0 /* typedef S24 MY_TYPE; #define FORMAT RTAUDIO_SINT24 #define SCALE 8388607.0 typedef signed int MY_TYPE; #define FORMAT RTAUDIO_SINT32 #define SCALE 2147483647.0 typedef float MY_TYPE; #define FORMAT RTAUDIO_FLOAT32 #define SCALE 1.0; typedef double MY_TYPE; #define FORMAT RTAUDIO_FLOAT64 #define SCALE 1.0; */ // Platform-dependent sleep routines. #if defined( __WINDOWS_ASIO__ ) || defined( __WINDOWS_DS__ ) #include #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) #else // Unix variants #include #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) ) #endif void usage( void ) { // Error function in case of incorrect command-line // argument specifications std::cout << "\nuseage: playraw N fs file \n"; std::cout << " where N = number of channels,\n"; std::cout << " fs = the sample rate, \n"; std::cout << " file = the raw file to play,\n"; std::cout << " device = optional device to use (default = 0),\n"; std::cout << " and channelOffset = an optional channel offset on the device (default = 0).\n\n"; exit( 0 ); } struct OutputData { FILE *fd; unsigned int channels; }; // Interleaved buffers int output( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *data ) { OutputData *oData = (OutputData*) data; // In general, it's not a good idea to do file input in the audio // callback function but I'm doing it here because I don't know the // length of the file we are reading. unsigned int count = fread( outputBuffer, oData->channels * sizeof( MY_TYPE ), nBufferFrames, oData->fd); if ( count < nBufferFrames ) { unsigned int bytes = (nBufferFrames - count) * oData->channels * sizeof( MY_TYPE ); unsigned int startByte = count * oData->channels * sizeof( MY_TYPE ); memset( (char *)(outputBuffer)+startByte, 0, bytes ); return 1; } return 0; } int main( int argc, char *argv[] ) { unsigned int channels, fs, bufferFrames, device = 0, offset = 0; char *file; // minimal command-line checking if ( argc < 4 || argc > 6 ) usage(); RtAudio dac; if ( dac.getDeviceCount() < 1 ) { std::cout << "\nNo audio devices found!\n"; exit( 0 ); } channels = (unsigned int) atoi( argv[1]) ; fs = (unsigned int) atoi( argv[2] ); file = argv[3]; if ( argc > 4 ) device = (unsigned int) atoi( argv[4] ); if ( argc > 5 ) offset = (unsigned int) atoi( argv[5] ); OutputData data; data.fd = fopen( file, "rb" ); if ( !data.fd ) { std::cout << "Unable to find or open file!\n"; exit( 1 ); } // Set our stream parameters for output only. bufferFrames = 512; RtAudio::StreamParameters oParams; oParams.deviceId = device; oParams.nChannels = channels; oParams.firstChannel = offset; data.channels = channels; try { dac.openStream( &oParams, NULL, FORMAT, fs, &bufferFrames, &output, (void *)&data ); dac.startStream(); } catch ( RtError& e ) { std::cout << '\n' << e.getMessage() << '\n' << std::endl; goto cleanup; } std::cout << "\nPlaying raw file " << file << " (buffer frames = " << bufferFrames << ")." << std::endl; while ( 1 ) { SLEEP( 100 ); // wake every 100 ms to check if we're done if ( dac.isStreamRunning() == false ) break; } cleanup: fclose( data.fd ); dac.closeStream(); return 0; } ================================================ FILE: rtaudio/tests/playsaw.cpp ================================================ /******************************************/ /* playsaw.cpp by Gary P. Scavone, 2006 This program will output sawtooth waveforms of different frequencies on each channel. */ /******************************************/ #include "RtAudio.h" #include #include /* typedef char MY_TYPE; #define FORMAT RTAUDIO_SINT8 #define SCALE 127.0 */ typedef signed short MY_TYPE; #define FORMAT RTAUDIO_SINT16 #define SCALE 32767.0 /* typedef S24 MY_TYPE; #define FORMAT RTAUDIO_SINT24 #define SCALE 8388607.0 typedef signed long MY_TYPE; #define FORMAT RTAUDIO_SINT32 #define SCALE 2147483647.0 typedef float MY_TYPE; #define FORMAT RTAUDIO_FLOAT32 #define SCALE 1.0 typedef double MY_TYPE; #define FORMAT RTAUDIO_FLOAT64 #define SCALE 1.0 */ // Platform-dependent sleep routines. #if defined( __WINDOWS_ASIO__ ) || defined( __WINDOWS_DS__ ) #include #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) #else // Unix variants #include #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) ) #endif #define BASE_RATE 0.005 #define TIME 1.0 void usage( void ) { // Error function in case of incorrect command-line // argument specifications std::cout << "\nuseage: playsaw N fs