From 586b0add5f1c71c08c9d7ee96cf45f5ad81c7947 Mon Sep 17 00:00:00 2001 From: Gary Scavone Date: Tue, 24 Mar 2009 23:02:13 -0400 Subject: [PATCH] Version 4.1.2 --- INSTALL | 7 +- README | 2 +- bin/treesed | 189 + configure.ac | 48 +- doc/Hierarchy.txt | 10 +- doc/README-Linux.txt | 2 +- doc/README-MacOSX.txt | 15 +- doc/README-NeXT.txt | 2 +- doc/README-SGI.txt | 2 +- doc/README-Win.txt | 2 +- doc/ReleaseNotes.txt | 18 +- doc/doxygen/Doxyfile | 123 +- doc/doxygen/compile.txt | 2 +- doc/doxygen/download.txt | 37 +- doc/doxygen/footer.html | 4 +- doc/doxygen/header.html | 4 +- doc/doxygen/index.txt | 5 +- doc/doxygen/links.txt | 10 +- doc/doxygen/maillist.txt | 2 + doc/doxygen/polyvoices.txt | 2 +- doc/doxygen/system.txt | 13 +- doc/treesed.html | 175 + include/Delay.h | 6 +- include/Instrmnt.h | 8 +- include/Noise.h | 16 +- include/RtAudio.h | 868 +-- include/RtDuplex.h | 15 +- include/RtError.h | 60 + include/RtWvIn.h | 7 +- include/RtWvOut.h | 9 +- include/SKINI.msg | 2 +- include/Stk.h | 67 +- include/TcpWvOut.h | 2 +- include/Voicer.h | 11 +- include/WvIn.h | 4 +- include/WvOut.h | 6 +- projects/demo/Makefile.in | 2 +- projects/demo/Md2Skini.cpp | 6 + projects/demo/Modal.bat | 2 +- projects/demo/Physical.bat | 2 +- projects/demo/StkDemo.bat | 2 +- projects/demo/Voice.bat | 2 +- projects/demo/demo.cpp | 22 +- projects/demo/tcl/Demo.tcl | 27 +- projects/demo/tcl/Modal.tcl | 27 +- projects/demo/tcl/Physical.tcl | 27 +- projects/effects/Effects.bat | 2 +- projects/effects/Makefile.in | 2 +- projects/examples/Makefile.in | 2 +- projects/examples/bethree.cpp | 3 +- projects/examples/controlbee.cpp | 3 +- projects/examples/io.cpp | 3 +- projects/examples/libMakefile.in | 77 + projects/examples/play.cpp | 10 +- projects/examples/sine.cpp | 3 +- projects/examples/threebees.cpp | 3 +- projects/ragamatic/Drone.cpp | 10 +- projects/ragamatic/Makefile.in | 4 +- projects/ragamatic/Raga.bat | 2 +- projects/ragamatic/Tabla.cpp | 12 +- projects/ragamatic/VoicDrum.cpp | 12 +- rawwaves/makefunc.c | 110 +- rawwaves/makemidi.c | 66 +- rawwaves/makewavs.c | 232 +- src/ADSR.cpp | 4 +- src/BandedWG.cpp | 20 +- src/BeeThree.cpp | 19 +- src/BlowBotl.cpp | 21 +- src/BlowHole.cpp | 21 +- src/Bowed.cpp | 21 +- src/Brass.cpp | 23 +- src/Chorus.cpp | 16 +- src/Clarinet.cpp | 21 +- src/Delay.cpp | 16 +- src/DelayA.cpp | 7 +- src/DelayL.cpp | 6 +- src/Drummer.cpp | 20 +- src/Echo.cpp | 10 +- src/FM.cpp | 25 +- src/FMVoices.cpp | 29 +- src/Flute.cpp | 23 +- src/HevyMetl.cpp | 19 +- src/Instrmnt.cpp | 13 +- src/JCRev.cpp | 2 +- src/Makefile.in | 5 +- src/Mandolin.cpp | 60 +- src/Mesh2D.cpp | 32 +- src/Messager.cpp | 10 +- src/Modal.cpp | 25 +- src/ModalBar.cpp | 23 +- src/Modulate.cpp | 7 +- src/Moog.cpp | 28 +- src/NRev.cpp | 2 +- src/Noise.cpp | 20 +- src/PRCRev.cpp | 8 +- src/PercFlut.cpp | 19 +- src/Phonemes.cpp | 20 +- src/PitShift.cpp | 6 +- src/PluckTwo.cpp | 10 +- src/Plucked.cpp | 14 +- src/Resonate.cpp | 22 +- src/Rhodey.cpp | 19 +- src/RtAudio.cpp | 8762 +++++++++++++++++------------- src/RtDuplex.cpp | 112 +- src/RtMidi.cpp | 189 +- src/RtWvIn.cpp | 58 +- src/RtWvOut.cpp | 67 +- src/Saxofony.cpp | 21 +- src/Shakers.cpp | 14 +- src/Simple.cpp | 19 +- src/Sitar.cpp | 10 +- src/StifKarp.cpp | 26 +- src/Stk.cpp | 20 +- src/Table.cpp | 6 +- src/TcpWvIn.cpp | 8 +- src/TcpWvOut.cpp | 10 +- src/Thread.cpp | 1 - src/TubeBell.cpp | 19 +- src/VoicForm.cpp | 23 +- src/Voicer.cpp | 20 +- src/WaveLoop.cpp | 5 + src/Whistle.cpp | 21 +- src/Wurley.cpp | 19 +- src/WvIn.cpp | 47 +- src/WvOut.cpp | 65 +- 125 files changed, 7301 insertions(+), 5337 deletions(-) create mode 100755 bin/treesed create mode 100644 doc/treesed.html create mode 100644 include/RtError.h create mode 100644 projects/examples/libMakefile.in diff --git a/INSTALL b/INSTALL index ece1580..8c67f9e 100644 --- a/INSTALL +++ b/INSTALL @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. The Synthesis ToolKit in C++ can be used in a variety of ways, depending on your particular needs. Some people just choose the classes they need for a particular project and copy those to their project directory. Others like to compile and link to a library of object files. STK was not designed with one particular style of use in mind. @@ -19,14 +19,15 @@ Several options can be passed to configure, including: --disable-realtime = only compile generic non-realtime classes --enable-debug = enable various debug output --with-alsa = choose native ALSA API support (linux only) + --with-jack = choose native JACK server API support (linux only) --enable-midiator = enable native MS-124W MIDI support (linux only) -Typing "./configure --help" will display all the available options. In addition, it is possible to specify the RAWWAVES and INCLUDE paths to configure as (ex. to set to /home/gary/rawwaves and /home/gary/include): +At the moment, it is not possible to specify more than one Linux audio API, though this will change in the next release. Typing "./configure --help" will display all the available options. In addition, it is possible to specify the RAWWAVES and INCLUDE paths to configure as (ex. to set to /home/gary/rawwaves and /home/gary/include): ./configure RAWWAVE_PATH="/home/gary/rawwaves/" ./configure INCLUDE_PATH="/home/gary/include/" -The ending "/" is required for the RAWWAVES path. The default behavior will set a relative path that works for the project files included with the distribution (assuming they are not moved). +The ending "/" is required for the RAWWAVES path. The default behavior will set a relative path that works for the project files included with the distribution (assuming they are not moved). You can also change the RAWWAVE_PATH dynamically via the static Stk::setRawwavePath() function. If you wish to use a different compiler than that selected by configure, specify that compiler in the command line (ex. to use CC): diff --git a/README b/README index cee4bca..a8a494c 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. This distribution of the Synthesis ToolKit in C++ (STK) contains the following: diff --git a/bin/treesed b/bin/treesed new file mode 100755 index 0000000..06ff4dd --- /dev/null +++ b/bin/treesed @@ -0,0 +1,189 @@ +#!/usr/bin/perl + +# treesed +# Written January 1996 by Rick Jansen (rick@sara.nl) +# URL: http://www.sara.nl/rick + +# usage: treesed pattern1 pattern2 -tree +# treesed pattern1 pattern2 -files file1 file2 ... + +# example: treesed href HREF -files *.html + +# Treesed searches for pattern1 and replaces pattern1 by pattern2 +# if pattern2 supplied. If only pattern1 given treesed just searches. +# Treesed will search in all files and subdirectories of the current +# directory + + + +#-------------------------------------------------------- +# Parameters + +$DoEdit=0; +$search_pattern = $ARGV[0]; +$search_pattern =~ s/(\W)/\\$1/g; # escape regexp chars +shift; + +while ($#ARGV >= 0) { + + if ($ARGV[0] eq '-files') { + @temp_ls = @ARGV[1 .. $#ARGV]; + # Get list of files, skip dirs + foreach $file (@ARGV[1 .. $#ARGV]) { + if (-f $file) { + push(@ls, $file); + } + } + last; + } + elsif ($ARGV[0] eq '-tree') { + &Get_LS; + last; + } + + if (! -f $ARGV[0]) { + if (defined($replacement_pattern)) { + print "usage: treesed pattern1 -tree/-files \n"; + exit(1); + } + + $replacement_pattern = $ARGV[0]; + #$replacement_pattern =~ s/(\W)/\\$1/g; # escape regexp chars + $DoEdit=1; + shift; + } + +} + +# No files? +if ($#ls < 0) { + print "xx No input files\n"; + exit(1); +} + +print "search_pattern: $search_pattern\n"; +print "replacement_pattern: $replacement_pattern\n"; +if ($DoEdit) { + print "\n** EDIT MODE!\n\n"; } +else { + print "\n** Search mode\n\n"; +} + +#foreach $file (@ls) { +# print "$file \n"; +#} + + +#-------------------------------------------------------- +# Search list of files for pattern + +$linepos=0; + +$| = 1; # Force flush after every write +foreach $file (@ls) { + #print "$file\n"; + print '.'; + $linepos++; + if ($linepos > 50) { + $linepos=0; + print "\n"; + } + + if (!open(FILE, $file)) { + print "\nCould not open $file\n"; + next; + } + + $Found = 0; + $Count = 0; + $lineno = 0; + @lines = (); + while () { + $lineno++; + if (/$search_pattern/i) { + #print; + $Count++; + $Found = 1; + push(@lines, $lineno); + } + } + close(FILE); + if ($Found) { + print "\n$file: $Count lines on: @lines\n"; + } + + if ($Found && $DoEdit) { &Edit($file); } + +} +$| = 0; +print "\n"; + + +exit(0); + + +#-------------------------------------------------------- +# Edit file + +sub Edit { + +# Replace $ARGV[0] with $ARGV[1] in $file + +local($file) = @_; +local($bakfile) = $file.'.'.$$; + +# First create backup +open(FILE, $file) || die "Could not open $file for read\n"; +open(BAKFILE, ">$bakfile") || die "Could not open $bakfile for backup\n"; +while () { + print BAKFILE; +} +close(BAKFILE); +close(FILE); + +# Now replace $ARGV[0] by $ARGV[1] in the backupfile, +# result into $file +open(BAKFILE, $bakfile) || die "Could not open $bakfile for read\n"; +open(FILE,">$file") || die "Could not open $file for write\n"; +$Count=0; +while () { + if (/$search_pattern/i) { $Count++; } + s/$search_pattern/$replacement_pattern/gi; + print FILE; +} +close(BAKFILE); +close(FILE); + +print +"\nReplaced $search_pattern by $replacement_pattern on $Count lines in $file\n"; + +} #sub Edit + +#-------------------------------------------------------- + +sub Get_LS { + +# Get a list of full path names into array @ls + +local(@localls)=`ls -R1`; +local($item,$Dir); + +#print "localls: @localls\n"; +$Dir=''; +foreach $item (@localls) { + #print "$item\n"; + if ($item =~ /:$/) { + $Dir=$item; + chop($Dir); + $Dir =~ s/:$/\//; + } + else { + chop($item); + $item = $Dir.$item; + if ($item !~ /^\s*$/) { push(@ls, $item); } + } +} +@localls=(); + +} # sub Get_LS + diff --git a/configure.ac b/configure.ac index 27022a3..83767de 100644 --- a/configure.ac +++ b/configure.ac @@ -1,16 +1,12 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(STK, 4.1, gary@ccrma.stanford.edu, stk) +AC_INIT(STK, 4.1.2, gary@ccrma.stanford.edu, stk) AC_CONFIG_SRCDIR(src/Stk.cpp) AC_CONFIG_FILES(src/Makefile projects/demo/Makefile projects/effects/Makefile projects/ragamatic/Makefile projects/examples/Makefile) # Checks for programs. AC_PROG_CC -AC_PROG_CXX(CC g++ c++ cxx) +AC_PROG_CXX(g++ CC c++ cxx) AC_PROG_CXX -AC_PROG_RANLIB - -# Checks for libraries. -AC_CHECK_LIB(stdc++, printf, , AC_MSG_ERROR(Stk requires the C++ library!) ) # Checks for header files. AC_HEADER_STDC @@ -40,6 +36,9 @@ AC_MSG_CHECKING(whether to compile realtime support) AC_ARG_ENABLE(realtime, [ --disable-realtime = only compile generic non-realtime classes], [AC_SUBST( realtime, [no] ) AC_SUBST( sound_api, [] )], [AC_SUBST( realtime, [yes] ) ] ) AC_MSG_RESULT($realtime) +# Check for math library +AC_CHECK_LIB(m, cos, , AC_MSG_ERROR(math library is needed!)) + if test $realtime = yes; then AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(realtime support requires the pthread library!)) AC_CHECK_FUNCS(gettimeofday select socket) @@ -54,7 +53,7 @@ AC_ARG_ENABLE(debug, # Check compiler and use -Wall if gnu. if test $GXX = "yes" ; then - AC_SUBST( warn, [-Wall] ) + AC_SUBST( warn, ["-Wall -g"] ) fi if test $realtime = yes; then @@ -63,16 +62,43 @@ if test $realtime = yes; then AC_MSG_CHECKING(for audio API) case $host in *-*-linux*) - AC_ARG_WITH(alsa, [ --with-alsa = choose native ALSA API support (linux only)], [AC_SUBST( sound_api, [-D__LINUX_ALSA__] ) AC_MSG_RESULT(using ALSA) ], [AC_SUBST( sound_api, [-D__LINUX_OSS__] ) AC_MSG_RESULT(using OSS)]) + AC_SUBST( sound_api, [_NO_API_] ) + # Look for Jack flag + AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (linux only)], [AC_SUBST( sound_api, [-D__LINUX_JACK__] ) AC_MSG_RESULT(using JACK)] , ) + if [test $sound_api = -D__LINUX_JACK__;] then + TEMP_LIBS=$LIBS + AC_CHECK_LIB(jack, jack_client_new, , AC_MSG_ERROR(JACK support requires the jack library!)) + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!)) + LIBS="`pkg-config --cflags --libs jack` $TEMP_LIBS -lasound" + audio_apis="-D__LINUX_JACK__" + fi + + # Look for Alsa flag + AC_ARG_WITH(alsa, [ --with-alsa = choose native ALSA API support (linux only)], [AC_SUBST( sound_api, [-D__LINUX_ALSA__] ) AC_MSG_RESULT(using ALSA)], ) if test $sound_api = -D__LINUX_ALSA__; then AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!)) + audio_apis="-D__LINUX_ALSA__ $audio_apis" fi + + # Look for OSS flag + AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [AC_SUBST( sound_api, [-D__LINUX_OSS__] ) AC_MSG_RESULT(using OSS)], ) + if test $sound_api = -D__LINUX_OSS__; then + audio_apis="-D__LINUX_OSS__ $audio_apis" + fi + + # If no audio api flags specified, use OSS + if [test $sound_api = _NO_API_;] then + AC_SUBST( sound_api, [-D__LINUX_OSS__] ) + AC_MSG_RESULT(using OSS) + AC_SUBST( audio_apis, [-D__LINUX_OSS__] ) + fi + AC_ARG_ENABLE(midiator, [ --enable-midiator = enable native MS-124W MIDI support (linux only)], [AC_SUBST( midiator, [-D__MIDIATOR__] )], [AC_SUBST( midiator, [] )]) ;; *-sgi*) - AC_SUBST( sound_api, [-D__IRIX_AL__] ) + AC_SUBST( audio_apis, ["-D__IRIX_AL__ -LANG:std -w"] ) AC_MSG_RESULT(using IRIX AL) AC_CHECK_LIB(audio, alOpenPort, , AC_MSG_ERROR(IRIX audio support requires the audio library!) ) AC_CHECK_LIB(md, mdOpenInPort, , AC_MSG_ERROR(IRIX MIDI support requires the md library!) ) @@ -81,9 +107,11 @@ if test $realtime = yes; then *-apple*) # Check for CoreAudio and CoreMIDI framework AC_CHECK_HEADERS(CoreAudio/CoreAudio.h CoreMIDI/CoreMIDI.h CoreServices/CoreServices.h, - [AC_SUBST( sound_api, [-D__MACOSX_CORE__] )], + [AC_SUBST( audio_apis, [-D__MACOSX_CORE__] )], [AC_MSG_ERROR(CoreAudio and/or CoreMIDI header files not found!)] ) AC_SUBST( frameworks, ["-framework CoreAudio -framework CoreMIDI -framework CoreFoundation"] ) + # Explicitly link with c++ library. + AC_CHECK_LIB(stdc++, printf, , AC_MSG_ERROR(Stk requires the C++ library!) ) ;; *) diff --git a/doc/Hierarchy.txt b/doc/Hierarchy.txt index e3bc2c4..7952a3e 100644 --- a/doc/Hierarchy.txt +++ b/doc/Hierarchy.txt @@ -1,6 +1,6 @@ STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. STK Classes - See the HTML documentation in the html directory for complete information. @@ -77,7 +77,7 @@ Sources: Envelope.cpp Linearly Goes to Target by Rate TcpWvIn.cpp Audio Streaming (socket server) Input Class (subclass of WvIn) Sinks: WvOut.cpp Output Master Class for RAW, WAV, SND (AU), AIFF, MAT-file files - RtWvOut.cpp Realtime Output Class (subclass of WvOut) + RtWvOut.cpp Realtime Audio Output Class (subclass of WvOut) TcpWvOut.cpp Audio Streaming (socket client) Output Class (subclass of WvOut) Duplex: RtDuplex.cpp Synchronous Realtime Audio Input/Output Class @@ -98,14 +98,14 @@ Non-Linear: JetTabl.cpp Cubic Jet Non-Linearity BowTabl.cpp x^(-3) Bow Non-Linearity ReedTabl.cpp One Breakpoint Saturating Reed Non-Linearity -Derived: Modulate.cpp Periodic and Random Vibrato: RawWvIn, SubNoise, OnePole +Derived: Modulate.cpp Periodic and Random Vibrato: WvIn, SubNoise, OnePole SingWave.cpp Looping wave table with randomness: Modulate, WaveLoop, Envelope ********** INSTRUMENTS AND ALGORITHMS ************** -Each Class will be listed either with all the unit generators it uses, -or the <> of which it is a flavor. All inherit from Instrmnt, +Each class is listed either with some of the unit generators it uses, +or in terms of the algorithm it implements. All inherit from Instrmnt, which inherits from Stk. Simple.cpp Simple Instrument Pulse oscillator + resonant filtered noise diff --git a/doc/README-Linux.txt b/doc/README-Linux.txt index cebef51..6c53ea2 100644 --- a/doc/README-Linux.txt +++ b/doc/README-Linux.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. Please read the file README and INSTALL for more general STK information. diff --git a/doc/README-MacOSX.txt b/doc/README-MacOSX.txt index ab9ba2d..9856452 100644 --- a/doc/README-MacOSX.txt +++ b/doc/README-MacOSX.txt @@ -1,12 +1,12 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. Please read the file README and INSTALL for more general STK information. Realtime support for Macintosh OS X uses the CoreAudio HAL API and is specified during compilation using the __MACOSX_CORE__ preprocessor definition. -It is necessary to download the OS X developer kit in order to compile STK. STK was successfully tested on OS X versions 10.1 and 10.2. +It is necessary to download the OS X developer kit in order to compile STK. STK was successfully tested on OS X version 10.2. The internal Macintosh audio hardware typically supports a sample rate of 44100 Hz only. Therefore, it is necessary to either specify this rate as a command-line option to the STK example programs or to change the default sample rate inside the Stk.h file before compilation. In addition, the RT_BUFFER_SIZE, specified in Stk.h, could be increased (to a higher power of two) for more robust performance. @@ -15,15 +15,8 @@ There is a potential conflict between the STK Delay class and a Delay() function Tcl/Tk on OS X: -The tcl/tk interpreter does not ship by default with OS X, but must be downloaded from the internet. Binary distributions exist but it is instead recommended that you download recent tcl and tk source distributions (http://dev.scriptics.com/software/tcltk/downloadnow84.tml) and compile them as follows: - - make -C tcl/macosx deploy - make -C tk/macosx deploy - sudo make -C tcl/macosx install-deploy - sudo make -C tk/macosx install-deploy - -(Note: the tcl and tk directories specified in the above lines will more likely be appended with version numbers) The default installation will place a link to the wish interpretor at /usr/bin/wish. The latest 8.4.1 release of tcl/tk has been tested on a 10.2 system and found to work correctly. In particular, redirection of a tcl/tk script to the interpreter (e.g., wish < test.tcl) works normally (which is not the case with binary distributions tested thus far). +The tcl/tk interpreter does not ship by default with OS X, but must be downloaded from the internet. The latest Tcl/Tk Aqua distribution (http://www.apple.com/downloads/macosx/unix_open_source/tcltk.html) has been successfully tested on a 10.2 system. The default installation will place a link to the wish interpretor at /usr/bin/wish. Initial tests have shown somewhat poor response between changes made in the tcl/tk script and the resulting audio updates. -Also, it is not recommended to connect by socket from a tcl/tk script to an STK program because the tcl/tk interpreter does not appear to properly close the socket connection, leaving the STK program in a "hung" state. +It is possible to connect a tcl/tk interface to an STK program via a socket connection. However, the tcl/tk interpreter does not appear to properly close the socket connection during disconnection. It is therefore necessary to type "Exit" in the STK program terminal window to properly exit the STK program. diff --git a/doc/README-NeXT.txt b/doc/README-NeXT.txt index 3ded5db..78f8d24 100644 --- a/doc/README-NeXT.txt +++ b/doc/README-NeXT.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. Please read the file README and INSTALL for more general STK information. diff --git a/doc/README-SGI.txt b/doc/README-SGI.txt index 0a77638..5be5d16 100644 --- a/doc/README-SGI.txt +++ b/doc/README-SGI.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. Please read the file README and INSTALL for more general STK information. diff --git a/doc/README-Win.txt b/doc/README-Win.txt index a4c3da2..b03e66b 100644 --- a/doc/README-Win.txt +++ b/doc/README-Win.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. Please read the file README for more general STK information. diff --git a/doc/ReleaseNotes.txt b/doc/ReleaseNotes.txt index 5efc281..5f2b3da 100644 --- a/doc/ReleaseNotes.txt +++ b/doc/ReleaseNotes.txt @@ -1,6 +1,20 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2002. +By Perry R. Cook and Gary P. Scavone, 1995-2004. + +v4.1.2: (15 March 2004) +- added Linux JACK support to RtAudio +- added optional doNormalize argument to WvIn to allow specification of data normalization or not +- added volume control to demo program and various tcl scripts +- added support for dynamic rawwavePath() setting +- WaveLoop bug fix +- fixed bug in ADSR::setReleaseTime() method +- fixed missing initialization of apInput in non-default constructor of DelayA class +- added time seeding of random number generator to Noise constructor +- update to the contentsAt() method of Delay class +- WAV file fixes (8-bit) in WvIn and WvOut classes +- configure changes +- updated include statements and appended "std::" as necessary throughout for compatibility with gcc 3 v4.1.1: (24 October 2002) - bug fix in RtAudio for Macintosh OS X and Windows ASIO duplex operation @@ -122,4 +136,4 @@ v1.0: v0.8: -- One of (if not THE) original distributions for SGI, NeXTStep, and basic Win support. I think this came out in 1996. \ No newline at end of file +- One of (if not THE) original distributions for SGI, NeXTStep, and basic Win support. I think this came out in 1996. diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index ecce779..c829856 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -1,44 +1,56 @@ -# Doxyfile 1.2.6 +# Doxyfile 1.3.6 #--------------------------------------------------------------------------- -# General configuration options +# Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = STK PROJECT_NUMBER = OUTPUT_DIRECTORY = . OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ALWAYS_DETAILED_SEC = NO -FULL_PATH_NAMES = NO -STRIP_FROM_PATH = +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -CLASS_DIAGRAMS = YES -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO -VERBATIM_HEADERS = YES SHOW_INCLUDE_FILES = YES -JAVADOC_AUTOBRIEF = NO -INHERIT_DOCS = YES INLINE_INFO = YES SORT_MEMBER_DOCS = NO -DISTRIBUTE_GROUP_DOC = NO -TAB_SIZE = 8 -ENABLED_SECTIONS = +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES -ALIASES = +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 -OPTIMIZE_OUTPUT_FOR_C = NO SHOW_USED_FILES = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages @@ -46,22 +58,37 @@ SHOW_USED_FILES = YES QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = . ../../include -FILE_PATTERNS = *.txt *.h +INPUT = . \ + ../../include +FILE_PATTERNS = *.txt \ + *.h \ + *.cpp RECURSIVE = YES -EXCLUDE = -EXCLUDE_PATTERNS = +EXCLUDE = ../../src/asio +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO @@ -72,11 +99,14 @@ IGNORE_PREFIX = #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = ../html +HTML_FILE_EXTENSION = .html HTML_HEADER = header.html HTML_FOOTER = footer.html HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO @@ -89,6 +119,8 @@ TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- GENERATE_LATEX = YES LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = letter EXTRA_PACKAGES = @@ -96,6 +128,7 @@ LATEX_HEADER = header.tex PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -104,12 +137,33 @@ RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- @@ -121,35 +175,38 @@ INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- -# Configuration::addtions related to external references +# Configuration::additions related to external references #--------------------------------------------------------------------------- -TAGFILES = +TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png DOT_PATH = +DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 1024 MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 0 GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- -# Configuration::addtions related to the search engine +# Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO -CGI_NAME = search.cgi -CGI_URL = -DOC_URL = -DOC_ABSPATH = -BIN_ABSPATH = /usr/local/bin/ -EXT_DOC_PATHS = - diff --git a/doc/doxygen/compile.txt b/doc/doxygen/compile.txt index 38d87a8..32777d0 100644 --- a/doc/doxygen/compile.txt +++ b/doc/doxygen/compile.txt @@ -58,7 +58,7 @@ g++ -Wall -D__LITTLE_ENDIAN__ -o sineosc Stk.cpp WvIn.cpp WaveLoop.cpp WvOut.cpp Note that the sineosc.cpp example does not make use of realtime audio or MIDI input/output classes. For programs using any of the STK realtime classes mentioned above, it is necessary to specify an audio/MIDI API preprocessor definition and link with the appropriate libraries or frameworks. -When working with a number of different projects that make use of ToolKit classes, the above approach can become cumbersome (especially when trying to synchronize with new STK releases). The example STK projects (e.g., demo, effects, ...) contain Makefiles (built by the configure script) which compile project-specific class objects from the distribution src and include directories. This approach makes it relatively easy when upgrading to a new STK release (by making path substitutions in the Makefile or by moving the projects to a similar relative path within the new STK source tree). A Makefile of this sort is provided in the projects/examples directory for compiling all the tutorial programs, as well as other example programs. To compile the sineosc.cpp program, for example, one need only type make sineosc from within the projects/examples directory. +When working with a number of different projects that make use of ToolKit classes, the above approach can become cumbersome (especially when trying to synchronize with new STK releases). Most of the STK projects (e.g., demo, effects, ...) contain Makefiles (built by the configure script) which compile project-specific class objects from the distribution src and include directories. This approach makes it relatively easy when upgrading to a new STK release (by making path substitutions in the Makefile or by moving the projects to a similar relative path within the new STK source tree). A Makefile is provided in the projects/examples directory for compiling all the tutorial programs, as well as other example programs. To compile the sineosc.cpp program, for example, one need only type make sineosc from within the projects/examples directory. Note that this particular Makefile depends on a static library, as described in the next section. \subsection library Library Use: diff --git a/doc/doxygen/download.txt b/doc/doxygen/download.txt index 13caf23..d988c52 100644 --- a/doc/doxygen/download.txt +++ b/doc/doxygen/download.txt @@ -1,26 +1,41 @@ /*! \page download Download and Release Notes -Version 4.1.1, 24 October 2002

+Version 4.1.2, 15 March 2004

\section notes Release Notes: -\subsection v4dot1dot3 Version 4.1.1 +\subsection v4dot1dot2 Version 4.1.2
    -
  • Bug fix in RtAudio for Macintosh OS X and Windows ASIO duplex operation.
  • -
  • Windows ASIO fix in Stk.h.
  • -
  • Documentation updates.
  • -
  • Expanded tutorial.
  • -
  • Fixed RtDuplex omission in src Makefile.
  • +
  • Added Linux JACK support to RtAudio.
  • +
  • Added optional doNormalize argument to WvIn to allow specification of data normalization or not.
  • +
  • Added volume control to demo program and various tcl scripts.
  • +
  • Added support for dynamic rawwavePath() setting.
  • +
  • WaveLoop bug fix.
  • +
  • Fixed bug in ADSR::setReleaseTime() method.
  • +
  • Fixed missing initialization of apInput in non-default constructor of DelayA class.
  • +
  • Added time seeding of random number generator to Noise constructor.
  • +
  • Update to the contentsAt() method of Delay class.
  • +
  • WAV file fixes (8-bit) in WvIn and WvOut classes.
  • +
  • Configure script changes.
  • +
  • Updated include statements and appended "std::" as necessary throughout for compatibility with gcc 3.
+\subsection v4dot1dot1 Version 4.1.1 +
    +
  • Bug fix in RtAudio for Macintosh OS X and Windows ASIO duplex operation.
  • +
  • Windows ASIO fix in Stk.h.
  • +
  • Documentation updates.
  • +
  • Expanded tutorial.
  • +
  • Fixed RtDuplex omission in src Makefile.
  • +
+ \subsection v4dot1 Version 4.1
    diff --git a/doc/doxygen/footer.html b/doc/doxygen/footer.html index 42d2d2a..5f091b7 100644 --- a/doc/doxygen/footer.html +++ b/doc/doxygen/footer.html @@ -2,8 +2,8 @@ - +
    The Synthesis ToolKit in C++ (STK)
    ©1995-2002 Perry R. Cook and Gary P. Scavone. All Rights Reserved.
    ©1995-2004 Perry R. Cook and Gary P. Scavone. All Rights Reserved.
    - \ No newline at end of file + diff --git a/doc/doxygen/header.html b/doc/doxygen/header.html index 94392f0..6c35998 100644 --- a/doc/doxygen/header.html +++ b/doc/doxygen/header.html @@ -5,6 +5,6 @@
    -  

    +    

    Home   Information   Classes   Download   Usage   Mail List   Requirements   Links   Tutorial

    -
    \ No newline at end of file +
    diff --git a/doc/doxygen/index.txt b/doc/doxygen/index.txt index 793ee50..63bdde7 100644 --- a/doc/doxygen/index.txt +++ b/doc/doxygen/index.txt @@ -2,6 +2,8 @@ +

    Perry R. Cook & Gary P. Scavone

    + The Synthesis ToolKit in C++ (STK) is a set of open source audio signal processing and algorithmic synthesis classes written in C++. STK was designed to facilitate rapid development of music synthesis and audio processing software, with an emphasis on cross-platform functionality, realtime control, ease of use, and educational example code. The Synthesis ToolKit is extremely portable (it's mostly platform-independent C and C++ code), and it's completely user-extensible (all source included, no unusual libraries, and no hidden drivers). We like to think that this increases the chances that our programs will still work in another 5-10 years. In fact, the ToolKit has been working continuously for nearly 8 years now. STK currently runs with "realtime" support (audio and MIDI) on SGI (Irix), Linux, Macintosh OS X, and Windows computer platforms. Generic, non-realtime support has been tested under NeXTStep, Sun, and other platforms and should work with any standard C++ compiler. - \ref information @@ -18,4 +20,5 @@ The Synthesis ToolKit in C++ (STK) is a set of open source audio signal p

    STK is a registered trademark of Analytical Graphics, Inc., 40 General Warren Blvd., Malvern, PA 19355, the manufacturer of the Satellite Tool KitŪ (STKŪ) family of satellite simulation software. Although the term "STK" is used in this website, the content of this site is in no way related to Analytical Graphics, Inc, or its registered STK mark. Nothing in this website should be construed to mean that a business relationship, either past or present, exists between Analytical Graphics, Inc. and the owners of this particular website. - \ No newline at end of file + + diff --git a/doc/doxygen/links.txt b/doc/doxygen/links.txt index d0ae7f1..5a373dc 100644 --- a/doc/doxygen/links.txt +++ b/doc/doxygen/links.txt @@ -4,8 +4,16 @@ - Kern Scores: A Library of Electronic Musical Scores (with automatic conversion to SKINI format) +- MIDI to SKINI file converter by Craig Sapp + +- Kern Score to SKINI file converter by Craig Sapp + +- Calico - A Polyphonic Score File Parser for STK by Greg Kellum + - PeRColate: A Port of STK for Max/MSP - A Partial Port of STK to Squeak -*/ \ No newline at end of file +- AUStk: a demo of integration of STK instruments into an AudioUnit by Airy Andre + +*/ diff --git a/doc/doxygen/maillist.txt b/doc/doxygen/maillist.txt index c281d39..b49182d 100644 --- a/doc/doxygen/maillist.txt +++ b/doc/doxygen/maillist.txt @@ -2,6 +2,8 @@ An STK mailing list has been set up to facilitate communication among STK users. Subscribing to this list is your best way of keeping on top of new releases, bug fixes, and various user developments.

    +For answers to frequently asked questions, check the list archives. +

    To join send a message to <stk-request@ccrma.stanford.edu> with the contents: subscribe diff --git a/doc/doxygen/polyvoices.txt b/doc/doxygen/polyvoices.txt index d867c70..fd4ce62 100644 --- a/doc/doxygen/polyvoices.txt +++ b/doc/doxygen/polyvoices.txt @@ -108,7 +108,7 @@ int main() } \endcode -Assuming the program is compiled as threebees, the three-voice SKINI scorefile bachfugue.ski could be redirected to the program as: +Assuming the program is compiled as threebees, the three-voice SKINI scorefile bachfugue.ski (also located in the scores directory with the examples) could be redirected to the program as: \code threebees < bachfugue.ski diff --git a/doc/doxygen/system.txt b/doc/doxygen/system.txt index 1ae5326..cf4b7e9 100644 --- a/doc/doxygen/system.txt +++ b/doc/doxygen/system.txt @@ -15,17 +15,10 @@ Macintosh OS X (specific):

    • A C++ compiler does not ship by default with OS X. It is necessary to download the Developer Kit from the Apple WWW site in order to compile STK.
    • -
    • The internal Macintosh audio hardware typically supports a sample rate of 44100 Hz only. Therefore, it is necessary to either specify this rate as a command-line option to the STK example programs or to change the default sample rate inside the Stk.h file before compilation. In addition, the RT_BUFFER_SIZE, specified in Stk.h, could be increased (to a higher power of two) for more robust performance.
    • -
    • The tcl/tk interpreter does not ship by default with OS X, but must be downloaded from the internet. Binary distributions exist but it is instead recommended that you download recent tcl and tk source distributions (http://dev.scriptics.com/software/tcltk/downloadnow84.tml) and compile them as follows: +
    • IMPORTANT:The internal Macintosh audio hardware typically supports a sample rate of 44100 Hz only. Therefore, it is necessary to either specify this rate as a command-line option to the STK example programs or to change the default sample rate inside the Stk.h file before compilation. In addition, the RT_BUFFER_SIZE, specified in Stk.h, could be increased (to a higher power of two) for more robust performance.
    • +
    • The tcl/tk interpreter does not ship by default with OS X, but must be downloaded from the internet. The latest Tcl/Tk Aqua distribution (http://www.apple.com/downloads/macosx/unix_open_source/tcltk.html) has been successfully tested on a 10.2 system. The default installation will place a link to the wish interpretor at /usr/bin/wish. - make -C tcl/macosx deploy
      - make -C tk/macosx deploy
      - sudo make -C tcl/macosx install-deploy
      - sudo make -C tk/macosx install-deploy
      - -(Note: the tcl and tk directories specified in the above lines will more likely be appended with version numbers) The default installation will place a link to the wish interpretor at /usr/bin/wish. The latest 8.4.1 release of tcl/tk has been tested on a 10.2 system and found to work correctly. In particular, redirection of a tcl/tk script to the interpreter (e.g., wish < test.tcl) works normally (which is not the case with binary distributions tested thus far). - -Initial tests have shown somewhat poor response between changes made in the tcl/tk script and the resulting audio updates. Also, it is not recommended to connect by socket from a tcl/tk script to an STK program because the tcl/tk interpreter does not appear to properly close the socket connection, leaving the STK program in a "hung" state.
    • +Initial tests have shown somewhat poor response between changes made in the tcl/tk script and the resulting audio updates. It is possible to connect a tcl/tk interface to an STK program via a socket connection. However, the tcl/tk interpreter does not appear to properly close the socket connection during disconnection. It is therefore necessary to type "Exit" in the STK program terminal window to properly exit the STK program.
    diff --git a/doc/treesed.html b/doc/treesed.html new file mode 100644 index 0000000..6fb9187 --- /dev/null +++ b/doc/treesed.html @@ -0,0 +1,175 @@ + + +Treesed Usage + + + +
    + +

    How to Use Treesed

    + +Go to the directory where you want to search or make changes. + +

    +There are two choices you can make when using treesed: +

      +
    1. Do I just want to search for a text, or do I want to search for a +text and replace it with something else? +
      +If you are just searching you are using Treesed in "search mode", otherwise it is in +"replace mode." +
    2. Do I want to search/replace only in files in my current directory, +or should files in all subdirectories (and all directories below that) +also be done? +
    +Some examples will make this clear. + +

    Searching

    +Say you are faced with the situation that the author of a slew of web-pages, Nathan Brazil, has left and has been succeeded by Mavra Chang. First, let us see which files are affected by this (what you type in is shown in bold): +
    +
    [localhost] treesed "Nathan Brazil" -files *.html
    +search_pattern: Nathan\ Brazil
    +replacement_pattern: 
    +
    +** Search mode
    +
    +.
    +midnight.html: 1 lines on: 2
    +..
    +well.html: 1 lines on: 3
    +
    +
    +We notice the following: +
      +
    • The search text "Nathan Brazil" is enclosed in +double-quotes ("). +
    • You specify which files to search with -files followed by a +list of file names--in this case *.html. +
    • Treesed reports the search pattern ("pattern" is just a fancy word +for "text") you specified (you can ignore +that \). +
    • Treesed reports an empty replacement_pattern. This is +correct, because you haven't entered one. +
    • It therefore deduces that is is in search mode. +
    • It finds two files containing "Nathan Brazil", and reports on which +lines of these files it found it; it does not show the lines themselves. +
    +Because you used -files, Treesed will search in the files you +specify in the current directory. You can also search files in +the current directory and all directories below it. However, in +that case you can not specify which file names to use, all files will be +searched: +
    +
    [localhost] treesed "Nathan Brazil" -tree
    +search_pattern: Nathan\ Brazil
    +replacement_pattern: 
    +
    +** Search mode
    +
    +.
    +midnight.html: 1 lines on: 2
    +...
    +well.html: 1 lines on: 3
    +.
    +new/echoes.html: 1 lines on: 2
    +
    +
    +We notice the following: +
      +
    • Instead of -files we now see -tree. +
    • We do not see a specification of file names. +
    • Treesed finds an occurence of "Nathan Brazil" in the file +echoes.html in the subdirectory new; it did not +find this file in the previous example (as it shouldn't). +
    + +

    Replacing

    +To replace a text you simply add the replacement text right after the +search text: +
    +
    [localhost] treesed "Nathan Brazil" "Mavra Change" -files *.html
    +search_pattern: Nathan\ Brazil
    +replacement_pattern: Mavra Chang
    +
    +** EDIT MODE!
    +
    +.
    +midnight.html: 1 lines on: 2
    +
    +Replaced Nathan\ Brazil by Mavra Chang on 1 lines in midnight.html
    +..
    +well.html: 1 lines on: 3
    +
    +Replaced Nathan\ Brazil by Mavra Chang on 1 lines in well.html
    +
    +
    +We notice the following: +
      +
    • Right after the search text "Nathan Brazil" you specify the +replacement text "Mavra Chang". +
    • As a result, Treesed now reports a non-empty +replacement_pattern. +
    • Hence it concludes it is in "edit mode", which means replacment mode. +
    • Treesed dutifully reports on which lines in which files it did the +replacement. +
    +To replace a text in all files in the current directory and the ones +below it, we do the following: +
    +
    [localhost] treesed "Nathan Brazil" "Mavra Chang" -tree
    +search_pattern: Nathan\ Brazil
    +replacement_pattern: Mavra Chang
    +
    +** EDIT MODE!
    +
    +.
    +midnight.html: 1 lines on: 2
    +
    +Replaced Nathan\ Brazil by Mavra Chang on 1 lines in midnight.html
    +
    +....
    +well.html: 1 lines on: 3
    +
    +Replaced Nathan\ Brazil by Mavra Chang on 1 lines in well.html
    +.
    +new/echoes.html: 1 lines on: 2
    +
    +Replaced Nathan\ Brazil by Mavra Chang on 1 lines in new/echoes.html
    +
    +
    +and we get the expected results, including the replace in +new/echoes.html. + +

    Old Versions

    +Treesed leaves behind quite a mess of old versions of the files it +changed (only in change-mode, of course). These old files have the same +name as the original file, with .ddddd appended to it. For +example, if treesed makes a change to midnight.html it will +leave the original version as something like +midnight.html.26299. You'll have to remove these files lest +your disk area clutters up. Here is a command that does that, but +beware! This command removes all files in the current directory and +all below it, that end in a period followed by one or more +digits: +
    +
    find . -name "*.[0-9]*" -exec rm {} \;
    +
    +
    + +It is interesting to note that if you use treesed again without cleaning +up, you may get files like midnight.html.26299.27654. These +will also be cleaned up by the above slightly dangerous command. + + +

    About Treesed

    +treesed is public domain software developed +and designed by Rick Jansen from Sara, Amsterdam, Netherlands, January +1996. + +

    + +

    About This Document

    +This usage document was created by the Division of Information Technology Services at The +University of Western Ontario. + + \ No newline at end of file diff --git a/include/Delay.h b/include/Delay.h index 8371596..84dcff0 100644 --- a/include/Delay.h +++ b/include/Delay.h @@ -53,9 +53,11 @@ public: //! Return the value at \e tapDelay samples from the delay-line input. /*! - The valid range for \e tapDelay is 1 to the delay-line length. + The tap point is determined modulo the delay-line length and is + relative to the last input value (i.e., a tapDelay of zero returns + the last input value). */ - MY_FLOAT contentsAt(long tapDelay) const; + MY_FLOAT contentsAt(unsigned long tapDelay) const; //! Return the last computed output value. MY_FLOAT lastOut(void) const; diff --git a/include/Instrmnt.h b/include/Instrmnt.h index 7022740..e954baf 100644 --- a/include/Instrmnt.h +++ b/include/Instrmnt.h @@ -13,7 +13,7 @@ #define __INSTRMNT_H #include "Stk.h" -#include +#include class Instrmnt : public Stk { @@ -36,6 +36,12 @@ class Instrmnt : public Stk //! Return the last output value. MY_FLOAT lastOut() const; + //! Return the last left output value. + MY_FLOAT lastOutLeft() const; + + //! Return the last right output value. + MY_FLOAT lastOutRight() const; + //! Compute one output sample. virtual MY_FLOAT tick() = 0; diff --git a/include/Noise.h b/include/Noise.h index 0c58561..326b881 100644 --- a/include/Noise.h +++ b/include/Noise.h @@ -19,12 +19,26 @@ class Noise : public Stk { public: - //! Default constructor. + //! Default constructor which seeds the random number generator with the system time. Noise(); + //! Constructor which seeds the random number generator with a given seed. + /*! + If the seed value is zero, the random number generator is + seeded with the system time. + */ + Noise( unsigned int seed ); + //! Class destructor. virtual ~Noise(); + //! Seed the random number generator with a specific seed value. + /*! + If no seed is provided or the seed value is zero, the random + number generator is seeded with the current system time. + */ + void setSeed( unsigned int seed = 0 ); + //! Return a random number between -1.0 and 1.0 using rand(). virtual MY_FLOAT tick(); diff --git a/include/RtAudio.h b/include/RtAudio.h index a9a2a9a..2b2ac0d 100644 --- a/include/RtAudio.h +++ b/include/RtAudio.h @@ -1,16 +1,16 @@ /************************************************************************/ /*! \class RtAudio - \brief Realtime audio i/o C++ class. + \brief Realtime audio i/o C++ classes. RtAudio provides a common API (Application Programming Interface) - for realtime audio input/output across Linux (native ALSA and - OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound - and ASIO) operating systems. + for realtime audio input/output across Linux (native ALSA, Jack, + and OSS), SGI, Macintosh OS X (CoreAudio), and Windows + (DirectSound and ASIO) operating systems. - RtAudio WWW site: http://www-ccrma.stanford.edu/~gary/rtaudio/ + RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/ RtAudio: a realtime audio i/o C++ class - Copyright (c) 2001-2002 Gary P. Scavone + Copyright (c) 2001-2004 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -37,185 +37,164 @@ */ /************************************************************************/ -#if !defined(__RTAUDIO_H) +// RtAudio: Version 3.0, 1 March 2004 + +#ifndef __RTAUDIO_H #define __RTAUDIO_H -#include +#include "RtError.h" +#include +#include -#if defined(__LINUX_ALSA__) - #include - #include - #include - - typedef snd_pcm_t *AUDIO_HANDLE; - typedef int DEVICE_ID; - typedef pthread_t THREAD_HANDLE; - typedef pthread_mutex_t MUTEX; - -#elif defined(__LINUX_OSS__) - #include - #include - - typedef int AUDIO_HANDLE; - typedef int DEVICE_ID; - typedef pthread_t THREAD_HANDLE; - typedef pthread_mutex_t MUTEX; - -#elif defined(__WINDOWS_DS__) +// Operating system dependent thread functionality. +#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) #include #include - // The following struct is used to hold the extra variables - // specific to the DirectSound implementation. - typedef struct { - void * object; - void * buffer; - UINT bufferPointer; - } AUDIO_HANDLE; + typedef unsigned long ThreadHandle; + typedef CRITICAL_SECTION StreamMutex; - typedef LPGUID DEVICE_ID; - typedef unsigned long THREAD_HANDLE; - typedef CRITICAL_SECTION MUTEX; - -#elif defined(__WINDOWS_ASIO__) - #include - #include - - typedef int AUDIO_HANDLE; - typedef int DEVICE_ID; - typedef unsigned long THREAD_HANDLE; - typedef CRITICAL_SECTION MUTEX; - -#elif defined(__IRIX_AL__) - #include - #include - #include - - typedef ALport AUDIO_HANDLE; - typedef long DEVICE_ID; - typedef pthread_t THREAD_HANDLE; - typedef pthread_mutex_t MUTEX; - -#elif defined(__MACOSX_CORE__) - - #include +#else // Various unix flavors with pthread support. #include - typedef unsigned int AUDIO_HANDLE; - typedef AudioDeviceID DEVICE_ID; - typedef pthread_t THREAD_HANDLE; - typedef pthread_mutex_t MUTEX; + typedef pthread_t ThreadHandle; + typedef pthread_mutex_t StreamMutex; #endif - -/************************************************************************/ -/*! \class RtError - \brief Exception handling class for RtAudio. - - The RtError class is quite simple but it does allow errors to be - "caught" by RtError::TYPE. Almost all RtAudio methods can "throw" - an RtError, most typically if an invalid stream identifier is - supplied to a method or a driver error occurs. There are a number - of cases within RtAudio where warning messages may be displayed - but an exception is not thrown. There is a private RtAudio method, - error(), which can be modified to globally control how these - messages are handled and reported. -*/ -/************************************************************************/ - -class RtError -{ -public: - //! Defined RtError types. - enum TYPE { - WARNING, - DEBUG_WARNING, - UNSPECIFIED, - NO_DEVICES_FOUND, - INVALID_DEVICE, - INVALID_STREAM, - MEMORY_ERROR, - INVALID_PARAMETER, - DRIVER_ERROR, - SYSTEM_ERROR, - THREAD_ERROR - }; - -protected: - char error_message[256]; - TYPE type; - -public: - //! The constructor. - RtError(const char *p, TYPE tipe = RtError::UNSPECIFIED); - - //! The destructor. - virtual ~RtError(void); - - //! Prints "thrown" error message to stdout. - virtual void printMessage(void); - - //! Returns the "thrown" error message TYPE. - virtual const TYPE& getType(void) { return type; } - - //! Returns the "thrown" error message string. - virtual const char *getMessage(void) { return error_message; } -}; - - -// This public structure type is used to pass callback information +// This global structure type is used to pass callback information // between the private RtAudio stream structure and global callback // handling functions. -typedef struct { - void *object; // Used as a "this" pointer. - int streamId; - DEVICE_ID device[2]; - THREAD_HANDLE thread; - void *callback; - void *buffers; - unsigned long waitTime; - bool blockTick; - bool stopStream; +struct CallbackInfo { + void *object; // Used as a "this" pointer. + ThreadHandle thread; bool usingCallback; + void *callback; void *userData; -} CALLBACK_INFO; + void *apiInfo; // void pointer for API specific callback information + // Default constructor. + CallbackInfo() + :object(0), usingCallback(false), callback(0), + userData(0), apiInfo(0) {} +}; -// *************************************************** // +// Support for signed integers and floats. Audio data fed to/from +// the tickStream() routine 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. +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; /*!< Upper 3 bytes of 32-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 int (*RtAudioCallback)(char *buffer, int bufferSize, void *userData); + +//! The public device information structure for returning queried values. +struct RtAudioDeviceInfo { + std::string name; /*!< Character string device identifier. */ + bool probed; /*!< true if the device capabilities were successfully probed. */ + int outputChannels; /*!< Maximum output channels supported by device. */ + int inputChannels; /*!< Maximum input channels supported by device. */ + int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ + bool isDefault; /*!< true if this is the default output or input device. */ + std::vector sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ + RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ + + // Default constructor. + RtAudioDeviceInfo() + :probed(false), outputChannels(0), inputChannels(0), + duplexChannels(0), isDefault(false), nativeFormats(0) {} +}; + +// **************************************************************** // // -// RtAudio class declaration. +// RtApi class declaration. // -// *************************************************** // +// 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, RtApiAl, RtApiDs, or RtApiAsio). +// +// **************************************************************** // -class RtAudio +class RtApi { public: - // Support for signed integers and floats. Audio data fed to/from - // the tickStream() routine 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. - typedef unsigned long RTAUDIO_FORMAT; - static const RTAUDIO_FORMAT RTAUDIO_SINT8; /*!< 8-bit signed integer. */ - static const RTAUDIO_FORMAT RTAUDIO_SINT16; /*!< 16-bit signed integer. */ - static const RTAUDIO_FORMAT RTAUDIO_SINT24; /*!< Upper 3 bytes of 32-bit signed integer. */ - static const RTAUDIO_FORMAT RTAUDIO_SINT32; /*!< 32-bit signed integer. */ - static const RTAUDIO_FORMAT RTAUDIO_FLOAT32; /*!< Normalized between plus/minus 1.0. */ - static const RTAUDIO_FORMAT RTAUDIO_FLOAT64; /*!< Normalized between plus/minus 1.0. */ + RtApi(); + virtual ~RtApi(); + void openStream( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int numberOfBuffers ); + virtual void setStreamCallback( RtAudioCallback callback, void *userData ) = 0; + virtual void cancelStreamCallback() = 0; + int getDeviceCount(void); + RtAudioDeviceInfo getDeviceInfo( int device ); + char * const getStreamBuffer(); + virtual void tickStream() = 0; + virtual void closeStream(); + virtual void startStream() = 0; + virtual void stopStream() = 0; + virtual void abortStream() = 0; - //static const int MAX_SAMPLE_RATES = 14; - enum { MAX_SAMPLE_RATES = 14 }; +protected: - typedef int (*RTAUDIO_CALLBACK)(char *buffer, int bufferSize, void *userData); + static const unsigned int MAX_SAMPLE_RATES; + static const unsigned int SAMPLE_RATES[]; - //! The public device information structure for passing queried values. - typedef struct { - char name[128]; /*!< Character string device identifier. */ - DEVICE_ID id[2]; /* No value reported by getDeviceInfo(). */ - bool probed; /*!< true if the device capabilities were successfully probed. */ + enum { FAILURE, SUCCESS }; + + enum StreamMode { + OUTPUT, + INPUT, + DUPLEX, + UNINITIALIZED = -75 + }; + + enum StreamState { + STREAM_STOPPED, + STREAM_RUNNING + }; + + // A protected structure for audio streams. + struct RtApiStream { + 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 or RUNNING + char *userBuffer; + char *deviceBuffer; + bool doConvertBuffer[2]; // Playback and record, respectively. + bool deInterleave[2]; // Playback and record, respectively. + bool doByteSwap[2]; // Playback and record, respectively. + int sampleRate; + int bufferSize; + int nBuffers; + int nUserChannels[2]; // Playback and record, respectively. + int nDeviceChannels[2]; // Playback and record channels, respectively. + RtAudioFormat userFormat; + RtAudioFormat deviceFormat[2]; // Playback and record, respectively. + StreamMutex mutex; + CallbackInfo callbackInfo; + + RtApiStream() + :apiHandle(0), userBuffer(0), deviceBuffer(0) {} + // :apiHandle(0), mode(UNINITIALIZED), state(STREAM_STOPPED), + // userBuffer(0), deviceBuffer(0) {} + }; + + // A protected device structure for audio devices. + struct RtApiDevice { + std::string name; /*!< Character string device identifier. */ + bool probed; /*!< true if the device capabilities were successfully probed. */ + void *apiDeviceId; // void pointer for API specific device information int maxOutputChannels; /*!< Maximum output channels supported by device. */ int maxInputChannels; /*!< Maximum input channels supported by device. */ int maxDuplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ @@ -224,19 +203,136 @@ public: int minDuplexChannels; /*!< Minimum simultaneous input/output channels supported by device. */ bool hasDuplexSupport; /*!< true if device supports duplex mode. */ bool isDefault; /*!< true if this is the default output or input device. */ - int nSampleRates; /*!< Number of discrete rates or -1 if range supported. */ - int sampleRates[MAX_SAMPLE_RATES]; /*!< Supported rates or (min, max) if range. */ - RTAUDIO_FORMAT nativeFormats; /*!< Bit mask of supported data formats. */ - } RTAUDIO_DEVICE; + std::vector sampleRates; /*!< Supported sample rates. */ + RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ - //! The default constructor. + // Default constructor. + RtApiDevice() + :probed(false), apiDeviceId(0), maxOutputChannels(0), maxInputChannels(0), + maxDuplexChannels(0), minOutputChannels(0), minInputChannels(0), + minDuplexChannels(0), isDefault(false), nativeFormats(0) {} + }; + + typedef signed short Int16; + typedef signed int Int32; + typedef float Float32; + typedef double Float64; + + char message_[256]; + int nDevices_; + std::vector devices_; + RtApiStream stream_; + + /*! + Protected, api-specific method to count and identify the system + audio devices. This function MUST be implemented by all subclasses. + */ + virtual void initialize(void) = 0; + + /*! + Protected, api-specific method which attempts to fill an + RtAudioDevice structure for a given device. This function MUST be + implemented by all subclasses. If an error is encountered during + the probe, a "warning" message is reported and the value of + "probed" remains false (no exception is thrown). A successful + probe is indicated by probed = true. + */ + virtual void probeDeviceInfo( RtApiDevice *info ); + + /*! + Protected, api-specific method which 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 (no + exception is thrown). A successful probe is indicated by a return + value of SUCCESS. + */ + virtual bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); + + /*! + Protected method which returns the index in the devices array to + the default input device. + */ + virtual int getDefaultInputDevice(void); + + /*! + Protected method which returns the index in the devices array to + the default output device. + */ + virtual int getDefaultOutputDevice(void); + + //! Protected common method to clear an RtApiDevice structure. + void clearDeviceInfo( RtApiDevice *info ); + + //! Protected common method to clear an RtApiStream structure. + void clearStreamInfo(); + + //! Protected common error method to allow global control over error handling. + void error( RtError::Type type ); + + /*! + Protected common method used to check whether a stream is open. + If not, an "invalid identifier" exception is thrown. + */ + void verifyStream(); + + /*! + Protected method used to perform format, channel number, and/or interleaving + conversions between the user and device buffers. + */ + void convertStreamBuffer( StreamMode mode ); + + //! Protected common method used to perform byte-swapping on buffers. + void byteSwapBuffer( char *buffer, int samples, RtAudioFormat format ); + + //! Protected common method which returns the number of bytes for a given format. + int formatBytes( RtAudioFormat format ); +}; + + +// **************************************************************** // +// +// 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 RtAudioApi and its +// subclasses. RtAudio creates an instance of an RtAudioApi subclass +// based on the user's API choice. If no choice is made, RtAudio +// attempts to make a "logical" API selection. +// +// **************************************************************** // + +class RtAudio +{ +public: + + //! Audio API specifier arguments. + enum RtAudioApi { + UNSPECIFIED, /*!< Search for a working compiled API. */ + LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ + LINUX_OSS, /*!< The Linux Open Sound System API. */ + LINUX_JACK, /*!< The Linux Jack Low-Latency Audio Server API. */ + MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ + IRIX_AL, /*!< The Irix Audio Library API. */ + WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */ + WINDOWS_DS /*!< The Microsoft Direct Sound API. */ + }; + + //! The default class constructor. /*! Probes the system to make sure at least one audio input/output device is available and determines the api-specific identifier for each device found. An RtError error can be thrown if no devices are found or if a memory allocation error occurs. + + If no API argument is specified and multiple API support has been + compiled, the default order of use is JACK, ALSA, OSS (Linux + systems) and ASIO, DS (Windows systems). */ - RtAudio(); + RtAudio( RtAudioApi api=UNSPECIFIED ); //! A constructor which can be used to open a stream during instantiation. /*! @@ -250,23 +346,21 @@ public: for the given parameters, if a memory allocation error occurs, or if a driver error occurs. \sa openStream() */ - RtAudio(int *streamId, - int outputDevice, int outputChannels, - int inputDevice, int inputChannels, - RTAUDIO_FORMAT format, int sampleRate, - int *bufferSize, int numberOfBuffers); + RtAudio( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int numberOfBuffers, RtAudioApi api=UNSPECIFIED ); //! The destructor. /*! - Stops and closes any open streams and devices and deallocates + Stops and closes an open stream and devices and deallocates buffer and structure memory. */ ~RtAudio(); //! A public method for opening a stream with the specified parameters. /*! - If successful, the opened stream ID is returned. Otherwise, an - RtError is thrown. + An RtError is thrown if a stream cannot be opened. \param outputDevice: If equal to 0, the default or first device found meeting the given parameters is opened. Otherwise, the @@ -280,7 +374,7 @@ public: the getDeviceInfo() method. \param inputChannels: The desired number of input channels. If equal to zero, the inputDevice identifier is ignored. - \param format: An RTAUDIO_FORMAT specifying the desired sample data format. + \param format: An RtAudioFormat specifying the desired sample data format. \param sampleRate: The desired sample rate (sample frames per second). \param *bufferSize: A pointer value indicating the desired internal buffer size in sample frames. The actual value used by the device is @@ -291,47 +385,47 @@ public: though at a cost of greater latency. A value of zero can be specified, in which case the lowest allowable value is used. */ - int openStream(int outputDevice, int outputChannels, - int inputDevice, int inputChannels, - RTAUDIO_FORMAT format, int sampleRate, - int *bufferSize, int numberOfBuffers); + void openStream( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int numberOfBuffers ); //! A public method which sets a user-defined callback function for a given stream. /*! - This method assigns a callback function to a specific, - previously opened stream for non-blocking stream functionality. A - separate process is initiated, though the user function is called - only when the stream is "running" (between calls to the - startStream() and stopStream() methods, respectively). The - callback process remains active for the duration of the stream and - is automatically shutdown when the stream is closed (via the - closeStream() method or by object destruction). The callback - process can also be shutdown and the user function de-referenced - through an explicit call to the cancelStreamCallback() method. - Note that a single stream can use only blocking or callback - functionality at the same time, though it is possible to alternate - modes on the same stream through the use of the - setStreamCallback() and cancelStreamCallback() methods (the - blocking tickStream() method can be used before a callback is set - and/or after a callback is cancelled). An RtError will be thrown - for an invalid device argument. + This method assigns a callback function to a previously opened + stream for non-blocking stream functionality. A separate process + is initiated, though the user function is called only when the + stream is "running" (between calls to the startStream() and + stopStream() methods, respectively). The callback process remains + active for the duration of the stream and is automatically + shutdown when the stream is closed (via the closeStream() method + or by object destruction). The callback process can also be + shutdown and the user function de-referenced through an explicit + call to the cancelStreamCallback() method. Note that the stream + can use only blocking or callback functionality at a particular + time, though it is possible to alternate modes on the same stream + through the use of the setStreamCallback() and + cancelStreamCallback() methods (the blocking tickStream() method + can be used before a callback is set and/or after a callback is + cancelled). An RtError will be thrown if called when no stream is + open or a thread errors occurs. */ - void setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData); + void setStreamCallback(RtAudioCallback callback, void *userData) { rtapi_->setStreamCallback( callback, userData ); }; - //! A public method which cancels a callback process and function for a given stream. + //! A public method which cancels a callback process and function for the stream. /*! This method shuts down a callback process and de-references the - user function for a specific stream. Callback functionality can + user function for the stream. Callback functionality can subsequently be restarted on the stream via the - setStreamCallback() method. An RtError will be thrown for an - invalid device argument. + setStreamCallback() method. An RtError will be thrown if called + when no stream is open. */ - void cancelStreamCallback(int streamId); + void cancelStreamCallback() { rtapi_->cancelStreamCallback(); }; //! A public method which returns the number of audio devices found. - int getDeviceCount(void); + int getDeviceCount(void) { return rtapi_->getDeviceCount(); }; - //! Fill a user-supplied RTAUDIO_DEVICE structure for a specified device number. + //! Return an RtAudioDeviceInfo structure for a specified device number. /*! Any device integer between 1 and getDeviceCount() is valid. If a device is busy or otherwise unavailable, the structure member @@ -340,185 +434,281 @@ public: or output device, the "isDefault" member will have a value of "true". An RtError will be thrown for an invalid device argument. */ - void getDeviceInfo(int device, RTAUDIO_DEVICE *info); + RtAudioDeviceInfo getDeviceInfo(int device) { return rtapi_->getDeviceInfo( device ); }; //! A public method which returns a pointer to the buffer for an open stream. /*! The user should fill and/or read the buffer data in interleaved format and then call the tickStream() method. An RtError will be - thrown for an invalid stream identifier. + thrown if called when no stream is open. */ - char * const getStreamBuffer(int streamId); + char * const getStreamBuffer() { return rtapi_->getStreamBuffer(); }; //! Public method used to trigger processing of input/output data for a stream. /*! This method blocks until all buffer data is read/written. An - RtError will be thrown for an invalid stream identifier or if - a driver error occurs. + RtError will be thrown if a driver error occurs or if called when + no stream is open. */ - void tickStream(int streamId); + void tickStream() { rtapi_->tickStream(); }; //! Public method which closes a stream and frees any associated buffers. /*! - If an invalid stream identifier is specified, this method - issues a warning and returns (an RtError is not thrown). + If a stream is not open, this method issues a warning and + returns (an RtError is not thrown). */ - void closeStream(int streamId); + void closeStream() { rtapi_->closeStream(); }; //! Public method which starts a stream. /*! - An RtError will be thrown for an invalid stream identifier - or if a driver error occurs. + An RtError will be thrown if a driver error occurs or if called + when no stream is open. */ - void startStream(int streamId); + void startStream() { rtapi_->startStream(); }; //! Stop a stream, allowing any samples remaining in the queue to be played out and/or read in. /*! - An RtError will be thrown for an invalid stream identifier - or if a driver error occurs. + An RtError will be thrown if a driver error occurs or if called + when no stream is open. */ - void stopStream(int streamId); + void stopStream() { rtapi_->stopStream(); }; //! Stop a stream, discarding any samples remaining in the input/output queue. /*! - An RtError will be thrown for an invalid stream identifier - or if a driver error occurs. + An RtError will be thrown if a driver error occurs or if called + when no stream is open. */ - void abortStream(int streamId); + void abortStream() { rtapi_->abortStream(); }; - //! Queries a stream to determine whether a call to the tickStream() method will block. - /*! - A return value of 0 indicates that the stream will NOT block. A positive - return value indicates the number of sample frames that cannot yet be - processed without blocking. - */ - int streamWillBlock(int streamId); -#if (defined(__MACOSX_CORE__) || defined(__WINDOWS_ASIO__)) + protected: + + void initialize( RtAudioApi api ); + + RtApi *rtapi_; +}; + + +// RtApi Subclass prototypes. + +#if defined(__LINUX_ALSA__) + +class RtApiAlsa: public RtApi +{ +public: + + RtApiAlsa(); + ~RtApiAlsa(); + void tickStream(); + void closeStream(); + void startStream(); + void stopStream(); + void abortStream(); + int streamWillBlock(); + void setStreamCallback( RtAudioCallback callback, void *userData ); + void cancelStreamCallback(); + + private: + + void initialize(void); + void probeDeviceInfo( RtApiDevice *info ); + bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); +}; + +#endif + +#if defined(__LINUX_JACK__) + +class RtApiJack: public RtApi +{ +public: + + RtApiJack(); + ~RtApiJack(); + void tickStream(); + void closeStream(); + void startStream(); + void stopStream(); + void abortStream(); + void setStreamCallback( RtAudioCallback callback, void *userData ); + void cancelStreamCallback(); // 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(int streamId, DEVICE_ID deviceId, void *inData, void *outData); + void callbackEvent( unsigned long nframes ); + + private: + + void initialize(void); + void probeDeviceInfo( RtApiDevice *info ); + bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); +}; + #endif -protected: +#if defined(__LINUX_OSS__) -private: +class RtApiOss: public RtApi +{ +public: - static const unsigned int SAMPLE_RATES[MAX_SAMPLE_RATES]; + RtApiOss(); + ~RtApiOss(); + void tickStream(); + void closeStream(); + void startStream(); + void stopStream(); + void abortStream(); + int streamWillBlock(); + void setStreamCallback( RtAudioCallback callback, void *userData ); + void cancelStreamCallback(); - enum { FAILURE, SUCCESS }; + private: - enum STREAM_MODE { - OUTPUT, - INPUT, - DUPLEX, - UNINITIALIZED = -75 - }; - - enum STREAM_STATE { - STREAM_STOPPED, - STREAM_RUNNING - }; - - typedef struct { - int device[2]; // Playback and record, respectively. - STREAM_MODE mode; // OUTPUT, INPUT, or DUPLEX. - AUDIO_HANDLE handle[2]; // Playback and record handles, respectively. - STREAM_STATE state; // STOPPED or RUNNING - char *userBuffer; - char *deviceBuffer; - bool doConvertBuffer[2]; // Playback and record, respectively. - bool deInterleave[2]; // Playback and record, respectively. - bool doByteSwap[2]; // Playback and record, respectively. - int sampleRate; - int bufferSize; - int nBuffers; - int nUserChannels[2]; // Playback and record, respectively. - int nDeviceChannels[2]; // Playback and record channels, respectively. - RTAUDIO_FORMAT userFormat; - RTAUDIO_FORMAT deviceFormat[2]; // Playback and record, respectively. - MUTEX mutex; - CALLBACK_INFO callbackInfo; - } RTAUDIO_STREAM; - - typedef signed short INT16; - typedef signed int INT32; - typedef float FLOAT32; - typedef double FLOAT64; - - char message[256]; - int nDevices; - RTAUDIO_DEVICE *devices; - - std::map streams; - - //! Private error method to allow global control over error handling. - void error(RtError::TYPE type); - - /*! - Private method to count the system audio devices, allocate the - RTAUDIO_DEVICE structures, and probe the device capabilities. - */ void initialize(void); - - /*! - Private method which returns the index in the devices array to - the default input device. - */ - int getDefaultInputDevice(void); - - /*! - Private method which returns the index in the devices array to - the default output device. - */ - int getDefaultOutputDevice(void); - - //! Private method to clear an RTAUDIO_DEVICE structure. - void clearDeviceInfo(RTAUDIO_DEVICE *info); - - /*! - Private method which attempts to fill an RTAUDIO_DEVICE - structure for a given device. If an error is encountered during - the probe, a "warning" message is reported and the value of - "probed" remains false (no exception is thrown). A successful - probe is indicated by probed = true. - */ - void probeDeviceInfo(RTAUDIO_DEVICE *info); - - /*! - Private method which attempts to open a device with the given parameters. - If an error is encountered during the probe, a "warning" message is - reported and FAILURE is returned (no exception is thrown). A - successful probe is indicated by a return value of SUCCESS. - */ - bool probeDeviceOpen(int device, RTAUDIO_STREAM *stream, - STREAM_MODE mode, int channels, - int sampleRate, RTAUDIO_FORMAT format, - int *bufferSize, int numberOfBuffers); - - /*! - Private common method used to check validity of a user-passed - stream ID. When the ID is valid, this method returns a pointer to - an RTAUDIO_STREAM structure (in the form of a void pointer). - Otherwise, an "invalid identifier" exception is thrown. - */ - void *verifyStream(int streamId); - - /*! - Private method used to perform format, channel number, and/or interleaving - conversions between the user and device buffers. - */ - void convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode); - - //! Private method used to perform byte-swapping on buffers. - void byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format); - - //! Private method which returns the number of bytes for a given format. - int formatBytes(RTAUDIO_FORMAT format); + void probeDeviceInfo( RtApiDevice *info ); + bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); }; +#endif + +#if defined(__MACOSX_CORE__) + +#include + +class RtApiCore: public RtApi +{ +public: + + RtApiCore(); + ~RtApiCore(); + int getDefaultOutputDevice(void); + int getDefaultInputDevice(void); + void tickStream(); + void closeStream(); + void startStream(); + void stopStream(); + void abortStream(); + void setStreamCallback( RtAudioCallback callback, void *userData ); + void cancelStreamCallback(); + + // 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( AudioDeviceID deviceId, void *inData, void *outData ); + + private: + + void initialize(void); + void probeDeviceInfo( RtApiDevice *info ); + bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); +}; + +#endif + +#if defined(__WINDOWS_DS__) + +class RtApiDs: public RtApi +{ +public: + + RtApiDs(); + ~RtApiDs(); + int getDefaultOutputDevice(void); + int getDefaultInputDevice(void); + void tickStream(); + void closeStream(); + void startStream(); + void stopStream(); + void abortStream(); + int streamWillBlock(); + void setStreamCallback( RtAudioCallback callback, void *userData ); + void cancelStreamCallback(); + + private: + + void initialize(void); + void probeDeviceInfo( RtApiDevice *info ); + bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); +}; + +#endif + +#if defined(__WINDOWS_ASIO__) + +class RtApiAsio: public RtApi +{ +public: + + RtApiAsio(); + ~RtApiAsio(); + void tickStream(); + void closeStream(); + void startStream(); + void stopStream(); + void abortStream(); + void setStreamCallback( RtAudioCallback callback, void *userData ); + void cancelStreamCallback(); + + // 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( long bufferIndex ); + + private: + + void initialize(void); + void probeDeviceInfo( RtApiDevice *info ); + bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); +}; + +#endif + +#if defined(__IRIX_AL__) + +class RtApiAl: public RtApi +{ +public: + + RtApiAl(); + ~RtApiAl(); + int getDefaultOutputDevice(void); + int getDefaultInputDevice(void); + void tickStream(); + void closeStream(); + void startStream(); + void stopStream(); + void abortStream(); + int streamWillBlock(); + void setStreamCallback( RtAudioCallback callback, void *userData ); + void cancelStreamCallback(); + + private: + + void initialize(void); + void probeDeviceInfo( RtApiDevice *info ); + bool probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ); +}; + +#endif + // Define the following flag to have extra information spewed to stderr. //#define __RTAUDIO_DEBUG__ diff --git a/include/RtDuplex.h b/include/RtDuplex.h index 7dea87c..1dd3fe8 100644 --- a/include/RtDuplex.h +++ b/include/RtDuplex.h @@ -85,14 +85,13 @@ public: protected: - RtAudio *audio; - MY_FLOAT *data; - MY_FLOAT *lastOutput; - int bufferSize; - bool stopped; - int stream; - long counter; - unsigned int channels; + RtAudio *audio_; + MY_FLOAT *data_; + MY_FLOAT *lastOutput_; + int bufferSize_; + bool stopped_; + long counter_; + unsigned int channels_; }; diff --git a/include/RtError.h b/include/RtError.h new file mode 100644 index 0000000..465cd75 --- /dev/null +++ b/include/RtError.h @@ -0,0 +1,60 @@ +/************************************************************************/ +/*! \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 + +class RtError +{ +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. */ + INVALID_STREAM, /*!< An invalid stream ID was specified. */ + MEMORY_ERROR, /*!< An error occured during memory allocation. */ + INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ + DRIVER_ERROR, /*!< A system driver error occured. */ + SYSTEM_ERROR, /*!< A system error occured. */ + THREAD_ERROR /*!< A thread error occured. */ + }; + +protected: + std::string message_; + Type type_; + +public: + //! The constructor. + RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type){} + + //! The destructor. + virtual ~RtError(void) {}; + + //! Prints thrown error message to stderr. + virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; } + + //! Returns the thrown error message type. + virtual const Type& getType(void) { return type_; } + + //! Returns the thrown error message string. + virtual const std::string& getMessage(void) { return message_; } + + //! Returns the thrown error message as a C string. + virtual const char *getMessageString(void) { return message_.c_str(); } +}; + +#endif diff --git a/include/RtWvIn.h b/include/RtWvIn.h index f9c46a0..5fa9195 100644 --- a/include/RtWvIn.h +++ b/include/RtWvIn.h @@ -90,10 +90,9 @@ public: protected: - RtAudio *audio; - bool stopped; - int stream; - long counter; + RtAudio *audio_; + bool stopped_; + long counter_; }; diff --git a/include/RtWvOut.h b/include/RtWvOut.h index 630e882..16e1b56 100644 --- a/include/RtWvOut.h +++ b/include/RtWvOut.h @@ -23,10 +23,12 @@ #include "WvOut.h" #include "RtAudio.h" +#include "Thread.h" class RtWvOut : protected WvOut { public: + //! Default constructor. /*! The \e device argument is passed to RtAudio during @@ -82,10 +84,9 @@ class RtWvOut : protected WvOut protected: - RtAudio *audio; - bool stopped; - int stream; - int bufferSize; + RtAudio *audio_; + bool stopped_; + int bufferSize_; }; diff --git a/include/SKINI.msg b/include/SKINI.msg index 3a45caa..1ec95db 100644 --- a/include/SKINI.msg +++ b/include/SKINI.msg @@ -29,7 +29,7 @@ #define __SK_ChannelPressure_ __SK_AfterTouch_ #define __SK_PitchWheel_ 224 #define __SK_PitchBend_ __SK_PitchWheel_ -#define __SK_PitchChange_ 249 +#define __SK_PitchChange_ 49 #define __SK_Clock_ 248 #define __SK_SongStart_ 250 diff --git a/include/Stk.h b/include/Stk.h index 0ee2133..43c36ed 100644 --- a/include/Stk.h +++ b/include/Stk.h @@ -3,10 +3,10 @@ \brief STK base class Nearly all STK classes inherit from this class. - The global sample rate can be queried and - modified via Stk. In addition, this class - provides error handling and byte-swapping - functions. + The global sample rate and rawwave path variables + can be queried and modified via Stk. In addition, + this class provides error handling and + byte-swapping functions. by Perry R. Cook and Gary P. Scavone, 1995 - 2002. */ @@ -15,11 +15,24 @@ #if !defined(__STK_H) #define __STK_H -// Most data in STK is passed and calculated with the following -// user-definable floating-point type. You can change this to "float" -// if you prefer or perhaps a "long double" in the future. +#include + +// Most data in STK is passed and calculated with the +// following user-definable floating-point type. You +// can change this to "float" if you prefer or perhaps +// a "long double" in the future. typedef double MY_FLOAT; +// The "MY_FLOAT" type will be deprecated in STK +// versions higher than 4.1.2 and replaced with the variable +// "StkFloat". +//typedef double StkFloat; +//#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) +// #pragma deprecated(MY_FLOAT) +//#else +// typedef StkFloat MY_FLOAT __attribute__ ((deprecated)); +//#endif + //! STK error handling class. /*! This is a fairly abstract exception handling class. There could @@ -74,8 +87,8 @@ public: static const STK_FORMAT STK_SINT8; /*!< -128 to +127 */ static const STK_FORMAT STK_SINT16; /*!< -32768 to +32767 */ static const STK_FORMAT STK_SINT32; /*!< -2147483648 to +2147483647. */ - static const STK_FORMAT STK_FLOAT32; /*!< Normalized between plus/minus 1.0. */ - static const STK_FORMAT STK_FLOAT64; /*!< Normalized between plus/minus 1.0. */ + static const STK_FORMAT MY_FLOAT32; /*!< Normalized between plus/minus 1.0. */ + static const STK_FORMAT MY_FLOAT64; /*!< Normalized between plus/minus 1.0. */ //! Static method which returns the current STK sample rate. static MY_FLOAT sampleRate(void); @@ -91,6 +104,12 @@ public: */ static void setSampleRate(MY_FLOAT newRate); + //! Static method which returns the current rawwave path. + static std::string rawwavePath(void); + + //! Static method which sets the STK rawwave path. + static void setRawwavePath(std::string newPath); + //! Static method which byte-swaps a 16-bit data type. static void swap16(unsigned char *ptr); @@ -105,6 +124,7 @@ public: private: static MY_FLOAT srate; + static std::string rawwavepath; protected: @@ -130,22 +150,23 @@ typedef double FLOAT64; #define TRUE 1 // The default sampling rate. -#define SRATE (MY_FLOAT) 22050.0 - -// Real-time audio input and output buffer size. If clicks are -// occuring in the input and/or output sound stream, a larger buffer -// size may help. Larger buffer sizes, however, produce more latency. +#define SRATE (MY_FLOAT) 44100.0 +// The default real-time audio input and output buffer size. If +// clicks are occuring in the input and/or output sound stream, a +// larger buffer size may help. Larger buffer sizes, however, produce +// more latency. #define RT_BUFFER_SIZE 512 -// The RAWWAVE_PATH definition is concatenated to the beginning of all -// references to rawwave files in the various STK core classes -// (ex. Clarinet.cpp). If you wish to move the rawwaves directory to -// a different location in your file system, you will need to set this -// path definition appropriately. The current definition is a -// relative reference that will work for the programs in the STK -// projects directory. The path can also be specified to configure and -// set via the Makefiles. +// The default rawwave path value is set with the preprocessor +// definition RAWWAVE_PATH. This can be specified as an argument to +// the configure script, in an integrated development environment, or +// below. The global STK rawwave path variable can be dynamically set +// with the Stk::setRawwavePath() function. This value is +// concatenated to the beginning of all references to rawwave files in +// the various STK core classes (ex. Clarinet.cpp). If you wish to +// move the rawwaves directory to a different location in your file +// system, you will need to set this path definition appropriately. #if !defined(RAWWAVE_PATH) #define RAWWAVE_PATH "../../rawwaves/" #endif @@ -158,7 +179,7 @@ typedef double FLOAT64; #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) #define __OS_WINDOWS__ #define __STK_REALTIME__ -#elif defined(__LINUX_OSS__) || defined(__LINUX_ALSA__) +#elif defined(__LINUX_OSS__) || defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) #define __OS_LINUX__ #define __STK_REALTIME__ #elif defined(__IRIX_AL__) diff --git a/include/TcpWvOut.h b/include/TcpWvOut.h index 25557bc..437d3d9 100644 --- a/include/TcpWvOut.h +++ b/include/TcpWvOut.h @@ -82,7 +82,7 @@ class TcpWvOut : protected WvOut protected: // Write a buffer of length \e frames via the socket connection. - void writeData( long frames ); + void writeData( unsigned long frames ); char msg[256]; char *buffer; diff --git a/include/Voicer.h b/include/Voicer.h index 61e874c..76912c0 100644 --- a/include/Voicer.h +++ b/include/Voicer.h @@ -112,12 +112,18 @@ public: //! Mix the output for all sounding voices. MY_FLOAT tick(); - //! Computer \e vectorSize output mixes and return them in \e vector. + //! Compute \e vectorSize output mixes and return them in \e vector. MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); //! Return the last output value. MY_FLOAT lastOut() const; + //! Return the last left output value. + MY_FLOAT lastOutLeft() const; + + //! Return the last right output value. + MY_FLOAT lastOutRight() const; + protected: typedef struct { @@ -135,7 +141,8 @@ protected: long tags; int muteTime; MY_FLOAT lastOutput; - + MY_FLOAT lastOutputLeft; + MY_FLOAT lastOutputRight; }; #endif diff --git a/include/WvIn.h b/include/WvIn.h index 0b2e06b..ba3d3c3 100644 --- a/include/WvIn.h +++ b/include/WvIn.h @@ -60,7 +60,7 @@ public: An StkError will be thrown if the file is not found, its format is unknown, or a read error occurs. */ - WvIn( const char *fileName, bool raw = FALSE ); + WvIn( const char *fileName, bool raw = FALSE, bool doNormalize = TRUE ); //! Class destructor. virtual ~WvIn(); @@ -70,7 +70,7 @@ public: An StkError will be thrown if the file is not found, its format is unknown, or a read error occurs. */ - void openFile( const char *fileName, bool raw = FALSE ); + void openFile( const char *fileName, bool raw = FALSE, bool doNormalize = TRUE ); //! If a file is open, close it. void closeFile(void); diff --git a/include/WvOut.h b/include/WvOut.h index 3fffec5..407bc63 100644 --- a/include/WvOut.h +++ b/include/WvOut.h @@ -83,19 +83,19 @@ class WvOut : public Stk //! Output a single sample to all channels in a sample frame. /*! - An StkError is thrown if a file read error occurs. + An StkError is thrown if a file write error occurs. */ virtual void tick(const MY_FLOAT sample); //! Output each sample in \e vector to all channels in \e vectorSize sample frames. /*! - An StkError is thrown if a file read error occurs. + An StkError is thrown if a file write error occurs. */ virtual void tick(const MY_FLOAT *vector, unsigned int vectorSize); //! Output the \e frameVector of sample frames of the given length. /*! - An StkError is thrown if a file read error occurs. + An StkError is thrown if a file write error occurs. */ virtual void tickFrame(const MY_FLOAT *frameVector, unsigned int frames = 1); diff --git a/projects/demo/Makefile.in b/projects/demo/Makefile.in index 8b34c20..c5fbcdd 100644 --- a/projects/demo/Makefile.in +++ b/projects/demo/Makefile.in @@ -40,7 +40,7 @@ LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) OBJECTS += RtMidi.o RtAudio.o RtWvOut.o Thread.o Socket.o - DEFS += @sound_api@ + DEFS += @audio_apis@ DEFS += @midiator@ endif diff --git a/projects/demo/Md2Skini.cpp b/projects/demo/Md2Skini.cpp index 0436f57..147f561 100644 --- a/projects/demo/Md2Skini.cpp +++ b/projects/demo/Md2Skini.cpp @@ -157,6 +157,12 @@ int main(int argc,char *argv[]) case __SK_ControlChange_: j = (int) byte2; switch(j) { + case __SK_PitchChange_: + sprintf(s,"PitchChange\t%.3f %d %.1f\n",0.0,channel,byte3); + if (writeFile) { + fprintf(file,"PitchChange\t%.3f %d %.1f\n",dt,channel,byte3); + } + break; case __SK_Volume_: sprintf(s,"Volume\t%.3f %d %.1f\n",0.0,channel,byte3); if (writeFile) { diff --git a/projects/demo/Modal.bat b/projects/demo/Modal.bat index 09d16b9..474234b 100755 --- a/projects/demo/Modal.bat +++ b/projects/demo/Modal.bat @@ -1 +1 @@ -wish < tcl/Modal.tcl | demo ModalBar -or -ip \ No newline at end of file +wish < tcl/Modal.tcl | ./demo ModalBar -or -ip \ No newline at end of file diff --git a/projects/demo/Physical.bat b/projects/demo/Physical.bat index ed11db8..f0e74fd 100755 --- a/projects/demo/Physical.bat +++ b/projects/demo/Physical.bat @@ -1 +1 @@ -wish < tcl/Physical.tcl | demo Clarinet -or -ip \ No newline at end of file +wish < tcl/Physical.tcl | ./demo Clarinet -or -ip \ No newline at end of file diff --git a/projects/demo/StkDemo.bat b/projects/demo/StkDemo.bat index c020db6..2a6ee42 100755 --- a/projects/demo/StkDemo.bat +++ b/projects/demo/StkDemo.bat @@ -1 +1 @@ -wish < tcl/Demo.tcl | demo Clarinet -or -ip \ No newline at end of file +wish < tcl/Demo.tcl | ./demo Clarinet -or -ip \ No newline at end of file diff --git a/projects/demo/Voice.bat b/projects/demo/Voice.bat index 9714b58..ecd3085 100755 --- a/projects/demo/Voice.bat +++ b/projects/demo/Voice.bat @@ -1 +1 @@ -wish < tcl/Voice.tcl | demo FMVoices -or -ip \ No newline at end of file +wish < tcl/Voice.tcl | ./demo FMVoices -or -ip \ No newline at end of file diff --git a/projects/demo/demo.cpp b/projects/demo/demo.cpp index 022dd30..2a0d2c0 100644 --- a/projects/demo/demo.cpp +++ b/projects/demo/demo.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include bool done; static void finish(int ignore){ done = true; } @@ -28,12 +28,13 @@ int main(int argc, char *argv[]) Reverb *reverb = 0; Voicer *voicer = 0; int i, nVoices = 1; + MY_FLOAT volume = 1.0; MY_FLOAT t60 = 1.0; // in seconds // If you want to change the default sample rate (set in Stk.h), do // it before instantiating any objects! If the sample rate is // specified in the command line, it will override this setting. - Stk::setSampleRate( 22050.0 ); + Stk::setSampleRate( 44100.0 ); // Check the command-line arguments for errors and to determine // the number of WvOut objects to be instantiated (in utilities.cpp). @@ -67,6 +68,11 @@ int main(int argc, char *argv[]) goto cleanup; } + // Set the number of ticks between realtime messages (default = + // RT_BUFFER_SIZE). + messager->setRtDelta( 64 ); + + // Set the reverb parameters. reverb = new PRCRev( t60 ); reverb->setEffectMix(0.2); @@ -86,7 +92,7 @@ int main(int argc, char *argv[]) nTicks = messager->getDelta(); for ( i=0; itick( voicer->tick() ); + sample = volume * reverb->tick( voicer->tick() ); for ( j=0; jtick(sample); } @@ -111,6 +117,10 @@ int main(int argc, char *argv[]) case __SK_ControlChange_: if (byte2 == 44.0) reverb->setEffectMix(byte3 * ONE_OVER_128); + else if (byte2 == 7.0) + volume = byte3 * ONE_OVER_128; + else if (byte2 == 49.0) + voicer->setFrequency( byte3 ); else voicer->controlChange( (int) byte2, byte3 ); break; @@ -127,6 +137,10 @@ int main(int argc, char *argv[]) voicer->pitchBend( byte2 ); break; + case __SK_Volume_: + volume = byte2 * ONE_OVER_128; + break; + case __SK_ProgramChange_: if ( voice != (int) byte2 ) { voicer->silence(); @@ -167,7 +181,7 @@ int main(int argc, char *argv[]) for ( i=0; i -#if defined(__OS_LINUX_) || defined(__OS_IRIX__) +#if defined(__OS_LINUX__) || defined(__OS_IRIX__) + #include #include #endif diff --git a/projects/examples/libMakefile.in b/projects/examples/libMakefile.in new file mode 100644 index 0000000..ffff809 --- /dev/null +++ b/projects/examples/libMakefile.in @@ -0,0 +1,77 @@ +### STK examples Makefile - for various flavors of unix + +PROGRAMS = sine play record io tcpIn tcpOut sineosc rtsine bethree controlbee foursine threebees +RM = /bin/rm + +INCLUDE = @include@ +ifeq ($(strip $(INCLUDE)), ) + INCLUDE = ../../include +endif +vpath %.h $(INCLUDE) + +CC = @CXX@ +DEFS = @byte_order@ +DEFS += @debug@ +CFLAGS = @cflags@ +CFLAGS += @warn@ -I$(INCLUDE) +LIBRARY = @LIBS@ +LIBRARY += @frameworks@ + +REALTIME = @realtime@ +ifeq ($(REALTIME),yes) + DEFS += @audio_apis@ + DEFS += @midiator@ +endif + +RAWWAVES = @rawwaves@ +ifeq ($(strip $(RAWWAVES)), ) + RAWWAVES = ../../rawwaves/ +endif +DEFS += -DRAWWAVE_PATH=\"$(RAWWAVES)\" + +all : $(PROGRAMS) + +$(OBJECTS) : Stk.h + +clean : + -rm $(PROGRAMS) + +strip : + strip $(PROGRAMS) + +#play: play.cpp Stk.o WvIn.o WvOut.o RtWvOut.o RtAudio.o +play: play.cpp + $(CC) $(CFLAGS) $(DEFS) -o play play.cpp -L../../src $(LIBRARY) -lstk + +record: record.cpp + $(CC) $(CFLAGS) $(DEFS) -o record record.cpp -L../../src $(LIBRARY) -lstk + +sine: sine.cpp + $(CC) $(CFLAGS) $(DEFS) -o sine sine.cpp -L../../src $(LIBRARY) -lstk + +io: io.cpp + $(CC) $(CFLAGS) $(DEFS) -o io io.cpp -L../../src $(LIBRARY) -lstk + +tcpIn: tcpIn.cpp + $(CC) $(CFLAGS) $(DEFS) -o tcpIn tcpIn.cpp -L../../src $(LIBRARY) -lstk + +tcpOut: tcpOut.cpp + $(CC) $(CFLAGS) $(DEFS) -o tcpOut tcpOut.cpp -L../../src $(LIBRARY) -lstk + +sineosc: sineosc.cpp + $(CC) $(CFLAGS) $(DEFS) -o sineosc sineosc.cpp -L../../src $(LIBRARY) -lstk + +rtsine: rtsine.cpp + $(CC) $(CFLAGS) $(DEFS) -o rtsine rtsine.cpp -L../../src $(LIBRARY) -lstk + +bethree: bethree.cpp + $(CC) $(CFLAGS) $(DEFS) -o bethree bethree.cpp -L../../src $(LIBRARY) -lstk + +controlbee: controlbee.cpp + $(CC) $(CFLAGS) $(DEFS) -o controlbee controlbee.cpp -L../../src $(LIBRARY) -lstk + +foursine: foursine.cpp + $(CC) $(CFLAGS) $(DEFS) -o foursine foursine.cpp -L../../src $(LIBRARY) -lstk + +threebees: threebees.cpp + $(CC) $(CFLAGS) $(DEFS) -o threebees threebees.cpp -L../../src $(LIBRARY) -lstk diff --git a/projects/examples/play.cpp b/projects/examples/play.cpp index daca503..8dd0271 100644 --- a/projects/examples/play.cpp +++ b/projects/examples/play.cpp @@ -44,12 +44,10 @@ int main(int argc, char *argv[]) exit(0); } - // Set the global STK sample rate to the file rate. - Stk::setSampleRate( input->getFileRate() ); - - // Set input read rate. + // Set input read rate based on the default STK sample rate. float rate = 1.0; - if ( argc == 3 ) rate = atof(argv[2]); + rate = input->getFileRate() / Stk::sampleRate(); + if ( argc == 3 ) rate *= atof(argv[2]); input->setRate( rate ); // Find out how many channels we have. @@ -57,7 +55,7 @@ int main(int argc, char *argv[]) // Define and open the realtime output device try { - output = new RtWvOut( channels, Stk::sampleRate(), 0, 512, 4 ); + output = new RtWvOut( channels, Stk::sampleRate(), 0, RT_BUFFER_SIZE, 4 ); } catch (StkError &) { goto cleanup; diff --git a/projects/examples/sine.cpp b/projects/examples/sine.cpp index 3c05b0b..66d892c 100644 --- a/projects/examples/sine.cpp +++ b/projects/examples/sine.cpp @@ -15,7 +15,6 @@ #include "WaveLoop.h" #include "WvOut.h" #include -#include void usage(void) { // Error function in case of incorrect command-line @@ -68,7 +67,7 @@ main(int argc, char *argv[]) // Define and open the soundfile for output. Other file // format options include: WVOUT_SND, WVOUT_AIF, WVOUT_MAT, // and WVOUT_RAW. Other data type options include: - // STK_SINT8, STK_SINT32, STK_FLOAT32, and STK_FLOAT64. + // STK_SINT8, STK_SINT32, MY_FLOAT32, and MY_FLOAT64. try { output = new WvOut( argv[2], channels, WvOut::WVOUT_WAV, Stk::STK_SINT16 ); } diff --git a/projects/examples/threebees.cpp b/projects/examples/threebees.cpp index b35ff3d..e3aee12 100644 --- a/projects/examples/threebees.cpp +++ b/projects/examples/threebees.cpp @@ -8,8 +8,9 @@ int main() { - // Set the global sample rate before creating class instances. + // Set the global sample rate and rawwave path before creating class instances. Stk::setSampleRate( 44100.0 ); + Stk::setRawwavePath( "../../rawwaves/" ); int i; RtWvOut *output = 0; diff --git a/projects/ragamatic/Drone.cpp b/projects/ragamatic/Drone.cpp index baec666..aae463d 100644 --- a/projects/ragamatic/Drone.cpp +++ b/projects/ragamatic/Drone.cpp @@ -49,7 +49,7 @@ void Drone :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Drone: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Drone: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -73,7 +73,7 @@ void Drone :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->pluck(amplitude); #if defined(_STK_DEBUG_) - cerr << "Drone: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Drone: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -81,16 +81,16 @@ void Drone :: noteOff(MY_FLOAT amplitude) { loopGain = (MY_FLOAT) 1.0 - amplitude; if ( loopGain < 0.0 ) { - cerr << "Drone: noteOff amplitude greater than 1.0!" << endl; + std::cerr << "Drone: noteOff amplitude greater than 1.0!" << std::endl; loopGain = 0.0; } else if ( loopGain > 1.0 ) { - cerr << "Drone: noteOff amplitude less than or zero!" << endl; + std::cerr << "Drone: noteOff amplitude less than or zero!" << std::endl; loopGain = (MY_FLOAT) 0.99999; } #if defined(_STK_DEBUG_) - cerr << "Drone: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Drone: NoteOff amplitude = " << amplitude << std::endl; #endif } diff --git a/projects/ragamatic/Makefile.in b/projects/ragamatic/Makefile.in index e5acf41..ee6d260 100644 --- a/projects/ragamatic/Makefile.in +++ b/projects/ragamatic/Makefile.in @@ -30,7 +30,7 @@ LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) OBJECTS += RtMidi.o RtAudio.o RtWvOut.o Thread.o Socket.o - DEFS += @sound_api@ + DEFS += @audio_apis@ DEFS += @midiator@ endif @@ -66,4 +66,4 @@ Drone.o: Drone.cpp $(CC) $(CFLAGS) $(DEFS) -c Drone.cpp -o $(OBJECT_PATH)/$@ VoicDrum.o: VoicDrum.cpp - $(CC) $(CFLAGS) $(DEFS) -c VoicDrum.cpp -o $(OBJECT_PATH)/$@ \ No newline at end of file + $(CC) $(CFLAGS) $(DEFS) -c VoicDrum.cpp -o $(OBJECT_PATH)/$@ diff --git a/projects/ragamatic/Raga.bat b/projects/ragamatic/Raga.bat index ddd2606..e19d574 100755 --- a/projects/ragamatic/Raga.bat +++ b/projects/ragamatic/Raga.bat @@ -1 +1 @@ -wish < tcl/Raga.tcl | ragamat -ip +wish < tcl/Raga.tcl | ./ragamat -ip diff --git a/projects/ragamatic/Tabla.cpp b/projects/ragamatic/Tabla.cpp index 08338a5..7ac9f54 100644 --- a/projects/ragamatic/Tabla.cpp +++ b/projects/ragamatic/Tabla.cpp @@ -40,16 +40,16 @@ Tabla :: ~Tabla() void Tabla :: noteOn(MY_FLOAT instrument, MY_FLOAT amplitude) { #if defined(_STK_DEBUG_) - cerr << "Tabla: NoteOn instrument = " << instrument << ", amplitude = " << amplitude << endl; + std::cerr << "Tabla: NoteOn instrument = " << instrument << ", amplitude = " << amplitude << std::endl; #endif MY_FLOAT gain = amplitude; if ( amplitude > 1.0 ) { - cerr << "Tabla: noteOn amplitude parameter is greater than 1.0!" << endl; + std::cerr << "Tabla: noteOn amplitude parameter is greater than 1.0!" << std::endl; gain = 1.0; } else if ( amplitude < 0.0 ) { - cerr << "Tabla: noteOn amplitude parameter is less than 0.0!" << endl; + std::cerr << "Tabla: noteOn amplitude parameter is less than 0.0!" << std::endl; return; } @@ -115,9 +115,9 @@ void Tabla :: noteOn(MY_FLOAT instrument, MY_FLOAT amplitude) } #if defined(_STK_DEBUG_) - cerr << "Number Sounding = " << nSounding << endl; - for (i=0; i -#include -#include - -void main() -{ - int i,j; - double temp; - double data[128]; - -/*************** TX81Z Master Gain *************/ - for (i=0;i<100;i++) { - data[i] = pow(2.0,-(99-i)/10.0); - } - data[0] = 0.0; - printf("double __FM4Op_gains[99] = {"); - for (i=0;i<100;i++) { - if (i%8 == 0) printf("\n"); - printf("%lf,",data[i]); - } - printf("};\n"); -/*************** TX81Z Sustain Level ***********/ - for (i=0;i<16;i++) { - data[i] = pow(2.0,-(15-i)/2.0); - } - data[0] = 0.0; - printf("double __FM4Op_susLevels[16] = {"); - for (i=0;i<16;i++) { - if (i%8 == 0) printf("\n"); - printf("%lf,",data[i]); - } - printf("};\n"); -/****************** Attack Rate ***************/ - for (i=0;i<32;i++) { - data[i] = 6.0 * pow(5.7,-(i-1)/5.0); - } - printf("double __FM4Op_attTimes[16] = {"); - for (i=0;i<32;i++) { - if (i%8 == 0) printf("\n"); - printf("%lf,",data[i]); - } - printf("};\n"); - exit(1); -} - +/**********************************************/ +/** Utility to make various functions **/ +/** like exponential and log gain curves. **/ +/** **/ +/** Included here: **/ +/** Yamaha TX81Z curves for master gain, **/ +/** Envelope Rates (in normalized units), **/ +/** envelope sustain level, and more.... **/ +/**********************************************/ + +#include +#include +#include + +void main() +{ + int i,j; + double temp; + double data[128]; + + /*************** TX81Z Master Gain *************/ + for (i=0;i<100;i++) { + data[i] = pow(2.0,-(99-i)/10.0); + } + data[0] = 0.0; + printf("double __FM4Op_gains[99] = {"); + for (i=0;i<100;i++) { + if (i%8 == 0) printf("\n"); + printf("%lf,",data[i]); + } + printf("};\n"); + /*************** TX81Z Sustain Level ***********/ + for (i=0;i<16;i++) { + data[i] = pow(2.0,-(15-i)/2.0); + } + data[0] = 0.0; + printf("double __FM4Op_susLevels[16] = {"); + for (i=0;i<16;i++) { + if (i%8 == 0) printf("\n"); + printf("%lf,",data[i]); + } + printf("};\n"); + /****************** Attack Rate ***************/ + for (i=0;i<32;i++) { + data[i] = 6.0 * pow(5.7,-(i-1)/5.0); + } + printf("double __FM4Op_attTimes[16] = {"); + for (i=0;i<32;i++) { + if (i%8 == 0) printf("\n"); + printf("%lf,",data[i]); + } + printf("};\n"); + exit(1); +} + diff --git a/rawwaves/makemidi.c b/rawwaves/makemidi.c index de68d25..480f638 100644 --- a/rawwaves/makemidi.c +++ b/rawwaves/makemidi.c @@ -1,33 +1,33 @@ -/**********************************************/ -/** Utility to make various functions **/ -/** like exponential and log gain curves. **/ -/** Specifically for direct MIDI parameter **/ -/** conversions. **/ -/** Included here: **/ -/** A440 Referenced Equal Tempered Pitches **/ -/** as a function of MIDI note number. **/ -/** **/ -/**********************************************/ - -#include -#include -#include - -void main() -{ - int i,j; - double temp; - double data[128]; - -/********* Pitch as fn. of MIDI Note **********/ - - printf("double __MIDI_To_Pitch[128] = {"); - for (i=0;i<128;i++) { - if (i%8 == 0) printf("\n"); - temp = 220.0 * pow(2.0,((double) i - 57) / 12.0); - printf("%.2lf,",temp); - } - printf("};\n"); - exit(1); -} - +/**********************************************/ +/** Utility to make various functions **/ +/** like exponential and log gain curves. **/ +/** Specifically for direct MIDI parameter **/ +/** conversions. **/ +/** Included here: **/ +/** A440 Referenced Equal Tempered Pitches **/ +/** as a function of MIDI note number. **/ +/** **/ +/**********************************************/ + +#include +#include +#include + +void main() +{ + int i,j; + double temp; + double data[128]; + + /********* Pitch as fn. of MIDI Note **********/ + + printf("double __MIDI_To_Pitch[128] = {"); + for (i=0;i<128;i++) { + if (i%8 == 0) printf("\n"); + temp = 220.0 * pow(2.0,((double) i - 57) / 12.0); + printf("%.2lf,",temp); + } + printf("};\n"); + exit(1); +} + diff --git a/rawwaves/makewavs.c b/rawwaves/makewavs.c index ab52ed3..20a89ac 100644 --- a/rawwaves/makewavs.c +++ b/rawwaves/makewavs.c @@ -1,116 +1,116 @@ -/**********************************************/ -/** Utility to make various flavors of **/ -/** sine wave (rectified, etc), and **/ -/** other commonly needed waveforms, like **/ -/** triangles, ramps, etc. **/ -/** The files generated are all 16 bit **/ -/** linear signed integer, of length **/ -/** as defined by LENGTH below **/ -/**********************************************/ - -#include -#include -#include - -#define LENGTH 256 -#define PI 3.14159265358979323846 - -void main() -{ - int i,j; - double temp; - short data[LENGTH + 2]; - FILE *fd; - - /////////// Yer Basic TX81Z Waves, Including Sine /////////// - fd = fopen("halfwave.raw","wb"); - for (i=0;i +#include +#include + +#define LENGTH 256 +#define PI 3.14159265358979323846 + +void main() +{ + int i,j; + double temp; + short data[LENGTH + 2]; + FILE *fd; + + /////////// Yer Basic TX81Z Waves, Including Sine /////////// + fd = fopen("halfwave.raw","wb"); + for (i=0;i 1568.0) freakency = 1568.0; @@ -197,13 +197,13 @@ void BandedWG :: setFrequency(MY_FLOAT frequency) delay[i].setDelay( length ); gains[i]=basegains[i]; // gains[i]=(MY_FLOAT) pow(basegains[i], 1/((MY_FLOAT)delay[i].getDelay())); - // cerr << gains[i]; + // std::cerr << gains[i]; } else { nModes = i; break; } - // cerr << endl; + // std::cerr << std::endl; // Set the bandpass filter resonances radius = 1.0 - PI * 32 / Stk::sampleRate(); //freakency * modes[i] / Stk::sampleRate()/32; @@ -257,7 +257,7 @@ void BandedWG :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->startBowing(amplitude, amplitude * 0.001); #if defined(_STK_DEBUG_) - cerr << "BandedWG: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "BandedWG: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -267,7 +267,7 @@ void BandedWG :: noteOff(MY_FLOAT amplitude) this->stopBowing((1.0 - amplitude) * 0.005); #if defined(_STK_DEBUG_) - cerr << "BandedWG: NoteOff amplitude = " << amplitude << endl; + std::cerr << "BandedWG: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -320,11 +320,11 @@ void BandedWG :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "BandedWG: Control value less than zero!" << endl; + std::cerr << "BandedWG: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "BandedWG: Control value greater than 128.0!" << endl; + std::cerr << "BandedWG: Control value greater than 128.0!" << std::endl; } if (number == __SK_BowPressure_) { // 2 @@ -353,7 +353,7 @@ void BandedWG :: controlChange(int number, MY_FLOAT value) else if (number == __SK_ModWheel_) { // 1 // baseGain = 0.9989999999 + (0.001 * norm ); baseGain = 0.8999999999999999 + (0.1 * norm); - // cerr << "Yuck!" << endl; + // std::cerr << "Yuck!" << std::endl; for (int i=0; isetPreset((int) value); else - cerr << "BandedWG: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "BandedWG: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "BandedWG: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "BandedWG: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/BeeThree.cpp b/src/BeeThree.cpp index 0b05118..e3f4989 100644 --- a/src/BeeThree.cpp +++ b/src/BeeThree.cpp @@ -33,25 +33,14 @@ /***************************************************/ #include "BeeThree.h" -#include BeeThree :: BeeThree() : FM() { - int i; - char files[4][128]; - - // Concatenate the STK RAWWAVE_PATH to the rawwave file - for ( i=0; i<4; i++ ) - strcpy( files[i], RAWWAVE_PATH); - - strcat(files[0], "sinewave.raw"); - strcat(files[1], "sinewave.raw"); - strcat(files[2], "sinewave.raw"); - strcat(files[3], "fwavblnk.raw"); - - for ( i=0; i<4; i++ ) - waves[i] = new WaveLoop( files[i], TRUE ); + // Concatenate the STK rawwave path to the rawwave files + for ( int i=0; i<3; i++ ) + waves[i] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + waves[3] = new WaveLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), TRUE ); this->setRatio(0, 0.999); this->setRatio(1, 1.997); diff --git a/src/BlowBotl.cpp b/src/BlowBotl.cpp index 6d4c84f..7e499a2 100644 --- a/src/BlowBotl.cpp +++ b/src/BlowBotl.cpp @@ -18,7 +18,6 @@ #include "BlowBotl.h" #include "SKINI.msg" -#include #define __BOTTLE_RADIUS_ 0.999 @@ -29,10 +28,8 @@ BlowBotl :: BlowBotl() dcBlock = new PoleZero(); dcBlock->setBlockZero(); - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency( 5.925 ); vibratoGain = 0.0; @@ -67,7 +64,7 @@ void BlowBotl :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "BlowBotl: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "BlowBotl: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -94,7 +91,7 @@ void BlowBotl :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) outputGain = amplitude + 0.001; #if defined(_STK_DEBUG_) - cerr << "BlowBotl: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "BlowBotl: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -103,7 +100,7 @@ void BlowBotl :: noteOff(MY_FLOAT amplitude) this->stopBlowing(amplitude * 0.02); #if defined(_STK_DEBUG_) - cerr << "BlowBotl: NoteOff amplitude = " << amplitude << endl; + std::cerr << "BlowBotl: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -134,11 +131,11 @@ void BlowBotl :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "BlowBotl: Control value less than zero!" << endl; + std::cerr << "BlowBotl: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "BlowBotl: Control value greater than 128.0!" << endl; + std::cerr << "BlowBotl: Control value greater than 128.0!" << std::endl; } if (number == __SK_NoiseLevel_) // 4 @@ -150,9 +147,9 @@ void BlowBotl :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 adsr->setTarget( norm ); else - cerr << "BlowBotl: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "BlowBotl: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "BlowBotl: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "BlowBotl: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/BlowHole.cpp b/src/BlowHole.cpp index 0754485..7ee7f69 100644 --- a/src/BlowHole.cpp +++ b/src/BlowHole.cpp @@ -36,7 +36,6 @@ #include "BlowHole.h" #include "SKINI.msg" #include -#include BlowHole :: BlowHole(MY_FLOAT lowestFrequency) { @@ -83,10 +82,8 @@ BlowHole :: BlowHole(MY_FLOAT lowestFrequency) // Start with register vent closed vent->setGain(0.0); - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency((MY_FLOAT) 5.735); outputGain = (MY_FLOAT) 1.0; noiseGain = (MY_FLOAT) 0.2; @@ -121,7 +118,7 @@ void BlowHole :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "BlowHole: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "BlowHole: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -181,7 +178,7 @@ void BlowHole :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) outputGain = amplitude + 0.001; #if defined(_STK_DEBUG_) - cerr << "BlowHole: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "BlowHole: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -190,7 +187,7 @@ void BlowHole :: noteOff(MY_FLOAT amplitude) this->stopBlowing(amplitude * 0.01); #if defined(_STK_DEBUG_) - cerr << "BlowHole: NoteOff amplitude = " << amplitude << endl; + std::cerr << "BlowHole: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -234,11 +231,11 @@ void BlowHole :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "BlowHole: Control value less than zero!" << endl; + std::cerr << "BlowHole: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "BlowHole: Control value greater than 128.0!" << endl; + std::cerr << "BlowHole: Control value greater than 128.0!" << std::endl; } if (number == __SK_ReedStiffness_) // 2 @@ -252,9 +249,9 @@ void BlowHole :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 envelope->setValue( norm ); else - cerr << "BlowHole: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "BlowHole: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "BlowHole: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "BlowHole: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Bowed.cpp b/src/Bowed.cpp index 478bfd3..48208ef 100644 --- a/src/Bowed.cpp +++ b/src/Bowed.cpp @@ -23,7 +23,6 @@ #include "Bowed.h" #include "SKINI.msg" -#include Bowed :: Bowed(MY_FLOAT lowestFrequency) { @@ -36,10 +35,8 @@ Bowed :: Bowed(MY_FLOAT lowestFrequency) bowTable = new BowTabl; bowTable->setSlope((MY_FLOAT) 3.0); - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency((MY_FLOAT) 6.12723); vibratoGain = (MY_FLOAT) 0.0; @@ -81,7 +78,7 @@ void Bowed :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Bowed: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Bowed: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -111,7 +108,7 @@ void Bowed :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->setFrequency(frequency); #if defined(_STK_DEBUG_) - cerr << "Bowed: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Bowed: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -120,7 +117,7 @@ void Bowed :: noteOff(MY_FLOAT amplitude) this->stopBowing(((MY_FLOAT) 1.0 - amplitude) * (MY_FLOAT) 0.005); #if defined(_STK_DEBUG_) - cerr << "Bowed: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Bowed: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -163,11 +160,11 @@ void Bowed :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Bowed: Control value less than zero!" << endl; + std::cerr << "Bowed: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Bowed: Control value greater than 128.0!" << endl; + std::cerr << "Bowed: Control value greater than 128.0!" << std::endl; } if (number == __SK_BowPressure_) // 2 @@ -184,9 +181,9 @@ void Bowed :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 adsr->setTarget(norm); else - cerr << "Bowed: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Bowed: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Bowed: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Bowed: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Brass.cpp b/src/Brass.cpp index 389af2b..a235c06 100644 --- a/src/Brass.cpp +++ b/src/Brass.cpp @@ -22,7 +22,6 @@ #include "Brass.h" #include "SKINI.msg" -#include #include Brass :: Brass(MY_FLOAT lowestFrequency) @@ -38,10 +37,8 @@ Brass :: Brass(MY_FLOAT lowestFrequency) adsr = new ADSR; adsr->setAllTimes( 0.005, 0.001, 1.0, 0.010); - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency( 6.137 ); vibratoGain = 0.0; @@ -73,7 +70,7 @@ void Brass :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Brass: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Brass: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -89,7 +86,7 @@ void Brass :: setLip(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Brass: setLip parameter is less than or equal to zero!" << endl; + std::cerr << "Brass: setLip parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -115,7 +112,7 @@ void Brass :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->startBlowing(amplitude, amplitude * 0.001); #if defined(_STK_DEBUG_) - cerr << "Brass: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Brass: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -124,7 +121,7 @@ void Brass :: noteOff(MY_FLOAT amplitude) this->stopBlowing(amplitude * 0.005); #if defined(_STK_DEBUG_) - cerr << "Brass: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Brass: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -151,11 +148,11 @@ void Brass :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Brass: Control value less than zero!" << endl; + std::cerr << "Brass: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Brass: Control value greater than 128.0!" << endl; + std::cerr << "Brass: Control value greater than 128.0!" << std::endl; } if (number == __SK_LipTension_) { // 2 @@ -171,9 +168,9 @@ void Brass :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 adsr->setTarget( norm ); else - cerr << "Brass: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Brass: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Brass: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Brass: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Chorus.cpp b/src/Chorus.cpp index 17be29c..e0a3bbf 100644 --- a/src/Chorus.cpp +++ b/src/Chorus.cpp @@ -9,8 +9,7 @@ /***************************************************/ #include "Chorus.h" -#include -#include +#include Chorus :: Chorus(MY_FLOAT baseDelay) { @@ -18,12 +17,9 @@ Chorus :: Chorus(MY_FLOAT baseDelay) delayLine[1] = new DelayL((long) (baseDelay), (long) baseDelay + 2); baseLength = baseDelay; - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char path[128]; - strcpy(path, RAWWAVE_PATH); - mods[0] = new WaveLoop( strcat(path,"sinewave.raw"), TRUE ); - strcpy(path, RAWWAVE_PATH); - mods[1] = new WaveLoop( strcat(path,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + mods[0] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + mods[1] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); mods[0]->setFrequency(0.2); mods[1]->setFrequency(0.222222); modDepth = 0.05; @@ -51,11 +47,11 @@ void Chorus :: setEffectMix(MY_FLOAT mix) { effectMix = mix; if ( mix < 0.0 ) { - cerr << "Chorus: setEffectMix parameter is less than zero!" << endl; + std::cerr << "Chorus: setEffectMix parameter is less than zero!" << std::endl; effectMix = 0.0; } else if ( mix > 1.0 ) { - cerr << "Chorus: setEffectMix parameter is greater than 1.0!" << endl; + std::cerr << "Chorus: setEffectMix parameter is greater than 1.0!" << std::endl; effectMix = 1.0; } } diff --git a/src/Clarinet.cpp b/src/Clarinet.cpp index 4bb6dd4..6c505d5 100644 --- a/src/Clarinet.cpp +++ b/src/Clarinet.cpp @@ -24,7 +24,6 @@ #include "Clarinet.h" #include "SKINI.msg" -#include Clarinet :: Clarinet(MY_FLOAT lowestFrequency) { @@ -37,10 +36,8 @@ Clarinet :: Clarinet(MY_FLOAT lowestFrequency) envelope = new Envelope; noise = new Noise; - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char path[128]; - strcpy(path, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(path,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency((MY_FLOAT) 5.735); outputGain = (MY_FLOAT) 1.0; noiseGain = (MY_FLOAT) 0.2; @@ -67,7 +64,7 @@ void Clarinet :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Clarinet: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Clarinet: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -97,7 +94,7 @@ void Clarinet :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) outputGain = amplitude + (MY_FLOAT) 0.001; #if defined(_STK_DEBUG_) - cerr << "Clarinet: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Clarinet: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -106,7 +103,7 @@ void Clarinet :: noteOff(MY_FLOAT amplitude) this->stopBlowing(amplitude * (MY_FLOAT) 0.01); #if defined(_STK_DEBUG_) - cerr << "Clarinet: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Clarinet: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -140,11 +137,11 @@ void Clarinet :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Clarinet: Control value less than zero!" << endl; + std::cerr << "Clarinet: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Clarinet: Control value greater than 128.0!" << endl; + std::cerr << "Clarinet: Control value greater than 128.0!" << std::endl; } if (number == __SK_ReedStiffness_) // 2 @@ -158,9 +155,9 @@ void Clarinet :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 envelope->setValue(norm); else - cerr << "Clarinet: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Clarinet: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Clarinet: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Clarinet: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Delay.cpp b/src/Delay.cpp index 45bc7d0..c6b2b1d 100644 --- a/src/Delay.cpp +++ b/src/Delay.cpp @@ -19,7 +19,7 @@ /***************************************************/ #include "Delay.h" -#include +#include Delay :: Delay() { @@ -64,13 +64,13 @@ void Delay :: clear(void) void Delay :: setDelay(long theDelay) { if (theDelay > length-1) { // The value is too big. - cerr << "Delay: setDelay(" << theDelay << ") too big!" << endl; + std::cerr << "Delay: setDelay(" << theDelay << ") too big!" << std::endl; // Force delay to maxLength. outPoint = inPoint + 1; delay = length - 1; } else if (theDelay < 0 ) { - cerr << "Delay: setDelay(" << theDelay << ") less than zero!" << endl; + std::cerr << "Delay: setDelay(" << theDelay << ") less than zero!" << std::endl; outPoint = inPoint; delay = 0; } @@ -109,15 +109,15 @@ MY_FLOAT Delay :: energy(void) const return e; } -MY_FLOAT Delay :: contentsAt(long tapDelay) const +MY_FLOAT Delay :: contentsAt(unsigned long tapDelay) const { long i = tapDelay; - if (i < 0) { - cerr << "Delay: contentsAt(" << tapDelay << ") too small!" << endl; - i = 0; + if (i < 1) { + std::cerr << "Delay: contentsAt(" << tapDelay << ") too small!" << std::endl; + i = 1; } else if (i > delay) { - cerr << "Delay: contentsAt(" << tapDelay << ") too big!" << endl; + std::cerr << "Delay: contentsAt(" << tapDelay << ") too big!" << std::endl; i = (long) delay; } diff --git a/src/DelayA.cpp b/src/DelayA.cpp index 5c7e4e5..40997a9 100644 --- a/src/DelayA.cpp +++ b/src/DelayA.cpp @@ -23,7 +23,7 @@ /***************************************************/ #include "DelayA.h" -#include +#include DelayA :: DelayA() { @@ -46,6 +46,7 @@ DelayA :: DelayA(MY_FLOAT theDelay, long maxDelay) inPoint = 0; this->setDelay(theDelay); + apInput = 0.0; doNextOut = true; } @@ -64,13 +65,13 @@ void DelayA :: setDelay(MY_FLOAT theDelay) MY_FLOAT outPointer; if (theDelay > length-1) { - cerr << "DelayA: setDelay(" << theDelay << ") too big!" << endl; + std::cerr << "DelayA: setDelay(" << theDelay << ") too big!" << std::endl; // Force delay to maxLength outPointer = inPoint + 1.0; delay = length - 1; } else if (theDelay < 0.5) { - cerr << "DelayA: setDelay(" << theDelay << ") less than 0.5 not possible!" << endl; + std::cerr << "DelayA: setDelay(" << theDelay << ") less than 0.5 not possible!" << std::endl; outPointer = inPoint + 0.4999999999; delay = 0.5; } diff --git a/src/DelayL.cpp b/src/DelayL.cpp index b2deea7..cbe6139 100644 --- a/src/DelayL.cpp +++ b/src/DelayL.cpp @@ -23,7 +23,7 @@ /***************************************************/ #include "DelayL.h" -#include +#include DelayL :: DelayL() { @@ -56,13 +56,13 @@ void DelayL :: setDelay(MY_FLOAT theDelay) MY_FLOAT outPointer; if (theDelay > length-1) { - cerr << "DelayL: setDelay(" << theDelay << ") too big!" << endl; + std::cerr << "DelayL: setDelay(" << theDelay << ") too big!" << std::endl; // Force delay to maxLength outPointer = inPoint + 1.0; delay = length - 1; } else if (theDelay < 0 ) { - cerr << "DelayL: setDelay(" << theDelay << ") less than zero!" << endl; + std::cerr << "DelayL: setDelay(" << theDelay << ") less than zero!" << std::endl; outPointer = inPoint; delay = 0; } diff --git a/src/Drummer.cpp b/src/Drummer.cpp index 92e6374..251dc4c 100644 --- a/src/Drummer.cpp +++ b/src/Drummer.cpp @@ -16,7 +16,6 @@ /***************************************************/ #include "Drummer.h" -#include #include // Not really General MIDI yet. Coming soon. @@ -75,16 +74,16 @@ Drummer :: ~Drummer() void Drummer :: noteOn(MY_FLOAT instrument, MY_FLOAT amplitude) { #if defined(_STK_DEBUG_) - cerr << "Drummer: NoteOn instrument = " << instrument << ", amplitude = " << amplitude << endl; + std::cerr << "Drummer: NoteOn instrument = " << instrument << ", amplitude = " << amplitude << std::endl; #endif MY_FLOAT gain = amplitude; if ( amplitude > 1.0 ) { - cerr << "Drummer: noteOn amplitude parameter is greater than 1.0!" << endl; + std::cerr << "Drummer: noteOn amplitude parameter is greater than 1.0!" << std::endl; gain = 1.0; } else if ( amplitude < 0.0 ) { - cerr << "Drummer: noteOn amplitude parameter is less than 0.0!" << endl; + std::cerr << "Drummer: noteOn amplitude parameter is less than 0.0!" << std::endl; return; } @@ -122,11 +121,8 @@ void Drummer :: noteOn(MY_FLOAT instrument, MY_FLOAT amplitude) nSounding += 1; sounding[nSounding-1] = noteNum; - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char path[128]; - strcpy(path, RAWWAVE_PATH); - strcat(path, waveNames[genMIDIMap[noteNum]]); - waves[nSounding-1] = new WvIn(path, TRUE); + // Concatenate the STK rawwave path to the rawwave file + waves[nSounding-1] = new WvIn( (Stk::rawwavePath() + waveNames[genMIDIMap[noteNum]]).c_str(), TRUE ); if (Stk::sampleRate() != 22050.0) waves[nSounding-1]->setRate( 22050.0 / Stk::sampleRate() ); filters[nSounding-1]->setPole((MY_FLOAT) 0.999 - (gain * 0.6) ); @@ -134,9 +130,9 @@ void Drummer :: noteOn(MY_FLOAT instrument, MY_FLOAT amplitude) } #if defined(_STK_DEBUG_) - cerr << "Number Sounding = " << nSounding << endl; - for (i=0; i +#include Echo :: Echo(MY_FLOAT longestDelay) { @@ -34,11 +34,11 @@ void Echo :: setDelay(MY_FLOAT delay) { MY_FLOAT size = delay; if ( delay < 0.0 ) { - cerr << "Echo: setDelay parameter is less than zero!" << endl; + std::cerr << "Echo: setDelay parameter is less than zero!" << std::endl; size = 0.0; } else if ( delay > length ) { - cerr << "Echo: setDelay parameter is greater than delay length!" << endl; + std::cerr << "Echo: setDelay parameter is greater than delay length!" << std::endl; size = length; } @@ -49,11 +49,11 @@ void Echo :: setEffectMix(MY_FLOAT mix) { effectMix = mix; if ( mix < 0.0 ) { - cerr << "Echo: setEffectMix parameter is less than zero!" << endl; + std::cerr << "Echo: setEffectMix parameter is less than zero!" << std::endl; effectMix = 0.0; } else if ( mix > 1.0 ) { - cerr << "Echo: setEffectMix parameter is greater than 1.0!" << endl; + std::cerr << "Echo: setEffectMix parameter is greater than 1.0!" << std::endl; effectMix = 1.0; } } diff --git a/src/FM.cpp b/src/FM.cpp index 9e2b9fc..d1ff415 100644 --- a/src/FM.cpp +++ b/src/FM.cpp @@ -25,7 +25,6 @@ #include "FM.h" #include "SKINI.msg" -#include #include FM :: FM(int operators) @@ -41,10 +40,8 @@ FM :: FM(int operators) twozero->setB2( -1.0 ); twozero->setGain( 0.0 ); - // Concatenate the STK RAWWAVE_PATH to the rawwave file. - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency(6.0); int i; @@ -115,11 +112,11 @@ void FM :: setFrequency(MY_FLOAT frequency) void FM :: setRatio(int waveIndex, MY_FLOAT ratio) { if ( waveIndex < 0 ) { - cerr << "FM: setRatio waveIndex parameter is less than zero!" << endl; + std::cerr << "FM: setRatio waveIndex parameter is less than zero!" << std::endl; return; } else if ( waveIndex >= nOperators ) { - cerr << "FM: setRatio waveIndex parameter is greater than the number of operators!" << endl; + std::cerr << "FM: setRatio waveIndex parameter is greater than the number of operators!" << std::endl; return; } @@ -133,11 +130,11 @@ void FM :: setRatio(int waveIndex, MY_FLOAT ratio) void FM :: setGain(int waveIndex, MY_FLOAT gain) { if ( waveIndex < 0 ) { - cerr << "FM: setGain waveIndex parameter is less than zero!" << endl; + std::cerr << "FM: setGain waveIndex parameter is less than zero!" << std::endl; return; } else if ( waveIndex >= nOperators ) { - cerr << "FM: setGain waveIndex parameter is greater than the number of operators!" << endl; + std::cerr << "FM: setGain waveIndex parameter is greater than the number of operators!" << std::endl; return; } @@ -181,7 +178,7 @@ void FM :: noteOff(MY_FLOAT amplitude) keyOff(); #if defined(_STK_DEBUG_) - cerr << "FM: NoteOff amplitude = " << amplitude << endl; + std::cerr << "FM: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -190,11 +187,11 @@ void FM :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "FM: Control value less than zero!" << endl; + std::cerr << "FM: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "FM: Control value greater than 128.0!" << endl; + std::cerr << "FM: Control value greater than 128.0!" << std::endl; } if (number == __SK_Breath_) // 2 @@ -212,10 +209,10 @@ void FM :: controlChange(int number, MY_FLOAT value) adsr[3]->setTarget( norm ); } else - cerr << "FM: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "FM: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "FM: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "FM: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/FMVoices.cpp b/src/FMVoices.cpp index 82c1af3..d893582 100644 --- a/src/FMVoices.cpp +++ b/src/FMVoices.cpp @@ -33,25 +33,14 @@ #include "FMVoices.h" #include "SKINI.msg" #include "Phonemes.h" -#include FMVoices :: FMVoices() : FM() { - int i; - char files[4][128]; - - // Concatenate the STK RAWWAVE_PATH to the rawwave file - for ( i=0; i<4; i++ ) - strcpy( files[i], RAWWAVE_PATH); - - strcat(files[0], "sinewave.raw"); - strcat(files[1], "sinewave.raw"); - strcat(files[2], "sinewave.raw"); - strcat(files[3], "fwavblnk.raw"); - - for ( i=0; i<4; i++ ) - waves[i] = new WaveLoop( files[i], TRUE ); + // Concatenate the STK rawwave path to the rawwave files + for ( int i=0; i<3; i++ ) + waves[i] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + waves[3] = new WaveLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), TRUE ); this->setRatio(0, 2.00); this->setRatio(1, 4.00); @@ -129,7 +118,7 @@ void FMVoices :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->keyOn(); #if defined(_STK_DEBUG_) - cerr << "FMVoices: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "FMVoices: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -162,11 +151,11 @@ void FMVoices :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "FMVoices: Control value less than zero!" << endl; + std::cerr << "FMVoices: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "FMVoices: Control value greater than 128.0!" << endl; + std::cerr << "FMVoices: Control value greater than 128.0!" << std::endl; } @@ -186,9 +175,9 @@ void FMVoices :: controlChange(int number, MY_FLOAT value) tilt[2] = tilt[1] * norm; } else - cerr << "FMVoices: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "FMVoices: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "FMVoices: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "FMVoices: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Flute.cpp b/src/Flute.cpp index 3671235..106bdaf 100644 --- a/src/Flute.cpp +++ b/src/Flute.cpp @@ -1,5 +1,5 @@ /***************************************************/ -/*! \class Fluate +/*! \class Flute \brief STK flute physical model class. This class implements a simple flute @@ -24,7 +24,6 @@ #include "Flute.h" #include "SKINI.msg" -#include Flute :: Flute(MY_FLOAT lowestFrequency) { @@ -39,10 +38,8 @@ Flute :: Flute(MY_FLOAT lowestFrequency) noise = new Noise(); adsr = new ADSR(); - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency( 5.925 ); this->clear(); @@ -84,7 +81,7 @@ void Flute :: setFrequency(MY_FLOAT frequency) { lastFrequency = frequency; if ( frequency <= 0.0 ) { - cerr << "Flute: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Flute: setFrequency parameter is less than or equal to zero!" << std::endl; lastFrequency = 220.0; } @@ -119,7 +116,7 @@ void Flute :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) outputGain = amplitude + 0.001; #if defined(_STK_DEBUG_) - cerr << "Flute: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Flute: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -128,7 +125,7 @@ void Flute :: noteOff(MY_FLOAT amplitude) this->stopBlowing(amplitude * 0.02); #if defined(_STK_DEBUG_) - cerr << "Flute: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Flute: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -177,11 +174,11 @@ void Flute :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Flute: Control value less than zero!" << endl; + std::cerr << "Flute: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Flute: Control value greater than 128.0!" << endl; + std::cerr << "Flute: Control value greater than 128.0!" << std::endl; } if (number == __SK_JetDelay_) // 2 @@ -195,9 +192,9 @@ void Flute :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 adsr->setTarget( norm ); else - cerr << "Flute: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Flute: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Flute: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Flute: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/HevyMetl.cpp b/src/HevyMetl.cpp index 33ada64..ef5f664 100644 --- a/src/HevyMetl.cpp +++ b/src/HevyMetl.cpp @@ -27,25 +27,14 @@ /***************************************************/ #include "HevyMetl.h" -#include HevyMetl :: HevyMetl() : FM() { - int i; - char files[4][128]; - - // Concatenate the STK RAWWAVE_PATH to the rawwave file - for ( i=0; i<4; i++ ) - strcpy( files[i], RAWWAVE_PATH); - - strcat(files[0], "sinewave.raw"); - strcat(files[1], "sinewave.raw"); - strcat(files[2], "sinewave.raw"); - strcat(files[3], "fwavblnk.raw"); - - for ( i=0; i<4; i++ ) - waves[i] = new WaveLoop( files[i], TRUE ); + // Concatenate the STK rawwave path to the rawwave files + for ( int i=0; i<3; i++ ) + waves[i] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + waves[3] = new WaveLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), TRUE ); this->setRatio(0, 1.0 * 1.000); this->setRatio(1, 4.0 * 0.999); diff --git a/src/Instrmnt.cpp b/src/Instrmnt.cpp index 05fb6fe..5c9af3a 100644 --- a/src/Instrmnt.cpp +++ b/src/Instrmnt.cpp @@ -21,7 +21,7 @@ Instrmnt :: ~Instrmnt() void Instrmnt :: setFrequency(MY_FLOAT frequency) { - cerr << "Instrmnt: virtual setFrequency function call!" << endl; + std::cerr << "Instrmnt: virtual setFrequency function call!" << std::endl; } MY_FLOAT Instrmnt :: lastOut() const @@ -29,6 +29,17 @@ MY_FLOAT Instrmnt :: lastOut() const return lastOutput; } +// Support for stereo output: +MY_FLOAT Instrmnt :: lastOutLeft(void) const +{ + return 0.5 * lastOutput; +} + +MY_FLOAT Instrmnt :: lastOutRight(void) const +{ + return 0.5 * lastOutput; +} + MY_FLOAT *Instrmnt :: tick(MY_FLOAT *vector, unsigned int vectorSize) { for (unsigned int i=0; i - Mandolin :: Mandolin(MY_FLOAT lowestFrequency) : PluckTwo(lowestFrequency) { - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char temp[128]; - strcpy(temp, RAWWAVE_PATH); - soundfile[0] = new WvIn( strcat(temp,"mand1.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[1] = new WvIn( strcat(temp,"mand2.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[2] = new WvIn( strcat(temp,"mand3.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[3] = new WvIn( strcat(temp,"mand4.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[4] = new WvIn( strcat(temp,"mand5.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[5] = new WvIn( strcat(temp,"mand6.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[6] = new WvIn( strcat(temp,"mand7.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[7] = new WvIn( strcat(temp,"mand8.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[8] = new WvIn( strcat(temp,"mand9.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[9] = new WvIn( strcat(temp,"mand10.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[10] = new WvIn( strcat(temp,"mand11.raw"), TRUE ); - strcpy(temp, RAWWAVE_PATH); - soundfile[11] = new WvIn( strcat(temp,"mand12.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave files + soundfile[0] = new WvIn( (Stk::rawwavePath() + "mand1.raw").c_str(), TRUE ); + soundfile[1] = new WvIn( (Stk::rawwavePath() + "mand2.raw").c_str(), TRUE ); + soundfile[2] = new WvIn( (Stk::rawwavePath() + "mand3.raw").c_str(), TRUE ); + soundfile[3] = new WvIn( (Stk::rawwavePath() + "mand4.raw").c_str(), TRUE ); + soundfile[4] = new WvIn( (Stk::rawwavePath() + "mand5.raw").c_str(), TRUE ); + soundfile[5] = new WvIn( (Stk::rawwavePath() + "mand6.raw").c_str(), TRUE ); + soundfile[6] = new WvIn( (Stk::rawwavePath() + "mand7.raw").c_str(), TRUE ); + soundfile[7] = new WvIn( (Stk::rawwavePath() + "mand8.raw").c_str(), TRUE ); + soundfile[8] = new WvIn( (Stk::rawwavePath() + "mand9.raw").c_str(), TRUE ); + soundfile[9] = new WvIn( (Stk::rawwavePath() + "mand10.raw").c_str(), TRUE ); + soundfile[10] = new WvIn( (Stk::rawwavePath() + "mand11.raw").c_str(), TRUE ); + soundfile[11] = new WvIn( (Stk::rawwavePath() + "mand12.raw").c_str(), TRUE ); + directBody = 1.0; mic = 0; dampTime = 0; @@ -83,11 +69,11 @@ void Mandolin :: pluck(MY_FLOAT amplitude) waveDone = false; pluckAmplitude = amplitude; if ( amplitude < 0.0 ) { - cerr << "Mandolin: pluck amplitude parameter less than zero!" << endl; + std::cerr << "Mandolin: pluck amplitude parameter less than zero!" << std::endl; pluckAmplitude = 0.0; } else if ( amplitude > 1.0 ) { - cerr << "Mandolin: pluck amplitude parameter greater than 1.0!" << endl; + std::cerr << "Mandolin: pluck amplitude parameter greater than 1.0!" << std::endl; pluckAmplitude = 1.0; } @@ -101,11 +87,11 @@ void Mandolin :: pluck(MY_FLOAT amplitude, MY_FLOAT position) // Pluck position puts zeroes at position * length. pluckPosition = position; if ( position < 0.0 ) { - cerr << "Mandolin: pluck position parameter less than zero!" << endl; + std::cerr << "Mandolin: pluck position parameter less than zero!" << std::endl; pluckPosition = 0.0; } else if ( position > 1.0 ) { - cerr << "Mandolin: pluck position parameter greater than 1.0!" << endl; + std::cerr << "Mandolin: pluck position parameter greater than 1.0!" << std::endl; pluckPosition = 1.0; } @@ -118,7 +104,7 @@ void Mandolin :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->pluck(amplitude); #if defined(_STK_DEBUG_) - cerr << "Mandolin: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Mandolin: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -165,11 +151,11 @@ void Mandolin :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Mandolin: Control value less than zero!" << endl; + std::cerr << "Mandolin: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Mandolin: Control value greater than 128.0!" << endl; + std::cerr << "Mandolin: Control value greater than 128.0!" << std::endl; } if (number == __SK_BodySize_) // 2 @@ -183,9 +169,9 @@ void Mandolin :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 mic = (int) (norm * 11.0); else - cerr << "Mandolin: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Mandolin: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Mandolin: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Mandolin: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Mesh2D.cpp b/src/Mesh2D.cpp index 3e8b119..bbd056d 100644 --- a/src/Mesh2D.cpp +++ b/src/Mesh2D.cpp @@ -145,11 +145,11 @@ void Mesh2D :: setNX(short lenX) { NX = lenX; if ( lenX < 2 ) { - cerr << "Mesh2D::setNX(" << lenX << "): Minimum length is 2!" << endl; + std::cerr << "Mesh2D::setNX(" << lenX << "): Minimum length is 2!" << std::endl; NX = 2; } else if ( lenX > NXMAX ) { - cerr << "Mesh2D::setNX(" << lenX << "): Maximum length is " << NXMAX << "!" << endl; + std::cerr << "Mesh2D::setNX(" << lenX << "): Maximum length is " << NXMAX << "!" << std::endl; NX = NXMAX; } } @@ -158,11 +158,11 @@ void Mesh2D :: setNY(short lenY) { NY = lenY; if ( lenY < 2 ) { - cerr << "Mesh2D::setNY(" << lenY << "): Minimum length is 2!" << endl; + std::cerr << "Mesh2D::setNY(" << lenY << "): Minimum length is 2!" << std::endl; NY = 2; } else if ( lenY > NYMAX ) { - cerr << "Mesh2D::setNY(" << lenY << "): Maximum length is " << NYMAX << "!" << endl; + std::cerr << "Mesh2D::setNY(" << lenY << "): Maximum length is " << NYMAX << "!" << std::endl; NY = NYMAX; } } @@ -171,11 +171,11 @@ void Mesh2D :: setDecay(MY_FLOAT decayFactor) { MY_FLOAT gain = decayFactor; if ( decayFactor < 0.0 ) { - cerr << "Mesh2D::setDecay decayFactor value is less than 0.0!" << endl; + std::cerr << "Mesh2D::setDecay decayFactor value is less than 0.0!" << std::endl; gain = 0.0; } else if ( decayFactor > 1.0 ) { - cerr << "Mesh2D::setDecay decayFactor value is greater than 1.0!" << endl; + std::cerr << "Mesh2D::setDecay decayFactor value is greater than 1.0!" << std::endl; gain = 1.0; } @@ -190,22 +190,22 @@ void Mesh2D :: setDecay(MY_FLOAT decayFactor) void Mesh2D :: setInputPosition(MY_FLOAT xFactor, MY_FLOAT yFactor) { if ( xFactor < 0.0 ) { - cerr << "Mesh2D::setInputPosition xFactor value is less than 0.0!" << endl; + std::cerr << "Mesh2D::setInputPosition xFactor value is less than 0.0!" << std::endl; xInput = 0; } else if ( xFactor > 1.0 ) { - cerr << "Mesh2D::setInputPosition xFactor value is greater than 1.0!" << endl; + std::cerr << "Mesh2D::setInputPosition xFactor value is greater than 1.0!" << std::endl; xInput = NX - 1; } else xInput = (short) (xFactor * (NX - 1)); if ( yFactor < 0.0 ) { - cerr << "Mesh2D::setInputPosition yFactor value is less than 0.0!" << endl; + std::cerr << "Mesh2D::setInputPosition yFactor value is less than 0.0!" << std::endl; yInput = 0; } else if ( yFactor > 1.0 ) { - cerr << "Mesh2D::setInputPosition yFactor value is greater than 1.0!" << endl; + std::cerr << "Mesh2D::setInputPosition yFactor value is greater than 1.0!" << std::endl; yInput = NY - 1; } else @@ -225,14 +225,14 @@ void Mesh2D :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) } #if defined(_STK_DEBUG_) - cerr << "Mesh2D: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Mesh2D: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } void Mesh2D :: noteOff(MY_FLOAT amplitude) { #if defined(_STK_DEBUG_) - cerr << "Mesh2D: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Mesh2D: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -362,11 +362,11 @@ void Mesh2D :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Mesh2D: Control value less than zero!" << endl; + std::cerr << "Mesh2D: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Mesh2D: Control value greater than 128.0!" << endl; + std::cerr << "Mesh2D: Control value greater than 128.0!" << std::endl; } if (number == 2) // 2 @@ -380,9 +380,9 @@ void Mesh2D :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 ; else - cerr << "Mesh2D: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Mesh2D: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Mesh2D: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Mesh2D: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Messager.cpp b/src/Messager.cpp index 9b3335d..8ae2c29 100644 --- a/src/Messager.cpp +++ b/src/Messager.cpp @@ -35,7 +35,7 @@ #include "Messager.h" #include -#include +#include int socket_port = 2001; @@ -76,7 +76,7 @@ Messager :: Messager(int inputMask, int port) // If STK_PIPE is not specified, let the users know they can exit // the program via the console if necessary. if ( !(sources & STK_PIPE) && sources ) - cout << "\nType `Exit' to quit.\n" << endl; + std::cout << "\nType `Exit' to quit.\n" << std::endl; sources |= STK_SOCKET; socket_port = port; @@ -148,7 +148,7 @@ void Messager :: setRtDelta(long nSamples) if ( nSamples > 0 ) rtDelta = nSamples; else - cerr << "Messager: setRtDelta(" << nSamples << ") less than or equal to zero!" << endl; + std::cerr << "Messager: setRtDelta(" << nSamples << ") less than or equal to zero!" << std::endl; } long Messager :: getDelta() const @@ -245,7 +245,7 @@ bool Messager :: socketMessage() if (nSockets == 0) pipefd = newfd; else - cout << "New socket connection made.\n" << endl; + std::cout << "New socket connection made.\n" << std::endl; // Set the socket to non-blocking mode. Socket::setBlocking( newfd, false ); @@ -274,7 +274,7 @@ bool Messager :: socketMessage() if ( nSockets == 1 && FD_ISSET(pipefd, &mask) ) { // The "piping" socket is still running. if (sources & STK_MIDI) { - cout << "MIDI input still running ... type 'Exit' to quit.\n" << endl; + std::cout << "MIDI input still running ... type 'Exit' to quit.\n" << std::endl; } else if (!(sources & STK_PIPE) ) { // The user didn't specify this connection, so quit now. diff --git a/src/Modal.cpp b/src/Modal.cpp index 8b2d65b..657bcf0 100644 --- a/src/Modal.cpp +++ b/src/Modal.cpp @@ -12,7 +12,6 @@ /***************************************************/ #include "Modal.h" -#include #include Modal :: Modal(int modes) @@ -38,10 +37,8 @@ Modal :: Modal(int modes) envelope = new Envelope; onepole = new OnePole; - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); // Set some default values. vibrato->setFrequency( 6.0 ); @@ -87,11 +84,11 @@ void Modal :: setFrequency(MY_FLOAT frequency) void Modal :: setRatioAndRadius(int modeIndex, MY_FLOAT ratio, MY_FLOAT radius) { if ( modeIndex < 0 ) { - cerr << "Modal: setRatioAndRadius modeIndex parameter is less than zero!" << endl; + std::cerr << "Modal: setRatioAndRadius modeIndex parameter is less than zero!" << std::endl; return; } else if ( modeIndex >= nModes ) { - cerr << "Modal: setRatioAndRadius modeIndex parameter is greater than the number of operators!" << endl; + std::cerr << "Modal: setRatioAndRadius modeIndex parameter is greater than the number of operators!" << std::endl; return; } @@ -106,7 +103,7 @@ void Modal :: setRatioAndRadius(int modeIndex, MY_FLOAT ratio, MY_FLOAT radius) while (temp * baseFrequency > nyquist) temp *= (MY_FLOAT) 0.5; ratios[modeIndex] = temp; #if defined(_STK_DEBUG_) - cerr << "Modal : Aliasing would occur here ... correcting." << endl; + std::cerr << "Modal : Aliasing would occur here ... correcting." << std::endl; #endif } radii[modeIndex] = radius; @@ -131,11 +128,11 @@ void Modal :: setDirectGain(MY_FLOAT aGain) void Modal :: setModeGain(int modeIndex, MY_FLOAT gain) { if ( modeIndex < 0 ) { - cerr << "Modal: setModeGain modeIndex parameter is less than zero!" << endl; + std::cerr << "Modal: setModeGain modeIndex parameter is less than zero!" << std::endl; return; } else if ( modeIndex >= nModes ) { - cerr << "Modal: setModeGain modeIndex parameter is greater than the number of operators!" << endl; + std::cerr << "Modal: setModeGain modeIndex parameter is greater than the number of operators!" << std::endl; return; } @@ -146,11 +143,11 @@ void Modal :: strike(MY_FLOAT amplitude) { MY_FLOAT gain = amplitude; if ( amplitude < 0.0 ) { - cerr << "Modal: strike amplitude is less than zero!" << endl; + std::cerr << "Modal: strike amplitude is less than zero!" << std::endl; gain = 0.0; } else if ( amplitude > 1.0 ) { - cerr << "Modal: strike amplitude is greater than 1.0!" << endl; + std::cerr << "Modal: strike amplitude is greater than 1.0!" << std::endl; gain = 1.0; } @@ -176,7 +173,7 @@ void Modal :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->setFrequency(frequency); #if defined(_STK_DEBUG_) - cerr << "Modal: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Modal: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -187,7 +184,7 @@ void Modal :: noteOff(MY_FLOAT amplitude) this->damp(1.0 - (amplitude * 0.03)); #if defined(_STK_DEBUG_) - cerr << "Modal: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Modal: NoteOff amplitude = " << amplitude << std::endl; #endif } diff --git a/src/ModalBar.cpp b/src/ModalBar.cpp index 85b4c55..cb570cc 100644 --- a/src/ModalBar.cpp +++ b/src/ModalBar.cpp @@ -30,16 +30,13 @@ #include "ModalBar.h" #include "SKINI.msg" -#include #include ModalBar :: ModalBar() : Modal() { - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - wave = new WvIn( strcat(file,"marmstk1.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + wave = new WvIn( (Stk::rawwavePath() + "marmstk1.raw").c_str(), TRUE ); wave->setRate((MY_FLOAT) 0.5 * 22050.0 / Stk::sampleRate() ); // Set the resonances for preset 0 (marimba). @@ -55,11 +52,11 @@ void ModalBar :: setStickHardness(MY_FLOAT hardness) { stickHardness = hardness; if ( hardness < 0.0 ) { - cerr << "ModalBar: setStickHardness parameter is less than zero!" << endl; + std::cerr << "ModalBar: setStickHardness parameter is less than zero!" << std::endl; stickHardness = 0.0; } else if ( hardness > 1.0 ) { - cerr << "ModalBar: setStickHarness parameter is greater than 1.0!" << endl; + std::cerr << "ModalBar: setStickHarness parameter is greater than 1.0!" << std::endl; stickHardness = 1.0; } @@ -71,11 +68,11 @@ void ModalBar :: setStrikePosition(MY_FLOAT position) { strikePosition = position; if ( position < 0.0 ) { - cerr << "ModalBar: setStrikePositions parameter is less than zero!" << endl; + std::cerr << "ModalBar: setStrikePositions parameter is less than zero!" << std::endl; strikePosition = 0.0; } else if ( position > 1.0 ) { - cerr << "ModalBar: setStrikePosition parameter is greater than 1.0!" << endl; + std::cerr << "ModalBar: setStrikePosition parameter is greater than 1.0!" << std::endl; strikePosition = 1.0; } @@ -160,11 +157,11 @@ void ModalBar :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "ModalBar: Control value less than zero!" << endl; + std::cerr << "ModalBar: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "ModalBar: Control value greater than 128.0!" << endl; + std::cerr << "ModalBar: Control value greater than 128.0!" << std::endl; } if (number == __SK_StickHardness_) // 2 @@ -182,9 +179,9 @@ void ModalBar :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 envelope->setTarget( norm ); else - cerr << "ModalBar: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "ModalBar: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "ModalBar: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "ModalBar: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Modulate.cpp b/src/Modulate.cpp index 465b4a5..c70ee4e 100644 --- a/src/Modulate.cpp +++ b/src/Modulate.cpp @@ -11,14 +11,11 @@ /***************************************************/ #include "Modulate.h" -#include Modulate :: Modulate() { - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency( 6.0 ); vibratoGain = 0.04; diff --git a/src/Moog.cpp b/src/Moog.cpp index 7e042fb..3541b04 100644 --- a/src/Moog.cpp +++ b/src/Moog.cpp @@ -21,20 +21,12 @@ #include "Moog.h" #include "SKINI.msg" -#include - Moog :: Moog() { - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char temp[128]; - char file[128]; - strcpy(temp, RAWWAVE_PATH); - strcpy(file,temp); - attacks[0] = new WvIn( strcat(file,"mandpluk.raw"), TRUE ); - strcpy(file,temp); - loops[0] = new WaveLoop( strcat(file,"impuls20.raw"), TRUE ); - strcpy(file,temp); - loops[1] = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); // vibrato + // Concatenate the STK rawwave path to the rawwave file + attacks[0] = new WvIn( (Stk::rawwavePath() + "mandpluk.raw").c_str(), TRUE ); + loops[0] = new WaveLoop( (Stk::rawwavePath() + "impuls20.raw").c_str(), TRUE ); + loops[1] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); // vibrato loops[1]->setFrequency((MY_FLOAT) 6.122); filters[0] = new FormSwep(); @@ -62,7 +54,7 @@ void Moog :: setFrequency(MY_FLOAT frequency) { baseFrequency = frequency; if ( frequency <= 0.0 ) { - cerr << "Moog: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Moog: setFrequency parameter is less than or equal to zero!" << std::endl; baseFrequency = 220.0; } @@ -92,7 +84,7 @@ void Moog :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) filters[1]->setSweepRate( filterRate * 22050.0 / Stk::sampleRate() ); #if defined(_STK_DEBUG_) - cerr << "Moog: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Moog: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -126,11 +118,11 @@ void Moog :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Moog: Control value less than zero!" << endl; + std::cerr << "Moog: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Moog: Control value greater than 128.0!" << endl; + std::cerr << "Moog: Control value greater than 128.0!" << std::endl; } if (number == __SK_FilterQ_) // 2 @@ -144,10 +136,10 @@ void Moog :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 adsr->setTarget( norm ); else - cerr << "Moog: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Moog: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Moog: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Moog: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/NRev.cpp b/src/NRev.cpp index 51f81cf..980c005 100644 --- a/src/NRev.cpp +++ b/src/NRev.cpp @@ -34,7 +34,7 @@ NRev :: NRev(MY_FLOAT T60) for (i=0; i<6; i++) { combDelays[i] = new Delay( lengths[i], lengths[i]); - combCoefficient[i] = pow(10, (-3 * lengths[i] / (T60 * Stk::sampleRate()))); + combCoefficient[i] = pow(10.0, (-3 * lengths[i] / (T60 * Stk::sampleRate()))); } for (i=0; i<8; i++) diff --git a/src/Noise.cpp b/src/Noise.cpp index bbab22d..197eeea 100644 --- a/src/Noise.cpp +++ b/src/Noise.cpp @@ -12,9 +12,19 @@ #include "Noise.h" #include +#include Noise :: Noise() : Stk() -{ +{ + // Seed the random number generator with system time. + this->setSeed( 0 ); + lastOutput = (MY_FLOAT) 0.0; +} + +Noise :: Noise( unsigned int seed ) : Stk() +{ + // Seed the random number generator + this->setSeed( seed ); lastOutput = (MY_FLOAT) 0.0; } @@ -22,6 +32,14 @@ Noise :: ~Noise() { } +void Noise :: setSeed( unsigned int seed ) +{ + if ( seed == 0 ) + srand( (unsigned int) time(NULL) ); + else + srand( seed ); +} + MY_FLOAT Noise :: tick() { lastOutput = (MY_FLOAT) (2.0 * rand() / (RAND_MAX + 1.0) ); diff --git a/src/PRCRev.cpp b/src/PRCRev.cpp index fbf155a..8b336b8 100644 --- a/src/PRCRev.cpp +++ b/src/PRCRev.cpp @@ -35,10 +35,10 @@ PRCRev :: PRCRev(MY_FLOAT T60) } for (i=0; i<2; i++) { - allpassDelays[i] = new Delay( lengths[i], lengths[i] ); - combDelays[i] = new Delay( lengths[i+2], lengths[i+2] ); - combCoefficient[i] = pow(10,(-3 * lengths[i+2] / (T60 * Stk::sampleRate()))); - } + allpassDelays[i] = new Delay( lengths[i], lengths[i] ); + combDelays[i] = new Delay( lengths[i+2], lengths[i+2] ); + combCoefficient[i] = pow(10.0,(-3 * lengths[i+2] / (T60 * Stk::sampleRate()))); + } allpassCoefficient = 0.7; effectMix = 0.5; diff --git a/src/PercFlut.cpp b/src/PercFlut.cpp index bf9bc18..4612d2f 100644 --- a/src/PercFlut.cpp +++ b/src/PercFlut.cpp @@ -27,25 +27,14 @@ /***************************************************/ #include "PercFlut.h" -#include PercFlut :: PercFlut() : FM() { - int i; - char files[4][128]; - - // Concatenate the STK RAWWAVE_PATH to the rawwave file - for ( i=0; i<4; i++ ) - strcpy( files[i], RAWWAVE_PATH); - - strcat(files[0], "sinewave.raw"); - strcat(files[1], "sinewave.raw"); - strcat(files[2], "sinewave.raw"); - strcat(files[3], "fwavblnk.raw"); - - for ( i=0; i<4; i++ ) - waves[i] = new WaveLoop( files[i], TRUE ); + // Concatenate the STK rawwave path to the rawwave files + for ( int i=0; i<3; i++ ) + waves[i] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + waves[3] = new WaveLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), TRUE ); this->setRatio(0, 1.50 * 1.000); this->setRatio(1, 3.00 * 0.995); diff --git a/src/Phonemes.cpp b/src/Phonemes.cpp index 4175b22..45f0c18 100644 --- a/src/Phonemes.cpp +++ b/src/Phonemes.cpp @@ -11,7 +11,7 @@ /***************************************************/ #include "Phonemes.h" -#include +#include const char Phonemes :: phonemeNames[32][4] = {"eee", "ihh", "ehh", "aaa", @@ -215,7 +215,7 @@ Phonemes :: ~Phonemes(void) const char *Phonemes :: name( unsigned int index ) { if ( index > 31 ) { - cerr << "Phonemes: name index is greater than 31!" << endl; + std::cerr << "Phonemes: name index is greater than 31!" << std::endl; return 0; } return phonemeNames[index]; @@ -224,7 +224,7 @@ const char *Phonemes :: name( unsigned int index ) MY_FLOAT Phonemes :: voiceGain( unsigned int index ) { if ( index > 31 ) { - cerr << "Phonemes: voiceGain index is greater than 31!" << endl; + std::cerr << "Phonemes: voiceGain index is greater than 31!" << std::endl; return 0.0; } return phonemeGains[index][0]; @@ -233,7 +233,7 @@ MY_FLOAT Phonemes :: voiceGain( unsigned int index ) MY_FLOAT Phonemes :: noiseGain( unsigned int index ) { if ( index > 31 ) { - cerr << "Phonemes: noiseGain index is greater than 31!" << endl; + std::cerr << "Phonemes: noiseGain index is greater than 31!" << std::endl; return 0.0; } return phonemeGains[index][1]; @@ -242,11 +242,11 @@ MY_FLOAT Phonemes :: noiseGain( unsigned int index ) MY_FLOAT Phonemes :: formantFrequency( unsigned int index, unsigned int partial ) { if ( index > 31 ) { - cerr << "Phonemes: formantFrequency index is greater than 31!" << endl; + std::cerr << "Phonemes: formantFrequency index is greater than 31!" << std::endl; return 0.0; } if ( partial > 3 ) { - cerr << "Phonemes: formantFrequency partial is greater than 3!" << endl; + std::cerr << "Phonemes: formantFrequency partial is greater than 3!" << std::endl; return 0.0; } return phonemeParameters[index][partial][0]; @@ -255,11 +255,11 @@ MY_FLOAT Phonemes :: formantFrequency( unsigned int index, unsigned int partial MY_FLOAT Phonemes :: formantRadius( unsigned int index, unsigned int partial ) { if ( index > 31 ) { - cerr << "Phonemes: formantRadius index is greater than 31!" << endl; + std::cerr << "Phonemes: formantRadius index is greater than 31!" << std::endl; return 0.0; } if ( partial > 3 ) { - cerr << "Phonemes: formantRadius partial is greater than 3!" << endl; + std::cerr << "Phonemes: formantRadius partial is greater than 3!" << std::endl; return 0.0; } return phonemeParameters[index][partial][1]; @@ -268,11 +268,11 @@ MY_FLOAT Phonemes :: formantRadius( unsigned int index, unsigned int partial ) MY_FLOAT Phonemes :: formantGain( unsigned int index, unsigned int partial ) { if ( index > 31 ) { - cerr << "Phonemes: formantGain index is greater than 31!" << endl; + std::cerr << "Phonemes: formantGain index is greater than 31!" << std::endl; return 0.0; } if ( partial > 3 ) { - cerr << "Phonemes: formantGain partial is greater than 3!" << endl; + std::cerr << "Phonemes: formantGain partial is greater than 3!" << std::endl; return 0.0; } return phonemeParameters[index][partial][2]; diff --git a/src/PitShift.cpp b/src/PitShift.cpp index 9ef54a2..44ba244 100644 --- a/src/PitShift.cpp +++ b/src/PitShift.cpp @@ -10,7 +10,7 @@ /***************************************************/ #include "PitShift.h" -#include +#include #include PitShift :: PitShift() @@ -40,11 +40,11 @@ void PitShift :: setEffectMix(MY_FLOAT mix) { effectMix = mix; if ( mix < 0.0 ) { - cerr << "PitShift: setEffectMix parameter is less than zero!" << endl; + std::cerr << "PitShift: setEffectMix parameter is less than zero!" << std::endl; effectMix = 0.0; } else if ( mix > 1.0 ) { - cerr << "PitShift: setEffectMix parameter is greater than 1.0!" << endl; + std::cerr << "PitShift: setEffectMix parameter is greater than 1.0!" << std::endl; effectMix = 1.0; } } diff --git a/src/PluckTwo.cpp b/src/PluckTwo.cpp index 9410d58..b1c5f6e 100644 --- a/src/PluckTwo.cpp +++ b/src/PluckTwo.cpp @@ -59,7 +59,7 @@ void PluckTwo :: setFrequency(MY_FLOAT frequency) { lastFrequency = frequency; if ( lastFrequency <= 0.0 ) { - cerr << "PluckTwo: setFrequency parameter less than or equal to zero!" << endl; + std::cerr << "PluckTwo: setFrequency parameter less than or equal to zero!" << std::endl; lastFrequency = 220.0; } @@ -83,7 +83,7 @@ void PluckTwo :: setDetune(MY_FLOAT detune) { detuning = detune; if ( detuning <= 0.0 ) { - cerr << "PluckTwo: setDetune parameter less than or equal to zero!" << endl; + std::cerr << "PluckTwo: setDetune parameter less than or equal to zero!" << std::endl; detuning = 0.1; } delayLine->setDelay(( lastLength / detuning) - (MY_FLOAT) 0.5); @@ -100,11 +100,11 @@ void PluckTwo :: setPluckPosition(MY_FLOAT position) { pluckPosition = position; if ( position < 0.0 ) { - cerr << "PluckTwo: setPluckPosition parameter is less than zero!" << endl; + std::cerr << "PluckTwo: setPluckPosition parameter is less than zero!" << std::endl; pluckPosition = 0.0; } else if ( position > 1.0 ) { - cerr << "PluckTwo: setPluckPosition parameter is greater than 1.0!" << endl; + std::cerr << "PluckTwo: setPluckPosition parameter is greater than 1.0!" << std::endl; pluckPosition = 1.0; } } @@ -121,7 +121,7 @@ void PluckTwo :: noteOff(MY_FLOAT amplitude) loopGain = ((MY_FLOAT) 1.0 - amplitude) * (MY_FLOAT) 0.5; #if defined(_STK_DEBUG_) - cerr << "PluckTwo: NoteOff amplitude = " << amplitude << endl; + std::cerr << "PluckTwo: NoteOff amplitude = " << amplitude << std::endl; #endif } diff --git a/src/Plucked.cpp b/src/Plucked.cpp index 834ba11..649e6d3 100644 --- a/src/Plucked.cpp +++ b/src/Plucked.cpp @@ -49,7 +49,7 @@ void Plucked :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Plucked: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Plucked: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -66,11 +66,11 @@ void Plucked :: pluck(MY_FLOAT amplitude) { MY_FLOAT gain = amplitude; if ( gain > 1.0 ) { - cerr << "Plucked: pluck amplitude greater than 1.0!" << endl; + std::cerr << "Plucked: pluck amplitude greater than 1.0!" << std::endl; gain = 1.0; } else if ( gain < 0.0 ) { - cerr << "Plucked: pluck amplitude less than zero!" << endl; + std::cerr << "Plucked: pluck amplitude less than zero!" << std::endl; gain = 0.0; } @@ -87,7 +87,7 @@ void Plucked :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->pluck(amplitude); #if defined(_STK_DEBUG_) - cerr << "Plucked: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Plucked: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -95,16 +95,16 @@ void Plucked :: noteOff(MY_FLOAT amplitude) { loopGain = (MY_FLOAT) 1.0 - amplitude; if ( loopGain < 0.0 ) { - cerr << "Plucked: noteOff amplitude greater than 1.0!" << endl; + std::cerr << "Plucked: noteOff amplitude greater than 1.0!" << std::endl; loopGain = 0.0; } else if ( loopGain > 1.0 ) { - cerr << "Plucked: noteOff amplitude less than or zero!" << endl; + std::cerr << "Plucked: noteOff amplitude less than or zero!" << std::endl; loopGain = (MY_FLOAT) 0.99999; } #if defined(_STK_DEBUG_) - cerr << "Plucked: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Plucked: NoteOff amplitude = " << amplitude << std::endl; #endif } diff --git a/src/Resonate.cpp b/src/Resonate.cpp index 5d3a2f9..63c5cf4 100644 --- a/src/Resonate.cpp +++ b/src/Resonate.cpp @@ -58,7 +58,7 @@ void Resonate :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->setResonance(frequency, poleRadius); #if defined(_STK_DEBUG_) - cerr << "Resonate: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Resonate: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } void Resonate :: noteOff(MY_FLOAT amplitude) @@ -66,7 +66,7 @@ void Resonate :: noteOff(MY_FLOAT amplitude) this->keyOff(); #if defined(_STK_DEBUG_) - cerr << "Resonate: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Resonate: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -74,17 +74,17 @@ void Resonate :: setResonance(MY_FLOAT frequency, MY_FLOAT radius) { poleFrequency = frequency; if ( frequency < 0.0 ) { - cerr << "Resonate: setResonance frequency parameter is less than zero!" << endl; + std::cerr << "Resonate: setResonance frequency parameter is less than zero!" << std::endl; poleFrequency = 0.0; } poleRadius = radius; if ( radius < 0.0 ) { - cerr << "Resonate: setResonance radius parameter is less than 0.0!" << endl; + std::cerr << "Resonate: setResonance radius parameter is less than 0.0!" << std::endl; poleRadius = 0.0; } else if ( radius >= 1.0 ) { - cerr << "Resonate: setResonance radius parameter is greater than or equal to 1.0, which is unstable!" << endl; + std::cerr << "Resonate: setResonance radius parameter is greater than or equal to 1.0, which is unstable!" << std::endl; poleRadius = 0.9999; } filter->setResonance( poleFrequency, poleRadius, TRUE ); @@ -94,13 +94,13 @@ void Resonate :: setNotch(MY_FLOAT frequency, MY_FLOAT radius) { zeroFrequency = frequency; if ( frequency < 0.0 ) { - cerr << "Resonate: setNotch frequency parameter is less than zero!" << endl; + std::cerr << "Resonate: setNotch frequency parameter is less than zero!" << std::endl; zeroFrequency = 0.0; } zeroRadius = radius; if ( radius < 0.0 ) { - cerr << "Resonate: setNotch radius parameter is less than 0.0!" << endl; + std::cerr << "Resonate: setNotch radius parameter is less than 0.0!" << std::endl; zeroRadius = 0.0; } @@ -124,11 +124,11 @@ void Resonate :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Resonate: Control value less than zero!" << endl; + std::cerr << "Resonate: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Resonate: Control value greater than 128.0!" << endl; + std::cerr << "Resonate: Control value greater than 128.0!" << std::endl; } if (number == 2) // 2 @@ -142,9 +142,9 @@ void Resonate :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 adsr->setTarget( norm ); else - cerr << "Resonate: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Resonate: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Resonate: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Resonate: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Rhodey.cpp b/src/Rhodey.cpp index 2915866..b8478f3 100644 --- a/src/Rhodey.cpp +++ b/src/Rhodey.cpp @@ -31,25 +31,14 @@ /***************************************************/ #include "Rhodey.h" -#include Rhodey :: Rhodey() : FM() { - int i; - char files[4][128]; - - // Concatenate the STK RAWWAVE_PATH to the rawwave file. - for ( i=0; i<4; i++ ) - strcpy( files[i], RAWWAVE_PATH); - - strcat(files[0], "sinewave.raw"); - strcat(files[1], "sinewave.raw"); - strcat(files[2], "sinewave.raw"); - strcat(files[3], "fwavblnk.raw"); - - for ( i=0; i<4; i++ ) - waves[i] = new WaveLoop( files[i], TRUE ); + // Concatenate the STK rawwave path to the rawwave files + for ( int i=0; i<3; i++ ) + waves[i] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + waves[3] = new WaveLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), TRUE ); this->setRatio(0, 1.0); this->setRatio(1, 0.5); diff --git a/src/RtAudio.cpp b/src/RtAudio.cpp index 8e44ba2..392f183 100644 --- a/src/RtAudio.cpp +++ b/src/RtAudio.cpp @@ -1,16 +1,16 @@ /************************************************************************/ /*! \class RtAudio - \brief Realtime audio i/o C++ class. + \brief Realtime audio i/o C++ classes. RtAudio provides a common API (Application Programming Interface) - for realtime audio input/output across Linux (native ALSA and - OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound - and ASIO) operating systems. + for realtime audio input/output across Linux (native ALSA, Jack, + and OSS), SGI, Macintosh OS X (CoreAudio), and Windows + (DirectSound and ASIO) operating systems. - RtAudio WWW site: http://www-ccrma.stanford.edu/~gary/rtaudio/ + RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/ RtAudio: a realtime audio i/o C++ class - Copyright (c) 2001-2002 Gary P. Scavone + Copyright (c) 2001-2004 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -37,31 +37,26 @@ */ /************************************************************************/ -// RtAudio: Version 2.1.1, 24 October 2002 +// RtAudio: Version 3.0, 1 March 2004 #include "RtAudio.h" -#include -#include -#include +#include // Static variable definitions. -const unsigned int RtAudio :: SAMPLE_RATES[] = { +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 }; -const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT8 = 1; -const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT16 = 2; -const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT24 = 4; -const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT32 = 8; -const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT32 = 16; -const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT64 = 32; #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A) - #define MUTEX_LOCK(A) EnterCriticalSection(A) + #define MUTEX_DESTROY(A) DeleteCriticalSection(A); + #define MUTEX_LOCK(A) EnterCriticalSection(A) #define MUTEX_UNLOCK(A) LeaveCriticalSection(A) #else // 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) #endif @@ -72,93 +67,202 @@ const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT64 = 32; // // *************************************************** // -RtAudio :: RtAudio() +RtAudio :: RtAudio( RtAudioApi api ) { - initialize(); - - if (nDevices <= 0) { - sprintf(message, "RtAudio: no audio devices found!"); - error(RtError::NO_DEVICES_FOUND); - } + initialize( api ); } -RtAudio :: RtAudio(int *streamId, - int outputDevice, int outputChannels, - int inputDevice, int inputChannels, - RTAUDIO_FORMAT format, int sampleRate, - int *bufferSize, int numberOfBuffers) +RtAudio :: RtAudio( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int numberOfBuffers, RtAudioApi api ) { - initialize(); - - if (nDevices <= 0) { - sprintf(message, "RtAudio: no audio devices found!"); - error(RtError::NO_DEVICES_FOUND); - } + initialize( api ); try { - *streamId = openStream(outputDevice, outputChannels, inputDevice, inputChannels, - format, sampleRate, bufferSize, numberOfBuffers); + rtapi_->openStream( outputDevice, outputChannels, + inputDevice, inputChannels, + format, sampleRate, + bufferSize, numberOfBuffers ); } catch (RtError &exception) { - // deallocate the RTAUDIO_DEVICE structures - if (devices) free(devices); + // Deallocate the RtApi instance. + delete rtapi_; throw exception; } } RtAudio :: ~RtAudio() { - // close any existing streams - while ( streams.size() ) - closeStream( streams.begin()->first ); - - // deallocate the RTAUDIO_DEVICE structures - if (devices) free(devices); + delete rtapi_; } -int RtAudio :: openStream(int outputDevice, int outputChannels, - int inputDevice, int inputChannels, - RTAUDIO_FORMAT format, int sampleRate, - int *bufferSize, int numberOfBuffers) +void RtAudio :: openStream( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int numberOfBuffers ) { - static int streamKey = 0; // Unique stream identifier ... OK for multiple instances. + rtapi_->openStream( outputDevice, outputChannels, inputDevice, + inputChannels, format, sampleRate, + bufferSize, numberOfBuffers ); +} + +void RtAudio::initialize( RtAudioApi api ) +{ + rtapi_ = 0; + + // First look for a compiled match to a specified API value. If one + // of these constructors throws an error, it will be passed up the + // inheritance chain. +#if defined(__LINUX_JACK__) + if ( api == LINUX_JACK ) + rtapi_ = new RtApiJack(); +#endif +#if defined(__LINUX_ALSA__) + if ( api == LINUX_ALSA ) + rtapi_ = new RtApiAlsa(); +#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(__IRIX_AL__) + if ( api == IRIX_AL ) + rtapi_ = new RtApiAl(); +#endif +#if defined(__MACOSX_CORE__) + if ( api == MACOSX_CORE ) + rtapi_ = new RtApiCore(); +#endif + + if ( rtapi_ ) return; + if ( api > 0 ) { + // No compiled support for specified API value. + throw RtError( "RtAudio: no compiled support for specified API argument!", RtError::INVALID_PARAMETER ); + } + + // No specified API ... search for "best" option. + try { +#if defined(__LINUX_JACK__) + rtapi_ = new RtApiJack(); +#elif defined(__WINDOWS_ASIO__) + rtapi_ = new RtApiAsio(); +#elif defined(__IRIX_AL__) + rtapi_ = new RtApiAl(); +#elif defined(__MACOSX_CORE__) + rtapi_ = new RtApiCore(); +#else + ; +#endif + } + catch (RtError &) { +#if defined(__RTAUDIO_DEBUG__) + fprintf(stderr, "\nRtAudio: no devices found for first api option (JACK, ASIO, Al, or CoreAudio).\n\n"); +#endif + rtapi_ = 0; + } + + if ( rtapi_ ) return; + +// Try second API support + if ( rtapi_ == 0 ) { + try { +#if defined(__LINUX_ALSA__) + rtapi_ = new RtApiAlsa(); +#elif defined(__WINDOWS_DS__) + rtapi_ = new RtApiDs(); +#else + ; +#endif + } + catch (RtError &) { +#if defined(__RTAUDIO_DEBUG__) + fprintf(stderr, "\nRtAudio: no devices found for second api option (Alsa or DirectSound).\n\n"); +#endif + rtapi_ = 0; + } + } + + if ( rtapi_ ) return; + + // Try third API support + if ( rtapi_ == 0 ) { +#if defined(__LINUX_OSS__) + try { + rtapi_ = new RtApiOss(); + } + catch (RtError &error) { + rtapi_ = 0; + } +#else + ; +#endif + } + + if ( rtapi_ == 0 ) { + // No devices found. + throw RtError( "RtAudio: no devices found for compiled audio APIs!", RtError::NO_DEVICES_FOUND ); + } +} + +RtApi :: RtApi() +{ + stream_.mode = UNINITIALIZED; + stream_.apiHandle = 0; + MUTEX_INITIALIZE(&stream_.mutex); +} + +RtApi :: ~RtApi() +{ + MUTEX_DESTROY(&stream_.mutex); +} + +void RtApi :: openStream( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int numberOfBuffers ) +{ + if ( stream_.mode != UNINITIALIZED ) { + sprintf(message_, "RtApi: only one open stream allowed per class instance."); + error(RtError::INVALID_STREAM); + } if (outputChannels < 1 && inputChannels < 1) { - sprintf(message,"RtAudio: one or both 'channel' parameters must be greater than zero."); + sprintf(message_,"RtApi: one or both 'channel' parameters must be greater than zero."); error(RtError::INVALID_PARAMETER); } if ( formatBytes(format) == 0 ) { - sprintf(message,"RtAudio: 'format' parameter value is undefined."); + sprintf(message_,"RtApi: 'format' parameter value is undefined."); error(RtError::INVALID_PARAMETER); } if ( outputChannels > 0 ) { - if (outputDevice > nDevices || outputDevice < 0) { - sprintf(message,"RtAudio: 'outputDevice' parameter value (%d) is invalid.", outputDevice); + if (outputDevice > nDevices_ || outputDevice < 0) { + sprintf(message_,"RtApi: 'outputDevice' parameter value (%d) is invalid.", outputDevice); error(RtError::INVALID_PARAMETER); } } if ( inputChannels > 0 ) { - if (inputDevice > nDevices || inputDevice < 0) { - sprintf(message,"RtAudio: 'inputDevice' parameter value (%d) is invalid.", inputDevice); + if (inputDevice > nDevices_ || inputDevice < 0) { + sprintf(message_,"RtApi: 'inputDevice' parameter value (%d) is invalid.", inputDevice); error(RtError::INVALID_PARAMETER); } } - // Allocate a new stream structure. - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) calloc(1, sizeof(RTAUDIO_STREAM)); - if (stream == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::MEMORY_ERROR); - } - stream->mode = UNINITIALIZED; - MUTEX_INITIALIZE(&stream->mutex); - + clearStreamInfo(); bool result = FAILURE; int device, defaultDevice = 0; - STREAM_MODE mode; + StreamMode mode; int channels; if ( outputChannels > 0 ) { @@ -172,22 +276,23 @@ int RtAudio :: openStream(int outputDevice, int outputChannels, else device = outputDevice - 1; - for (int i=-1; i= 0 ) { + for ( int i=-1; i= 0 ) { if ( i == defaultDevice ) continue; device = i; } - if (devices[device].probed == false) { + if (devices_[device].probed == false) { // If the device wasn't successfully probed before, try it - // again now. - clearDeviceInfo(&devices[device]); - probeDeviceInfo(&devices[device]); + // (again) now. + clearDeviceInfo(&devices_[device]); + probeDeviceInfo(&devices_[device]); } - if ( devices[device].probed ) - result = probeDeviceOpen(device, stream, mode, channels, sampleRate, + if ( devices_[device].probed ) + result = probeDeviceOpen(device, mode, channels, sampleRate, format, bufferSize, numberOfBuffers); - if (result == SUCCESS) break; + if ( result == SUCCESS ) break; if ( outputDevice > 0 ) break; + clearStreamInfo(); } } @@ -203,153 +308,116 @@ int RtAudio :: openStream(int outputDevice, int outputChannels, else device = inputDevice - 1; - for (int i=-1; i= 0 ) { if ( i == defaultDevice ) continue; device = i; } - if (devices[device].probed == false) { + if (devices_[device].probed == false) { // If the device wasn't successfully probed before, try it - // again now. - clearDeviceInfo(&devices[device]); - probeDeviceInfo(&devices[device]); + // (again) now. + clearDeviceInfo(&devices_[device]); + probeDeviceInfo(&devices_[device]); } - if ( devices[device].probed ) - result = probeDeviceOpen(device, stream, mode, channels, sampleRate, + if ( devices_[device].probed ) + result = probeDeviceOpen(device, mode, channels, sampleRate, format, bufferSize, numberOfBuffers); if (result == SUCCESS) break; if ( outputDevice > 0 ) break; } } - streams[++streamKey] = (void *) stream; if ( result == SUCCESS ) - return streamKey; + return; // If we get here, all attempted probes failed. Close any opened - // devices and delete the allocated stream. - closeStream(streamKey); + // devices and clear the stream structure. + if ( stream_.mode != UNINITIALIZED ) closeStream(); + clearStreamInfo(); if ( ( outputDevice == 0 && outputChannels > 0 ) || ( inputDevice == 0 && inputChannels > 0 ) ) - sprintf(message,"RtAudio: no devices found for given parameters."); + sprintf(message_,"RtApi: no devices found for given stream parameters."); else - sprintf(message,"RtAudio: unable to open specified device(s) with given stream parameters."); + sprintf(message_,"RtApi: unable to open specified device(s) with given stream parameters."); error(RtError::INVALID_PARAMETER); - return -1; -} - -int RtAudio :: getDeviceCount(void) -{ - return nDevices; -} - -void RtAudio :: getDeviceInfo(int device, RTAUDIO_DEVICE *info) -{ - if (device > nDevices || device < 1) { - sprintf(message, "RtAudio: invalid device specifier (%d)!", device); - error(RtError::INVALID_DEVICE); - } - - int deviceIndex = device - 1; - - // If the device wasn't successfully probed before, try it now (or again). - if (devices[deviceIndex].probed == false) { - clearDeviceInfo(&devices[deviceIndex]); - probeDeviceInfo(&devices[deviceIndex]); - } - - // Clear the info structure. - memset(info, 0, sizeof(RTAUDIO_DEVICE)); - - strncpy(info->name, devices[deviceIndex].name, 128); - info->probed = devices[deviceIndex].probed; - if ( info->probed == true ) { - info->maxOutputChannels = devices[deviceIndex].maxOutputChannels; - info->maxInputChannels = devices[deviceIndex].maxInputChannels; - info->maxDuplexChannels = devices[deviceIndex].maxDuplexChannels; - info->minOutputChannels = devices[deviceIndex].minOutputChannels; - info->minInputChannels = devices[deviceIndex].minInputChannels; - info->minDuplexChannels = devices[deviceIndex].minDuplexChannels; - info->hasDuplexSupport = devices[deviceIndex].hasDuplexSupport; - info->nSampleRates = devices[deviceIndex].nSampleRates; - if (info->nSampleRates == -1) { - info->sampleRates[0] = devices[deviceIndex].sampleRates[0]; - info->sampleRates[1] = devices[deviceIndex].sampleRates[1]; - } - else { - for (int i=0; inSampleRates; i++) - info->sampleRates[i] = devices[deviceIndex].sampleRates[i]; - } - info->nativeFormats = devices[deviceIndex].nativeFormats; - if ( deviceIndex == getDefaultOutputDevice() || - deviceIndex == getDefaultInputDevice() ) - info->isDefault = true; - } - return; } -char * const RtAudio :: getStreamBuffer(int streamId) +int RtApi :: getDeviceCount(void) { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - return stream->userBuffer; + return devices_.size(); } -#if defined(__LINUX_ALSA__) || defined(__LINUX_OSS__) || defined(__IRIX_AL__) - -extern "C" void *callbackHandler(void * ptr); - -void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData) +RtAudioDeviceInfo RtApi :: getDeviceInfo( int device ) { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - CALLBACK_INFO *info = (CALLBACK_INFO *) &stream->callbackInfo; - if ( info->usingCallback ) { - sprintf(message, "RtAudio: A callback is already set for this stream!"); - error(RtError::WARNING); - return; + if (device > (int) devices_.size() || device < 1) { + sprintf(message_, "RtApi: invalid device specifier (%d)!", device); + error(RtError::INVALID_DEVICE); } - info->callback = (void *) callback; - info->userData = userData; - info->usingCallback = true; - info->object = (void *) this; - info->streamId = streamId; + RtAudioDeviceInfo info; + int deviceIndex = device - 1; - int err = pthread_create(&info->thread, NULL, callbackHandler, &stream->callbackInfo); - - if (err) { - info->usingCallback = false; - sprintf(message, "RtAudio: error starting callback thread!"); - error(RtError::THREAD_ERROR); + // If the device wasn't successfully probed before, try it now (or again). + if (devices_[deviceIndex].probed == false) { + clearDeviceInfo(&devices_[deviceIndex]); + probeDeviceInfo(&devices_[deviceIndex]); } + + info.name.append( devices_[deviceIndex].name ); + info.probed = devices_[deviceIndex].probed; + if ( info.probed == true ) { + info.outputChannels = devices_[deviceIndex].maxOutputChannels; + info.inputChannels = devices_[deviceIndex].maxInputChannels; + info.duplexChannels = devices_[deviceIndex].maxDuplexChannels; + for (unsigned int i=0; icallbackInfo.usingCallback) { - - if (stream->state == STREAM_RUNNING) - stopStream( streamId ); - - MUTEX_LOCK(&stream->mutex); - - stream->callbackInfo.usingCallback = false; - pthread_cancel(stream->callbackInfo.thread); - pthread_join(stream->callbackInfo.thread, NULL); - stream->callbackInfo.thread = 0; - stream->callbackInfo.callback = NULL; - stream->callbackInfo.userData = NULL; - - MUTEX_UNLOCK(&stream->mutex); - } + verifyStream(); + return stream_.userBuffer; +} + +int RtApi :: getDefaultInputDevice(void) +{ + // Should be implemented in subclasses if appropriate. + return 0; +} + +int RtApi :: getDefaultOutputDevice(void) +{ + // Should be implemented in subclasses if appropriate. + return 0; +} + +void RtApi :: closeStream(void) +{ + // MUST be implemented in subclasses! +} + +void RtApi :: probeDeviceInfo( RtApiDevice *info ) +{ + // MUST be implemented in subclasses! +} + +bool RtApi :: probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ) +{ + // MUST be implemented in subclasses! + return FAILURE; } -#endif // *************************************************** // // @@ -357,2050 +425,9 @@ void RtAudio :: cancelStreamCallback(int streamId) // // *************************************************** // -#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. This same functionality can be -// achieved with better synchrony by opening two separate streams for -// the devices and using RtAudio blocking calls (i.e. tickStream()). -// -// The possibility of having multiple RtAudio streams accessing the -// same CoreAudio device is not currently supported. The problem -// involves the inability to install our callbackHandler function for -// the same device more than once. I experimented with a workaround -// for this, but it requires an additional buffer for mixing output -// data before filling the CoreAudio device buffer. In the end, I -// decided it wasn't worth supporting. -// -// Property listeners are currently not used. The issue is 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. Some initial listener code is -// commented out. - -void RtAudio :: initialize(void) -{ - OSStatus err = noErr; - UInt32 dataSize; - AudioDeviceID *deviceList = NULL; - nDevices = 0; - - // Find out how many audio devices there are, if any. - err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &dataSize, NULL); - if (err != noErr) { - sprintf(message, "RtAudio: OSX error getting device info!"); - error(RtError::SYSTEM_ERROR); - } - - nDevices = dataSize / sizeof(AudioDeviceID); - if (nDevices == 0) return; - - // Allocate the RTAUDIO_DEVICE structures. - devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE)); - if (devices == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::MEMORY_ERROR); - } - - // Make space for the devices we are about to get. - deviceList = (AudioDeviceID *) malloc( dataSize ); - if (deviceList == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::MEMORY_ERROR); - } - - // Get the array of AudioDeviceIDs. - err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &dataSize, (void *) deviceList); - if (err != noErr) { - free(deviceList); - sprintf(message, "RtAudio: OSX error getting device properties!"); - error(RtError::SYSTEM_ERROR); - } - - // Write device identifiers to device structures and then - // probe the device capabilities. - for (int i=0; iid[0], 0, false, - kAudioDevicePropertyDeviceManufacturer, - &dataSize, name ); - if (err != noErr) { - sprintf( message, "RtAudio: OSX error getting device manufacturer." ); - error(RtError::DEBUG_WARNING); - return; - } - strncpy(fullname, name, 256); - strcat(fullname, ": " ); - - dataSize = 256; - err = AudioDeviceGetProperty( info->id[0], 0, false, - kAudioDevicePropertyDeviceName, - &dataSize, name ); - if (err != noErr) { - sprintf( message, "RtAudio: OSX error getting device name." ); - error(RtError::DEBUG_WARNING); - return; - } - strncat(fullname, name, 254); - strncat(info->name, fullname, 128); - - // Get output channel information. - unsigned int i, minChannels, maxChannels, nStreams = 0; - AudioBufferList *bufferList = nil; - err = AudioDeviceGetPropertyInfo( info->id[0], 0, false, - kAudioDevicePropertyStreamConfiguration, - &dataSize, NULL ); - if (err == noErr && dataSize > 0) { - bufferList = (AudioBufferList *) malloc( dataSize ); - if (bufferList == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::DEBUG_WARNING); - return; - } - - err = AudioDeviceGetProperty( info->id[0], 0, false, - kAudioDevicePropertyStreamConfiguration, - &dataSize, bufferList ); - if (err == noErr) { - maxChannels = 0; - minChannels = 1000; - nStreams = bufferList->mNumberBuffers; - for ( i=0; imBuffers[i].mNumberChannels; - if ( bufferList->mBuffers[i].mNumberChannels < minChannels ) - minChannels = bufferList->mBuffers[i].mNumberChannels; - } - } - } - if (err != noErr || dataSize <= 0) { - sprintf( message, "RtAudio: OSX error getting output channels for device (%s).", info->name ); - error(RtError::DEBUG_WARNING); - return; - } - - free (bufferList); - if ( nStreams ) { - if ( maxChannels > 0 ) - info->maxOutputChannels = maxChannels; - if ( minChannels > 0 ) - info->minOutputChannels = minChannels; - } - - // Get input channel information. - bufferList = nil; - err = AudioDeviceGetPropertyInfo( info->id[0], 0, true, - kAudioDevicePropertyStreamConfiguration, - &dataSize, NULL ); - if (err == noErr && dataSize > 0) { - bufferList = (AudioBufferList *) malloc( dataSize ); - if (bufferList == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::DEBUG_WARNING); - return; - } - err = AudioDeviceGetProperty( info->id[0], 0, true, - kAudioDevicePropertyStreamConfiguration, - &dataSize, bufferList ); - if (err == noErr) { - maxChannels = 0; - minChannels = 1000; - nStreams = bufferList->mNumberBuffers; - for ( i=0; imBuffers[i].mNumberChannels < minChannels ) - minChannels = bufferList->mBuffers[i].mNumberChannels; - maxChannels += bufferList->mBuffers[i].mNumberChannels; - } - } - } - if (err != noErr || dataSize <= 0) { - sprintf( message, "RtAudio: OSX error getting input channels for device (%s).", info->name ); - error(RtError::DEBUG_WARNING); - return; - } - - free (bufferList); - if ( nStreams ) { - if ( maxChannels > 0 ) - info->maxInputChannels = maxChannels; - if ( minChannels > 0 ) - info->minInputChannels = minChannels; - } - - // If device opens for both playback and capture, we determine the channels. - if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) { - info->hasDuplexSupport = true; - info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? - info->maxInputChannels : info->maxOutputChannels; - info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? - info->minInputChannels : info->minOutputChannels; - } - - // Probe the device sample rate and data format parameters. The - // core audio query mechanism is performed on a "stream" - // description, which can have a variable number of channels and - // apply to input or output only. - - // Create a stream description structure. - AudioStreamBasicDescription description; - dataSize = sizeof( AudioStreamBasicDescription ); - memset(&description, 0, sizeof(AudioStreamBasicDescription)); - bool isInput = false; - if ( info->maxOutputChannels == 0 ) isInput = true; - bool isDuplex = false; - if ( info->maxDuplexChannels > 0 ) isDuplex = true; - - // Determine the supported sample rates. - info->nSampleRates = 0; - for (i=0; iid[0], isInput, &description, isDuplex ) ) - info->sampleRates[info->nSampleRates++] = SAMPLE_RATES[i]; - } - - if (info->nSampleRates == 0) { - sprintf( message, "RtAudio: No supported sample rates found for OSX device (%s).", info->name ); - error(RtError::DEBUG_WARNING); - return; - } - - // Check for continuous sample rate support. - description.mSampleRate = kAudioStreamAnyRate; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) { - info->sampleRates[1] = info->sampleRates[info->nSampleRates-1]; - info->nSampleRates = -1; - } - - // Determine the supported data formats. - info->nativeFormats = 0; - description.mFormatID = kAudioFormatLinearPCM; - description.mBitsPerChannel = 8; - description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT8; - else { - description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT8; - } - - description.mBitsPerChannel = 16; - description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT16; - else { - description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT16; - } - - description.mBitsPerChannel = 32; - description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT32; - else { - description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT32; - } - - description.mBitsPerChannel = 24; - description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsAlignedHigh | kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT24; - else { - description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_SINT24; - } - - description.mBitsPerChannel = 32; - description.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_FLOAT32; - else { - description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_FLOAT32; - } - - description.mBitsPerChannel = 64; - description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_FLOAT64; - else { - description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; - if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) - info->nativeFormats |= RTAUDIO_FLOAT64; - } - - // Check that we have at least one supported format. - if (info->nativeFormats == 0) { - sprintf(message, "RtAudio: OSX PCM device (%s) data format not supported by RtAudio.", - info->name); - error(RtError::DEBUG_WARNING); - return; - } - - info->probed = true; -} - -OSStatus callbackHandler(AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* infoPointer) -{ - CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer; - - RtAudio *object = (RtAudio *) info->object; - try { - object->callbackEvent( info->streamId, inDevice, (void *)inInputData, (void *)outOutputData ); - } - catch (RtError &exception) { - fprintf(stderr, "\nCallback handler error (%s)!\n\n", exception.getMessage()); - return kAudioHardwareUnspecifiedError; - } - - return kAudioHardwareNoError; -} - -/* -OSStatus deviceListener(AudioDeviceID inDevice, - UInt32 channel, - Boolean isInput, - AudioDevicePropertyID propertyID, - void* infoPointer) -{ - CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer; - - RtAudio *object = (RtAudio *) info->object; - try { - object->settingChange( info->streamId ); - } - catch (RtError &exception) { - fprintf(stderr, "\nDevice listener error (%s)!\n\n", exception.getMessage()); - return kAudioHardwareUnspecifiedError; - } - - return kAudioHardwareNoError; -} -*/ - -bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, - STREAM_MODE mode, int channels, - int sampleRate, RTAUDIO_FORMAT format, - int *bufferSize, int numberOfBuffers) -{ - // Check to make sure we don't already have a stream accessing this device. - RTAUDIO_STREAM *streamPtr; - std::map::const_iterator i; - for ( i=streams.begin(); i!=streams.end(); ++i ) { - streamPtr = (RTAUDIO_STREAM *) i->second; - if ( streamPtr->device[0] == device || streamPtr->device[1] == device ) { - sprintf(message, "RtAudio: no current OS X support for multiple streams accessing the same device!"); - error(RtError::WARNING); - return FAILURE; - } - } - - // Setup for stream mode. - bool isInput = false; - AudioDeviceID id = devices[device].id[0]; - if ( mode == INPUT ) isInput = true; - - // Search for a stream which contains the desired number of channels. - OSStatus err = noErr; - UInt32 dataSize; - unsigned int deviceChannels, nStreams; - UInt32 iChannel = 0, iStream = 0; - AudioBufferList *bufferList = nil; - err = AudioDeviceGetPropertyInfo( id, 0, isInput, - kAudioDevicePropertyStreamConfiguration, - &dataSize, NULL ); - - if (err == noErr && dataSize > 0) { - bufferList = (AudioBufferList *) malloc( dataSize ); - if (bufferList == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - err = AudioDeviceGetProperty( id, 0, isInput, - kAudioDevicePropertyStreamConfiguration, - &dataSize, bufferList ); - - if (err == noErr) { - stream->deInterleave[mode] = false; - nStreams = bufferList->mNumberBuffers; - for ( iStream=0; iStreammBuffers[iStream].mNumberChannels >= (unsigned int) channels ) break; - iChannel += bufferList->mBuffers[iStream].mNumberChannels; - } - // If we didn't find a single stream above, see if we can meet - // the channel specification in mono mode (i.e. using separate - // non-interleaved buffers). This can only work if there are N - // consecutive one-channel streams, where N is the number of - // desired channels. - iChannel = 0; - if ( iStream >= nStreams && nStreams >= (unsigned int) channels ) { - int counter = 0; - for ( iStream=0; iStreammBuffers[iStream].mNumberChannels == 1 ) - counter++; - else - counter = 0; - if ( counter == channels ) { - iStream -= channels - 1; - iChannel -= channels - 1; - stream->deInterleave[mode] = true; - break; - } - iChannel += bufferList->mBuffers[iStream].mNumberChannels; - } - } - } - } - if (err != noErr || dataSize <= 0) { - if ( bufferList ) free( bufferList ); - sprintf( message, "RtAudio: OSX error getting channels for device (%s).", devices[device].name ); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - - if (iStream >= nStreams) { - free (bufferList); - sprintf( message, "RtAudio: unable to find OSX audio stream on device (%s) for requested channels (%d).", - devices[device].name, channels ); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - - // This is ok even for mono mode ... it gets updated later. - deviceChannels = bufferList->mBuffers[iStream].mNumberChannels; - free (bufferList); - - // Determine the buffer size. - AudioValueRange bufferRange; - dataSize = sizeof(AudioValueRange); - err = AudioDeviceGetProperty( id, 0, isInput, - kAudioDevicePropertyBufferSizeRange, - &dataSize, &bufferRange); - if (err != noErr) { - sprintf( message, "RtAudio: OSX error getting buffer size range for device (%s).", - devices[device].name ); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - - long bufferBytes = *bufferSize * deviceChannels * formatBytes(RTAUDIO_FLOAT32); - if (bufferRange.mMinimum > bufferBytes) bufferBytes = (int) bufferRange.mMinimum; - else if (bufferRange.mMaximum < bufferBytes) bufferBytes = (int) bufferRange.mMaximum; - - // Set the buffer size. For mono mode, I'm assuming we only need to - // make this setting for the first channel. - UInt32 theSize = (UInt32) bufferBytes; - dataSize = sizeof( UInt32); - err = AudioDeviceSetProperty(id, NULL, 0, isInput, - kAudioDevicePropertyBufferSize, - dataSize, &theSize); - if (err != noErr) { - sprintf( message, "RtAudio: OSX error setting the buffer size for device (%s).", - devices[device].name ); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - - // If attempting to setup a duplex stream, the bufferSize parameter - // MUST be the same in both directions! - *bufferSize = bufferBytes / ( deviceChannels * formatBytes(RTAUDIO_FLOAT32) ); - if ( stream->mode == OUTPUT && mode == INPUT && *bufferSize != stream->bufferSize ) { - sprintf( message, "RtAudio: OSX error setting buffer size for duplex stream on device (%s).", - devices[device].name ); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - - stream->bufferSize = *bufferSize; - stream->nBuffers = 1; - - // Set the stream format description. Do for each channel in mono mode. - AudioStreamBasicDescription description; - dataSize = sizeof( AudioStreamBasicDescription ); - if ( stream->deInterleave[mode] ) nStreams = channels; - else nStreams = 1; - for ( unsigned int i=0; idoByteSwap[mode] = false; - if ( !description.mFormatFlags & kLinearPCMFormatFlagIsBigEndian ) - stream->doByteSwap[mode] = true; - - // From the CoreAudio documentation, PCM data must be supplied as - // 32-bit floats. - stream->userFormat = format; - stream->deviceFormat[mode] = RTAUDIO_FLOAT32; - - if ( stream->deInterleave[mode] ) - stream->nDeviceChannels[mode] = channels; - else - stream->nDeviceChannels[mode] = description.mChannelsPerFrame; - stream->nUserChannels[mode] = channels; - - // Set handle and flags for buffer conversion. - stream->handle[mode] = iStream; - 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->nUserChannels[mode] > 1 && stream->deInterleave[mode]) - stream->doConvertBuffer[mode] = true; - - // Allocate necessary internal buffers. - if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) { - - long buffer_bytes; - if (stream->nUserChannels[0] >= stream->nUserChannels[1]) - buffer_bytes = stream->nUserChannels[0]; - else - buffer_bytes = stream->nUserChannels[1]; - - buffer_bytes *= *bufferSize * formatBytes(stream->userFormat); - if (stream->userBuffer) free(stream->userBuffer); - stream->userBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->userBuffer == NULL) - goto memory_error; - } - - if ( stream->deInterleave[mode] ) { - - long buffer_bytes; - bool makeBuffer = true; - if ( mode == OUTPUT ) - buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); - else { // mode == INPUT - buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]); - if ( stream->mode == OUTPUT && stream->deviceBuffer ) { - long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); - if ( buffer_bytes < bytes_out ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - buffer_bytes *= *bufferSize; - if (stream->deviceBuffer) free(stream->deviceBuffer); - stream->deviceBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->deviceBuffer == NULL) - goto memory_error; - - // If not de-interleaving, we point stream->deviceBuffer to the - // OS X supplied device buffer before doing any necessary data - // conversions. This presents a problem if we have a duplex - // stream using one device which needs de-interleaving and - // another device which doesn't. So, save a pointer to our own - // device buffer in the CALLBACK_INFO structure. - stream->callbackInfo.buffers = stream->deviceBuffer; - } - } - - stream->sampleRate = sampleRate; - stream->device[mode] = device; - stream->state = STREAM_STOPPED; - stream->callbackInfo.object = (void *) this; - stream->callbackInfo.waitTime = (unsigned long) (200000.0 * stream->bufferSize / stream->sampleRate); - stream->callbackInfo.device[mode] = id; - if ( stream->mode == OUTPUT && mode == INPUT && stream->device[0] == device ) - // Only one callback procedure per device. - stream->mode = DUPLEX; - else { - err = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream->callbackInfo ); - if (err != noErr) { - sprintf( message, "RtAudio: OSX error setting callback for device (%s).", devices[device].name ); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - if ( stream->mode == OUTPUT && mode == INPUT ) - stream->mode = DUPLEX; - else - stream->mode = mode; - } - - // If we wanted to use property listeners, they would be setup here. - - return SUCCESS; - - memory_error: - if (stream->userBuffer) { - free(stream->userBuffer); - stream->userBuffer = 0; - } - sprintf(message, "RtAudio: OSX error allocating buffer memory (%s).", devices[device].name); - error(RtError::WARNING); - return FAILURE; -} - -void RtAudio :: cancelStreamCallback(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - if (stream->callbackInfo.usingCallback) { - - if (stream->state == STREAM_RUNNING) - stopStream( streamId ); - - MUTEX_LOCK(&stream->mutex); - - stream->callbackInfo.usingCallback = false; - stream->callbackInfo.userData = NULL; - stream->state = STREAM_STOPPED; - stream->callbackInfo.callback = NULL; - - MUTEX_UNLOCK(&stream->mutex); - } -} - -void RtAudio :: closeStream(int streamId) -{ - // We don't want an exception to be thrown here because this - // function is called by our class destructor. So, do our own - // streamId check. - if ( streams.find( streamId ) == streams.end() ) { - sprintf(message, "RtAudio: invalid stream identifier!"); - error(RtError::WARNING); - return; - } - - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId]; - - AudioDeviceID id; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - id = devices[stream->device[0]].id[0]; - if (stream->state == STREAM_RUNNING) - AudioDeviceStop( id, callbackHandler ); - AudioDeviceRemoveIOProc( id, callbackHandler ); - } - - if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) { - id = devices[stream->device[1]].id[0]; - if (stream->state == STREAM_RUNNING) - AudioDeviceStop( id, callbackHandler ); - AudioDeviceRemoveIOProc( id, callbackHandler ); - } - - pthread_mutex_destroy(&stream->mutex); - - if (stream->userBuffer) - free(stream->userBuffer); - - if ( stream->deInterleave[0] || stream->deInterleave[1] ) - free(stream->callbackInfo.buffers); - - free(stream); - streams.erase(streamId); -} - -void RtAudio :: startStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_RUNNING) - goto unlock; - - OSStatus err; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - - err = AudioDeviceStart(devices[stream->device[0]].id[0], callbackHandler); - if (err != noErr) { - sprintf(message, "RtAudio: OSX error starting callback procedure on device (%s).", - devices[stream->device[0]].name); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) { - - err = AudioDeviceStart(devices[stream->device[1]].id[0], callbackHandler); - if (err != noErr) { - sprintf(message, "RtAudio: OSX error starting input callback procedure on device (%s).", - devices[stream->device[0]].name); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - stream->callbackInfo.streamId = streamId; - stream->state = STREAM_RUNNING; - stream->callbackInfo.blockTick = true; - stream->callbackInfo.stopStream = false; - - unlock: - MUTEX_UNLOCK(&stream->mutex); -} - -void RtAudio :: stopStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) - goto unlock; - - OSStatus err; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - - err = AudioDeviceStop(devices[stream->device[0]].id[0], callbackHandler); - if (err != noErr) { - sprintf(message, "RtAudio: OSX error stopping callback procedure on device (%s).", - devices[stream->device[0]].name); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) { - - err = AudioDeviceStop(devices[stream->device[1]].id[0], callbackHandler); - if (err != noErr) { - sprintf(message, "RtAudio: OSX error stopping input callback procedure on device (%s).", - devices[stream->device[0]].name); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - stream->state = STREAM_STOPPED; - - unlock: - MUTEX_UNLOCK(&stream->mutex); -} - -void RtAudio :: abortStream(int streamId) -{ - stopStream( streamId ); -} - -// I don't know how this function can be implemented. -int RtAudio :: streamWillBlock(int streamId) -{ - sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for OS X."); - error(RtError::WARNING); - return 0; -} - -void RtAudio :: tickStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - if (stream->state == STREAM_STOPPED) - return; - - if (stream->callbackInfo.usingCallback) { - sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!"); - error(RtError::WARNING); - return; - } - - // Block waiting here until the user data is processed in callbackEvent(). - while ( stream->callbackInfo.blockTick ) - usleep(stream->callbackInfo.waitTime); - - MUTEX_LOCK(&stream->mutex); - - stream->callbackInfo.blockTick = true; - - MUTEX_UNLOCK(&stream->mutex); -} - -void RtAudio :: callbackEvent( int streamId, DEVICE_ID deviceId, void *inData, void *outData ) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - CALLBACK_INFO *info; - AudioBufferList *inBufferList = (AudioBufferList *) inData; - AudioBufferList *outBufferList = (AudioBufferList *) outData; - - if (stream->state == STREAM_STOPPED) return; - - info = (CALLBACK_INFO *) &stream->callbackInfo; - if ( !info->usingCallback ) { - // Block waiting here until we get new user data in tickStream(). - while ( !info->blockTick ) - usleep(info->waitTime); - } - else if ( info->stopStream ) { - // Check if the stream should be stopped (via the previous user - // callback return value). We stop the stream here, rather than - // after the function call, so that output data can first be - // processed. - this->stopStream(info->streamId); - return; - } - - MUTEX_LOCK(&stream->mutex); - - // Invoke user callback first, to get fresh output data. Don't - // invoke the user callback if duplex mode, the input/output devices - // are different, and this function is called for the input device. - if ( info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[0] ) ) { - RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback; - info->stopStream = callback(stream->userBuffer, stream->bufferSize, info->userData); - } - - if ( stream->mode == OUTPUT || ( stream->mode == DUPLEX && deviceId == info->device[0] ) ) { - - if (stream->doConvertBuffer[0]) { - - if ( !stream->deInterleave[0] ) - stream->deviceBuffer = (char *) outBufferList->mBuffers[stream->handle[0]].mData; - else - stream->deviceBuffer = (char *) stream->callbackInfo.buffers; - - convertStreamBuffer(stream, OUTPUT); - if ( stream->doByteSwap[0] ) - byteSwapBuffer(stream->deviceBuffer, - stream->bufferSize * stream->nDeviceChannels[0], - stream->deviceFormat[0]); - - if ( stream->deInterleave[0] ) { - int bufferBytes = outBufferList->mBuffers[stream->handle[0]].mDataByteSize; - for ( int i=0; inDeviceChannels[0]; i++ ) { - memcpy(outBufferList->mBuffers[stream->handle[0]+i].mData, - &stream->deviceBuffer[i*bufferBytes], bufferBytes ); - } - } - - } - else { - if (stream->doByteSwap[0]) - byteSwapBuffer(stream->userBuffer, - stream->bufferSize * stream->nUserChannels[0], - stream->userFormat); - - memcpy(outBufferList->mBuffers[stream->handle[0]].mData, - stream->userBuffer, - outBufferList->mBuffers[stream->handle[0]].mDataByteSize ); - } - } - - if ( stream->mode == INPUT || ( stream->mode == DUPLEX && deviceId == info->device[1] ) ) { - - if (stream->doConvertBuffer[1]) { - - if ( stream->deInterleave[1] ) { - stream->deviceBuffer = (char *) stream->callbackInfo.buffers; - int bufferBytes = inBufferList->mBuffers[stream->handle[1]].mDataByteSize; - for ( int i=0; inDeviceChannels[1]; i++ ) { - memcpy(&stream->deviceBuffer[i*bufferBytes], - inBufferList->mBuffers[stream->handle[1]+i].mData, bufferBytes ); - } - } - else - stream->deviceBuffer = (char *) inBufferList->mBuffers[stream->handle[1]].mData; - - if ( stream->doByteSwap[1] ) - byteSwapBuffer(stream->deviceBuffer, - stream->bufferSize * stream->nDeviceChannels[1], - stream->deviceFormat[1]); - convertStreamBuffer(stream, INPUT); - - } - else { - memcpy(stream->userBuffer, - inBufferList->mBuffers[stream->handle[1]].mData, - inBufferList->mBuffers[stream->handle[1]].mDataByteSize ); - - if (stream->doByteSwap[1]) - byteSwapBuffer(stream->userBuffer, - stream->bufferSize * stream->nUserChannels[1], - stream->userFormat); - } - } - - if ( !info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[1] ) ) - info->blockTick = false; - - MUTEX_UNLOCK(&stream->mutex); - -} - -void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - stream->callbackInfo.callback = (void *) callback; - stream->callbackInfo.userData = userData; - stream->callbackInfo.usingCallback = true; -} - -//******************** End of __MACOSX_CORE__ *********************// - -#elif defined(__LINUX_ALSA__) - -#define MAX_DEVICES 16 - -void RtAudio :: initialize(void) -{ - int card, result, device; - char name[32]; - const char *cardId; - char deviceNames[MAX_DEVICES][32]; - snd_ctl_t *handle; - snd_ctl_card_info_t *info; - snd_ctl_card_info_alloca(&info); - - // Count cards and devices - nDevices = 0; - card = -1; - snd_card_next(&card); - while ( card >= 0 ) { - sprintf(name, "hw:%d", card); - result = snd_ctl_open(&handle, name, 0); - if (result < 0) { - sprintf(message, "RtAudio: ALSA control open (%i): %s.", card, snd_strerror(result)); - error(RtError::DEBUG_WARNING); - goto next_card; - } - result = snd_ctl_card_info(handle, info); - if (result < 0) { - sprintf(message, "RtAudio: ALSA control hardware info (%i): %s.", card, snd_strerror(result)); - error(RtError::DEBUG_WARNING); - goto next_card; - } - cardId = snd_ctl_card_info_get_id(info); - device = -1; - while (1) { - result = snd_ctl_pcm_next_device(handle, &device); - if (result < 0) { - sprintf(message, "RtAudio: ALSA control next device (%i): %s.", card, snd_strerror(result)); - error(RtError::DEBUG_WARNING); - break; - } - if (device < 0) - break; - if ( strlen(cardId) ) - sprintf( deviceNames[nDevices++], "hw:%s,%d", cardId, device ); - else - sprintf( deviceNames[nDevices++], "hw:%d,%d", card, device ); - if ( nDevices > MAX_DEVICES ) break; - } - if ( nDevices > MAX_DEVICES ) break; - next_card: - snd_ctl_close(handle); - snd_card_next(&card); - } - - if (nDevices == 0) return; - - // Allocate the RTAUDIO_DEVICE structures. - devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE)); - if (devices == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::MEMORY_ERROR); - } - - // Write device ascii identifiers to device structures and then - // probe the device capabilities. - for (int i=0; iname, 32 ); - card = strtok(name, ","); - err = snd_ctl_open(&chandle, card, 0); - if (err < 0) { - sprintf(message, "RtAudio: ALSA control open (%s): %s.", card, snd_strerror(err)); - error(RtError::DEBUG_WARNING); - return; - } - unsigned int dev = (unsigned int) atoi( strtok(NULL, ",") ); - - // First try for playback - stream = SND_PCM_STREAM_PLAYBACK; - snd_pcm_info_set_device(pcminfo, dev); - snd_pcm_info_set_subdevice(pcminfo, 0); - snd_pcm_info_set_stream(pcminfo, stream); - - if ((err = snd_ctl_pcm_info(chandle, pcminfo)) < 0) { - if (err == -ENOENT) { - sprintf(message, "RtAudio: ALSA pcm device (%s) doesn't handle output!", info->name); - error(RtError::DEBUG_WARNING); - } - else { - sprintf(message, "RtAudio: ALSA snd_ctl_pcm_info error for device (%s) output: %s", - info->name, snd_strerror(err)); - error(RtError::DEBUG_WARNING); - } - goto capture_probe; - } - - err = snd_pcm_open(&handle, info->name, stream, open_mode | SND_PCM_NONBLOCK ); - if (err < 0) { - if ( err == EBUSY ) - sprintf(message, "RtAudio: ALSA pcm playback device (%s) is busy: %s.", - info->name, snd_strerror(err)); - else - sprintf(message, "RtAudio: ALSA pcm playback open (%s) error: %s.", - info->name, snd_strerror(err)); - error(RtError::DEBUG_WARNING); - goto capture_probe; - } - - // We have an open device ... allocate the parameter structure. - err = snd_pcm_hw_params_any(handle, params); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.", - info->name, snd_strerror(err)); - error(RtError::WARNING); - goto capture_probe; - } - - // Get output channel information. - info->minOutputChannels = snd_pcm_hw_params_get_channels_min(params); - info->maxOutputChannels = snd_pcm_hw_params_get_channels_max(params); - - snd_pcm_close(handle); - - capture_probe: - // Now try for capture - stream = SND_PCM_STREAM_CAPTURE; - snd_pcm_info_set_stream(pcminfo, stream); - - err = snd_ctl_pcm_info(chandle, pcminfo); - snd_ctl_close(chandle); - if ( err < 0 ) { - if (err == -ENOENT) { - sprintf(message, "RtAudio: ALSA pcm device (%s) doesn't handle input!", info->name); - error(RtError::DEBUG_WARNING); - } - else { - sprintf(message, "RtAudio: ALSA snd_ctl_pcm_info error for device (%s) input: %s", - info->name, snd_strerror(err)); - error(RtError::DEBUG_WARNING); - } - if (info->maxOutputChannels == 0) - // didn't open for playback either ... device invalid - return; - goto probe_parameters; - } - - err = snd_pcm_open(&handle, info->name, stream, open_mode | SND_PCM_NONBLOCK); - if (err < 0) { - if ( err == EBUSY ) - sprintf(message, "RtAudio: ALSA pcm capture device (%s) is busy: %s.", - info->name, snd_strerror(err)); - else - sprintf(message, "RtAudio: ALSA pcm capture open (%s) error: %s.", - info->name, snd_strerror(err)); - error(RtError::DEBUG_WARNING); - if (info->maxOutputChannels == 0) - // didn't open for playback either ... device invalid - return; - goto probe_parameters; - } - - // We have an open capture device ... allocate the parameter structure. - err = snd_pcm_hw_params_any(handle, params); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.", - info->name, snd_strerror(err)); - error(RtError::WARNING); - if (info->maxOutputChannels > 0) - goto probe_parameters; - else - return; - } - - // Get input channel information. - info->minInputChannels = snd_pcm_hw_params_get_channels_min(params); - info->maxInputChannels = snd_pcm_hw_params_get_channels_max(params); - - snd_pcm_close(handle); - - // If device opens for both playback and capture, we determine the channels. - if (info->maxOutputChannels == 0 || info->maxInputChannels == 0) - goto probe_parameters; - - info->hasDuplexSupport = true; - info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? - info->maxInputChannels : info->maxOutputChannels; - info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? - info->minInputChannels : info->minOutputChannels; - - probe_parameters: - // 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->maxOutputChannels >= info->maxInputChannels) - stream = SND_PCM_STREAM_PLAYBACK; - else - stream = SND_PCM_STREAM_CAPTURE; - - err = snd_pcm_open(&handle, info->name, stream, open_mode); - if (err < 0) { - sprintf(message, "RtAudio: ALSA pcm (%s) won't reopen during probe: %s.", - info->name, snd_strerror(err)); - error(RtError::WARNING); - return; - } - - // We have an open device ... allocate the parameter structure. - err = snd_pcm_hw_params_any(handle, params); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA hardware reopen probe error (%s): %s.", - info->name, snd_strerror(err)); - error(RtError::WARNING); - return; - } - - // Test a non-standard sample rate to see if continuous rate is supported. - int dir = 0; - if (snd_pcm_hw_params_test_rate(handle, params, 35500, dir) == 0) { - // It appears that continuous sample rate support is available. - info->nSampleRates = -1; - info->sampleRates[0] = snd_pcm_hw_params_get_rate_min(params, &dir); - info->sampleRates[1] = snd_pcm_hw_params_get_rate_max(params, &dir); - } - else { - // No continuous rate support ... test our discrete set of sample rate values. - info->nSampleRates = 0; - for (int i=0; isampleRates[info->nSampleRates] = SAMPLE_RATES[i]; - info->nSampleRates++; - } - } - if (info->nSampleRates == 0) { - snd_pcm_close(handle); - return; - } - } - - // Probe the supported data formats ... we don't care about endian-ness just yet - snd_pcm_format_t format; - info->nativeFormats = 0; - format = SND_PCM_FORMAT_S8; - if (snd_pcm_hw_params_test_format(handle, params, format) == 0) - info->nativeFormats |= RTAUDIO_SINT8; - format = SND_PCM_FORMAT_S16; - if (snd_pcm_hw_params_test_format(handle, params, format) == 0) - info->nativeFormats |= RTAUDIO_SINT16; - format = SND_PCM_FORMAT_S24; - if (snd_pcm_hw_params_test_format(handle, params, format) == 0) - info->nativeFormats |= RTAUDIO_SINT24; - format = SND_PCM_FORMAT_S32; - if (snd_pcm_hw_params_test_format(handle, params, format) == 0) - info->nativeFormats |= RTAUDIO_SINT32; - format = SND_PCM_FORMAT_FLOAT; - if (snd_pcm_hw_params_test_format(handle, params, format) == 0) - info->nativeFormats |= RTAUDIO_FLOAT32; - format = SND_PCM_FORMAT_FLOAT64; - if (snd_pcm_hw_params_test_format(handle, params, format) == 0) - info->nativeFormats |= RTAUDIO_FLOAT64; - - // Check that we have at least one supported format - if (info->nativeFormats == 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA PCM device (%s) data format not supported by RtAudio.", - info->name); - error(RtError::WARNING); - return; - } - - // That's all ... close the device and return - snd_pcm_close(handle); - info->probed = true; - return; -} - -bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, - STREAM_MODE mode, int channels, - int sampleRate, RTAUDIO_FORMAT format, - int *bufferSize, int numberOfBuffers) -{ -#if defined(__RTAUDIO_DEBUG__) - snd_output_t *out; - snd_output_stdio_attach(&out, stderr, 0); -#endif - - // I'm not using the "plug" interface ... too much inconsistent behavior. - const char *name = devices[device].name; - - snd_pcm_stream_t alsa_stream; - if (mode == OUTPUT) - alsa_stream = SND_PCM_STREAM_PLAYBACK; - else - alsa_stream = SND_PCM_STREAM_CAPTURE; - - int err; - snd_pcm_t *handle; - int alsa_open_mode = SND_PCM_ASYNC; - err = snd_pcm_open(&handle, name, alsa_stream, alsa_open_mode); - if (err < 0) { - sprintf(message,"RtAudio: ALSA pcm device (%s) won't open: %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - - // Fill the parameter structure. - snd_pcm_hw_params_t *hw_params; - snd_pcm_hw_params_alloca(&hw_params); - err = snd_pcm_hw_params_any(handle, hw_params); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error getting parameter handle (%s): %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - -#if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\nRtAudio: ALSA dump hardware params just after device open:\n\n"); - snd_pcm_hw_params_dump(hw_params, out); -#endif - - - // Set access ... try interleaved access first, then non-interleaved - if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) ) { - err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); - } - else if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED) ) { - err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); - stream->deInterleave[mode] = true; - } - else { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA device (%s) access not supported by RtAudio.", name); - error(RtError::WARNING); - return FAILURE; - } - - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error setting access ( (%s): %s.", name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - - // Determine how to set the device format. - stream->userFormat = format; - snd_pcm_format_t device_format; - - if (format == RTAUDIO_SINT8) - device_format = SND_PCM_FORMAT_S8; - else if (format == RTAUDIO_SINT16) - device_format = SND_PCM_FORMAT_S16; - else if (format == RTAUDIO_SINT24) - device_format = SND_PCM_FORMAT_S24; - else if (format == RTAUDIO_SINT32) - device_format = SND_PCM_FORMAT_S32; - else if (format == RTAUDIO_FLOAT32) - device_format = SND_PCM_FORMAT_FLOAT; - else if (format == RTAUDIO_FLOAT64) - device_format = SND_PCM_FORMAT_FLOAT64; - - if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { - stream->deviceFormat[mode] = format; - goto set_format; - } - - // The user requested format is not natively supported by the device. - device_format = SND_PCM_FORMAT_FLOAT64; - if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { - stream->deviceFormat[mode] = RTAUDIO_FLOAT64; - goto set_format; - } - - device_format = SND_PCM_FORMAT_FLOAT; - if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { - stream->deviceFormat[mode] = RTAUDIO_FLOAT32; - goto set_format; - } - - device_format = SND_PCM_FORMAT_S32; - if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { - stream->deviceFormat[mode] = RTAUDIO_SINT32; - goto set_format; - } - - device_format = SND_PCM_FORMAT_S24; - if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { - stream->deviceFormat[mode] = RTAUDIO_SINT24; - goto set_format; - } - - device_format = SND_PCM_FORMAT_S16; - if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { - stream->deviceFormat[mode] = RTAUDIO_SINT16; - goto set_format; - } - - device_format = SND_PCM_FORMAT_S8; - if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { - stream->deviceFormat[mode] = RTAUDIO_SINT8; - goto set_format; - } - - // If we get here, no supported format was found. - sprintf(message,"RtAudio: ALSA pcm device (%s) data format not supported by RtAudio.", name); - snd_pcm_close(handle); - error(RtError::WARNING); - return FAILURE; - - set_format: - err = snd_pcm_hw_params_set_format(handle, hw_params, device_format); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error setting format (%s): %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - - // Determine whether byte-swaping is necessary. - stream->doByteSwap[mode] = false; - if (device_format != SND_PCM_FORMAT_S8) { - err = snd_pcm_format_cpu_endian(device_format); - if (err == 0) - stream->doByteSwap[mode] = true; - else if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error getting format endian-ness (%s): %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - } - - // Set the sample rate. - err = snd_pcm_hw_params_set_rate(handle, hw_params, (unsigned int)sampleRate, 0); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error setting sample rate (%d) on device (%s): %s.", - sampleRate, name, snd_strerror(err)); - error(RtError::WARNING); - 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; - int device_channels = snd_pcm_hw_params_get_channels_max(hw_params); - if (device_channels < channels) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: channels (%d) not supported by device (%s).", - channels, name); - error(RtError::WARNING); - return FAILURE; - } - - device_channels = snd_pcm_hw_params_get_channels_min(hw_params); - if (device_channels < channels) device_channels = channels; - stream->nDeviceChannels[mode] = device_channels; - - // Set the device channels. - err = snd_pcm_hw_params_set_channels(handle, hw_params, device_channels); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error setting channels (%d) on device (%s): %s.", - device_channels, name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - - // Set the buffer number, which in ALSA is referred to as the "period". - int dir; - int periods = numberOfBuffers; - // Even though the hardware might allow 1 buffer, it won't work reliably. - if (periods < 2) periods = 2; - err = snd_pcm_hw_params_get_periods_min(hw_params, &dir); - if (err > periods) periods = err; - err = snd_pcm_hw_params_get_periods_max(hw_params, &dir); - if (err < periods) periods = err; - - err = snd_pcm_hw_params_set_periods(handle, hw_params, periods, 0); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error setting periods (%s): %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - - // Set the buffer (or period) size. - err = snd_pcm_hw_params_get_period_size_min(hw_params, &dir); - if (err > *bufferSize) *bufferSize = err; - - err = snd_pcm_hw_params_set_period_size(handle, hw_params, *bufferSize, 0); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error setting period size (%s): %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - 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 ) { - sprintf( message, "RtAudio: ALSA error setting buffer size for duplex stream on device (%s).", - name ); - error(RtError::DEBUG_WARNING); - return FAILURE; - } - - stream->bufferSize = *bufferSize; - - // Install the hardware configuration - err = snd_pcm_hw_params(handle, hw_params); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error installing hardware configuration (%s): %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - -#if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\nRtAudio: ALSA dump hardware params after installation:\n\n"); - snd_pcm_hw_params_dump(hw_params, out); -#endif - - /* - // Install the software configuration - snd_pcm_sw_params_t *sw_params = NULL; - snd_pcm_sw_params_alloca(&sw_params); - snd_pcm_sw_params_current(handle, sw_params); - err = snd_pcm_sw_params(handle, sw_params); - if (err < 0) { - snd_pcm_close(handle); - sprintf(message, "RtAudio: ALSA error installing software configuration (%s): %s.", - name, snd_strerror(err)); - error(RtError::WARNING); - return FAILURE; - } - */ - - // Set handle and flags for buffer conversion - stream->handle[mode] = handle; - 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->nUserChannels[mode] > 1 && stream->deInterleave[mode]) - stream->doConvertBuffer[mode] = true; - - // Allocate necessary internal buffers - if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) { - - long buffer_bytes; - if (stream->nUserChannels[0] >= stream->nUserChannels[1]) - buffer_bytes = stream->nUserChannels[0]; - else - buffer_bytes = stream->nUserChannels[1]; - - buffer_bytes *= *bufferSize * formatBytes(stream->userFormat); - if (stream->userBuffer) free(stream->userBuffer); - stream->userBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->userBuffer == NULL) - goto memory_error; - } - - if ( stream->doConvertBuffer[mode] ) { - - long buffer_bytes; - bool makeBuffer = true; - if ( mode == OUTPUT ) - buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); - else { // mode == INPUT - buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]); - if ( stream->mode == OUTPUT && stream->deviceBuffer ) { - long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); - if ( buffer_bytes < bytes_out ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - buffer_bytes *= *bufferSize; - if (stream->deviceBuffer) free(stream->deviceBuffer); - stream->deviceBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->deviceBuffer == NULL) - goto memory_error; - } - } - - 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 = periods; - stream->sampleRate = sampleRate; - - return SUCCESS; - - memory_error: - if (stream->handle[0]) { - snd_pcm_close(stream->handle[0]); - stream->handle[0] = 0; - } - if (stream->handle[1]) { - snd_pcm_close(stream->handle[1]); - stream->handle[1] = 0; - } - if (stream->userBuffer) { - free(stream->userBuffer); - stream->userBuffer = 0; - } - sprintf(message, "RtAudio: ALSA error allocating buffer memory (%s).", name); - error(RtError::WARNING); - return FAILURE; -} - -void RtAudio :: closeStream(int streamId) -{ - // We don't want an exception to be thrown here because this - // function is called by our class destructor. So, do our own - // streamId check. - if ( streams.find( streamId ) == streams.end() ) { - sprintf(message, "RtAudio: invalid stream identifier!"); - error(RtError::WARNING); - return; - } - - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId]; - - if (stream->callbackInfo.usingCallback) { - pthread_cancel(stream->callbackInfo.thread); - pthread_join(stream->callbackInfo.thread, NULL); - } - - if (stream->state == STREAM_RUNNING) { - if (stream->mode == OUTPUT || stream->mode == DUPLEX) - snd_pcm_drop(stream->handle[0]); - if (stream->mode == INPUT || stream->mode == DUPLEX) - snd_pcm_drop(stream->handle[1]); - } - - pthread_mutex_destroy(&stream->mutex); - - if (stream->handle[0]) - snd_pcm_close(stream->handle[0]); - - if (stream->handle[1]) - snd_pcm_close(stream->handle[1]); - - if (stream->userBuffer) - free(stream->userBuffer); - - if (stream->deviceBuffer) - free(stream->deviceBuffer); - - free(stream); - streams.erase(streamId); -} - -void RtAudio :: startStream(int streamId) -{ - // This method calls snd_pcm_prepare if the device isn't already in that state. - - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_RUNNING) - goto unlock; - - int err; - snd_pcm_state_t state; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - state = snd_pcm_state(stream->handle[0]); - if (state != SND_PCM_STATE_PREPARED) { - err = snd_pcm_prepare(stream->handle[0]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.", - devices[stream->device[0]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - } - - if (stream->mode == INPUT || stream->mode == DUPLEX) { - state = snd_pcm_state(stream->handle[1]); - if (state != SND_PCM_STATE_PREPARED) { - err = snd_pcm_prepare(stream->handle[1]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.", - devices[stream->device[1]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - } - stream->state = STREAM_RUNNING; - - unlock: - MUTEX_UNLOCK(&stream->mutex); -} - -void RtAudio :: stopStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) - goto unlock; - - int err; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - err = snd_pcm_drain(stream->handle[0]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.", - devices[stream->device[0]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - if (stream->mode == INPUT || stream->mode == DUPLEX) { - err = snd_pcm_drain(stream->handle[1]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.", - devices[stream->device[1]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - stream->state = STREAM_STOPPED; - - unlock: - MUTEX_UNLOCK(&stream->mutex); -} - -void RtAudio :: abortStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) - goto unlock; - - int err; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - err = snd_pcm_drop(stream->handle[0]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.", - devices[stream->device[0]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - if (stream->mode == INPUT || stream->mode == DUPLEX) { - err = snd_pcm_drop(stream->handle[1]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.", - devices[stream->device[1]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - stream->state = STREAM_STOPPED; - - unlock: - MUTEX_UNLOCK(&stream->mutex); -} - -int RtAudio :: streamWillBlock(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - int err = 0, frames = 0; - if (stream->state == STREAM_STOPPED) - goto unlock; - - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - err = snd_pcm_avail_update(stream->handle[0]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.", - devices[stream->device[0]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - frames = err; - - if (stream->mode == INPUT || stream->mode == DUPLEX) { - err = snd_pcm_avail_update(stream->handle[1]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.", - devices[stream->device[1]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - if (frames > err) frames = err; - } - - frames = stream->bufferSize - frames; - if (frames < 0) frames = 0; - - unlock: - MUTEX_UNLOCK(&stream->mutex); - return frames; -} - -void RtAudio :: tickStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - int stopStream = 0; - if (stream->state == STREAM_STOPPED) { - if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds - return; - } - else if (stream->callbackInfo.usingCallback) { - RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback; - stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData); - } - - MUTEX_LOCK(&stream->mutex); - - // The state might change while waiting on a mutex. - if (stream->state == STREAM_STOPPED) - goto unlock; - - int err; - char *buffer; - int channels; - RTAUDIO_FORMAT format; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - - // Setup parameters and do buffer conversion if necessary. - if (stream->doConvertBuffer[0]) { - convertStreamBuffer(stream, OUTPUT); - buffer = stream->deviceBuffer; - channels = stream->nDeviceChannels[0]; - format = stream->deviceFormat[0]; - } - else { - buffer = stream->userBuffer; - 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->deInterleave[0]) { - void *bufs[channels]; - size_t offset = stream->bufferSize * formatBytes(format); - for (int i=0; ihandle[0], bufs, stream->bufferSize); - } - else - err = snd_pcm_writei(stream->handle[0], buffer, stream->bufferSize); - - if (err < stream->bufferSize) { - // Either an error or underrun occured. - if (err == -EPIPE) { - snd_pcm_state_t state = snd_pcm_state(stream->handle[0]); - if (state == SND_PCM_STATE_XRUN) { - sprintf(message, "RtAudio: ALSA underrun detected."); - error(RtError::WARNING); - err = snd_pcm_prepare(stream->handle[0]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error preparing handle after underrun: %s.", - snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - else { - sprintf(message, "RtAudio: ALSA error, current state is %s.", - snd_pcm_state_name(state)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - goto unlock; - } - else { - sprintf(message, "RtAudio: ALSA audio write error for device (%s): %s.", - devices[stream->device[0]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - } - - 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; - channels = stream->nUserChannels[1]; - format = stream->userFormat; - } - - // Read samples from device in interleaved/non-interleaved format. - if (stream->deInterleave[1]) { - void *bufs[channels]; - size_t offset = stream->bufferSize * formatBytes(format); - for (int i=0; ihandle[1], bufs, stream->bufferSize); - } - else - err = snd_pcm_readi(stream->handle[1], buffer, stream->bufferSize); - - if (err < stream->bufferSize) { - // Either an error or underrun occured. - if (err == -EPIPE) { - snd_pcm_state_t state = snd_pcm_state(stream->handle[1]); - if (state == SND_PCM_STATE_XRUN) { - sprintf(message, "RtAudio: ALSA overrun detected."); - error(RtError::WARNING); - err = snd_pcm_prepare(stream->handle[1]); - if (err < 0) { - sprintf(message, "RtAudio: ALSA error preparing handle after overrun: %s.", - snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - else { - sprintf(message, "RtAudio: ALSA error, current state is %s.", - snd_pcm_state_name(state)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - goto unlock; - } - else { - sprintf(message, "RtAudio: ALSA audio read error for device (%s): %s.", - devices[stream->device[1]].name, snd_strerror(err)); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - } - - // Do byte swapping if necessary. - if (stream->doByteSwap[1]) - byteSwapBuffer(buffer, stream->bufferSize * channels, format); - - // Do buffer conversion if necessary. - if (stream->doConvertBuffer[1]) - convertStreamBuffer(stream, INPUT); - } - - unlock: - MUTEX_UNLOCK(&stream->mutex); - - if (stream->callbackInfo.usingCallback && stopStream) - this->stopStream(streamId); -} - -extern "C" void *callbackHandler(void *ptr) -{ - CALLBACK_INFO *info = (CALLBACK_INFO *) ptr; - RtAudio *object = (RtAudio *) info->object; - int stream = info->streamId; - bool *usingCallback = &info->usingCallback; - - while ( *usingCallback ) { - pthread_testcancel(); - try { - object->tickStream(stream); - } - catch (RtError &exception) { - fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n", - exception.getMessage()); - break; - } - } - - return 0; -} - -//******************** End of __LINUX_ALSA__ *********************// - -#elif defined(__LINUX_OSS__) +#if defined(__LINUX_OSS__) +#include #include #include #include @@ -2414,10 +441,28 @@ extern "C" void *callbackHandler(void *ptr) #define MAX_DEVICES 16 #define MAX_CHANNELS 16 -void RtAudio :: initialize(void) +extern "C" void *ossCallbackHandler(void * ptr); + +RtApiOss :: RtApiOss() +{ + this->initialize(); + + if (nDevices_ <= 0) { + sprintf(message_, "RtApiOss: no Linux OSS audio devices found!"); + error(RtError::NO_DEVICES_FOUND); + } +} + +RtApiOss :: ~RtApiOss() +{ + if ( stream_.mode != UNINITIALIZED ) + closeStream(); +} + +void RtApiOss :: initialize(void) { // Count cards and devices - nDevices = 0; + nDevices_ = 0; // We check /dev/dsp before probing devices. /dev/dsp is supposed to // be a link to the "default" audio device, of the form /dev/dsp0, @@ -2444,13 +489,13 @@ void RtAudio :: initialize(void) } } else { - sprintf(message, "RtAudio: cannot read value of symbolic link %s.", DAC_NAME); + sprintf(message_, "RtApiOss: cannot read value of symbolic link %s.", DAC_NAME); error(RtError::SYSTEM_ERROR); } } } else { - sprintf(message, "RtAudio: cannot stat %s.", DAC_NAME); + sprintf(message_, "RtApiOss: cannot stat %s.", DAC_NAME); error(RtError::SYSTEM_ERROR); } @@ -2460,9 +505,8 @@ void RtAudio :: initialize(void) // numbers until we reach MAX_DSP_DEVICES. This should tell us how // many devices we have ... it is not a fullproof scheme, but hopefully // it will work most of the time. - int fd = 0; - char names[MAX_DEVICES][16]; + RtApiDevice device; for (i=-1; i= 0) close(fd); - strncpy(names[nDevices], device_name, 16); - nDevices++; + device.name.erase(); + device.name.append( (const char *)device_name, strlen(device_name)+1); + devices_.push_back(device); + nDevices_++; } - - if (nDevices == 0) return; - - // Allocate the RTAUDIO_DEVICE structures. - devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE)); - if (devices == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::MEMORY_ERROR); - } - - // Write device ascii identifiers to device control structure and then probe capabilities. - for (i=0; iname, O_WRONLY | O_NONBLOCK); + fd = open(info->name.c_str(), O_WRONLY | O_NONBLOCK); if (fd == -1) { // Open device failed ... either busy or doesn't exist if (errno == EBUSY || errno == EAGAIN) - sprintf(message, "RtAudio: OSS playback device (%s) is busy and cannot be probed.", - info->name); + sprintf(message_, "RtApiOss: OSS playback device (%s) is busy and cannot be probed.", + info->name.c_str()); else - sprintf(message, "RtAudio: OSS playback device (%s) open error.", info->name); + sprintf(message_, "RtApiOss: OSS playback device (%s) open error.", info->name.c_str()); error(RtError::DEBUG_WARNING); goto capture_probe; } @@ -2583,14 +600,14 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) capture_probe: // Now try for capture - fd = open(info->name, O_RDONLY | O_NONBLOCK); + fd = open(info->name.c_str(), O_RDONLY | O_NONBLOCK); if (fd == -1) { // Open device for capture failed ... either busy or doesn't exist if (errno == EBUSY || errno == EAGAIN) - sprintf(message, "RtAudio: OSS capture device (%s) is busy and cannot be probed.", - info->name); + sprintf(message_, "RtApiOss: OSS capture device (%s) is busy and cannot be probed.", + info->name.c_str()); else - sprintf(message, "RtAudio: OSS capture device (%s) open error.", info->name); + sprintf(message_, "RtApiOss: OSS capture device (%s) open error.", info->name.c_str()); error(RtError::DEBUG_WARNING); if (info->maxOutputChannels == 0) // didn't open for playback either ... device invalid @@ -2621,8 +638,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) close(fd); if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) { - sprintf(message, "RtAudio: OSS device (%s) reports zero channels for input and output.", - info->name); + sprintf(message_, "RtApiOss: device (%s) reports zero channels for input and output.", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -2631,7 +648,7 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) if (info->maxOutputChannels == 0 || info->maxInputChannels == 0) goto probe_parameters; - fd = open(info->name, O_RDWR | O_NONBLOCK); + fd = open(info->name.c_str(), O_RDWR | O_NONBLOCK); if (fd == -1) goto probe_parameters; @@ -2669,18 +686,18 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) // be it. if (info->maxOutputChannels >= info->maxInputChannels) { - fd = open(info->name, O_WRONLY | O_NONBLOCK); + fd = open(info->name.c_str(), O_WRONLY | O_NONBLOCK); channels = info->maxOutputChannels; } else { - fd = open(info->name, O_RDONLY | O_NONBLOCK); + fd = open(info->name.c_str(), O_RDONLY | O_NONBLOCK); channels = info->maxInputChannels; } if (fd == -1) { // We've got some sort of conflict ... abort - sprintf(message, "RtAudio: OSS device (%s) won't reopen during probe.", - info->name); + sprintf(message_, "RtApiOss: device (%s) won't reopen during probe.", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -2690,16 +707,16 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) { // We've got some sort of conflict ... abort close(fd); - sprintf(message, "RtAudio: OSS device (%s) won't revert to previous channel setting.", - info->name); + sprintf(message_, "RtApiOss: device (%s) won't revert to previous channel setting.", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) { close(fd); - sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.", - info->name); + sprintf(message_, "RtApiOss: device (%s) can't get supported audio formats.", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -2737,8 +754,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) // Check that we have at least one supported format if (info->nativeFormats == 0) { close(fd); - sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.", - info->name); + sprintf(message_, "RtApiOss: device (%s) data format not supported by RtAudio.", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -2747,72 +764,54 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) i = format; if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1 || format != i) { close(fd); - sprintf(message, "RtAudio: OSS device (%s) error setting data format.", - info->name); + sprintf(message_, "RtApiOss: device (%s) error setting data format.", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } - // Probe the supported sample rates ... first get lower limit - int speed = 1; - if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) { - // If we get here, we're probably using an ALSA driver with OSS-emulation, - // which doesn't conform to the OSS specification. In this case, - // we'll probe our predefined list of sample rates for working values. - info->nSampleRates = 0; - for (i=0; isampleRates[info->nSampleRates] = SAMPLE_RATES[i]; - info->nSampleRates++; - } - } - if (info->nSampleRates == 0) { - close(fd); - return; - } - goto finished; + // Probe the supported sample rates. + info->sampleRates.clear(); + for (unsigned int k=0; ksampleRates.push_back(speed); } - info->sampleRates[0] = speed; - // Now get upper limit - speed = 1000000; - if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) { + if (info->sampleRates.size() == 0) { close(fd); - sprintf(message, "RtAudio: OSS device (%s) error setting sample rate.", - info->name); + sprintf(message_, "RtApiOss: no supported sample rates found for device (%s).", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } - info->sampleRates[1] = speed; - info->nSampleRates = -1; - finished: // That's all ... close the device and return + // That's all ... close the device and return close(fd); info->probed = true; return; } -bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, - STREAM_MODE mode, int channels, - int sampleRate, RTAUDIO_FORMAT format, +bool RtApiOss :: probeDeviceOpen(int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers) { int buffers, buffer_bytes, device_channels, device_format; int srate, temp, fd; + int *handle = (int *) stream_.apiHandle; - const char *name = devices[device].name; + const char *name = devices_[device].name.c_str(); if (mode == OUTPUT) fd = open(name, O_WRONLY | O_NONBLOCK); else { // mode == INPUT - if (stream->mode == OUTPUT && stream->device[0] == device) { + if (stream_.mode == OUTPUT && stream_.device[0] == device) { // We just set the same device for playback ... close and reopen for duplex (OSS only). - close(stream->handle[0]); - stream->handle[0] = 0; + close(handle[0]); + handle[0] = 0; // First check that the number previously set channels is the same. - if (stream->nUserChannels[0] != channels) { - sprintf(message, "RtAudio: input/output channels must be equal for OSS duplex device (%s).", name); + if (stream_.nUserChannels[0] != channels) { + sprintf(message_, "RtApiOss: input/output channels must be equal for OSS duplex device (%s).", name); goto error; } fd = open(name, O_RDWR | O_NONBLOCK); @@ -2823,10 +822,10 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, if (fd == -1) { if (errno == EBUSY || errno == EAGAIN) - sprintf(message, "RtAudio: OSS device (%s) is busy and cannot be opened.", + sprintf(message_, "RtApiOss: device (%s) is busy and cannot be opened.", name); else - sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name); + sprintf(message_, "RtApiOss: device (%s) cannot be opened.", name); goto error; } @@ -2835,14 +834,14 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, if (mode == OUTPUT) fd = open(name, O_WRONLY | O_SYNC); else { // mode == INPUT - if (stream->mode == OUTPUT && stream->device[0] == device) + if (stream_.mode == OUTPUT && stream_.device[0] == device) fd = open(name, O_RDWR | O_SYNC); else fd = open(name, O_RDONLY | O_SYNC); } if (fd == -1) { - sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name); + sprintf(message_, "RtApiOss: device (%s) cannot be opened.", name); goto error; } @@ -2850,37 +849,37 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, int mask; if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) { close(fd); - sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.", + sprintf(message_, "RtApiOss: device (%s) can't get supported audio formats.", name); goto error; } // Determine how to set the device format. - stream->userFormat = format; + stream_.userFormat = format; device_format = -1; - stream->doByteSwap[mode] = false; + stream_.doByteSwap[mode] = false; if (format == RTAUDIO_SINT8) { if (mask & AFMT_S8) { device_format = AFMT_S8; - stream->deviceFormat[mode] = RTAUDIO_SINT8; + stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } else if (format == RTAUDIO_SINT16) { if (mask & AFMT_S16_NE) { device_format = AFMT_S16_NE; - stream->deviceFormat[mode] = RTAUDIO_SINT16; + stream_.deviceFormat[mode] = RTAUDIO_SINT16; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S16_BE) { device_format = AFMT_S16_BE; - stream->deviceFormat[mode] = RTAUDIO_SINT16; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT16; + stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S16_LE) { device_format = AFMT_S16_LE; - stream->deviceFormat[mode] = RTAUDIO_SINT16; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT16; + stream_.doByteSwap[mode] = true; } #endif } @@ -2888,19 +887,19 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, else if (format == RTAUDIO_SINT32) { if (mask & AFMT_S32_NE) { device_format = AFMT_S32_NE; - stream->deviceFormat[mode] = RTAUDIO_SINT32; + stream_.deviceFormat[mode] = RTAUDIO_SINT32; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S32_BE) { device_format = AFMT_S32_BE; - stream->deviceFormat[mode] = RTAUDIO_SINT32; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT32; + stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S32_LE) { device_format = AFMT_S32_LE; - stream->deviceFormat[mode] = RTAUDIO_SINT32; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT32; + stream_.doByteSwap[mode] = true; } #endif } @@ -2910,74 +909,74 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, // The user requested format is not natively supported by the device. if (mask & AFMT_S16_NE) { device_format = AFMT_S16_NE; - stream->deviceFormat[mode] = RTAUDIO_SINT16; + stream_.deviceFormat[mode] = RTAUDIO_SINT16; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S16_BE) { device_format = AFMT_S16_BE; - stream->deviceFormat[mode] = RTAUDIO_SINT16; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT16; + stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S16_LE) { device_format = AFMT_S16_LE; - stream->deviceFormat[mode] = RTAUDIO_SINT16; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT16; + stream_.doByteSwap[mode] = true; } #endif #if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE) else if (mask & AFMT_S32_NE) { device_format = AFMT_S32_NE; - stream->deviceFormat[mode] = RTAUDIO_SINT32; + stream_.deviceFormat[mode] = RTAUDIO_SINT32; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S32_BE) { device_format = AFMT_S32_BE; - stream->deviceFormat[mode] = RTAUDIO_SINT32; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT32; + stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S32_LE) { device_format = AFMT_S32_LE; - stream->deviceFormat[mode] = RTAUDIO_SINT32; - stream->doByteSwap[mode] = true; + stream_.deviceFormat[mode] = RTAUDIO_SINT32; + stream_.doByteSwap[mode] = true; } #endif #endif else if (mask & AFMT_S8) { device_format = AFMT_S8; - stream->deviceFormat[mode] = RTAUDIO_SINT8; + stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } - if (stream->deviceFormat[mode] == 0) { + if (stream_.deviceFormat[mode] == 0) { // This really shouldn't happen ... close(fd); - sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.", + sprintf(message_, "RtApiOss: device (%s) data format not supported by RtAudio.", name); goto error; } // Determine the number of channels for this device. Note that the // channel value requested by the user might be < min_X_Channels. - stream->nUserChannels[mode] = channels; + stream_.nUserChannels[mode] = channels; device_channels = channels; if (mode == OUTPUT) { - if (channels < devices[device].minOutputChannels) - device_channels = devices[device].minOutputChannels; + if (channels < devices_[device].minOutputChannels) + device_channels = devices_[device].minOutputChannels; } else { // mode == INPUT - if (stream->mode == OUTPUT && stream->device[0] == device) { + if (stream_.mode == OUTPUT && stream_.device[0] == device) { // We're doing duplex setup here. - if (channels < devices[device].minDuplexChannels) - device_channels = devices[device].minDuplexChannels; + if (channels < devices_[device].minDuplexChannels) + device_channels = devices_[device].minDuplexChannels; } else { - if (channels < devices[device].minInputChannels) - device_channels = devices[device].minInputChannels; + if (channels < devices_[device].minInputChannels) + device_channels = devices_[device].minInputChannels; } } - stream->nDeviceChannels[mode] = device_channels; + stream_.nDeviceChannels[mode] = device_channels; // Attempt to set the buffer size. According to OSS, the minimum // number of buffers is two. The supposed minimum buffer size is 16 @@ -2986,24 +985,24 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, // 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. - buffer_bytes = *bufferSize * formatBytes(stream->deviceFormat[mode]) * device_channels; + buffer_bytes = *bufferSize * formatBytes(stream_.deviceFormat[mode]) * device_channels; if (buffer_bytes < 16) buffer_bytes = 16; buffers = numberOfBuffers; if (buffers < 2) buffers = 2; temp = ((int) buffers << 16) + (int)(log10((double)buffer_bytes)/log10(2.0)); if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &temp)) { close(fd); - sprintf(message, "RtAudio: OSS error setting fragment size for device (%s).", + sprintf(message_, "RtApiOss: error setting fragment size for device (%s).", name); goto error; } - stream->nBuffers = buffers; + stream_.nBuffers = buffers; // Set the data format. temp = device_format; if (ioctl(fd, SNDCTL_DSP_SETFMT, &device_format) == -1 || device_format != temp) { close(fd); - sprintf(message, "RtAudio: OSS error setting data format for device (%s).", + sprintf(message_, "RtApiOss: error setting data format for device (%s).", name); goto error; } @@ -3012,7 +1011,7 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, temp = device_channels; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &device_channels) == -1 || device_channels != temp) { close(fd); - sprintf(message, "RtAudio: OSS error setting %d channels on device (%s).", + sprintf(message_, "RtApiOss: error setting %d channels on device (%s).", temp, name); goto error; } @@ -3022,7 +1021,7 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, temp = srate; if (ioctl(fd, SNDCTL_DSP_SPEED, &srate) == -1) { close(fd); - sprintf(message, "RtAudio: OSS error setting sample rate = %d on device (%s).", + sprintf(message_, "RtApiOss: error setting sample rate = %d on device (%s).", temp, name); goto error; } @@ -3030,375 +1029,424 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, // Verify the sample rate setup worked. if (abs(srate - temp) > 100) { close(fd); - sprintf(message, "RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %d.", + sprintf(message_, "RtApiOss: error ... audio device (%s) doesn't support sample rate of %d.", name, temp); goto error; } - stream->sampleRate = sampleRate; + stream_.sampleRate = sampleRate; if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buffer_bytes) == -1) { close(fd); - sprintf(message, "RtAudio: OSS error getting buffer size for device (%s).", + sprintf(message_, "RtApiOss: error getting buffer size for device (%s).", name); goto error; } // Save buffer size (in sample frames). - *bufferSize = buffer_bytes / (formatBytes(stream->deviceFormat[mode]) * device_channels); - stream->bufferSize = *bufferSize; + *bufferSize = buffer_bytes / (formatBytes(stream_.deviceFormat[mode]) * device_channels); + stream_.bufferSize = *bufferSize; - if (mode == INPUT && stream->mode == OUTPUT && - stream->device[0] == device) { + 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] = device_channels; + stream_.deviceFormat[0] = stream_.deviceFormat[1]; + stream_.nDeviceChannels[0] = device_channels; } + // Allocate the stream handles if necessary and then save. + if ( stream_.apiHandle == 0 ) { + handle = (int *) calloc(2, sizeof(int)); + stream_.apiHandle = (void *) handle; + handle[0] = 0; + handle[1] = 0; + } + else { + handle = (int *) stream_.apiHandle; + } + handle[mode] = fd; + // 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; + stream_.doConvertBuffer[mode] = false; + if (stream_.userFormat != stream_.deviceFormat[mode]) + stream_.doConvertBuffer[mode] = true; + if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode]) + stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers - if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) { + if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; - if (stream->nUserChannels[0] >= stream->nUserChannels[1]) - buffer_bytes = stream->nUserChannels[0]; + if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) + buffer_bytes = stream_.nUserChannels[0]; else - buffer_bytes = stream->nUserChannels[1]; + buffer_bytes = stream_.nUserChannels[1]; - buffer_bytes *= *bufferSize * formatBytes(stream->userFormat); - if (stream->userBuffer) free(stream->userBuffer); - stream->userBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->userBuffer == NULL) { + buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); + if (stream_.userBuffer) free(stream_.userBuffer); + stream_.userBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.userBuffer == NULL) { close(fd); - sprintf(message, "RtAudio: OSS error allocating user buffer memory (%s).", + sprintf(message_, "RtApiOss: error allocating user buffer memory (%s).", name); goto error; } } - if ( stream->doConvertBuffer[mode] ) { + if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) - buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT - buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]); - if ( stream->mode == OUTPUT && stream->deviceBuffer ) { - long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; - if (stream->deviceBuffer) free(stream->deviceBuffer); - stream->deviceBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->deviceBuffer == NULL) { + if (stream_.deviceBuffer) free(stream_.deviceBuffer); + stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.deviceBuffer == NULL) { close(fd); - free(stream->userBuffer); - sprintf(message, "RtAudio: OSS error allocating device buffer memory (%s).", + sprintf(message_, "RtApiOss: error allocating device buffer memory (%s).", name); goto error; } } } - stream->device[mode] = device; - stream->handle[mode] = fd; - stream->state = STREAM_STOPPED; - if ( stream->mode == OUTPUT && mode == INPUT ) { - stream->mode = DUPLEX; - if (stream->device[0] == device) - stream->handle[0] = fd; + stream_.device[mode] = device; + stream_.state = STREAM_STOPPED; + + if ( stream_.mode == OUTPUT && mode == INPUT ) { + stream_.mode = DUPLEX; + if (stream_.device[0] == device) + handle[0] = fd; } else - stream->mode = mode; + stream_.mode = mode; return SUCCESS; error: - if (stream->handle[0]) { - close(stream->handle[0]); - stream->handle[0] = 0; + if (handle) { + if (handle[0]) + close(handle[0]); + free(handle); + stream_.apiHandle = 0; } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + error(RtError::WARNING); return FAILURE; } -void RtAudio :: closeStream(int streamId) +void RtApiOss :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own - // streamId check. - if ( streams.find( streamId ) == streams.end() ) { - sprintf(message, "RtAudio: invalid stream identifier!"); + // stream check. + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtApiOss::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId]; - - if (stream->callbackInfo.usingCallback) { - pthread_cancel(stream->callbackInfo.thread); - pthread_join(stream->callbackInfo.thread, NULL); + int *handle = (int *) stream_.apiHandle; + if (stream_.state == STREAM_RUNNING) { + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) + ioctl(handle[0], SNDCTL_DSP_RESET, 0); + else + ioctl(handle[1], SNDCTL_DSP_RESET, 0); + stream_.state = STREAM_STOPPED; } - if (stream->state == STREAM_RUNNING) { - if (stream->mode == OUTPUT || stream->mode == DUPLEX) - ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0); - if (stream->mode == INPUT || stream->mode == DUPLEX) - ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0); + if (stream_.callbackInfo.usingCallback) { + stream_.callbackInfo.usingCallback = false; + pthread_join(stream_.callbackInfo.thread, NULL); } - pthread_mutex_destroy(&stream->mutex); + if (handle) { + if (handle[0]) close(handle[0]); + if (handle[1]) close(handle[1]); + free(handle); + stream_.apiHandle = 0; + } - if (stream->handle[0]) - close(stream->handle[0]); + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } - if (stream->handle[1]) - close(stream->handle[1]); + if (stream_.deviceBuffer) { + free(stream_.deviceBuffer); + stream_.deviceBuffer = 0; + } - if (stream->userBuffer) - free(stream->userBuffer); - - if (stream->deviceBuffer) - free(stream->deviceBuffer); - - free(stream); - streams.erase(streamId); + stream_.mode = UNINITIALIZED; } -void RtAudio :: startStream(int streamId) +void RtApiOss :: startStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_RUNNING) return; - MUTEX_LOCK(&stream->mutex); + MUTEX_LOCK(&stream_.mutex); - stream->state = STREAM_RUNNING; + stream_.state = STREAM_RUNNING; // No need to do anything else here ... OSS automatically starts // when fed samples. - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); } -void RtAudio :: stopStream(int streamId) +void RtApiOss :: stopStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) - goto unlock; + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); int err; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - err = ioctl(stream->handle[0], SNDCTL_DSP_SYNC, 0); + int *handle = (int *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + err = ioctl(handle[0], SNDCTL_DSP_POST, 0); + //err = ioctl(handle[0], SNDCTL_DSP_SYNC, 0); if (err < -1) { - sprintf(message, "RtAudio: OSS error stopping device (%s).", - devices[stream->device[0]].name); + sprintf(message_, "RtApiOss: error stopping device (%s).", + devices_[stream_.device[0]].name.c_str()); error(RtError::DRIVER_ERROR); } } else { - err = ioctl(stream->handle[1], SNDCTL_DSP_SYNC, 0); + err = ioctl(handle[1], SNDCTL_DSP_POST, 0); + //err = ioctl(handle[1], SNDCTL_DSP_SYNC, 0); if (err < -1) { - sprintf(message, "RtAudio: OSS error stopping device (%s).", - devices[stream->device[1]].name); + sprintf(message_, "RtApiOss: error stopping device (%s).", + devices_[stream_.device[1]].name.c_str()); error(RtError::DRIVER_ERROR); } } - stream->state = STREAM_STOPPED; - unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); } -void RtAudio :: abortStream(int streamId) +void RtApiOss :: abortStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) - goto unlock; - - int err; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - err = ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0); - if (err < -1) { - sprintf(message, "RtAudio: OSS error aborting device (%s).", - devices[stream->device[0]].name); - error(RtError::DRIVER_ERROR); - } - } - else { - err = ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0); - if (err < -1) { - sprintf(message, "RtAudio: OSS error aborting device (%s).", - devices[stream->device[1]].name); - error(RtError::DRIVER_ERROR); - } - } - stream->state = STREAM_STOPPED; - - unlock: - MUTEX_UNLOCK(&stream->mutex); + stopStream(); } -int RtAudio :: streamWillBlock(int streamId) +int RtApiOss :: streamWillBlock() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_STOPPED) return 0; - MUTEX_LOCK(&stream->mutex); + MUTEX_LOCK(&stream_.mutex); int bytes = 0, channels = 0, frames = 0; - if (stream->state == STREAM_STOPPED) - goto unlock; - audio_buf_info info; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - ioctl(stream->handle[0], SNDCTL_DSP_GETOSPACE, &info); + int *handle = (int *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + ioctl(handle[0], SNDCTL_DSP_GETOSPACE, &info); bytes = info.bytes; - channels = stream->nDeviceChannels[0]; + channels = stream_.nDeviceChannels[0]; } - if (stream->mode == INPUT || stream->mode == DUPLEX) { - ioctl(stream->handle[1], SNDCTL_DSP_GETISPACE, &info); - if (stream->mode == DUPLEX ) { + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + ioctl(handle[1], SNDCTL_DSP_GETISPACE, &info); + if (stream_.mode == DUPLEX ) { bytes = (bytes < info.bytes) ? bytes : info.bytes; - channels = stream->nDeviceChannels[0]; + channels = stream_.nDeviceChannels[0]; } else { bytes = info.bytes; - channels = stream->nDeviceChannels[1]; + channels = stream_.nDeviceChannels[1]; } } - frames = (int) (bytes / (channels * formatBytes(stream->deviceFormat[0]))); - frames -= stream->bufferSize; + frames = (int) (bytes / (channels * formatBytes(stream_.deviceFormat[0]))); + frames -= stream_.bufferSize; if (frames < 0) frames = 0; - unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); return frames; } -void RtAudio :: tickStream(int streamId) +void RtApiOss :: tickStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); int stopStream = 0; - if (stream->state == STREAM_STOPPED) { - if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds + if (stream_.state == STREAM_STOPPED) { + if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds return; } - else if (stream->callbackInfo.usingCallback) { - RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback; - stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData); + else if (stream_.callbackInfo.usingCallback) { + RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; + stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData); } - MUTEX_LOCK(&stream->mutex); + MUTEX_LOCK(&stream_.mutex); // The state might change while waiting on a mutex. - if (stream->state == STREAM_STOPPED) + if (stream_.state == STREAM_STOPPED) goto unlock; - int result; + int result, *handle; char *buffer; int samples; - RTAUDIO_FORMAT format; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { + RtAudioFormat format; + handle = (int *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { // Setup parameters and do buffer conversion if necessary. - if (stream->doConvertBuffer[0]) { - convertStreamBuffer(stream, OUTPUT); - buffer = stream->deviceBuffer; - samples = stream->bufferSize * stream->nDeviceChannels[0]; - format = stream->deviceFormat[0]; + if (stream_.doConvertBuffer[0]) { + convertStreamBuffer(OUTPUT); + buffer = stream_.deviceBuffer; + samples = stream_.bufferSize * stream_.nDeviceChannels[0]; + format = stream_.deviceFormat[0]; } else { - buffer = stream->userBuffer; - samples = stream->bufferSize * stream->nUserChannels[0]; - format = stream->userFormat; + buffer = stream_.userBuffer; + samples = stream_.bufferSize * stream_.nUserChannels[0]; + format = stream_.userFormat; } // Do byte swapping if necessary. - if (stream->doByteSwap[0]) + if (stream_.doByteSwap[0]) byteSwapBuffer(buffer, samples, format); // Write samples to device. - result = write(stream->handle[0], buffer, samples * formatBytes(format)); + result = write(handle[0], buffer, samples * formatBytes(format)); if (result == -1) { // This could be an underrun, but the basic OSS API doesn't provide a means for determining that. - sprintf(message, "RtAudio: OSS audio write error for device (%s).", - devices[stream->device[0]].name); + sprintf(message_, "RtApiOss: audio write error for device (%s).", + devices_[stream_.device[0]].name.c_str()); error(RtError::DRIVER_ERROR); } } - if (stream->mode == INPUT || stream->mode == DUPLEX) { + 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]; + if (stream_.doConvertBuffer[1]) { + buffer = stream_.deviceBuffer; + samples = stream_.bufferSize * stream_.nDeviceChannels[1]; + format = stream_.deviceFormat[1]; } else { - buffer = stream->userBuffer; - samples = stream->bufferSize * stream->nUserChannels[1]; - format = stream->userFormat; + buffer = stream_.userBuffer; + samples = stream_.bufferSize * stream_.nUserChannels[1]; + format = stream_.userFormat; } // Read samples from device. - result = read(stream->handle[1], buffer, samples * formatBytes(format)); + result = read(handle[1], buffer, samples * formatBytes(format)); if (result == -1) { // This could be an overrun, but the basic OSS API doesn't provide a means for determining that. - sprintf(message, "RtAudio: OSS audio read error for device (%s).", - devices[stream->device[1]].name); + sprintf(message_, "RtApiOss: audio read error for device (%s).", + devices_[stream_.device[1]].name.c_str()); error(RtError::DRIVER_ERROR); } // Do byte swapping if necessary. - if (stream->doByteSwap[1]) + if (stream_.doByteSwap[1]) byteSwapBuffer(buffer, samples, format); // Do buffer conversion if necessary. - if (stream->doConvertBuffer[1]) - convertStreamBuffer(stream, INPUT); + if (stream_.doConvertBuffer[1]) + convertStreamBuffer(INPUT); } unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); - if (stream->callbackInfo.usingCallback && stopStream) - this->stopStream(streamId); + if (stream_.callbackInfo.usingCallback && stopStream) + this->stopStream(); } -extern "C" void *callbackHandler(void *ptr) +void RtApiOss :: setStreamCallback(RtAudioCallback callback, void *userData) { - CALLBACK_INFO *info = (CALLBACK_INFO *) ptr; - RtAudio *object = (RtAudio *) info->object; - int stream = info->streamId; + verifyStream(); + + CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; + if ( info->usingCallback ) { + sprintf(message_, "RtApiOss: A callback is already set for this stream!"); + error(RtError::WARNING); + return; + } + + info->callback = (void *) callback; + info->userData = userData; + info->usingCallback = true; + info->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); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + + int err = pthread_create(&(info->thread), &attr, ossCallbackHandler, &stream_.callbackInfo); + pthread_attr_destroy(&attr); + if (err) { + info->usingCallback = false; + sprintf(message_, "RtApiOss: error starting callback thread!"); + error(RtError::THREAD_ERROR); + } +} + +void RtApiOss :: cancelStreamCallback() +{ + verifyStream(); + + if (stream_.callbackInfo.usingCallback) { + + if (stream_.state == STREAM_RUNNING) + stopStream(); + + MUTEX_LOCK(&stream_.mutex); + + stream_.callbackInfo.usingCallback = false; + pthread_join(stream_.callbackInfo.thread, NULL); + stream_.callbackInfo.thread = 0; + stream_.callbackInfo.callback = NULL; + stream_.callbackInfo.userData = NULL; + + MUTEX_UNLOCK(&stream_.mutex); + } +} + +extern "C" void *ossCallbackHandler(void *ptr) +{ + CallbackInfo *info = (CallbackInfo *) ptr; + RtApiOss *object = (RtApiOss *) info->object; bool *usingCallback = &info->usingCallback; while ( *usingCallback ) { pthread_testcancel(); try { - object->tickStream(stream); + object->tickStream(); } catch (RtError &exception) { - fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n", - exception.getMessage()); + fprintf(stderr, "\nRtApiOss: callback thread error (%s) ... closing thread.\n\n", + exception.getMessageString()); break; } } @@ -3406,21 +1454,2907 @@ extern "C" void *callbackHandler(void *ptr) return 0; } - //******************** End of __LINUX_OSS__ *********************// +#endif -#elif defined(__WINDOWS_ASIO__) // ASIO API on Windows +#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. This same functionality can be +// achieved with better synchrony by opening two separate streams for +// the devices and using RtAudio blocking calls (i.e. tickStream()). +// +// 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. + +// A structure to hold various information related to the CoreAuio API +// implementation. +struct CoreHandle { + UInt32 index[2]; + bool stopStream; + bool xrun; + char *deviceBuffer; + pthread_cond_t condition; + + CoreHandle() + :stopStream(false), xrun(false), deviceBuffer(0) {} +}; + +RtApiCore :: RtApiCore() +{ + this->initialize(); + + if (nDevices_ <= 0) { + sprintf(message_, "RtApiCore: no Macintosh OS-X Core Audio devices found!"); + error(RtError::NO_DEVICES_FOUND); + } +} + +RtApiCore :: ~RtApiCore() +{ + // The subclass destructor gets called before the base class + // destructor, so close an existing stream before deallocating + // apiDeviceId memory. + if ( stream_.mode != UNINITIALIZED ) closeStream(); + + // Free our allocated apiDeviceId memory. + AudioDeviceID *id; + for ( unsigned int i=0; iapiDeviceId; + err = AudioDeviceGetProperty( *id, 0, false, + kAudioDevicePropertyDeviceManufacturer, + &dataSize, name ); + if (err != noErr) { + sprintf( message_, "RtApiCore: OS-X error getting device manufacturer." ); + error(RtError::DEBUG_WARNING); + return; + } + strncpy(fullname, name, 256); + strcat(fullname, ": " ); + + dataSize = 256; + err = AudioDeviceGetProperty( *id, 0, false, + kAudioDevicePropertyDeviceName, + &dataSize, name ); + if (err != noErr) { + sprintf( message_, "RtApiCore: OS-X error getting device name." ); + error(RtError::DEBUG_WARNING); + return; + } + strncat(fullname, name, 254); + info->name.erase(); + info->name.append( (const char *)fullname, strlen(fullname)+1); + + // Get output channel information. + unsigned int i, minChannels = 0, maxChannels = 0, nStreams = 0; + AudioBufferList *bufferList = nil; + err = AudioDeviceGetPropertyInfo( *id, 0, false, + kAudioDevicePropertyStreamConfiguration, + &dataSize, NULL ); + if (err == noErr && dataSize > 0) { + bufferList = (AudioBufferList *) malloc( dataSize ); + if (bufferList == NULL) { + sprintf(message_, "RtApiCore: memory allocation error!"); + error(RtError::DEBUG_WARNING); + return; + } + + err = AudioDeviceGetProperty( *id, 0, false, + kAudioDevicePropertyStreamConfiguration, + &dataSize, bufferList ); + if (err == noErr) { + maxChannels = 0; + minChannels = 1000; + nStreams = bufferList->mNumberBuffers; + for ( i=0; imBuffers[i].mNumberChannels; + if ( bufferList->mBuffers[i].mNumberChannels < minChannels ) + minChannels = bufferList->mBuffers[i].mNumberChannels; + } + } + } + free (bufferList); + + if (err != noErr || dataSize <= 0) { + sprintf( message_, "RtApiCore: OS-X error getting output channels for device (%s).", + info->name.c_str() ); + error(RtError::DEBUG_WARNING); + return; + } + + if ( nStreams ) { + if ( maxChannels > 0 ) + info->maxOutputChannels = maxChannels; + if ( minChannels > 0 ) + info->minOutputChannels = minChannels; + } + + // Get input channel information. + bufferList = nil; + err = AudioDeviceGetPropertyInfo( *id, 0, true, + kAudioDevicePropertyStreamConfiguration, + &dataSize, NULL ); + if (err == noErr && dataSize > 0) { + bufferList = (AudioBufferList *) malloc( dataSize ); + if (bufferList == NULL) { + sprintf(message_, "RtApiCore: memory allocation error!"); + error(RtError::DEBUG_WARNING); + return; + } + err = AudioDeviceGetProperty( *id, 0, true, + kAudioDevicePropertyStreamConfiguration, + &dataSize, bufferList ); + if (err == noErr) { + maxChannels = 0; + minChannels = 1000; + nStreams = bufferList->mNumberBuffers; + for ( i=0; imBuffers[i].mNumberChannels < minChannels ) + minChannels = bufferList->mBuffers[i].mNumberChannels; + maxChannels += bufferList->mBuffers[i].mNumberChannels; + } + } + } + free (bufferList); + + if (err != noErr || dataSize <= 0) { + sprintf( message_, "RtApiCore: OS-X error getting input channels for device (%s).", + info->name.c_str() ); + error(RtError::DEBUG_WARNING); + return; + } + + if ( nStreams ) { + if ( maxChannels > 0 ) + info->maxInputChannels = maxChannels; + if ( minChannels > 0 ) + info->minInputChannels = minChannels; + } + + // If device opens for both playback and capture, we determine the channels. + if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) { + info->hasDuplexSupport = true; + info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? + info->maxInputChannels : info->maxOutputChannels; + info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? + info->minInputChannels : info->minOutputChannels; + } + + // Probe the device sample rate and data format parameters. The + // core audio query mechanism is performed on a "stream" + // description, which can have a variable number of channels and + // apply to input or output only. + + // Create a stream description structure. + AudioStreamBasicDescription description; + dataSize = sizeof( AudioStreamBasicDescription ); + memset(&description, 0, sizeof(AudioStreamBasicDescription)); + bool isInput = false; + if ( info->maxOutputChannels == 0 ) isInput = true; + bool isDuplex = false; + if ( info->maxDuplexChannels > 0 ) isDuplex = true; + + // Determine the supported sample rates. + info->sampleRates.clear(); + for (unsigned int k=0; ksampleRates.push_back( SAMPLE_RATES[k] ); + } + + if (info->sampleRates.size() == 0) { + sprintf( message_, "RtApiCore: No supported sample rates found for OS-X device (%s).", + info->name.c_str() ); + error(RtError::DEBUG_WARNING); + return; + } + + // Determine the supported data formats. + info->nativeFormats = 0; + description.mFormatID = kAudioFormatLinearPCM; + description.mBitsPerChannel = 8; + description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT8; + else { + description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT8; + } + + description.mBitsPerChannel = 16; + description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT16; + else { + description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT16; + } + + description.mBitsPerChannel = 32; + description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT32; + else { + description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT32; + } + + description.mBitsPerChannel = 24; + description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsAlignedHigh | kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT24; + else { + description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_SINT24; + } + + description.mBitsPerChannel = 32; + description.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_FLOAT32; + else { + description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_FLOAT32; + } + + description.mBitsPerChannel = 64; + description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_FLOAT64; + else { + description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; + if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) + info->nativeFormats |= RTAUDIO_FLOAT64; + } + + // Check that we have at least one supported format. + if (info->nativeFormats == 0) { + sprintf(message_, "RtApiCore: OS-X device (%s) data format not supported by RtAudio.", + info->name.c_str()); + error(RtError::DEBUG_WARNING); + return; + } + + info->probed = true; +} + +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; + try { + object->callbackEvent( inDevice, (void *)inInputData, (void *)outOutputData ); + } + catch (RtError &exception) { + fprintf(stderr, "\nRtApiCore: callback handler error (%s)!\n\n", exception.getMessageString()); + return kAudioHardwareUnspecifiedError; + } + + return kAudioHardwareNoError; +} + +OSStatus deviceListener(AudioDeviceID inDevice, + UInt32 channel, + Boolean isInput, + AudioDevicePropertyID propertyID, + void* handlePointer) +{ + CoreHandle *handle = (CoreHandle *) handlePointer; + if ( propertyID == kAudioDeviceProcessorOverload ) { + if ( isInput ) + fprintf(stderr, "\nRtApiCore: OS-X audio input overrun detected!\n"); + else + fprintf(stderr, "\nRtApiCore: OS-X audio output underrun detected!\n"); + handle->xrun = true; + } + + return kAudioHardwareNoError; +} + +bool RtApiCore :: probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ) +{ + // Setup for stream mode. + bool isInput = false; + AudioDeviceID id = *((AudioDeviceID *) devices_[device].apiDeviceId); + if ( mode == INPUT ) isInput = true; + + // Search for a stream which contains the desired number of channels. + OSStatus err = noErr; + UInt32 dataSize; + unsigned int deviceChannels, nStreams = 0; + UInt32 iChannel = 0, iStream = 0; + AudioBufferList *bufferList = nil; + err = AudioDeviceGetPropertyInfo( id, 0, isInput, + kAudioDevicePropertyStreamConfiguration, + &dataSize, NULL ); + + if (err == noErr && dataSize > 0) { + bufferList = (AudioBufferList *) malloc( dataSize ); + if (bufferList == NULL) { + sprintf(message_, "RtApiCore: memory allocation error in probeDeviceOpen()!"); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + err = AudioDeviceGetProperty( id, 0, isInput, + kAudioDevicePropertyStreamConfiguration, + &dataSize, bufferList ); + + if (err == noErr) { + stream_.deInterleave[mode] = false; + nStreams = bufferList->mNumberBuffers; + for ( iStream=0; iStreammBuffers[iStream].mNumberChannels >= (unsigned int) channels ) break; + iChannel += bufferList->mBuffers[iStream].mNumberChannels; + } + // If we didn't find a single stream above, see if we can meet + // the channel specification in mono mode (i.e. using separate + // non-interleaved buffers). This can only work if there are N + // consecutive one-channel streams, where N is the number of + // desired channels. + iChannel = 0; + if ( iStream >= nStreams && nStreams >= (unsigned int) channels ) { + int counter = 0; + for ( iStream=0; iStreammBuffers[iStream].mNumberChannels == 1 ) + counter++; + else + counter = 0; + if ( counter == channels ) { + iStream -= channels - 1; + iChannel -= channels - 1; + stream_.deInterleave[mode] = true; + break; + } + iChannel += bufferList->mBuffers[iStream].mNumberChannels; + } + } + } + } + if (err != noErr || dataSize <= 0) { + if ( bufferList ) free( bufferList ); + sprintf( message_, "RtApiCore: OS-X error getting channels for device (%s).", + devices_[device].name.c_str() ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + + if (iStream >= nStreams) { + free (bufferList); + sprintf( message_, "RtApiCore: unable to find OS-X audio stream on device (%s) for requested channels (%d).", + devices_[device].name.c_str(), channels ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + + // This is ok even for mono mode ... it gets updated later. + deviceChannels = bufferList->mBuffers[iStream].mNumberChannels; + free (bufferList); + + // Determine the buffer size. + AudioValueRange bufferRange; + dataSize = sizeof(AudioValueRange); + err = AudioDeviceGetProperty( id, 0, isInput, + kAudioDevicePropertyBufferSizeRange, + &dataSize, &bufferRange); + if (err != noErr) { + sprintf( message_, "RtApiCore: OS-X error getting buffer size range for device (%s).", + devices_[device].name.c_str() ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + + long bufferBytes = *bufferSize * deviceChannels * formatBytes(RTAUDIO_FLOAT32); + if (bufferRange.mMinimum > bufferBytes) bufferBytes = (int) bufferRange.mMinimum; + else if (bufferRange.mMaximum < bufferBytes) bufferBytes = (int) bufferRange.mMaximum; + + // Set the buffer size. For mono mode, I'm assuming we only need to + // make this setting for the first channel. + UInt32 theSize = (UInt32) bufferBytes; + dataSize = sizeof( UInt32); + err = AudioDeviceSetProperty(id, NULL, 0, isInput, + kAudioDevicePropertyBufferSize, + dataSize, &theSize); + if (err != noErr) { + sprintf( message_, "RtApiCore: OS-X error setting the buffer size for device (%s).", + devices_[device].name.c_str() ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + + // If attempting to setup a duplex stream, the bufferSize parameter + // MUST be the same in both directions! + *bufferSize = bufferBytes / ( deviceChannels * formatBytes(RTAUDIO_FLOAT32) ); + if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { + sprintf( message_, "RtApiCore: OS-X error setting buffer size for duplex stream on device (%s).", + devices_[device].name.c_str() ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + + stream_.bufferSize = *bufferSize; + stream_.nBuffers = 1; + + // Set the stream format description. Do for each channel in mono mode. + AudioStreamBasicDescription description; + dataSize = sizeof( AudioStreamBasicDescription ); + if ( stream_.deInterleave[mode] ) nStreams = channels; + else nStreams = 1; + for ( unsigned int i=0; i 1 && stream_.deInterleave[mode]) + stream_.doConvertBuffer[mode] = true; + + // Allocate our CoreHandle structure for the stream. + CoreHandle *handle; + if ( stream_.apiHandle == 0 ) { + handle = (CoreHandle *) calloc(1, sizeof(CoreHandle)); + if ( handle == NULL ) { + sprintf(message_, "RtApiCore: OS-X error allocating coreHandle memory (%s).", + devices_[device].name.c_str()); + goto error; + } + handle->index[0] = 0; + handle->index[1] = 0; + if ( pthread_cond_init(&handle->condition, NULL) ) { + sprintf(message_, "RtApiCore: error initializing pthread condition variable (%s).", + devices_[device].name.c_str()); + goto error; + } + stream_.apiHandle = (void *) handle; + } + else + handle = (CoreHandle *) stream_.apiHandle; + handle->index[mode] = iStream; + + // Allocate necessary internal buffers. + if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { + + long buffer_bytes; + if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) + buffer_bytes = stream_.nUserChannels[0]; + else + buffer_bytes = stream_.nUserChannels[1]; + + buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); + if (stream_.userBuffer) free(stream_.userBuffer); + stream_.userBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.userBuffer == NULL) { + sprintf(message_, "RtApiCore: OS-X error allocating user buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } + } + + if ( stream_.deInterleave[mode] ) { + + long buffer_bytes; + bool makeBuffer = true; + if ( mode == OUTPUT ) + buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); + else { // mode == INPUT + buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); + if ( buffer_bytes < bytes_out ) makeBuffer = false; + } + } + + if ( makeBuffer ) { + buffer_bytes *= *bufferSize; + if (stream_.deviceBuffer) free(stream_.deviceBuffer); + stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.deviceBuffer == NULL) { + sprintf(message_, "RtApiCore: error allocating device buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } + + // If not de-interleaving, we point stream_.deviceBuffer to the + // OS X supplied device buffer before doing any necessary data + // conversions. This presents a problem if we have a duplex + // stream using one device which needs de-interleaving and + // another device which doesn't. So, save a pointer to our own + // device buffer in the CallbackInfo structure. + handle->deviceBuffer = stream_.deviceBuffer; + } + } + + stream_.sampleRate = sampleRate; + stream_.device[mode] = device; + stream_.state = STREAM_STOPPED; + stream_.callbackInfo.object = (void *) this; + + if ( stream_.mode == OUTPUT && mode == INPUT && stream_.device[0] == device ) + // Only one callback procedure per device. + stream_.mode = DUPLEX; + else { + err = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo ); + if (err != noErr) { + sprintf( message_, "RtApiCore: OS-X error setting callback for device (%s).", devices_[device].name.c_str() ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + if ( stream_.mode == OUTPUT && mode == INPUT ) + stream_.mode = DUPLEX; + else + stream_.mode = mode; + } + + // Setup the device property listener for over/underload. + err = AudioDeviceAddPropertyListener( id, iChannel, isInput, + kAudioDeviceProcessorOverload, + deviceListener, (void *) handle ); + + return SUCCESS; + + error: + if ( handle ) { + pthread_cond_destroy(&handle->condition); + free(handle); + stream_.apiHandle = 0; + } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + + error(RtError::WARNING); + return FAILURE; +} + +void RtApiCore :: closeStream() +{ + // We don't want an exception to be thrown here because this + // function is called by our class destructor. So, do our own + // stream check. + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtApiCore::closeStream(): no open stream to close!"); + error(RtError::WARNING); + return; + } + + AudioDeviceID id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + if (stream_.state == STREAM_RUNNING) + AudioDeviceStop( id, callbackHandler ); + AudioDeviceRemoveIOProc( id, callbackHandler ); + } + + id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId ); + if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) { + if (stream_.state == STREAM_RUNNING) + AudioDeviceStop( id, callbackHandler ); + AudioDeviceRemoveIOProc( id, callbackHandler ); + } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + + if ( stream_.deInterleave[0] || stream_.deInterleave[1] ) { + free(stream_.deviceBuffer); + stream_.deviceBuffer = 0; + } + + CoreHandle *handle = (CoreHandle *) stream_.apiHandle; + + // Destroy pthread condition variable and free the CoreHandle structure. + if ( handle ) { + pthread_cond_destroy(&handle->condition); + free( handle ); + stream_.apiHandle = 0; + } + + stream_.mode = UNINITIALIZED; +} + +void RtApiCore :: startStream() +{ + verifyStream(); + if (stream_.state == STREAM_RUNNING) return; + + MUTEX_LOCK(&stream_.mutex); + + OSStatus err; + AudioDeviceID id; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + + id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); + err = AudioDeviceStart(id, callbackHandler); + if (err != noErr) { + sprintf(message_, "RtApiCore: OS-X error starting callback procedure on device (%s).", + devices_[stream_.device[0]].name.c_str()); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) { + + id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId ); + err = AudioDeviceStart(id, callbackHandler); + if (err != noErr) { + sprintf(message_, "RtApiCore: OS-X error starting input callback procedure on device (%s).", + devices_[stream_.device[0]].name.c_str()); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + CoreHandle *handle = (CoreHandle *) stream_.apiHandle; + handle->stopStream = false; + stream_.state = STREAM_RUNNING; + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiCore :: stopStream() +{ + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; + + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); + + OSStatus err; + AudioDeviceID id; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + + id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); + err = AudioDeviceStop(id, callbackHandler); + if (err != noErr) { + sprintf(message_, "RtApiCore: OS-X error stopping callback procedure on device (%s).", + devices_[stream_.device[0]].name.c_str()); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) { + + id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId ); + err = AudioDeviceStop(id, callbackHandler); + if (err != noErr) { + sprintf(message_, "RtApiCore: OS-X error stopping input callback procedure on device (%s).", + devices_[stream_.device[0]].name.c_str()); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiCore :: abortStream() +{ + stopStream(); +} + +void RtApiCore :: tickStream() +{ + verifyStream(); + + if (stream_.state == STREAM_STOPPED) return; + + if (stream_.callbackInfo.usingCallback) { + sprintf(message_, "RtApiCore: tickStream() should not be used when a callback function is set!"); + error(RtError::WARNING); + return; + } + + CoreHandle *handle = (CoreHandle *) stream_.apiHandle; + + MUTEX_LOCK(&stream_.mutex); + + pthread_cond_wait(&handle->condition, &stream_.mutex); + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiCore :: callbackEvent( AudioDeviceID deviceId, void *inData, void *outData ) +{ + verifyStream(); + + if (stream_.state == STREAM_STOPPED) return; + + CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; + CoreHandle *handle = (CoreHandle *) stream_.apiHandle; + AudioBufferList *inBufferList = (AudioBufferList *) inData; + AudioBufferList *outBufferList = (AudioBufferList *) outData; + + if ( info->usingCallback && handle->stopStream ) { + // Check if the stream should be stopped (via the previous user + // callback return value). We stop the stream here, rather than + // after the function call, so that output data can first be + // processed. + this->stopStream(); + return; + } + + MUTEX_LOCK(&stream_.mutex); + + // Invoke user callback first, to get fresh output data. Don't + // invoke the user callback if duplex mode AND the input/output devices + // are different AND this function is called for the input device. + AudioDeviceID id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); + if ( info->usingCallback && (stream_.mode != DUPLEX || deviceId == id ) ) { + RtAudioCallback callback = (RtAudioCallback) info->callback; + handle->stopStream = callback(stream_.userBuffer, stream_.bufferSize, info->userData); + if ( handle->xrun == true ) { + handle->xrun = false; + MUTEX_UNLOCK(&stream_.mutex); + return; + } + } + + if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == id ) ) { + + if (stream_.doConvertBuffer[0]) { + + if ( !stream_.deInterleave[0] ) + stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->index[0]].mData; + else + stream_.deviceBuffer = handle->deviceBuffer; + + convertStreamBuffer(OUTPUT); + if ( stream_.doByteSwap[0] ) + byteSwapBuffer(stream_.deviceBuffer, + stream_.bufferSize * stream_.nDeviceChannels[0], + stream_.deviceFormat[0]); + + if ( stream_.deInterleave[0] ) { + int bufferBytes = outBufferList->mBuffers[handle->index[0]].mDataByteSize; + for ( int i=0; imBuffers[handle->index[0]+i].mData, + &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); + } + } + + } + else { + if (stream_.doByteSwap[0]) + byteSwapBuffer(stream_.userBuffer, + stream_.bufferSize * stream_.nUserChannels[0], + stream_.userFormat); + + memcpy(outBufferList->mBuffers[handle->index[0]].mData, + stream_.userBuffer, + outBufferList->mBuffers[handle->index[0]].mDataByteSize ); + } + } + + if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == id ) ) { + + if (stream_.doConvertBuffer[1]) { + + if ( stream_.deInterleave[1] ) { + stream_.deviceBuffer = (char *) handle->deviceBuffer; + int bufferBytes = inBufferList->mBuffers[handle->index[1]].mDataByteSize; + for ( int i=0; imBuffers[handle->index[1]+i].mData, bufferBytes ); + } + } + else + stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->index[1]].mData; + + if ( stream_.doByteSwap[1] ) + byteSwapBuffer(stream_.deviceBuffer, + stream_.bufferSize * stream_.nDeviceChannels[1], + stream_.deviceFormat[1]); + convertStreamBuffer(INPUT); + + } + else { + memcpy(stream_.userBuffer, + inBufferList->mBuffers[handle->index[1]].mData, + inBufferList->mBuffers[handle->index[1]].mDataByteSize ); + + if (stream_.doByteSwap[1]) + byteSwapBuffer(stream_.userBuffer, + stream_.bufferSize * stream_.nUserChannels[1], + stream_.userFormat); + } + } + + if ( !info->usingCallback && (stream_.mode != DUPLEX || deviceId == id ) ) + pthread_cond_signal(&handle->condition); + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiCore :: setStreamCallback(RtAudioCallback callback, void *userData) +{ + verifyStream(); + + if ( stream_.callbackInfo.usingCallback ) { + sprintf(message_, "RtApiCore: A callback is already set for this stream!"); + error(RtError::WARNING); + return; + } + + stream_.callbackInfo.callback = (void *) callback; + stream_.callbackInfo.userData = userData; + stream_.callbackInfo.usingCallback = true; +} + +void RtApiCore :: cancelStreamCallback() +{ + verifyStream(); + + if (stream_.callbackInfo.usingCallback) { + + if (stream_.state == STREAM_RUNNING) + stopStream(); + + MUTEX_LOCK(&stream_.mutex); + + stream_.callbackInfo.usingCallback = false; + stream_.callbackInfo.userData = NULL; + stream_.state = STREAM_STOPPED; + stream_.callbackInfo.callback = NULL; + + MUTEX_UNLOCK(&stream_.mutex); + } +} + + +//******************** End of __MACOSX_CORE__ *********************// +#endif + +#if defined(__LINUX_JACK__) + +// JACK is a low-latency audio server, written primarily for the +// GNU/Linux operating system. It can connect a number of different +// applications to an audio device, as well as allowing them to share +// audio between themselves. +// +// The JACK server must be running before RtApiJack can be instantiated. +// RtAudio will report just a single "device", which is the JACK audio +// server. The JACK server is typically started in a terminal as follows: +// +// .jackd -d alsa -d hw:0 +// +// Many of the parameters normally set for a stream are fixed by the +// JACK server and can be specified when the JACK server is started. +// In particular, +// +// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4 +// +// specifies a sample rate of 44100 Hz, a buffer size of 512 sample +// frames, and number of buffers = 4. Once the server is running, it +// is not possible to override these values. If the values are not +// specified in the command-line, the JACK server uses default values. + +#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]; + bool clientOpen; + bool stopStream; + pthread_cond_t condition; + + JackHandle() + :client(0), clientOpen(false), stopStream(false) {} +}; + +std::string jackmsg; + +static void jackerror (const char *desc) +{ + jackmsg.erase(); + jackmsg.append( desc, strlen(desc)+1 ); +} + +RtApiJack :: RtApiJack() +{ + this->initialize(); + + if (nDevices_ <= 0) { + sprintf(message_, "RtApiJack: no Linux Jack server found or connection error (jack: %s)!", + jackmsg.c_str()); + error(RtError::NO_DEVICES_FOUND); + } +} + +RtApiJack :: ~RtApiJack() +{ + if ( stream_.mode != UNINITIALIZED ) closeStream(); +} + +void RtApiJack :: initialize(void) +{ + nDevices_ = 0; + + // Tell the jack server to call jackerror() when it experiences an + // error. This function saves the error message for subsequent + // reporting via the normal RtAudio error function. + jack_set_error_function( jackerror ); + + // Look for jack server and try to become a client. + jack_client_t *client; + if ( (client = jack_client_new( "RtApiJack" )) == 0) + return; + + RtApiDevice device; + // Determine the name of the device. + device.name = "Jack Server"; + devices_.push_back(device); + nDevices_++; + + jack_client_close(client); +} + +void RtApiJack :: probeDeviceInfo(RtApiDevice *info) +{ + // Look for jack server and try to become a client. + jack_client_t *client; + if ( (client = jack_client_new( "RtApiJack" )) == 0) { + sprintf(message_, "RtApiJack: error connecting to Linux Jack server in probeDeviceInfo() (jack: %s)!", + jackmsg.c_str()); + error(RtError::WARNING); + return; + } + + // Get the current jack server sample rate. + info->sampleRates.clear(); + info->sampleRates.push_back( jack_get_sample_rate(client) ); + + // Count the available ports as device channels. Jack "input ports" + // equal RtAudio output channels. + const char **ports; + char *port; + unsigned int nChannels = 0; + ports = jack_get_ports( client, NULL, NULL, JackPortIsInput ); + if ( ports ) { + port = (char *) ports[nChannels]; + while ( port ) + port = (char *) ports[++nChannels]; + free( ports ); + info->maxOutputChannels = nChannels; + info->minOutputChannels = 1; + } + + // Jack "output ports" equal RtAudio input channels. + nChannels = 0; + ports = jack_get_ports( client, NULL, NULL, JackPortIsOutput ); + if ( ports ) { + port = (char *) ports[nChannels]; + while ( port ) + port = (char *) ports[++nChannels]; + free( ports ); + info->maxInputChannels = nChannels; + info->minInputChannels = 1; + } + + if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) { + jack_client_close(client); + sprintf(message_, "RtApiJack: error determining jack input/output channels!"); + error(RtError::WARNING); + return; + } + + if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) { + info->hasDuplexSupport = true; + info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? + info->maxInputChannels : info->maxOutputChannels; + info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? + info->minInputChannels : info->minOutputChannels; + } + + // Get the jack data format type. There isn't much documentation + // regarding supported data formats in jack. I'm assuming here that + // the default type will always be a floating-point type, of length + // equal to either 4 or 8 bytes. + int sample_size = sizeof( jack_default_audio_sample_t ); + if ( sample_size == 4 ) + info->nativeFormats = RTAUDIO_FLOAT32; + else if ( sample_size == 8 ) + info->nativeFormats = RTAUDIO_FLOAT64; + + // Check that we have a supported format + if (info->nativeFormats == 0) { + jack_client_close(client); + sprintf(message_, "RtApiJack: error determining jack server data format!"); + error(RtError::WARNING); + return; + } + + jack_client_close(client); + info->probed = true; +} + +int jackCallbackHandler(jack_nframes_t nframes, void *infoPointer) +{ + CallbackInfo *info = (CallbackInfo *) infoPointer; + RtApiJack *object = (RtApiJack *) info->object; + try { + object->callbackEvent( (unsigned long) nframes ); + } + catch (RtError &exception) { + fprintf(stderr, "\nRtApiJack: callback handler error (%s)!\n\n", exception.getMessageString()); + return 0; + } + + return 0; +} + +void jackShutdown(void *infoPointer) +{ + CallbackInfo *info = (CallbackInfo *) infoPointer; + JackHandle *handle = (JackHandle *) info->apiInfo; + handle->clientOpen = false; + RtApiJack *object = (RtApiJack *) info->object; + try { + object->closeStream(); + } + catch (RtError &exception) { + fprintf(stderr, "\nRtApiJack: jackShutdown error (%s)!\n\n", exception.getMessageString()); + return; + } + + fprintf(stderr, "\nRtApiJack: the Jack server is shutting down ... stream stopped and closed!!!\n\n"); +} + +int jackXrun( void * ) +{ + fprintf(stderr, "\nRtApiJack: audio overrun/underrun reported!\n"); + return 0; +} + +bool RtApiJack :: probeDeviceOpen(int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers) +{ + // Compare the jack server channels to the requested number of channels. + if ( (mode == OUTPUT && devices_[device].maxOutputChannels < channels ) || + (mode == INPUT && devices_[device].maxInputChannels < channels ) ) { + sprintf(message_, "RtApiJack: the Jack server does not support requested channels!"); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + + JackHandle *handle = (JackHandle *) stream_.apiHandle; + + // Look for jack server and try to become a client (only do once per stream). + char label[32]; + jack_client_t *client = 0; + if ( mode == OUTPUT || (mode == INPUT && stream_.mode != OUTPUT) ) { + snprintf(label, 32, "RtApiJack"); + if ( (client = jack_client_new( (const char *) label )) == 0) { + sprintf(message_, "RtApiJack: cannot connect to Linux Jack server in probeDeviceOpen() (jack: %s)!", + jackmsg.c_str()); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + } + else { + // The handle must have been created on an earlier pass. + client = handle->client; + } + + // First, check the jack server sample rate. + int jack_rate; + jack_rate = (int) jack_get_sample_rate(client); + if ( sampleRate != jack_rate ) { + jack_client_close(client); + sprintf( message_, "RtApiJack: the requested sample rate (%d) is different than the JACK server rate (%d).", + sampleRate, jack_rate ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + stream_.sampleRate = jack_rate; + + // The jack server seems to support just a single floating-point + // data type. Since we already checked it before, just use what we + // found then. + stream_.deviceFormat[mode] = devices_[device].nativeFormats; + stream_.userFormat = format; + + // Jack always uses non-interleaved buffers. We'll need to + // de-interleave if we have more than one channel. + stream_.deInterleave[mode] = false; + if ( channels > 1 ) + stream_.deInterleave[mode] = true; + + // 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; + + stream_.doConvertBuffer[mode] = false; + if (stream_.userFormat != stream_.deviceFormat[mode]) + stream_.doConvertBuffer[mode] = true; + if (stream_.deInterleave[mode]) + stream_.doConvertBuffer[mode] = true; + + // Allocate our JackHandle structure for the stream. + if ( handle == 0 ) { + handle = (JackHandle *) calloc(1, sizeof(JackHandle)); + if ( handle == NULL ) { + sprintf(message_, "RtApiJack: error allocating JackHandle memory (%s).", + devices_[device].name.c_str()); + goto error; + } + handle->ports[0] = 0; + handle->ports[1] = 0; + if ( pthread_cond_init(&handle->condition, NULL) ) { + sprintf(message_, "RtApiJack: error initializing pthread condition variable!"); + goto error; + } + stream_.apiHandle = (void *) handle; + handle->client = client; + handle->clientOpen = true; + } + + // Allocate necessary internal buffers. + if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { + + long buffer_bytes; + if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) + buffer_bytes = stream_.nUserChannels[0]; + else + buffer_bytes = stream_.nUserChannels[1]; + + buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); + if (stream_.userBuffer) free(stream_.userBuffer); + stream_.userBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.userBuffer == NULL) { + sprintf(message_, "RtApiJack: error allocating user buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } + } + + if ( stream_.doConvertBuffer[mode] ) { + + long buffer_bytes; + bool makeBuffer = true; + if ( mode == OUTPUT ) + buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); + else { // mode == INPUT + buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); + if ( buffer_bytes < bytes_out ) makeBuffer = false; + } + } + + if ( makeBuffer ) { + buffer_bytes *= *bufferSize; + if (stream_.deviceBuffer) free(stream_.deviceBuffer); + stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.deviceBuffer == NULL) { + sprintf(message_, "RtApiJack: error allocating device buffer memory (%s).", + devices_[device].name.c_str()); + 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 ) { + sprintf(message_, "RtApiJack: error allocating port handle memory (%s).", + devices_[device].name.c_str()); + goto error; + } + + stream_.device[mode] = device; + stream_.state = STREAM_STOPPED; + stream_.callbackInfo.usingCallback = false; + stream_.callbackInfo.object = (void *) this; + stream_.callbackInfo.apiInfo = (void *) handle; + + 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, NULL ); + jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); + } + + return SUCCESS; + + error: + if ( handle ) { + pthread_cond_destroy(&handle->condition); + if ( handle->clientOpen == true ) + jack_client_close(handle->client); + + if ( handle->ports[0] ) free(handle->ports[0]); + if ( handle->ports[1] ) free(handle->ports[1]); + + free( handle ); + stream_.apiHandle = 0; + } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + + error(RtError::WARNING); + return FAILURE; +} + +void RtApiJack :: closeStream() +{ + // We don't want an exception to be thrown here because this + // function is called by our class destructor. So, do our own + // stream check. + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtApiJack::closeStream(): no open stream to close!"); + error(RtError::WARNING); + return; + } + + JackHandle *handle = (JackHandle *) stream_.apiHandle; + if ( handle && handle->clientOpen == true ) { + 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); + free( handle ); + stream_.apiHandle = 0; + } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + + if (stream_.deviceBuffer) { + free(stream_.deviceBuffer); + stream_.deviceBuffer = 0; + } + + stream_.mode = UNINITIALIZED; +} + + +void RtApiJack :: startStream() +{ + verifyStream(); + if (stream_.state == STREAM_RUNNING) return; + + MUTEX_LOCK(&stream_.mutex); + + char label[64]; + JackHandle *handle = (JackHandle *) stream_.apiHandle; + if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { + for ( int i=0; iports[0][i] = jack_port_register(handle->client, (const char *)label, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + } + } + + if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { + for ( int i=0; iports[1][i] = jack_port_register(handle->client, (const char *)label, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + } + } + + if (jack_activate(handle->client)) { + sprintf(message_, "RtApiJack: unable to activate JACK client!"); + error(RtError::SYSTEM_ERROR); + } + + const char **ports; + int result; + // Get the list of available ports. + if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { + ports = jack_get_ports(handle->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); + if ( ports == NULL) { + sprintf(message_, "RtApiJack: error determining available jack input ports!"); + error(RtError::SYSTEM_ERROR); + } + + // 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. + for ( int i=0; iclient, jack_port_name(handle->ports[0][i]), ports[i] ); + if ( result ) { + free(ports); + sprintf(message_, "RtApiJack: error connecting output ports!"); + error(RtError::SYSTEM_ERROR); + } + } + free(ports); + } + + if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { + ports = jack_get_ports( handle->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput ); + if ( ports == NULL) { + sprintf(message_, "RtApiJack: error determining available jack output ports!"); + error(RtError::SYSTEM_ERROR); + } + + // Now make the port connections. See note above. + for ( int i=0; iclient, ports[i], jack_port_name(handle->ports[1][i]) ); + if ( result ) { + free(ports); + sprintf(message_, "RtApiJack: error connecting input ports!"); + error(RtError::SYSTEM_ERROR); + } + } + free(ports); + } + + handle->stopStream = false; + stream_.state = STREAM_RUNNING; + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiJack :: stopStream() +{ + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; + + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); + + JackHandle *handle = (JackHandle *) stream_.apiHandle; + jack_deactivate(handle->client); + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiJack :: abortStream() +{ + stopStream(); +} + +void RtApiJack :: tickStream() +{ + verifyStream(); + + if (stream_.state == STREAM_STOPPED) return; + + if (stream_.callbackInfo.usingCallback) { + sprintf(message_, "RtApiJack: tickStream() should not be used when a callback function is set!"); + error(RtError::WARNING); + return; + } + + JackHandle *handle = (JackHandle *) stream_.apiHandle; + + MUTEX_LOCK(&stream_.mutex); + + pthread_cond_wait(&handle->condition, &stream_.mutex); + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiJack :: callbackEvent( unsigned long nframes ) +{ + verifyStream(); + + if (stream_.state == STREAM_STOPPED) return; + + CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; + JackHandle *handle = (JackHandle *) stream_.apiHandle; + if ( info->usingCallback && handle->stopStream ) { + // Check if the stream should be stopped (via the previous user + // callback return value). We stop the stream here, rather than + // after the function call, so that output data can first be + // processed. + this->stopStream(); + return; + } + + MUTEX_LOCK(&stream_.mutex); + + // Invoke user callback first, to get fresh output data. + if ( info->usingCallback ) { + RtAudioCallback callback = (RtAudioCallback) info->callback; + handle->stopStream = callback(stream_.userBuffer, stream_.bufferSize, info->userData); + } + + jack_default_audio_sample_t *jackbuffer; + long bufferBytes = nframes * sizeof (jack_default_audio_sample_t); + if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { + + if (stream_.doConvertBuffer[0]) { + convertStreamBuffer(OUTPUT); + + for ( int i=0; iports[0][i], + (jack_nframes_t) nframes); + memcpy(jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); + } + } + else { // single channel only + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][0], + (jack_nframes_t) nframes); + memcpy(jackbuffer, stream_.userBuffer, bufferBytes ); + } + } + + if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { + + if (stream_.doConvertBuffer[1]) { + for ( int i=0; iports[1][i], + (jack_nframes_t) nframes); + memcpy(&stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes ); + } + convertStreamBuffer(INPUT); + } + else { // single channel only + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][0], + (jack_nframes_t) nframes); + memcpy(stream_.userBuffer, jackbuffer, bufferBytes ); + } + } + + if ( !info->usingCallback ) + pthread_cond_signal(&handle->condition); + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiJack :: setStreamCallback(RtAudioCallback callback, void *userData) +{ + verifyStream(); + + if ( stream_.callbackInfo.usingCallback ) { + sprintf(message_, "RtApiJack: A callback is already set for this stream!"); + error(RtError::WARNING); + return; + } + + stream_.callbackInfo.callback = (void *) callback; + stream_.callbackInfo.userData = userData; + stream_.callbackInfo.usingCallback = true; +} + +void RtApiJack :: cancelStreamCallback() +{ + verifyStream(); + + if (stream_.callbackInfo.usingCallback) { + + if (stream_.state == STREAM_RUNNING) + stopStream(); + + MUTEX_LOCK(&stream_.mutex); + + stream_.callbackInfo.usingCallback = false; + stream_.callbackInfo.userData = NULL; + stream_.state = STREAM_STOPPED; + stream_.callbackInfo.callback = NULL; + + MUTEX_UNLOCK(&stream_.mutex); + } +} + +#endif + +#if defined(__LINUX_ALSA__) + +#include +#include +#include + +extern "C" void *alsaCallbackHandler(void * ptr); + +RtApiAlsa :: RtApiAlsa() +{ + this->initialize(); + + if (nDevices_ <= 0) { + sprintf(message_, "RtApiAlsa: no Linux ALSA audio devices found!"); + error(RtError::NO_DEVICES_FOUND); + } +} + +RtApiAlsa :: ~RtApiAlsa() +{ + if ( stream_.mode != UNINITIALIZED ) + closeStream(); +} + +void RtApiAlsa :: initialize(void) +{ + int card, subdevice, result; + char name[64]; + const char *cardId; + snd_ctl_t *handle; + snd_ctl_card_info_t *info; + snd_ctl_card_info_alloca(&info); + RtApiDevice device; + + // Count cards and devices + nDevices_ = 0; + card = -1; + snd_card_next(&card); + while ( card >= 0 ) { + sprintf(name, "hw:%d", card); + result = snd_ctl_open(&handle, name, 0); + if (result < 0) { + sprintf(message_, "RtApiAlsa: control open (%i): %s.", card, snd_strerror(result)); + error(RtError::DEBUG_WARNING); + goto next_card; + } + result = snd_ctl_card_info(handle, info); + if (result < 0) { + sprintf(message_, "RtApiAlsa: control hardware info (%i): %s.", card, snd_strerror(result)); + error(RtError::DEBUG_WARNING); + goto next_card; + } + cardId = snd_ctl_card_info_get_id(info); + subdevice = -1; + while (1) { + result = snd_ctl_pcm_next_device(handle, &subdevice); + if (result < 0) { + sprintf(message_, "RtApiAlsa: control next device (%i): %s.", card, snd_strerror(result)); + error(RtError::DEBUG_WARNING); + break; + } + if (subdevice < 0) + break; + sprintf( name, "hw:%d,%d", card, subdevice ); + // If a cardId exists and it contains at least one non-numeric + // character, use it to identify the device. This avoids a bug + // in ALSA such that a numeric string is interpreted as a device + // number. + for ( unsigned int i=0; iname.c_str(), 64 ); + card = strtok(name, ","); + err = snd_ctl_open(&chandle, card, SND_CTL_NONBLOCK); + if (err < 0) { + sprintf(message_, "RtApiAlsa: control open (%s): %s.", card, snd_strerror(err)); + error(RtError::DEBUG_WARNING); + return; + } + unsigned int dev = (unsigned int) atoi( strtok(NULL, ",") ); + + // First try for playback + stream = SND_PCM_STREAM_PLAYBACK; + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + + if ((err = snd_ctl_pcm_info(chandle, pcminfo)) < 0) { + if (err == -ENOENT) { + sprintf(message_, "RtApiAlsa: pcm device (%s) doesn't handle output!", info->name.c_str()); + error(RtError::DEBUG_WARNING); + } + else { + sprintf(message_, "RtApiAlsa: snd_ctl_pcm_info error for device (%s) output: %s", + info->name.c_str(), snd_strerror(err)); + error(RtError::DEBUG_WARNING); + } + goto capture_probe; + } + + err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode | SND_PCM_NONBLOCK ); + if (err < 0) { + if ( err == EBUSY ) + sprintf(message_, "RtApiAlsa: pcm playback device (%s) is busy: %s.", + info->name.c_str(), snd_strerror(err)); + else + sprintf(message_, "RtApiAlsa: pcm playback open (%s) error: %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::DEBUG_WARNING); + goto capture_probe; + } + + // We have an open device ... allocate the parameter structure. + err = snd_pcm_hw_params_any(handle, params); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + goto capture_probe; + } + + // Get output channel information. + unsigned int value; + err = snd_pcm_hw_params_get_channels_min(params, &value); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: hardware minimum channel probe error (%s): %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + goto capture_probe; + } + info->minOutputChannels = value; + + err = snd_pcm_hw_params_get_channels_max(params, &value); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: hardware maximum channel probe error (%s): %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + goto capture_probe; + } + info->maxOutputChannels = value; + + snd_pcm_close(handle); + + capture_probe: + // Now try for capture + stream = SND_PCM_STREAM_CAPTURE; + snd_pcm_info_set_stream(pcminfo, stream); + + err = snd_ctl_pcm_info(chandle, pcminfo); + snd_ctl_close(chandle); + if ( err < 0 ) { + if (err == -ENOENT) { + sprintf(message_, "RtApiAlsa: pcm device (%s) doesn't handle input!", info->name.c_str()); + error(RtError::DEBUG_WARNING); + } + else { + sprintf(message_, "RtApiAlsa: snd_ctl_pcm_info error for device (%s) input: %s", + info->name.c_str(), snd_strerror(err)); + error(RtError::DEBUG_WARNING); + } + if (info->maxOutputChannels == 0) + // didn't open for playback either ... device invalid + return; + goto probe_parameters; + } + + err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode | SND_PCM_NONBLOCK); + if (err < 0) { + if ( err == EBUSY ) + sprintf(message_, "RtApiAlsa: pcm capture device (%s) is busy: %s.", + info->name.c_str(), snd_strerror(err)); + else + sprintf(message_, "RtApiAlsa: pcm capture open (%s) error: %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::DEBUG_WARNING); + if (info->maxOutputChannels == 0) + // didn't open for playback either ... device invalid + return; + goto probe_parameters; + } + + // We have an open capture device ... allocate the parameter structure. + err = snd_pcm_hw_params_any(handle, params); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + if (info->maxOutputChannels > 0) + goto probe_parameters; + else + return; + } + + // Get input channel information. + err = snd_pcm_hw_params_get_channels_min(params, &value); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: hardware minimum in channel probe error (%s): %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + if (info->maxOutputChannels > 0) + goto probe_parameters; + else + return; + } + info->minInputChannels = value; + + err = snd_pcm_hw_params_get_channels_max(params, &value); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: hardware maximum in channel probe error (%s): %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + if (info->maxOutputChannels > 0) + goto probe_parameters; + else + return; + } + info->maxInputChannels = value; + + snd_pcm_close(handle); + + // If device opens for both playback and capture, we determine the channels. + if (info->maxOutputChannels == 0 || info->maxInputChannels == 0) + goto probe_parameters; + + info->hasDuplexSupport = true; + info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? + info->maxInputChannels : info->maxOutputChannels; + info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? + info->minInputChannels : info->minOutputChannels; + + probe_parameters: + // 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->maxOutputChannels >= info->maxInputChannels) + stream = SND_PCM_STREAM_PLAYBACK; + else + stream = SND_PCM_STREAM_CAPTURE; + + err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode); + if (err < 0) { + sprintf(message_, "RtApiAlsa: pcm (%s) won't reopen during probe: %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + return; + } + + // We have an open device ... allocate the parameter structure. + err = snd_pcm_hw_params_any(handle, params); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: hardware reopen probe error (%s): %s.", + info->name.c_str(), snd_strerror(err)); + error(RtError::WARNING); + return; + } + + // Test our discrete set of sample rate values. + int dir = 0; + info->sampleRates.clear(); + for (unsigned int i=0; isampleRates.push_back(SAMPLE_RATES[i]); + } + if (info->sampleRates.size() == 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: no supported sample rates found for device (%s).", + info->name.c_str()); + error(RtError::DEBUG_WARNING); + return; + } + + // Probe the supported data formats ... we don't care about endian-ness just yet + snd_pcm_format_t format; + info->nativeFormats = 0; + format = SND_PCM_FORMAT_S8; + if (snd_pcm_hw_params_test_format(handle, params, format) == 0) + info->nativeFormats |= RTAUDIO_SINT8; + format = SND_PCM_FORMAT_S16; + if (snd_pcm_hw_params_test_format(handle, params, format) == 0) + info->nativeFormats |= RTAUDIO_SINT16; + format = SND_PCM_FORMAT_S24; + if (snd_pcm_hw_params_test_format(handle, params, format) == 0) + info->nativeFormats |= RTAUDIO_SINT24; + format = SND_PCM_FORMAT_S32; + if (snd_pcm_hw_params_test_format(handle, params, format) == 0) + info->nativeFormats |= RTAUDIO_SINT32; + format = SND_PCM_FORMAT_FLOAT; + if (snd_pcm_hw_params_test_format(handle, params, format) == 0) + info->nativeFormats |= RTAUDIO_FLOAT32; + format = SND_PCM_FORMAT_FLOAT64; + if (snd_pcm_hw_params_test_format(handle, params, format) == 0) + info->nativeFormats |= RTAUDIO_FLOAT64; + + // Check that we have at least one supported format + if (info->nativeFormats == 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: pcm device (%s) data format not supported by RtAudio.", + info->name.c_str()); + error(RtError::WARNING); + return; + } + + // That's all ... close the device and return + snd_pcm_close(handle); + info->probed = true; + return; +} + +bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers ) +{ +#if defined(__RTAUDIO_DEBUG__) + snd_output_t *out; + snd_output_stdio_attach(&out, stderr, 0); +#endif + + // I'm not using the "plug" interface ... too much inconsistent behavior. + const char *name = devices_[device].name.c_str(); + + snd_pcm_stream_t alsa_stream; + if (mode == OUTPUT) + alsa_stream = SND_PCM_STREAM_PLAYBACK; + else + alsa_stream = SND_PCM_STREAM_CAPTURE; + + int err; + snd_pcm_t *handle; + int alsa_open_mode = SND_PCM_ASYNC; + err = snd_pcm_open(&handle, name, alsa_stream, alsa_open_mode); + if (err < 0) { + sprintf(message_,"RtApiAlsa: pcm device (%s) won't open: %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + + // Fill the parameter structure. + snd_pcm_hw_params_t *hw_params; + snd_pcm_hw_params_alloca(&hw_params); + err = snd_pcm_hw_params_any(handle, hw_params); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error getting parameter handle (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + 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 ... try interleaved access first, then non-interleaved + if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) ) { + err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + } + else if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED) ) { + err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); + stream_.deInterleave[mode] = true; + } + else { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: device (%s) access not supported by RtAudio.", name); + error(RtError::WARNING); + return FAILURE; + } + + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error setting access ( (%s): %s.", name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + + // Determine how to set the device format. + stream_.userFormat = format; + snd_pcm_format_t device_format = SND_PCM_FORMAT_UNKNOWN; + + if (format == RTAUDIO_SINT8) + device_format = SND_PCM_FORMAT_S8; + else if (format == RTAUDIO_SINT16) + device_format = SND_PCM_FORMAT_S16; + else if (format == RTAUDIO_SINT24) + device_format = SND_PCM_FORMAT_S24; + else if (format == RTAUDIO_SINT32) + device_format = SND_PCM_FORMAT_S32; + else if (format == RTAUDIO_FLOAT32) + device_format = SND_PCM_FORMAT_FLOAT; + else if (format == RTAUDIO_FLOAT64) + device_format = SND_PCM_FORMAT_FLOAT64; + + if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { + stream_.deviceFormat[mode] = format; + goto set_format; + } + + // The user requested format is not natively supported by the device. + device_format = SND_PCM_FORMAT_FLOAT64; + if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { + stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; + goto set_format; + } + + device_format = SND_PCM_FORMAT_FLOAT; + if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { + stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; + goto set_format; + } + + device_format = SND_PCM_FORMAT_S32; + if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { + stream_.deviceFormat[mode] = RTAUDIO_SINT32; + goto set_format; + } + + device_format = SND_PCM_FORMAT_S24; + if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { + stream_.deviceFormat[mode] = RTAUDIO_SINT24; + goto set_format; + } + + device_format = SND_PCM_FORMAT_S16; + if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { + stream_.deviceFormat[mode] = RTAUDIO_SINT16; + goto set_format; + } + + device_format = SND_PCM_FORMAT_S8; + if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { + stream_.deviceFormat[mode] = RTAUDIO_SINT8; + goto set_format; + } + + // If we get here, no supported format was found. + sprintf(message_,"RtApiAlsa: pcm device (%s) data format not supported by RtAudio.", name); + snd_pcm_close(handle); + error(RtError::WARNING); + return FAILURE; + + set_format: + err = snd_pcm_hw_params_set_format(handle, hw_params, device_format); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error setting format (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + + // Determine whether byte-swaping is necessary. + stream_.doByteSwap[mode] = false; + if (device_format != SND_PCM_FORMAT_S8) { + err = snd_pcm_format_cpu_endian(device_format); + if (err == 0) + stream_.doByteSwap[mode] = true; + else if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error getting format endian-ness (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + } + + // Set the sample rate. + err = snd_pcm_hw_params_set_rate(handle, hw_params, (unsigned int)sampleRate, 0); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error setting sample rate (%d) on device (%s): %s.", + sampleRate, name, snd_strerror(err)); + error(RtError::WARNING); + 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; + err = snd_pcm_hw_params_get_channels_max(hw_params, &value); + int device_channels = value; + if (err < 0 || device_channels < channels) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: channels (%d) not supported by device (%s).", + channels, name); + error(RtError::WARNING); + return FAILURE; + } + + err = snd_pcm_hw_params_get_channels_min(hw_params, &value); + if (err < 0 ) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error getting min channels count on device (%s).", name); + error(RtError::WARNING); + return FAILURE; + } + device_channels = value; + if (device_channels < channels) device_channels = channels; + stream_.nDeviceChannels[mode] = device_channels; + + // Set the device channels. + err = snd_pcm_hw_params_set_channels(handle, hw_params, device_channels); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error setting channels (%d) on device (%s): %s.", + device_channels, name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + + // Set the buffer number, which in ALSA is referred to as the "period". + int dir; + unsigned int periods = numberOfBuffers; + // Even though the hardware might allow 1 buffer, it won't work reliably. + if (periods < 2) periods = 2; + err = snd_pcm_hw_params_get_periods_min(hw_params, &value, &dir); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error getting min periods on device (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + if (value > periods) periods = value; + err = snd_pcm_hw_params_get_periods_max(hw_params, &value, &dir); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error getting max periods on device (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + if (value < periods) periods = value; + + err = snd_pcm_hw_params_set_periods(handle, hw_params, periods, 0); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error setting periods (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + + // Set the buffer (or period) size. + snd_pcm_uframes_t period_size; + err = snd_pcm_hw_params_get_period_size_min(hw_params, &period_size, &dir); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error getting period size (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + return FAILURE; + } + if (*bufferSize < (int) period_size) *bufferSize = (int) period_size; + + err = snd_pcm_hw_params_set_period_size(handle, hw_params, *bufferSize, 0); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error setting period size (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + 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 ) { + sprintf( message_, "RtApiAlsa: error setting buffer size for duplex stream on device (%s).", + name ); + error(RtError::DEBUG_WARNING); + return FAILURE; + } + + stream_.bufferSize = *bufferSize; + + // Install the hardware configuration + err = snd_pcm_hw_params(handle, hw_params); + if (err < 0) { + snd_pcm_close(handle); + sprintf(message_, "RtApiAlsa: error installing hardware configuration (%s): %s.", + name, snd_strerror(err)); + error(RtError::WARNING); + 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 + + // Allocate the stream handle if necessary and then save. + snd_pcm_t **handles; + if ( stream_.apiHandle == 0 ) { + handles = (snd_pcm_t **) calloc(2, sizeof(snd_pcm_t *)); + if ( handle == NULL ) { + sprintf(message_, "RtApiAlsa: error allocating handle memory (%s).", + devices_[device].name.c_str()); + goto error; + } + stream_.apiHandle = (void *) handles; + handles[0] = 0; + handles[1] = 0; + } + else { + handles = (snd_pcm_t **) stream_.apiHandle; + } + handles[mode] = handle; + + // 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_.nUserChannels[mode] > 1 && stream_.deInterleave[mode]) + stream_.doConvertBuffer[mode] = true; + + // Allocate necessary internal buffers + if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { + + long buffer_bytes; + if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) + buffer_bytes = stream_.nUserChannels[0]; + else + buffer_bytes = stream_.nUserChannels[1]; + + buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); + if (stream_.userBuffer) free(stream_.userBuffer); + stream_.userBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.userBuffer == NULL) { + sprintf(message_, "RtApiAlsa: error allocating user buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } + } + + if ( stream_.doConvertBuffer[mode] ) { + + long buffer_bytes; + bool makeBuffer = true; + if ( mode == OUTPUT ) + buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); + else { // mode == INPUT + buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); + if ( buffer_bytes < bytes_out ) makeBuffer = false; + } + } + + if ( makeBuffer ) { + buffer_bytes *= *bufferSize; + if (stream_.deviceBuffer) free(stream_.deviceBuffer); + stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.deviceBuffer == NULL) { + sprintf(message_, "RtApiAlsa: error allocating device buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } + } + } + + 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 = periods; + stream_.sampleRate = sampleRate; + + return SUCCESS; + + error: + if (handles) { + if (handles[0]) + snd_pcm_close(handles[0]); + if (handles[1]) + snd_pcm_close(handles[1]); + free(handles); + stream_.apiHandle = 0; + } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + + error(RtError::WARNING); + return FAILURE; +} + +void RtApiAlsa :: closeStream() +{ + // We don't want an exception to be thrown here because this + // function is called by our class destructor. So, do our own + // stream check. + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtApiAlsa::closeStream(): no open stream to close!"); + error(RtError::WARNING); + return; + } + + snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + if (stream_.state == STREAM_RUNNING) { + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) + snd_pcm_drop(handle[0]); + if (stream_.mode == INPUT || stream_.mode == DUPLEX) + snd_pcm_drop(handle[1]); + stream_.state = STREAM_STOPPED; + } + + if (stream_.callbackInfo.usingCallback) { + stream_.callbackInfo.usingCallback = false; + pthread_join(stream_.callbackInfo.thread, NULL); + } + + if (handle) { + if (handle[0]) snd_pcm_close(handle[0]); + if (handle[1]) snd_pcm_close(handle[1]); + free(handle); + handle = 0; + } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + + if (stream_.deviceBuffer) { + free(stream_.deviceBuffer); + stream_.deviceBuffer = 0; + } + + stream_.mode = UNINITIALIZED; +} + +void RtApiAlsa :: startStream() +{ + // This method calls snd_pcm_prepare if the device isn't already in that state. + + verifyStream(); + if (stream_.state == STREAM_RUNNING) return; + + MUTEX_LOCK(&stream_.mutex); + + int err; + snd_pcm_state_t state; + snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + state = snd_pcm_state(handle[0]); + if (state != SND_PCM_STATE_PREPARED) { + err = snd_pcm_prepare(handle[0]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error preparing pcm device (%s): %s.", + devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + } + + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + state = snd_pcm_state(handle[1]); + if (state != SND_PCM_STATE_PREPARED) { + err = snd_pcm_prepare(handle[1]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error preparing pcm device (%s): %s.", + devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + } + stream_.state = STREAM_RUNNING; + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiAlsa :: stopStream() +{ + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; + + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); + + int err; + snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + err = snd_pcm_drain(handle[0]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", + devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + err = snd_pcm_drain(handle[1]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", + devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiAlsa :: abortStream() +{ + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; + + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); + + int err; + snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + err = snd_pcm_drop(handle[0]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", + devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + err = snd_pcm_drop(handle[1]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", + devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + MUTEX_UNLOCK(&stream_.mutex); +} + +int RtApiAlsa :: streamWillBlock() +{ + verifyStream(); + if (stream_.state == STREAM_STOPPED) return 0; + + MUTEX_LOCK(&stream_.mutex); + + int err = 0, frames = 0; + snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + err = snd_pcm_avail_update(handle[0]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error getting available frames for device (%s): %s.", + devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + } + + frames = err; + + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + err = snd_pcm_avail_update(handle[1]); + if (err < 0) { + sprintf(message_, "RtApiAlsa: error getting available frames for device (%s): %s.", + devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + if (frames > err) frames = err; + } + + frames = stream_.bufferSize - frames; + if (frames < 0) frames = 0; + + MUTEX_UNLOCK(&stream_.mutex); + return frames; +} + +void RtApiAlsa :: tickStream() +{ + verifyStream(); + + int stopStream = 0; + if (stream_.state == STREAM_STOPPED) { + if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds + return; + } + else if (stream_.callbackInfo.usingCallback) { + RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; + stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData); + } + + MUTEX_LOCK(&stream_.mutex); + + // The state might change while waiting on a mutex. + if (stream_.state == STREAM_STOPPED) + goto unlock; + + int err; + char *buffer; + int channels; + snd_pcm_t **handle; + RtAudioFormat format; + handle = (snd_pcm_t **) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + + // Setup parameters and do buffer conversion if necessary. + if (stream_.doConvertBuffer[0]) { + convertStreamBuffer(OUTPUT); + buffer = stream_.deviceBuffer; + channels = stream_.nDeviceChannels[0]; + format = stream_.deviceFormat[0]; + } + else { + buffer = stream_.userBuffer; + 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_.deInterleave[0]) { + void *bufs[channels]; + size_t offset = stream_.bufferSize * formatBytes(format); + for (int i=0; istopStream(); +} + +void RtApiAlsa :: setStreamCallback(RtAudioCallback callback, void *userData) +{ + verifyStream(); + + CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; + if ( info->usingCallback ) { + sprintf(message_, "RtApiAlsa: A callback is already set for this stream!"); + error(RtError::WARNING); + return; + } + + info->callback = (void *) callback; + info->userData = userData; + info->usingCallback = true; + info->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); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + + int err = pthread_create(&info->thread, &attr, alsaCallbackHandler, &stream_.callbackInfo); + pthread_attr_destroy(&attr); + if (err) { + info->usingCallback = false; + sprintf(message_, "RtApiAlsa: error starting callback thread!"); + error(RtError::THREAD_ERROR); + } +} + +void RtApiAlsa :: cancelStreamCallback() +{ + verifyStream(); + + if (stream_.callbackInfo.usingCallback) { + + if (stream_.state == STREAM_RUNNING) + stopStream(); + + MUTEX_LOCK(&stream_.mutex); + + stream_.callbackInfo.usingCallback = false; + pthread_join(stream_.callbackInfo.thread, NULL); + stream_.callbackInfo.thread = 0; + stream_.callbackInfo.callback = NULL; + stream_.callbackInfo.userData = NULL; + + MUTEX_UNLOCK(&stream_.mutex); + } +} + +extern "C" void *alsaCallbackHandler(void *ptr) +{ + CallbackInfo *info = (CallbackInfo *) ptr; + RtApiAlsa *object = (RtApiAlsa *) info->object; + bool *usingCallback = &info->usingCallback; + + while ( *usingCallback ) { + try { + object->tickStream(); + } + catch (RtError &exception) { + fprintf(stderr, "\nRtApiAlsa: callback thread error (%s) ... closing thread.\n\n", + exception.getMessageString()); + break; + } + } + + pthread_exit(NULL); +} + +//******************** End of __LINUX_ALSA__ *********************// +#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. 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. +// implementation is similar to that used for OS-X CoreAudio and Linux +// Jack. The primary constraint with ASIO is that it only allows +// access to a single driver at a time. Thus, it is not possible to +// have more than one simultaneous RtAudio stream. // // This implementation also requires a number of external ASIO files // and a few global variables. The ASIO callback scheme does not // allow for the passing of user data, so we must create a global // pointer to our callbackInfo structure. +// +// On unix systems, we make use of a pthread condition variable. +// Since there is no equivalent in Windows, I hacked something based +// on information found in +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html. #include "asio/asiosys.h" #include "asio/asio.h" @@ -3429,60 +4363,72 @@ extern "C" void *callbackHandler(void *ptr) AsioDrivers drivers; ASIOCallbacks asioCallbacks; -CALLBACK_INFO *asioCallbackInfo; ASIODriverInfo driverInfo; +CallbackInfo *asioCallbackInfo; -void RtAudio :: initialize(void) +struct AsioHandle { + bool stopStream; + ASIOBufferInfo *bufferInfos; + HANDLE condition; + + AsioHandle() + :stopStream(false), bufferInfos(0) {} +}; + +RtApiAsio :: RtApiAsio() { - nDevices = drivers.asioGetNumDev(); - if (nDevices <= 0) return; + this->initialize(); - // Allocate the RTAUDIO_DEVICE structures. - devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE)); - if (devices == NULL) { - sprintf(message, "RtAudio: memory allocation error!"); - error(RtError::MEMORY_ERROR); + if (nDevices_ <= 0) { + sprintf(message_, "RtApiAsio: no Windows ASIO audio drivers found!"); + error(RtError::NO_DEVICES_FOUND); } +} - // Write device driver names to device structures and then probe the - // device capabilities. - for (int i=0; i 0 ) { - sprintf(message, "RtAudio: unable to probe ASIO driver while a stream is open."); + if ( stream_.mode != UNINITIALIZED ) { + sprintf(message_, "RtApiAsio: unable to probe driver while a stream is open."); error(RtError::DEBUG_WARNING); return; } - if ( !drivers.loadDriver( info->name ) ) { - sprintf(message, "RtAudio: ASIO error loading driver (%s).", info->name); + if ( !drivers.loadDriver( (char *)info->name.c_str() ) ) { + sprintf(message_, "RtApiAsio: error loading driver (%s).", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -3498,7 +4444,7 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) sprintf(details, "driver/hardware not present"); else sprintf(details, "unspecified"); - sprintf(message, "RtAudio: ASIO error (%s) initializing driver (%s).", details, info->name); + sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", details, info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -3508,7 +4454,7 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO error getting input/output channel count (%s).", info->name); + sprintf(message_, "RtApiAsio: error getting input/output channel count (%s).", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -3529,16 +4475,16 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) } // Determine the supported sample rates. - info->nSampleRates = 0; - for (int i=0; isampleRates.clear(); + for (unsigned int i=0; isampleRates[info->nSampleRates++] = SAMPLE_RATES[i]; + info->sampleRates.push_back( SAMPLE_RATES[i] ); } - if (info->nSampleRates == 0) { + if (info->sampleRates.size() == 0) { drivers.removeCurrentDriver(); - sprintf( message, "RtAudio: No supported sample rates found for ASIO driver (%s).", info->name ); + sprintf( message_, "RtApiAsio: No supported sample rates found for driver (%s).", info->name.c_str() ); error(RtError::DEBUG_WARNING); return; } @@ -3551,7 +4497,7 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO error getting driver (%s) channel information.", info->name); + sprintf(message_, "RtApiAsio: error getting driver (%s) channel information.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -3568,8 +4514,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) // Check that we have at least one supported format. if (info->nativeFormats == 0) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) data format not supported by RtAudio.", - info->name); + sprintf(message_, "RtApiAsio: driver (%s) data format not supported by RtAudio.", + info->name.c_str()); error(RtError::DEBUG_WARNING); return; } @@ -3580,12 +4526,12 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) void bufferSwitch(long index, ASIOBool processNow) { - RtAudio *object = (RtAudio *) asioCallbackInfo->object; + RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object; try { - object->callbackEvent( asioCallbackInfo->streamId, index, (void *)NULL, (void *)NULL ); + object->callbackEvent( index ); } catch (RtError &exception) { - fprintf(stderr, "\nCallback handler error (%s)!\n\n", exception.getMessage()); + fprintf(stderr, "\nRtApiAsio: callback handler error (%s)!\n\n", exception.getMessageString()); return; } @@ -3602,14 +4548,14 @@ void sampleRateChanged(ASIOSampleRate sRate) RtAudio *object = (RtAudio *) asioCallbackInfo->object; try { - object->stopStream( asioCallbackInfo->streamId ); + object->stopStream(); } catch (RtError &exception) { - fprintf(stderr, "\nRtAudio: sampleRateChanged() error (%s)!\n\n", exception.getMessage()); + fprintf(stderr, "\nRtApiAsio: sampleRateChanged() error (%s)!\n\n", exception.getMessageString()); return; } - fprintf(stderr, "\nRtAudio: ASIO driver reports sample rate changed to %d ... stream stopped!!!", (int) sRate); + fprintf(stderr, "\nRtApiAsio: driver reports sample rate changed to %d ... stream stopped!!!", (int) sRate); } long asioMessages(long selector, long value, void* message, double* opt) @@ -3635,7 +4581,7 @@ long asioMessages(long selector, long value, void* message, double* opt) // done by completely destruct is. I.e. ASIOStop(), // ASIODisposeBuffers(), Destruction Afterwards you initialize the // driver again. - fprintf(stderr, "\nRtAudio: ASIO driver reset requested!!!"); + fprintf(stderr, "\nRtApiAsio: driver reset requested!!!"); ret = 1L; break; case kAsioResyncRequest: @@ -3646,7 +4592,7 @@ long asioMessages(long selector, long value, void* message, double* opt) // which could lose data because the Mutex was held too long by // another thread. However a driver can issue it in other // situations, too. - fprintf(stderr, "\nRtAudio: ASIO driver resync requested!!!"); + fprintf(stderr, "\nRtApiAsio: driver resync requested!!!"); ret = 1L; break; case kAsioLatenciesChanged: @@ -3654,7 +4600,7 @@ long asioMessages(long selector, long value, void* message, double* opt) // latencies changed. Beware, it this does not mean that the // buffer sizes have changed! You might need to update internal // delay data. - fprintf(stderr, "\nRtAudio: ASIO driver latency may have changed!!!"); + fprintf(stderr, "\nRtApiAsio: driver latency may have changed!!!"); ret = 1L; break; case kAsioEngineVersion: @@ -3680,30 +4626,22 @@ long asioMessages(long selector, long value, void* message, double* opt) return ret; } -bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, - STREAM_MODE mode, int channels, - int sampleRate, RTAUDIO_FORMAT format, - int *bufferSize, int numberOfBuffers) +bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers) { - // Don't attempt to load another driver if a stream is already open. - if ( streams.size() > 0 ) { - sprintf(message, "RtAudio: unable to load ASIO driver while a stream is open."); - error(RtError::WARNING); - return FAILURE; - } - // For ASIO, a duplex stream MUST use the same driver. - if ( mode == INPUT && stream->mode == OUTPUT && stream->device[0] != device ) { - sprintf(message, "RtAudio: ASIO duplex stream must use the same device for input and output."); + if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) { + sprintf(message_, "RtApiAsio: duplex stream must use the same device for input and output."); error(RtError::WARNING); return FAILURE; } // Only load the driver once for duplex stream. ASIOError result; - if ( mode != INPUT || stream->mode != OUTPUT ) { - if ( !drivers.loadDriver( devices[device].name ) ) { - sprintf(message, "RtAudio: ASIO error loading driver (%s).", devices[device].name); + if ( mode != INPUT || stream_.mode != OUTPUT ) { + if ( !drivers.loadDriver( (char *)devices_[device].name.c_str() ) ) { + sprintf(message_, "RtApiAsio: error loading driver (%s).", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3719,7 +4657,7 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, sprintf(details, "driver/hardware not present"); else sprintf(details, "unspecified"); - sprintf(message, "RtAudio: ASIO error (%s) initializing driver (%s).", details, devices[device].name); + sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", details, devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3730,8 +4668,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO error getting input/output channel count (%s).", - devices[device].name); + sprintf(message_, "RtApiAsio: error getting input/output channel count (%s).", + devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3739,20 +4677,20 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, if ( ( mode == OUTPUT && channels > outputChannels) || ( mode == INPUT && channels > inputChannels) ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) does not support requested channel count (%d).", - devices[device].name, channels); + sprintf(message_, "RtApiAsio: driver (%s) does not support requested channel count (%d).", + devices_[device].name.c_str(), channels); error(RtError::DEBUG_WARNING); return FAILURE; } - stream->nDeviceChannels[mode] = channels; - stream->nUserChannels[mode] = channels; + stream_.nDeviceChannels[mode] = channels; + stream_.nUserChannels[mode] = channels; // Verify the sample rate is supported. result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) does not support requested sample rate (%d).", - devices[device].name, sampleRate); + sprintf(message_, "RtApiAsio: driver (%s) does not support requested sample rate (%d).", + devices_[device].name.c_str(), sampleRate); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3761,8 +4699,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) error setting sample rate (%d).", - devices[device].name, sampleRate); + sprintf(message_, "RtApiAsio: driver (%s) error setting sample rate (%d).", + devices_[device].name.c_str(), sampleRate); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3775,37 +4713,37 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) error getting data format.", - devices[device].name); + sprintf(message_, "RtApiAsio: driver (%s) error getting data format.", + devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } // Assuming WINDOWS host is always little-endian. - stream->doByteSwap[mode] = false; - stream->userFormat = format; - stream->deviceFormat[mode] = 0; + 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; + 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; + 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; + 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; + stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; + if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true; } - if ( stream->deviceFormat[mode] == 0 ) { + if ( stream_.deviceFormat[mode] == 0 ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) data format not supported by RtAudio.", - devices[device].name); + sprintf(message_, "RtApiAsio: driver (%s) data format not supported by RtAudio.", + devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3817,8 +4755,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) error getting buffer size.", - devices[device].name); + sprintf(message_, "RtApiAsio: driver (%s) error getting buffer size.", + devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3827,51 +4765,67 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, else if ( *bufferSize > maxSize ) *bufferSize = maxSize; else if ( granularity == -1 ) { // Make sure bufferSize is a power of two. - double power = log10( *bufferSize ) / log10( 2.0 ); - *bufferSize = pow( 2.0, floor(power+0.5) ); + double power = log10( (double) *bufferSize ) / log10( 2.0 ); + *bufferSize = (int) pow( 2.0, floor(power+0.5) ); if ( *bufferSize < minSize ) *bufferSize = minSize; else if ( *bufferSize > maxSize ) *bufferSize = maxSize; else *bufferSize = preferSize; } - if ( mode == INPUT && stream->mode == OUTPUT && stream->bufferSize != *bufferSize ) - cout << "possible input/output buffersize discrepancy" << endl; + if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) + std::cerr << "Possible input/output buffersize discrepancy!" << std::endl; - stream->bufferSize = *bufferSize; - stream->nBuffers = 2; + stream_.bufferSize = *bufferSize; + stream_.nBuffers = 2; // ASIO always uses deinterleaved channels. - stream->deInterleave[mode] = true; + stream_.deInterleave[mode] = true; + + // Allocate, if necessary, our AsioHandle structure for the stream. + AsioHandle *handle = (AsioHandle *) stream_.apiHandle; + if ( handle == 0 ) { + handle = (AsioHandle *) calloc(1, sizeof(AsioHandle)); + if ( handle == NULL ) { + drivers.removeCurrentDriver(); + sprintf(message_, "RtApiAsio: error allocating AsioHandle memory (%s).", + devices_[device].name.c_str()); + error(RtError::DEBUG_WARNING); + 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. - if ( mode == INPUT && stream->mode == OUTPUT ) { - free(stream->callbackInfo.buffers); - result = ASIODisposeBuffers(); - if ( result != ASE_OK ) { - drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) error disposing previously allocated buffers.", - devices[device].name); - error(RtError::DEBUG_WARNING); - return FAILURE; - } + if ( mode == INPUT && stream_.mode == OUTPUT ) { + ASIODisposeBuffers(); + if ( handle->bufferInfos ) free( handle->bufferInfos ); } // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. - int i, nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1]; - stream->callbackInfo.buffers = 0; - ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); - stream->callbackInfo.buffers = (void *) bufferInfos; - ASIOBufferInfo *infos = bufferInfos; - for ( i=0; inDeviceChannels[1]; i++, infos++ ) { - infos->isInput = ASIOTrue; + int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; + handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); + if (handle->bufferInfos == NULL) { + sprintf(message_, "RtApiAsio: error allocating bufferInfo memory (%s).", + devices_[device].name.c_str()); + goto error; + } + ASIOBufferInfo *infos; + infos = handle->bufferInfos; + for ( i=0; iisInput = ASIOFalse; infos->channelNum = i; infos->buffers[0] = infos->buffers[1] = 0; } - - for ( i=0; inDeviceChannels[0]; i++, infos++ ) { - infos->isInput = ASIOFalse; + for ( i=0; iisInput = ASIOTrue; infos->channelNum = i; infos->buffers[0] = infos->buffers[1] = 0; } @@ -3881,342 +4835,368 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; asioCallbacks.bufferSwitchTimeInfo = NULL; - result = ASIOCreateBuffers( bufferInfos, nChannels, stream->bufferSize, &asioCallbacks); + result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks); if ( result != ASE_OK ) { - drivers.removeCurrentDriver(); - sprintf(message, "RtAudio: ASIO driver (%s) error creating buffers.", - devices[device].name); - error(RtError::DEBUG_WARNING); - return FAILURE; + sprintf(message_, "RtApiAsio: driver (%s) error creating buffers.", + devices_[device].name.c_str()); + goto error; } // 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->nUserChannels[mode] > 1 && stream->deInterleave[mode]) - stream->doConvertBuffer[mode] = true; + 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_.nUserChannels[mode] > 1 && stream_.deInterleave[mode]) + stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers - if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) { + if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; - if (stream->nUserChannels[0] >= stream->nUserChannels[1]) - buffer_bytes = stream->nUserChannels[0]; + if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) + buffer_bytes = stream_.nUserChannels[0]; else - buffer_bytes = stream->nUserChannels[1]; + buffer_bytes = stream_.nUserChannels[1]; - buffer_bytes *= *bufferSize * formatBytes(stream->userFormat); - if (stream->userBuffer) free(stream->userBuffer); - stream->userBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->userBuffer == NULL) - goto memory_error; + buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); + if (stream_.userBuffer) free(stream_.userBuffer); + stream_.userBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.userBuffer == NULL) { + sprintf(message_, "RtApiAsio: error allocating user buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } } - if ( stream->doConvertBuffer[mode] ) { + if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) - buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT - buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]); - if ( stream->mode == OUTPUT && stream->deviceBuffer ) { - long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; - if (stream->deviceBuffer) free(stream->deviceBuffer); - stream->deviceBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->deviceBuffer == NULL) - goto memory_error; + if (stream_.deviceBuffer) free(stream_.deviceBuffer); + stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.deviceBuffer == NULL) { + sprintf(message_, "RtApiAsio: error allocating device buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } } } - stream->device[mode] = device; - stream->state = STREAM_STOPPED; - if ( stream->mode == OUTPUT && mode == INPUT ) + 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; + stream_.mode = DUPLEX; else - stream->mode = mode; - stream->sampleRate = sampleRate; - asioCallbackInfo = &stream->callbackInfo; - stream->callbackInfo.object = (void *) this; - stream->callbackInfo.waitTime = (unsigned long) (200.0 * stream->bufferSize / stream->sampleRate); + stream_.mode = mode; + stream_.sampleRate = sampleRate; + asioCallbackInfo = &stream_.callbackInfo; + stream_.callbackInfo.object = (void *) this; return SUCCESS; - memory_error: + error: ASIODisposeBuffers(); drivers.removeCurrentDriver(); - if (stream->callbackInfo.buffers) - free(stream->callbackInfo.buffers); - stream->callbackInfo.buffers = 0; - - if (stream->userBuffer) { - free(stream->userBuffer); - stream->userBuffer = 0; + if ( handle ) { + CloseHandle( handle->condition ); + if ( handle->bufferInfos ) + free( handle->bufferInfos ); + free( handle ); + stream_.apiHandle = 0; } - sprintf(message, "RtAudio: error allocating buffer memory (%s).", - devices[device].name); + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + error(RtError::WARNING); return FAILURE; } -void RtAudio :: cancelStreamCallback(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - if (stream->callbackInfo.usingCallback) { - - if (stream->state == STREAM_RUNNING) - stopStream( streamId ); - - MUTEX_LOCK(&stream->mutex); - - stream->callbackInfo.usingCallback = false; - stream->callbackInfo.userData = NULL; - stream->state = STREAM_STOPPED; - stream->callbackInfo.callback = NULL; - - MUTEX_UNLOCK(&stream->mutex); - } -} - -void RtAudio :: closeStream(int streamId) +void RtApiAsio :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own // streamId check. - if ( streams.find( streamId ) == streams.end() ) { - sprintf(message, "RtAudio: invalid stream identifier!"); + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtApiAsio::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId]; - - if (stream->state == STREAM_RUNNING) + if (stream_.state == STREAM_RUNNING) ASIOStop(); ASIODisposeBuffers(); - //ASIOExit(); drivers.removeCurrentDriver(); - DeleteCriticalSection(&stream->mutex); - - if (stream->callbackInfo.buffers) - free(stream->callbackInfo.buffers); - - if (stream->userBuffer) - free(stream->userBuffer); - - if (stream->deviceBuffer) - free(stream->deviceBuffer); - - free(stream); - streams.erase(streamId); -} - -void RtAudio :: startStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_RUNNING) { - MUTEX_UNLOCK(&stream->mutex); - return; + AsioHandle *handle = (AsioHandle *) stream_.apiHandle; + if ( handle ) { + CloseHandle( handle->condition ); + if ( handle->bufferInfos ) + free( handle->bufferInfos ); + free( handle ); + stream_.apiHandle = 0; } - stream->callbackInfo.blockTick = true; - stream->callbackInfo.stopStream = false; - stream->callbackInfo.streamId = streamId; - ASIOError result = ASIOStart(); - if ( result != ASE_OK ) { - sprintf(message, "RtAudio: ASIO error starting device (%s).", - devices[stream->device[0]].name); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); - } - stream->state = STREAM_RUNNING; - - MUTEX_UNLOCK(&stream->mutex); -} - -void RtAudio :: stopStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) { - MUTEX_UNLOCK(&stream->mutex); - return; + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; } - ASIOError result = ASIOStop(); - if ( result != ASE_OK ) { - sprintf(message, "RtAudio: ASIO error stopping device (%s).", - devices[stream->device[0]].name); - MUTEX_UNLOCK(&stream->mutex); - error(RtError::DRIVER_ERROR); + if (stream_.deviceBuffer) { + free(stream_.deviceBuffer); + stream_.deviceBuffer = 0; } - stream->state = STREAM_STOPPED; - MUTEX_UNLOCK(&stream->mutex); + stream_.mode = UNINITIALIZED; } -void RtAudio :: abortStream(int streamId) +void RtApiAsio :: setStreamCallback(RtAudioCallback callback, void *userData) { - stopStream( streamId ); -} + verifyStream(); -// I don't know how this function can be implemented. -int RtAudio :: streamWillBlock(int streamId) -{ - sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for ASIO."); - error(RtError::WARNING); - return 0; -} - -void RtAudio :: tickStream(int streamId) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - if (stream->state == STREAM_STOPPED) - return; - - if (stream->callbackInfo.usingCallback) { - sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!"); + if ( stream_.callbackInfo.usingCallback ) { + sprintf(message_, "RtApiAsio: A callback is already set for this stream!"); error(RtError::WARNING); return; } - // Block waiting here until the user data is processed in callbackEvent(). - while ( stream->callbackInfo.blockTick ) - Sleep(stream->callbackInfo.waitTime); - - MUTEX_LOCK(&stream->mutex); - - stream->callbackInfo.blockTick = true; - - MUTEX_UNLOCK(&stream->mutex); + stream_.callbackInfo.callback = (void *) callback; + stream_.callbackInfo.userData = userData; + stream_.callbackInfo.usingCallback = true; } -void RtAudio :: callbackEvent(int streamId, int bufferIndex, void *inData, void *outData) +void RtApiAsio :: cancelStreamCallback() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); - CALLBACK_INFO *info = asioCallbackInfo; - if ( !info->usingCallback ) { - // Block waiting here until we get new user data in tickStream(). - while ( !info->blockTick ) - Sleep(info->waitTime); + if (stream_.callbackInfo.usingCallback) { + + if (stream_.state == STREAM_RUNNING) + stopStream(); + + MUTEX_LOCK(&stream_.mutex); + + stream_.callbackInfo.usingCallback = false; + stream_.callbackInfo.userData = NULL; + stream_.state = STREAM_STOPPED; + stream_.callbackInfo.callback = NULL; + + MUTEX_UNLOCK(&stream_.mutex); } - else if ( info->stopStream ) { +} + +void RtApiAsio :: startStream() +{ + verifyStream(); + if (stream_.state == STREAM_RUNNING) return; + + MUTEX_LOCK(&stream_.mutex); + + ASIOError result = ASIOStart(); + if ( result != ASE_OK ) { + sprintf(message_, "RtApiAsio: error starting device (%s).", + devices_[stream_.device[0]].name.c_str()); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + AsioHandle *handle = (AsioHandle *) stream_.apiHandle; + handle->stopStream = false; + stream_.state = STREAM_RUNNING; + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiAsio :: stopStream() +{ + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; + + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); + + ASIOError result = ASIOStop(); + if ( result != ASE_OK ) { + sprintf(message_, "RtApiAsio: error stopping device (%s).", + devices_[stream_.device[0]].name.c_str()); + MUTEX_UNLOCK(&stream_.mutex); + error(RtError::DRIVER_ERROR); + } + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiAsio :: abortStream() +{ + stopStream(); +} + +void RtApiAsio :: tickStream() +{ + verifyStream(); + + if (stream_.state == STREAM_STOPPED) + return; + + if (stream_.callbackInfo.usingCallback) { + sprintf(message_, "RtApiAsio: tickStream() should not be used when a callback function is set!"); + error(RtError::WARNING); + return; + } + + AsioHandle *handle = (AsioHandle *) stream_.apiHandle; + + MUTEX_LOCK(&stream_.mutex); + + // Release the stream_mutex here and wait for the event + // to become signaled by the callback process. + MUTEX_UNLOCK(&stream_.mutex); + WaitForMultipleObjects(1, &handle->condition, FALSE, INFINITE); + ResetEvent( handle->condition ); +} + +void RtApiAsio :: callbackEvent(long bufferIndex) +{ + verifyStream(); + + if (stream_.state == STREAM_STOPPED) return; + + CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; + AsioHandle *handle = (AsioHandle *) stream_.apiHandle; + if ( info->usingCallback && handle->stopStream ) { // Check if the stream should be stopped (via the previous user // callback return value). We stop the stream here, rather than // after the function call, so that output data can first be // processed. - this->stopStream(asioCallbackInfo->streamId); + this->stopStream(); return; } - MUTEX_LOCK(&stream->mutex); + MUTEX_LOCK(&stream_.mutex); // Invoke user callback first, to get fresh output data. if ( info->usingCallback ) { - RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback; - if ( callback(stream->userBuffer, stream->bufferSize, info->userData) ) - info->stopStream = true; + RtAudioCallback callback = (RtAudioCallback) info->callback; + if ( callback(stream_.userBuffer, stream_.bufferSize, info->userData) ) + handle->stopStream = true; } - int nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1]; - int bufferBytes; - ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) info->buffers; - if ( stream->mode == OUTPUT || stream->mode == DUPLEX ) { + int bufferBytes, j; + int nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; + if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[0]); - if (stream->doConvertBuffer[0]) { + bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[0]); + if (stream_.doConvertBuffer[0]) { - convertStreamBuffer(stream, OUTPUT); - if ( stream->doByteSwap[0] ) - byteSwapBuffer(stream->deviceBuffer, - stream->bufferSize * stream->nDeviceChannels[0], - stream->deviceFormat[0]); + convertStreamBuffer(OUTPUT); + if ( stream_.doByteSwap[0] ) + byteSwapBuffer(stream_.deviceBuffer, + stream_.bufferSize * stream_.nDeviceChannels[0], + stream_.deviceFormat[0]); // Always de-interleave ASIO output data. - for ( int i=0; inDeviceChannels[0]; i++, bufferInfos++ ) { - memcpy(bufferInfos->buffers[bufferIndex], - &stream->deviceBuffer[i*bufferBytes], bufferBytes ); + j = 0; + for ( int i=0; ibufferInfos[i].isInput != ASIOTrue ) + memcpy(handle->bufferInfos[i].buffers[bufferIndex], + &stream_.deviceBuffer[j++*bufferBytes], bufferBytes ); } } else { // single channel only - if (stream->doByteSwap[0]) - byteSwapBuffer(stream->userBuffer, - stream->bufferSize * stream->nUserChannels[0], - stream->userFormat); + if (stream_.doByteSwap[0]) + byteSwapBuffer(stream_.userBuffer, + stream_.bufferSize * stream_.nUserChannels[0], + stream_.userFormat); - memcpy(bufferInfos->buffers[bufferIndex], stream->userBuffer, bufferBytes ); + for ( int i=0; ibufferInfos[i].isInput != ASIOTrue ) { + memcpy(handle->bufferInfos[i].buffers[bufferIndex], stream_.userBuffer, bufferBytes ); + break; + } + } } } - if ( stream->mode == INPUT || stream->mode == DUPLEX ) { + if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[1]); - if (stream->doConvertBuffer[1]) { + bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]); + if (stream_.doConvertBuffer[1]) { // Always interleave ASIO input data. - for ( int i=0; inDeviceChannels[1]; i++, bufferInfos++ ) - memcpy(&stream->deviceBuffer[i*bufferBytes], bufferInfos->buffers[bufferIndex], bufferBytes ); + j = 0; + for ( int i=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]); - convertStreamBuffer(stream, INPUT); + if ( stream_.doByteSwap[1] ) + byteSwapBuffer(stream_.deviceBuffer, + stream_.bufferSize * stream_.nDeviceChannels[1], + stream_.deviceFormat[1]); + convertStreamBuffer(INPUT); } else { // single channel only - memcpy(stream->userBuffer, bufferInfos->buffers[bufferIndex], bufferBytes ); + for ( int i=0; ibufferInfos[i].isInput == ASIOTrue ) { + memcpy(stream_.userBuffer, + handle->bufferInfos[i].buffers[bufferIndex], + bufferBytes ); + break; + } + } - if (stream->doByteSwap[1]) - byteSwapBuffer(stream->userBuffer, - stream->bufferSize * stream->nUserChannels[1], - stream->userFormat); + if (stream_.doByteSwap[1]) + byteSwapBuffer(stream_.userBuffer, + stream_.bufferSize * stream_.nUserChannels[1], + stream_.userFormat); } } if ( !info->usingCallback ) - info->blockTick = false; + SetEvent( handle->condition ); - MUTEX_UNLOCK(&stream->mutex); -} - -void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - stream->callbackInfo.callback = (void *) callback; - stream->callbackInfo.userData = userData; - stream->callbackInfo.usingCallback = true; + MUTEX_UNLOCK(&stream_.mutex); } //******************** End of __WINDOWS_ASIO__ *********************// +#endif -#elif defined(__WINDOWS_DS__) // Windows DirectSound API +#if defined(__WINDOWS_DS__) // Windows DirectSound API #include +// A structure to hold various information related to the DirectSound +// API implementation. +struct DsHandle { + void *object; + void *buffer; + UINT bufferPointer; +}; + // Declarations for utility functions, callbacks, and structures // specific to the DirectSound implementation. static bool CALLBACK deviceCountCallback(LPGUID lpguid, @@ -4241,6 +5221,8 @@ static bool CALLBACK deviceIdCallback(LPGUID lpguid, static char* getErrorString(int code); +extern "C" unsigned __stdcall callbackHandler(void *ptr); + struct enum_info { char name[64]; LPGUID id; @@ -4248,7 +5230,22 @@ struct enum_info { bool isValid; }; -int RtAudio :: getDefaultInputDevice(void) +RtApiDs :: RtApiDs() +{ + this->initialize(); + + if (nDevices_ <= 0) { + sprintf(message_, "RtApiDs: no Windows DirectSound audio devices found!"); + error(RtError::NO_DEVICES_FOUND); + } +} + +RtApiDs :: ~RtApiDs() +{ + if ( stream_.mode != UNINITIALIZED ) closeStream(); +} + +int RtApiDs :: getDefaultInputDevice(void) { enum_info info; info.name[0] = '\0'; @@ -4256,19 +5253,21 @@ int RtAudio :: getDefaultInputDevice(void) // Enumerate through devices to find the default output. HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Error performing default input device enumeration: %s.", + sprintf(message_, "RtApiDs: Error performing default input device enumeration: %s.", getErrorString(result)); error(RtError::WARNING); return 0; } - for ( int i=0; iname, 64 ); + strncpy( dsinfo.name, info->name.c_str(), 64 ); dsinfo.isValid = false; // Enumerate through input devices to find the id (if it exists). HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Error performing input device id enumeration: %s.", + sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.", getErrorString(result)); error(RtError::WARNING); return; @@ -4386,8 +5375,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) LPDIRECTSOUNDCAPTURE input; result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL ); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.", - info->name, getErrorString(result)); + sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.", + info->name.c_str(), getErrorString(result)); error(RtError::WARNING); goto playback_probe; } @@ -4397,8 +5386,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) result = input->GetCaps( &in_caps ); if ( FAILED(result) ) { input->Release(); - sprintf(message, "RtAudio: Could not get DirectSound capture capabilities (%s): %s.", - info->name, getErrorString(result)); + sprintf(message_, "RtApiDs: Could not get capture capabilities (%s): %s.", + info->name.c_str(), getErrorString(result)); error(RtError::WARNING); goto playback_probe; } @@ -4408,6 +5397,7 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) info->maxInputChannels = in_caps.dwChannels; // Get sample rate and format information. + info->sampleRates.clear(); if( in_caps.dwChannels == 2 ) { if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->nativeFormats |= RTAUDIO_SINT16; @@ -4417,14 +5407,14 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->nativeFormats |= RTAUDIO_SINT8; if ( info->nativeFormats & RTAUDIO_SINT16 ) { - if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->sampleRates[info->nSampleRates++] = 11025; - if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->sampleRates[info->nSampleRates++] = 22050; - if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->sampleRates[info->nSampleRates++] = 44100; + if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->sampleRates.push_back( 11025 ); + if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->sampleRates.push_back( 22050 ); + if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->sampleRates.push_back( 44100 ); } else if ( info->nativeFormats & RTAUDIO_SINT8 ) { - if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->sampleRates[info->nSampleRates++] = 11025; - if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->sampleRates[info->nSampleRates++] = 22050; - if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->sampleRates[info->nSampleRates++] = 44100; + if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->sampleRates.push_back( 11025 ); + if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->sampleRates.push_back( 22050 ); + if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->sampleRates.push_back( 44100 ); } } else if ( in_caps.dwChannels == 1 ) { @@ -4436,14 +5426,14 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->nativeFormats |= RTAUDIO_SINT8; if ( info->nativeFormats & RTAUDIO_SINT16 ) { - if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->sampleRates[info->nSampleRates++] = 11025; - if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->sampleRates[info->nSampleRates++] = 22050; - if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->sampleRates[info->nSampleRates++] = 44100; + if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->sampleRates.push_back( 11025 ); + if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->sampleRates.push_back( 22050 ); + if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->sampleRates.push_back( 44100 ); } else if ( info->nativeFormats & RTAUDIO_SINT8 ) { - if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->sampleRates[info->nSampleRates++] = 11025; - if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->sampleRates[info->nSampleRates++] = 22050; - if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->sampleRates[info->nSampleRates++] = 44100; + if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->sampleRates.push_back( 11025 ); + if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->sampleRates.push_back( 22050 ); + if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->sampleRates.push_back( 44100 ); } } else info->minInputChannels = 0; // technically, this would be an error @@ -4457,7 +5447,7 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) // Enumerate through output devices to find the id (if it exists). result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Error performing output device id enumeration: %s.", + sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.", getErrorString(result)); error(RtError::WARNING); return; @@ -4471,8 +5461,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) DSCAPS out_caps; result = DirectSoundCreate( dsinfo.id, &output, NULL ); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.", - info->name, getErrorString(result)); + sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.", + info->name.c_str(), getErrorString(result)); error(RtError::WARNING); goto check_parameters; } @@ -4481,8 +5471,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) result = output->GetCaps( &out_caps ); if ( FAILED(result) ) { output->Release(); - sprintf(message, "RtAudio: Could not get DirectSound playback capabilities (%s): %s.", - info->name, getErrorString(result)); + sprintf(message_, "RtApiDs: Could not get playback capabilities (%s): %s.", + info->name.c_str(), getErrorString(result)); error(RtError::WARNING); goto check_parameters; } @@ -4493,49 +5483,19 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) // Get sample rate information. Use capture device rate information // if it exists. - if ( info->nSampleRates == 0 ) { - info->sampleRates[0] = (int) out_caps.dwMinSecondarySampleRate; - info->sampleRates[1] = (int) out_caps.dwMaxSecondarySampleRate; - if ( out_caps.dwFlags & DSCAPS_CONTINUOUSRATE ) - info->nSampleRates = -1; - else if ( out_caps.dwMinSecondarySampleRate == out_caps.dwMaxSecondarySampleRate ) { - if ( out_caps.dwMinSecondarySampleRate == 0 ) { - // This is a bogus driver report ... fake the range and cross - // your fingers. - info->sampleRates[0] = 11025; - info->sampleRates[1] = 48000; - info->nSampleRates = -1; /* continuous range */ - sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using defaults (%s).", - info->name); - error(RtError::DEBUG_WARNING); - } - else { - info->nSampleRates = 1; - } - } - else if ( (out_caps.dwMinSecondarySampleRate < 1000.0) && - (out_caps.dwMaxSecondarySampleRate > 50000.0) ) { - // This is a bogus driver report ... support for only two - // distant rates. We'll assume this is a range. - info->nSampleRates = -1; - sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using range (%s).", - info->name); - error(RtError::WARNING); - } - else info->nSampleRates = 2; + if ( info->sampleRates.size() == 0 ) { + info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate ); + info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate ); } else { - // Check input rates against output rate range - for ( int i=info->nSampleRates-1; i>=0; i-- ) { - if ( info->sampleRates[i] <= out_caps.dwMaxSecondarySampleRate ) - break; - info->nSampleRates--; + // Check input rates against output rate range. + for ( unsigned int i=info->sampleRates.size()-1; i>=0; i-- ) { + if ( (unsigned int) info->sampleRates[i] > out_caps.dwMaxSecondarySampleRate ) + info->sampleRates.erase( info->sampleRates.begin() + i ); } - while ( info->sampleRates[0] < out_caps.dwMinSecondarySampleRate ) { - info->nSampleRates--; - for ( int i=0; inSampleRates; i++) - info->sampleRates[i] = info->sampleRates[i+1]; - if ( info->nSampleRates <= 0 ) break; + while ( info->sampleRates.size() > 0 && + ((unsigned int) info->sampleRates[0] < out_caps.dwMinSecondarySampleRate) ) { + info->sampleRates.erase( info->sampleRates.begin() ); } } @@ -4546,10 +5506,18 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) output->Release(); check_parameters: - if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 ) + if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 ) { + sprintf(message_, "RtApiDs: no reported input or output channels for device (%s).", + info->name.c_str()); + error(RtError::DEBUG_WARNING); return; - if ( info->nSampleRates == 0 || info->nativeFormats == 0 ) + } + if ( info->sampleRates.size() == 0 || info->nativeFormats == 0 ) { + sprintf(message_, "RtApiDs: no reported sample rates or data formats for device (%s).", + info->name.c_str()); + error(RtError::DEBUG_WARNING); return; + } // Determine duplex status. if (info->maxInputChannels < info->maxOutputChannels) @@ -4569,13 +5537,13 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) return; } -bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, - STREAM_MODE mode, int channels, - int sampleRate, RTAUDIO_FORMAT format, - int *bufferSize, int numberOfBuffers) +bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, + int *bufferSize, int numberOfBuffers) { HRESULT result; HWND hWnd = GetForegroundWindow(); + // 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 @@ -4603,23 +5571,23 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, waveFormat.nSamplesPerSec = (unsigned long) sampleRate; // Determine the data format. - if ( devices[device].nativeFormats ) { // 8-bit and/or 16-bit support + if ( devices_[device].nativeFormats ) { // 8-bit and/or 16-bit support if ( format == RTAUDIO_SINT8 ) { - if ( devices[device].nativeFormats & RTAUDIO_SINT8 ) + if ( devices_[device].nativeFormats & RTAUDIO_SINT8 ) waveFormat.wBitsPerSample = 8; else waveFormat.wBitsPerSample = 16; } else { - if ( devices[device].nativeFormats & RTAUDIO_SINT16 ) + if ( devices_[device].nativeFormats & RTAUDIO_SINT16 ) waveFormat.wBitsPerSample = 16; else waveFormat.wBitsPerSample = 8; } } else { - sprintf(message, "RtAudio: no reported data formats for DirectSound device (%s).", - devices[device].name); + sprintf(message_, "RtApiDs: no reported data formats for device (%s).", + devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -4628,24 +5596,29 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; enum_info dsinfo; - strncpy( dsinfo.name, devices[device].name, 64 ); + void *ohandle = 0, *bhandle = 0; + strncpy( dsinfo.name, devices_[device].name.c_str(), 64 ); dsinfo.isValid = false; if ( mode == OUTPUT ) { - if ( devices[device].maxOutputChannels < channels ) + if ( devices_[device].maxOutputChannels < channels ) { + sprintf(message_, "RtApiDs: requested channels (%d) > than supported (%d) by device (%s).", + channels, devices_[device].maxOutputChannels, devices_[device].name.c_str()); + error(RtError::DEBUG_WARNING); return FAILURE; + } // Enumerate through output devices to find the id (if it exists). result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Error performing output device id enumeration: %s.", + sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.", getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } if ( dsinfo.isValid == false ) { - sprintf(message, "RtAudio: DS output device (%s) id not found!", devices[device].name); + sprintf(message_, "RtApiDs: output device (%s) id not found!", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -4657,8 +5630,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = DirectSoundCreate( id, &object, NULL ); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.", - devices[device].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -4667,16 +5640,16 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = object->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to set DirectSound cooperative level (%s): %s.", - devices[device].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to set cooperative level (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } // Even though we will write to the secondary buffer, we need to - // access the primary buffer to set the correct output format. - // The default is 8-bit, 22 kHz! - // Setup the DS primary buffer description. + // access the primary buffer to set the correct output format + // (since the default is 8-bit, 22 kHz!). Setup the DS primary + // buffer description. ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC)); bufferDescription.dwSize = sizeof(DSBUFFERDESC); bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; @@ -4684,8 +5657,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to access DS primary buffer (%s): %s.", - devices[device].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to access primary buffer (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } @@ -4694,8 +5667,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = buffer->SetFormat(&waveFormat); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to set DS primary buffer format (%s): %s.", - devices[device].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to set primary buffer format (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } @@ -4720,8 +5693,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to create secondary DS buffer (%s): %s.", - devices[device].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to create secondary DS buffer (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } @@ -4737,8 +5710,9 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.", - devices[device].name, getErrorString(result)); + buffer->Release(); + sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } @@ -4750,33 +5724,34 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to unlock DS buffer(%s): %s.", - devices[device].name, getErrorString(result)); + buffer->Release(); + sprintf(message_, "RtApiDs: Unable to unlock buffer(%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } - stream->handle[0].object = (void *) object; - stream->handle[0].buffer = (void *) buffer; - stream->nDeviceChannels[0] = channels; + ohandle = (void *) object; + bhandle = (void *) buffer; + stream_.nDeviceChannels[0] = channels; } if ( mode == INPUT ) { - if ( devices[device].maxInputChannels < channels ) + if ( devices_[device].maxInputChannels < channels ) return FAILURE; // Enumerate through input devices to find the id (if it exists). result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Error performing input device id enumeration: %s.", + sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.", getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } if ( dsinfo.isValid == false ) { - sprintf(message, "RtAudio: DS input device (%s) id not found!", devices[device].name); + sprintf(message_, "RtAudioDS: input device (%s) id not found!", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } @@ -4788,8 +5763,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = DirectSoundCaptureCreate( id, &object, NULL ); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.", - devices[device].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } @@ -4807,8 +5782,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = object->CreateCaptureBuffer(&bufferDescription, &buffer, NULL); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to create DS capture buffer (%s): %s.", - devices[device].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to create capture buffer (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } @@ -4817,8 +5792,9 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.", - devices[device].name, getErrorString(result)); + buffer->Release(); + sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } @@ -4830,234 +5806,286 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { object->Release(); - sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.", - devices[device].name, getErrorString(result)); + buffer->Release(); + sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.", + devices_[device].name.c_str(), getErrorString(result)); error(RtError::WARNING); return FAILURE; } - stream->handle[1].object = (void *) object; - stream->handle[1].buffer = (void *) buffer; - stream->nDeviceChannels[1] = channels; + ohandle = (void *) object; + bhandle = (void *) buffer; + stream_.nDeviceChannels[1] = channels; } - stream->userFormat = format; + stream_.userFormat = format; if ( waveFormat.wBitsPerSample == 8 ) - stream->deviceFormat[mode] = RTAUDIO_SINT8; + stream_.deviceFormat[mode] = RTAUDIO_SINT8; else - stream->deviceFormat[mode] = RTAUDIO_SINT16; - stream->nUserChannels[mode] = channels; + stream_.deviceFormat[mode] = RTAUDIO_SINT16; + stream_.nUserChannels[mode] = channels; *bufferSize = buffer_size / (channels * nBuffers * waveFormat.wBitsPerSample / 8); - stream->bufferSize = *bufferSize; + stream_.bufferSize = *bufferSize; // 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; + stream_.doConvertBuffer[mode] = false; + if (stream_.userFormat != stream_.deviceFormat[mode]) + stream_.doConvertBuffer[mode] = true; + if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode]) + stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers - if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) { + if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; - if (stream->nUserChannels[0] >= stream->nUserChannels[1]) - buffer_bytes = stream->nUserChannels[0]; + if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) + buffer_bytes = stream_.nUserChannels[0]; else - buffer_bytes = stream->nUserChannels[1]; + buffer_bytes = stream_.nUserChannels[1]; - buffer_bytes *= *bufferSize * formatBytes(stream->userFormat); - if (stream->userBuffer) free(stream->userBuffer); - stream->userBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->userBuffer == NULL) - goto memory_error; + buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); + if (stream_.userBuffer) free(stream_.userBuffer); + stream_.userBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.userBuffer == NULL) { + sprintf(message_, "RtApiDs: error allocating user buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } } - if ( stream->doConvertBuffer[mode] ) { + if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) - buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT - buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]); - if ( stream->mode == OUTPUT && stream->deviceBuffer ) { - long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; - if (stream->deviceBuffer) free(stream->deviceBuffer); - stream->deviceBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->deviceBuffer == NULL) - goto memory_error; + if (stream_.deviceBuffer) free(stream_.deviceBuffer); + stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.deviceBuffer == NULL) { + sprintf(message_, "RtApiDs: error allocating device buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } } } - 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; + // Allocate our DsHandle structures for the stream. + DsHandle *handles; + if ( stream_.apiHandle == 0 ) { + handles = (DsHandle *) calloc(2, sizeof(DsHandle)); + if ( handles == NULL ) { + sprintf(message_, "RtApiDs: Error allocating DsHandle memory (%s).", + devices_[device].name.c_str()); + goto error; + } + handles[0].object = 0; + handles[1].object = 0; + stream_.apiHandle = (void *) handles; + } else - stream->mode = mode; - stream->nBuffers = nBuffers; - stream->sampleRate = sampleRate; + handles = (DsHandle *) stream_.apiHandle; + handles[mode].object = ohandle; + handles[mode].buffer = bhandle; + + 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; return SUCCESS; - memory_error: - if (stream->handle[0].object) { - LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object; - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer; - if (buffer) { - buffer->Release(); - stream->handle[0].buffer = NULL; + error: + if (handles) { + if (handles[0].object) { + LPDIRECTSOUND object = (LPDIRECTSOUND) handles[0].object; + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; + if (buffer) buffer->Release(); + object->Release(); } - object->Release(); - stream->handle[0].object = NULL; - } - if (stream->handle[1].object) { - LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object; - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer; - if (buffer) { - buffer->Release(); - stream->handle[1].buffer = NULL; + if (handles[1].object) { + LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handles[1].object; + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; + if (buffer) buffer->Release(); + object->Release(); } - object->Release(); - stream->handle[1].object = NULL; + free(handles); + stream_.apiHandle = 0; } - if (stream->userBuffer) { - free(stream->userBuffer); - stream->userBuffer = 0; + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; } - sprintf(message, "RtAudio: error allocating buffer memory (%s).", - devices[device].name); + error(RtError::WARNING); return FAILURE; } -void RtAudio :: cancelStreamCallback(int streamId) +void RtApiDs :: setStreamCallback(RtAudioCallback callback, void *userData) { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); - if (stream->callbackInfo.usingCallback) { - - if (stream->state == STREAM_RUNNING) - stopStream( streamId ); - - MUTEX_LOCK(&stream->mutex); - - stream->callbackInfo.usingCallback = false; - WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE ); - CloseHandle( (HANDLE)stream->callbackInfo.thread ); - stream->callbackInfo.thread = 0; - stream->callbackInfo.callback = NULL; - stream->callbackInfo.userData = NULL; - - MUTEX_UNLOCK(&stream->mutex); - } -} - -void RtAudio :: closeStream(int streamId) -{ - // We don't want an exception to be thrown here because this - // function is called by our class destructor. So, do our own - // streamId check. - if ( streams.find( streamId ) == streams.end() ) { - sprintf(message, "RtAudio: invalid stream identifier!"); + CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; + if ( info->usingCallback ) { + sprintf(message_, "RtApiDs: A callback is already set for this stream!"); error(RtError::WARNING); return; } - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId]; + info->callback = (void *) callback; + info->userData = userData; + info->usingCallback = true; + info->object = (void *) this; - if (stream->callbackInfo.usingCallback) { - stream->callbackInfo.usingCallback = false; - WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE ); - CloseHandle( (HANDLE)stream->callbackInfo.thread ); + unsigned thread_id; + info->thread = _beginthreadex(NULL, 0, &callbackHandler, + &stream_.callbackInfo, 0, &thread_id); + if (info->thread == 0) { + info->usingCallback = false; + sprintf(message_, "RtApiDs: error starting callback thread!"); + error(RtError::THREAD_ERROR); } - DeleteCriticalSection(&stream->mutex); - - if (stream->handle[0].object) { - LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object; - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer; - if (buffer) { - buffer->Stop(); - buffer->Release(); - } - object->Release(); - } - - if (stream->handle[1].object) { - LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object; - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer; - if (buffer) { - buffer->Stop(); - buffer->Release(); - } - object->Release(); - } - - if (stream->userBuffer) - free(stream->userBuffer); - - if (stream->deviceBuffer) - free(stream->deviceBuffer); - - free(stream); - streams.erase(streamId); + // When spawning multiple threads in quick succession, it appears to be + // necessary to wait a bit for each to initialize ... another windoism! + Sleep(1); } -void RtAudio :: startStream(int streamId) +void RtApiDs :: cancelStreamCallback() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); - MUTEX_LOCK(&stream->mutex); + if (stream_.callbackInfo.usingCallback) { - if (stream->state == STREAM_RUNNING) - goto unlock; + if (stream_.state == STREAM_RUNNING) + stopStream(); - HRESULT result; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer; - result = buffer->Play(0, 0, DSBPLAY_LOOPING ); - if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to start DS buffer (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); - error(RtError::DRIVER_ERROR); - } + MUTEX_LOCK(&stream_.mutex); + + stream_.callbackInfo.usingCallback = false; + WaitForSingleObject( (HANDLE)stream_.callbackInfo.thread, INFINITE ); + CloseHandle( (HANDLE)stream_.callbackInfo.thread ); + stream_.callbackInfo.thread = 0; + stream_.callbackInfo.callback = NULL; + stream_.callbackInfo.userData = NULL; + + MUTEX_UNLOCK(&stream_.mutex); } - - if (stream->mode == INPUT || stream->mode == DUPLEX) { - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer; - result = buffer->Start(DSCBSTART_LOOPING ); - if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to start DS capture buffer (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); - error(RtError::DRIVER_ERROR); - } - } - stream->state = STREAM_RUNNING; - - unlock: - MUTEX_UNLOCK(&stream->mutex); } -void RtAudio :: stopStream(int streamId) +void RtApiDs :: closeStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) { - MUTEX_UNLOCK(&stream->mutex); + // We don't want an exception to be thrown here because this + // function is called by our class destructor. So, do our own + // streamId check. + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtApiDs::closeStream(): no open stream to close!"); + error(RtError::WARNING); return; } + if (stream_.callbackInfo.usingCallback) { + stream_.callbackInfo.usingCallback = false; + WaitForSingleObject( (HANDLE)stream_.callbackInfo.thread, INFINITE ); + CloseHandle( (HANDLE)stream_.callbackInfo.thread ); + } + + DsHandle *handles = (DsHandle *) stream_.apiHandle; + if (handles) { + if (handles[0].object) { + LPDIRECTSOUND object = (LPDIRECTSOUND) handles[0].object; + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; + if (buffer) { + buffer->Stop(); + buffer->Release(); + } + object->Release(); + } + + if (handles[1].object) { + LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handles[1].object; + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; + if (buffer) { + buffer->Stop(); + buffer->Release(); + } + object->Release(); + } + free(handles); + stream_.apiHandle = 0; + } + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } + + if (stream_.deviceBuffer) { + free(stream_.deviceBuffer); + stream_.deviceBuffer = 0; + } + + stream_.mode = UNINITIALIZED; +} + +void RtApiDs :: startStream() +{ + verifyStream(); + if (stream_.state == STREAM_RUNNING) return; + + MUTEX_LOCK(&stream_.mutex); + + HRESULT result; + DsHandle *handles = (DsHandle *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; + result = buffer->Play(0, 0, DSBPLAY_LOOPING ); + if ( FAILED(result) ) { + sprintf(message_, "RtApiDs: Unable to start buffer (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); + error(RtError::DRIVER_ERROR); + } + } + + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; + result = buffer->Start(DSCBSTART_LOOPING ); + if ( FAILED(result) ) { + sprintf(message_, "RtApiDs: Unable to start capture buffer (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); + error(RtError::DRIVER_ERROR); + } + } + stream_.state = STREAM_RUNNING; + + MUTEX_UNLOCK(&stream_.mutex); +} + +void RtApiDs :: stopStream() +{ + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; + + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); + // There is no specific DirectSound API call to "drain" a buffer // before stopping. We can hack this for playback by writing zeroes // for another bufferSize * nBuffers frames. For capture, the @@ -5069,24 +6097,25 @@ void RtAudio :: stopStream(int streamId) LPVOID buffer2 = NULL; DWORD bufferSize1 = 0; DWORD bufferSize2 = 0; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { + DsHandle *handles = (DsHandle *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { DWORD currentPos, safePos; - long buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0]; - buffer_bytes *= formatBytes(stream->deviceFormat[0]); + long buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0]; + buffer_bytes *= formatBytes(stream_.deviceFormat[0]); - LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer; - UINT nextWritePos = stream->handle[0].bufferPointer; - dsBufferSize = buffer_bytes * stream->nBuffers; + LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; + UINT nextWritePos = handles[0].bufferPointer; + dsBufferSize = buffer_bytes * stream_.nBuffers; // Write zeroes for nBuffer counts. - for (int i=0; inBuffers; i++) { + for (int i=0; iGetCurrentPosition(¤tPos, &safePos); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5095,16 +6124,16 @@ void RtAudio :: stopStream(int streamId) // Check whether the entire write region is behind the play pointer. while ( currentPos < endWrite ) { - float millis = (endWrite - currentPos) * 900.0; - millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate); + double millis = (endWrite - currentPos) * 900.0; + millis /= ( formatBytes(stream_.deviceFormat[0]) * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); // Wake up, find out where we are now result = dsBuffer->GetCurrentPosition( ¤tPos, &safePos ); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset @@ -5114,8 +6143,8 @@ void RtAudio :: stopStream(int streamId) result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to lock buffer during playback (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5126,39 +6155,39 @@ void RtAudio :: stopStream(int streamId) // Update our buffer offset and unlock sound buffer dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to unlock buffer during playback (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize; - stream->handle[0].bufferPointer = nextWritePos; + handles[0].bufferPointer = nextWritePos; } // If we play again, start at the beginning of the buffer. - stream->handle[0].bufferPointer = 0; + handles[0].bufferPointer = 0; } - if (stream->mode == INPUT || stream->mode == DUPLEX) { - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer; + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; buffer1 = NULL; bufferSize1 = 0; result = buffer->Stop(); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to stop capture buffer (%s): %s", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } - dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1]; - dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers; + dsBufferSize = stream_.bufferSize * stream_.nDeviceChannels[1]; + dsBufferSize *= formatBytes(stream_.deviceFormat[1]) * stream_.nBuffers; // 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, dsBufferSize, &buffer1, &bufferSize1, NULL, NULL, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5168,50 +6197,51 @@ void RtAudio :: stopStream(int streamId) // Unlock the DS buffer result = buffer->Unlock(buffer1, bufferSize1, NULL, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // If we start recording again, we must begin at beginning of buffer. - stream->handle[1].bufferPointer = 0; + handles[1].bufferPointer = 0; } - stream->state = STREAM_STOPPED; - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); } -void RtAudio :: abortStream(int streamId) +void RtApiDs :: abortStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; - MUTEX_LOCK(&stream->mutex); - - if (stream->state == STREAM_STOPPED) - goto unlock; + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); HRESULT result; long dsBufferSize; LPVOID audioPtr; DWORD dataLen; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer; + DsHandle *handles = (DsHandle *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; result = buffer->Stop(); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to stop DS buffer (%s): %s", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to stop buffer (%s): %s", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } - dsBufferSize = stream->bufferSize * stream->nDeviceChannels[0]; - dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers; + dsBufferSize = stream_.bufferSize * stream_.nDeviceChannels[0]; + dsBufferSize *= formatBytes(stream_.deviceFormat[0]) * stream_.nBuffers; // 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, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5221,36 +6251,36 @@ void RtAudio :: abortStream(int streamId) // Unlock the DS buffer result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to unlock DS buffer (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to unlock buffer (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // If we start playing again, we must begin at beginning of buffer. - stream->handle[0].bufferPointer = 0; + handles[0].bufferPointer = 0; } - if (stream->mode == INPUT || stream->mode == DUPLEX) { - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer; + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; audioPtr = NULL; dataLen = 0; result = buffer->Stop(); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to stop capture buffer (%s): %s", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } - dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1]; - dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers; + dsBufferSize = stream_.bufferSize * stream_.nDeviceChannels[1]; + dsBufferSize *= formatBytes(stream_.deviceFormat[1]) * stream_.nBuffers; // 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, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5260,112 +6290,108 @@ void RtAudio :: abortStream(int streamId) // Unlock the DS buffer result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // If we start recording again, we must begin at beginning of buffer. - stream->handle[1].bufferPointer = 0; + handles[1].bufferPointer = 0; } - stream->state = STREAM_STOPPED; - unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); } -int RtAudio :: streamWillBlock(int streamId) +int RtApiDs :: streamWillBlock() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_STOPPED) return 0; - MUTEX_LOCK(&stream->mutex); + MUTEX_LOCK(&stream_.mutex); int channels; int frames = 0; - if (stream->state == STREAM_STOPPED) - goto unlock; - HRESULT result; DWORD currentPos, safePos; channels = 1; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { + DsHandle *handles = (DsHandle *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { - LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer; - UINT nextWritePos = stream->handle[0].bufferPointer; - channels = stream->nDeviceChannels[0]; - DWORD dsBufferSize = stream->bufferSize * channels; - dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers; + LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; + UINT nextWritePos = handles[0].bufferPointer; + channels = stream_.nDeviceChannels[0]; + DWORD dsBufferSize = stream_.bufferSize * channels; + dsBufferSize *= formatBytes(stream_.deviceFormat[0]) * stream_.nBuffers; // Find out where the read and "safe write" pointers are. result = dsBuffer->GetCurrentPosition(¤tPos, &safePos); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset frames = currentPos - nextWritePos; - frames /= channels * formatBytes(stream->deviceFormat[0]); + frames /= channels * formatBytes(stream_.deviceFormat[0]); } - if (stream->mode == INPUT || stream->mode == DUPLEX) { + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { - LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer; - UINT nextReadPos = stream->handle[1].bufferPointer; - channels = stream->nDeviceChannels[1]; - DWORD dsBufferSize = stream->bufferSize * channels; - dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers; + LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; + UINT nextReadPos = handles[1].bufferPointer; + channels = stream_.nDeviceChannels[1]; + DWORD dsBufferSize = stream_.bufferSize * channels; + dsBufferSize *= formatBytes(stream_.deviceFormat[1]) * stream_.nBuffers; // Find out where the write and "safe read" pointers are. result = dsBuffer->GetCurrentPosition(¤tPos, &safePos); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset - if (stream->mode == DUPLEX ) { + if (stream_.mode == DUPLEX ) { // Take largest value of the two. int temp = safePos - nextReadPos; - temp /= channels * formatBytes(stream->deviceFormat[1]); + temp /= channels * formatBytes(stream_.deviceFormat[1]); frames = ( temp > frames ) ? temp : frames; } else { frames = safePos - nextReadPos; - frames /= channels * formatBytes(stream->deviceFormat[1]); + frames /= channels * formatBytes(stream_.deviceFormat[1]); } } - frames = stream->bufferSize - frames; + frames = stream_.bufferSize - frames; if (frames < 0) frames = 0; - unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); return frames; } -void RtAudio :: tickStream(int streamId) +void RtApiDs :: tickStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); int stopStream = 0; - if (stream->state == STREAM_STOPPED) { - if (stream->callbackInfo.usingCallback) Sleep(50); // sleep 50 milliseconds + if (stream_.state == STREAM_STOPPED) { + if (stream_.callbackInfo.usingCallback) Sleep(50); // sleep 50 milliseconds return; } - else if (stream->callbackInfo.usingCallback) { - RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback; - stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData); + else if (stream_.callbackInfo.usingCallback) { + RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; + stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData); } - MUTEX_LOCK(&stream->mutex); + MUTEX_LOCK(&stream_.mutex); // The state might change while waiting on a mutex. - if (stream->state == STREAM_STOPPED) { - MUTEX_UNLOCK(&stream->mutex); + if (stream_.state == STREAM_STOPPED) { + MUTEX_UNLOCK(&stream_.mutex); return; } @@ -5377,32 +6403,33 @@ void RtAudio :: tickStream(int streamId) DWORD bufferSize2 = 0; char *buffer; long buffer_bytes; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { + DsHandle *handles = (DsHandle *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { // Setup parameters and do buffer conversion if necessary. - if (stream->doConvertBuffer[0]) { - convertStreamBuffer(stream, OUTPUT); - buffer = stream->deviceBuffer; - buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0]; - buffer_bytes *= formatBytes(stream->deviceFormat[0]); + if (stream_.doConvertBuffer[0]) { + convertStreamBuffer(OUTPUT); + buffer = stream_.deviceBuffer; + buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0]; + buffer_bytes *= formatBytes(stream_.deviceFormat[0]); } else { - buffer = stream->userBuffer; - buffer_bytes = stream->bufferSize * stream->nUserChannels[0]; - buffer_bytes *= formatBytes(stream->userFormat); + buffer = stream_.userBuffer; + buffer_bytes = stream_.bufferSize * stream_.nUserChannels[0]; + buffer_bytes *= formatBytes(stream_.userFormat); } // No byte swapping necessary in DirectSound implementation. - LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer; - UINT nextWritePos = stream->handle[0].bufferPointer; - DWORD dsBufferSize = buffer_bytes * stream->nBuffers; + LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; + UINT nextWritePos = handles[0].bufferPointer; + DWORD dsBufferSize = buffer_bytes * stream_.nBuffers; // Find out where the read and "safe write" pointers are. result = dsBuffer->GetCurrentPosition(¤tPos, &safePos); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5420,16 +6447,16 @@ void RtAudio :: tickStream(int streamId) // A "fudgefactor" less than 1 is used because it was found // that sleeping too long was MUCH worse than sleeping for // several shorter periods. - float millis = (endWrite - currentPos) * 900.0; - millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate); + double millis = (endWrite - currentPos) * 900.0; + millis /= ( formatBytes(stream_.deviceFormat[0]) * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); // Wake up, find out where we are now result = dsBuffer->GetCurrentPosition( ¤tPos, &safePos ); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset @@ -5439,8 +6466,8 @@ void RtAudio :: tickStream(int streamId) result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to lock buffer during playback (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5451,37 +6478,37 @@ void RtAudio :: tickStream(int streamId) // Update our buffer offset and unlock sound buffer dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.", - devices[stream->device[0]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to unlock buffer during playback (%s): %s.", + devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize; - stream->handle[0].bufferPointer = nextWritePos; + handles[0].bufferPointer = nextWritePos; } - if (stream->mode == INPUT || stream->mode == DUPLEX) { + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { // Setup parameters. - if (stream->doConvertBuffer[1]) { - buffer = stream->deviceBuffer; - buffer_bytes = stream->bufferSize * stream->nDeviceChannels[1]; - buffer_bytes *= formatBytes(stream->deviceFormat[1]); + if (stream_.doConvertBuffer[1]) { + buffer = stream_.deviceBuffer; + buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[1]; + buffer_bytes *= formatBytes(stream_.deviceFormat[1]); } else { - buffer = stream->userBuffer; - buffer_bytes = stream->bufferSize * stream->nUserChannels[1]; - buffer_bytes *= formatBytes(stream->userFormat); + buffer = stream_.userBuffer; + buffer_bytes = stream_.bufferSize * stream_.nUserChannels[1]; + buffer_bytes *= formatBytes(stream_.userFormat); } - LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer; - UINT nextReadPos = stream->handle[1].bufferPointer; - DWORD dsBufferSize = buffer_bytes * stream->nBuffers; + LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; + UINT nextReadPos = handles[1].bufferPointer; + DWORD dsBufferSize = buffer_bytes * stream_.nBuffers; // Find out where the write and "safe read" pointers are. result = dsBuffer->GetCurrentPosition(¤tPos, &safePos); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5491,16 +6518,16 @@ void RtAudio :: tickStream(int streamId) // Check whether the entire write region is behind the play pointer. while ( safePos < endRead ) { // See comments for playback. - float millis = (endRead - safePos) * 900.0; - millis /= ( formatBytes(stream->deviceFormat[1]) * stream->sampleRate); + double millis = (endRead - safePos) * 900.0; + millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); // Wake up, find out where we are now result = dsBuffer->GetCurrentPosition( ¤tPos, &safePos ); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5511,8 +6538,8 @@ void RtAudio :: tickStream(int streamId) result = dsBuffer->Lock (nextReadPos, buffer_bytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to lock DS buffer during capture (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to lock buffer during capture (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } @@ -5524,23 +6551,23 @@ void RtAudio :: tickStream(int streamId) nextReadPos = (nextReadPos + bufferSize1 + bufferSize2) % dsBufferSize; dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2); if ( FAILED(result) ) { - sprintf(message, "RtAudio: Unable to unlock DS buffer during capture (%s): %s.", - devices[stream->device[1]].name, getErrorString(result)); + sprintf(message_, "RtApiDs: Unable to unlock buffer during capture (%s): %s.", + devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } - stream->handle[1].bufferPointer = nextReadPos; + handles[1].bufferPointer = nextReadPos; // No byte swapping necessary in DirectSound implementation. // Do buffer conversion if necessary. - if (stream->doConvertBuffer[1]) - convertStreamBuffer(stream, INPUT); + if (stream_.doConvertBuffer[1]) + convertStreamBuffer(INPUT); } - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); - if (stream->callbackInfo.usingCallback && stopStream) - this->stopStream(streamId); + if (stream_.callbackInfo.usingCallback && stopStream) + this->stopStream(); } // Definitions for utility functions and callbacks @@ -5548,18 +6575,17 @@ void RtAudio :: tickStream(int streamId) extern "C" unsigned __stdcall callbackHandler(void *ptr) { - CALLBACK_INFO *info = (CALLBACK_INFO *) ptr; - RtAudio *object = (RtAudio *) info->object; - int stream = info->streamId; + CallbackInfo *info = (CallbackInfo *) ptr; + RtApiDs *object = (RtApiDs *) info->object; bool *usingCallback = &info->usingCallback; while ( *usingCallback ) { try { - object->tickStream(stream); + object->tickStream(); } catch (RtError &exception) { - fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n", - exception.getMessage()); + fprintf(stderr, "\nRtApiDs: callback thread error (%s) ... closing thread.\n\n", + exception.getMessageString()); break; } } @@ -5568,37 +6594,6 @@ extern "C" unsigned __stdcall callbackHandler(void *ptr) return 0; } -void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData) -{ - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); - - CALLBACK_INFO *info = (CALLBACK_INFO *) &stream->callbackInfo; - if ( info->usingCallback ) { - sprintf(message, "RtAudio: A callback is already set for this stream!"); - error(RtError::WARNING); - return; - } - - info->callback = (void *) callback; - info->userData = userData; - info->usingCallback = true; - info->object = (void *) this; - info->streamId = streamId; - - unsigned thread_id; - info->thread = _beginthreadex(NULL, 0, &callbackHandler, - &stream->callbackInfo, 0, &thread_id); - if (info->thread == 0) { - info->usingCallback = false; - sprintf(message, "RtAudio: error starting callback thread!"); - error(RtError::THREAD_ERROR); - } - - // When spawning multiple threads in quick succession, it appears to be - // necessary to wait a bit for each to initialize ... another windoism! - Sleep(1); -} - static bool CALLBACK deviceCountCallback(LPGUID lpguid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, @@ -5742,133 +6737,174 @@ static char* getErrorString(int code) } //******************** End of __WINDOWS_DS__ *********************// +#endif -#elif defined(__IRIX_AL__) // SGI's AL API for IRIX +#if defined(__IRIX_AL__) // SGI's AL API for IRIX +#include #include #include -void RtAudio :: initialize(void) +extern "C" void *callbackHandler(void * ptr); + +RtApiAl :: RtApiAl() +{ + this->initialize(); + + if (nDevices_ <= 0) { + sprintf(message_, "RtApiAl: no Irix AL audio devices found!"); + error(RtError::NO_DEVICES_FOUND); + } +} + +RtApiAl :: ~RtApiAl() +{ + // The subclass destructor gets called before the base class + // destructor, so close any existing streams before deallocating + // apiDeviceId memory. + if ( stream_.mode != UNINITIALIZED ) closeStream(); + + // Free our allocated apiDeviceId memory. + long *id; + for ( unsigned int i=0; iid[0]; + long *id = (long *) info->apiDeviceId; + resource = id[0]; if (resource > 0) { // Probe output device parameters. result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0); if (result < 0) { - sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.", - info->name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.", + info->name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); } else { @@ -5878,33 +6914,31 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) result = alGetParamInfo(resource, AL_RATE, &pinfo); if (result < 0) { - sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.", - info->name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.", + info->name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); } else { - info->nSampleRates = 0; - for (i=0; i= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) { - info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i]; - info->nSampleRates++; - } + info->sampleRates.clear(); + for (unsigned int k=0; k= pinfo.min.i && SAMPLE_RATES[k] <= pinfo.max.i ) + info->sampleRates.push_back( SAMPLE_RATES[k] ); } } // The AL library supports all our formats, except 24-bit and 32-bit ints. - info->nativeFormats = (RTAUDIO_FORMAT) 51; + info->nativeFormats = (RtAudioFormat) 51; } // Now get input resource ID if it exists. - resource = info->id[1]; + resource = id[1]; if (resource > 0) { // Probe input device parameters. result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0); if (result < 0) { - sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.", - info->name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.", + info->name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); } else { @@ -5914,8 +6948,8 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) result = alGetParamInfo(resource, AL_RATE, &pinfo); if (result < 0) { - sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.", - info->name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.", + info->name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); } else { @@ -5923,22 +6957,20 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) // overwrite the rates determined for the output device. Since // the input device is most likely to be more limited than the // output device, this is ok. - info->nSampleRates = 0; - for (i=0; i= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) { - info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i]; - info->nSampleRates++; - } + info->sampleRates.clear(); + for (unsigned int k=0; k= pinfo.min.i && SAMPLE_RATES[k] <= pinfo.max.i ) + info->sampleRates.push_back( SAMPLE_RATES[k] ); } } // The AL library supports all our formats, except 24-bit and 32-bit ints. - info->nativeFormats = (RTAUDIO_FORMAT) 51; + info->nativeFormats = (RtAudioFormat) 51; } if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 ) return; - if ( info->nSampleRates == 0 ) + if ( info->sampleRates.size() == 0 ) return; // Determine duplex status. @@ -5959,20 +6991,21 @@ void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info) return; } -bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, - STREAM_MODE mode, int channels, - int sampleRate, RTAUDIO_FORMAT format, +bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, + int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers) { - int result, resource, nBuffers; + int result, nBuffers; + long resource; ALconfig al_config; ALport port; ALpv pvs[2]; + long *id = (long *) devices_[device].apiDeviceId; // Get a new ALconfig structure. al_config = alNewConfig(); if ( !al_config ) { - sprintf(message,"RtAudio: can't get AL config: %s.", + sprintf(message_,"RtApiAl: can't get AL config: %s.", alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; @@ -5981,7 +7014,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, // Set the channels. result = alSetChannels(al_config, channels); if ( result < 0 ) { - sprintf(message,"RtAudio: can't set %d channels in AL config: %s.", + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: can't set %d channels in AL config: %s.", channels, alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; @@ -6002,7 +7036,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, buffer_size = alGetQueueSize(al_config); result = alSetQueueSize(al_config, buffer_size); if ( result < 0 ) { - sprintf(message,"RtAudio: can't set buffer size (%ld) in AL config: %s.", + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: can't set buffer size (%ld) in AL config: %s.", buffer_size, alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; @@ -6011,8 +7046,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, } // Set the data format. - stream->userFormat = format; - stream->deviceFormat[mode] = format; + stream_.userFormat = format; + stream_.deviceFormat[mode] = format; if (format == RTAUDIO_SINT8) { result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP); result = alSetWidth(al_config, AL_SAMPLE_8); @@ -6026,13 +7061,13 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, // The AL library uses the lower 3 bytes, so we'll need to do our // own conversion. result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT); - stream->deviceFormat[mode] = RTAUDIO_FLOAT32; + stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; } else if (format == RTAUDIO_SINT32) { // The AL library doesn't seem to support the 32-bit integer // format, so we'll need to do our own conversion. result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT); - stream->deviceFormat[mode] = RTAUDIO_FLOAT32; + stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; } else if (format == RTAUDIO_FLOAT32) result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT); @@ -6040,7 +7075,8 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = alSetSampFmt(al_config, AL_SAMPFMT_DOUBLE); if ( result == -1 ) { - sprintf(message,"RtAudio: AL error setting sample format in AL config: %s.", + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: error setting sample format in AL config: %s.", alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; @@ -6052,19 +7088,21 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, if (device == 0) resource = AL_DEFAULT_OUTPUT; else - resource = devices[device].id[0]; + resource = id[0]; result = alSetDevice(al_config, resource); if ( result == -1 ) { - sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.", - devices[device].name, alGetErrorString(oserror())); + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.", + devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; } // Open the port. - port = alOpenPort("RtAudio Output Port", "w", al_config); + port = alOpenPort("RtApiAl Output Port", "w", al_config); if( !port ) { - sprintf(message,"RtAudio: AL error opening output port: %s.", + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: error opening output port: %s.", alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; @@ -6078,8 +7116,9 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = alSetParams(resource, pvs, 2); if ( result < 0 ) { alClosePort(port); - sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.", - sampleRate, devices[device].name, alGetErrorString(oserror())); + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: error setting sample rate (%d) for device (%s): %s.", + sampleRate, devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; } @@ -6090,19 +7129,21 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, if (device == 0) resource = AL_DEFAULT_INPUT; else - resource = devices[device].id[1]; + resource = id[1]; result = alSetDevice(al_config, resource); if ( result == -1 ) { - sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.", - devices[device].name, alGetErrorString(oserror())); + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.", + devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; } // Open the port. - port = alOpenPort("RtAudio Output Port", "r", al_config); + port = alOpenPort("RtApiAl Input Port", "r", al_config); if( !port ) { - sprintf(message,"RtAudio: AL error opening input port: %s.", + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: error opening input port: %s.", alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; @@ -6116,8 +7157,9 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, result = alSetParams(resource, pvs, 2); if ( result < 0 ) { alClosePort(port); - sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.", - sampleRate, devices[device].name, alGetErrorString(oserror())); + alFreeConfig(al_config); + sprintf(message_,"RtApiAl: error setting sample rate (%d) for device (%s): %s.", + sampleRate, devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::WARNING); return FAILURE; } @@ -6125,324 +7167,407 @@ bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream, alFreeConfig(al_config); - stream->nUserChannels[mode] = channels; - stream->nDeviceChannels[mode] = channels; + stream_.nUserChannels[mode] = channels; + stream_.nDeviceChannels[mode] = channels; - // Set handle and flags for buffer conversion - stream->handle[mode] = port; - stream->doConvertBuffer[mode] = false; - if (stream->userFormat != stream->deviceFormat[mode]) - stream->doConvertBuffer[mode] = true; + // Save stream handle. + ALport *handle = (ALport *) stream_.apiHandle; + if ( handle == 0 ) { + handle = (ALport *) calloc(2, sizeof(ALport)); + if ( handle == NULL ) { + sprintf(message_, "RtApiAl: Irix Al error allocating handle memory (%s).", + devices_[device].name.c_str()); + goto error; + } + stream_.apiHandle = (void *) handle; + handle[0] = 0; + handle[1] = 0; + } + handle[mode] = port; + + // Set flags for buffer conversion + stream_.doConvertBuffer[mode] = false; + if (stream_.userFormat != stream_.deviceFormat[mode]) + stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers - if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) { + if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; - if (stream->nUserChannels[0] >= stream->nUserChannels[1]) - buffer_bytes = stream->nUserChannels[0]; + if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) + buffer_bytes = stream_.nUserChannels[0]; else - buffer_bytes = stream->nUserChannels[1]; + buffer_bytes = stream_.nUserChannels[1]; - buffer_bytes *= *bufferSize * formatBytes(stream->userFormat); - if (stream->userBuffer) free(stream->userBuffer); - stream->userBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->userBuffer == NULL) - goto memory_error; + buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); + if (stream_.userBuffer) free(stream_.userBuffer); + stream_.userBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.userBuffer == NULL) { + sprintf(message_, "RtApiAl: error allocating user buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } } - if ( stream->doConvertBuffer[mode] ) { + if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) - buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT - buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]); - if ( stream->mode == OUTPUT && stream->deviceBuffer ) { - long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]); + buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; - if (stream->deviceBuffer) free(stream->deviceBuffer); - stream->deviceBuffer = (char *) calloc(buffer_bytes, 1); - if (stream->deviceBuffer == NULL) - goto memory_error; + if (stream_.deviceBuffer) free(stream_.deviceBuffer); + stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); + if (stream_.deviceBuffer == NULL) { + sprintf(message_, "RtApiAl: error allocating device buffer memory (%s).", + devices_[device].name.c_str()); + goto error; + } } } - stream->device[mode] = device; - stream->state = STREAM_STOPPED; - if ( stream->mode == OUTPUT && mode == INPUT ) + 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; + stream_.mode = DUPLEX; else - stream->mode = mode; - stream->nBuffers = nBuffers; - stream->bufferSize = *bufferSize; - stream->sampleRate = sampleRate; + stream_.mode = mode; + stream_.nBuffers = nBuffers; + stream_.bufferSize = *bufferSize; + stream_.sampleRate = sampleRate; return SUCCESS; - memory_error: - if (stream->handle[0]) { - alClosePort(stream->handle[0]); - stream->handle[0] = 0; + error: + if (handle) { + if (handle[0]) + alClosePort(handle[0]); + if (handle[1]) + alClosePort(handle[1]); + free(handle); + stream_.apiHandle = 0; } - if (stream->handle[1]) { - alClosePort(stream->handle[1]); - stream->handle[1] = 0; + + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; } - if (stream->userBuffer) { - free(stream->userBuffer); - stream->userBuffer = 0; - } - sprintf(message, "RtAudio: ALSA error allocating buffer memory for device (%s).", - devices[device].name); + error(RtError::WARNING); return FAILURE; } -void RtAudio :: closeStream(int streamId) +void RtApiAl :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own // streamId check. - if ( streams.find( streamId ) == streams.end() ) { - sprintf(message, "RtAudio: invalid stream identifier!"); + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtApiAl::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId]; - - if (stream->callbackInfo.usingCallback) { - pthread_cancel(stream->callbackInfo.thread); - pthread_join(stream->callbackInfo.thread, NULL); + ALport *handle = (ALport *) stream_.apiHandle; + if (stream_.state == STREAM_RUNNING) { + int buffer_size = stream_.bufferSize * stream_.nBuffers; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) + alDiscardFrames(handle[0], buffer_size); + if (stream_.mode == INPUT || stream_.mode == DUPLEX) + alDiscardFrames(handle[1], buffer_size); + stream_.state = STREAM_STOPPED; } - pthread_mutex_destroy(&stream->mutex); + if (stream_.callbackInfo.usingCallback) { + stream_.callbackInfo.usingCallback = false; + pthread_join(stream_.callbackInfo.thread, NULL); + } - if (stream->handle[0]) - alClosePort(stream->handle[0]); + if (handle) { + if (handle[0]) alClosePort(handle[0]); + if (handle[1]) alClosePort(handle[1]); + free(handle); + stream_.apiHandle = 0; + } - if (stream->handle[1]) - alClosePort(stream->handle[1]); + if (stream_.userBuffer) { + free(stream_.userBuffer); + stream_.userBuffer = 0; + } - if (stream->userBuffer) - free(stream->userBuffer); + if (stream_.deviceBuffer) { + free(stream_.deviceBuffer); + stream_.deviceBuffer = 0; + } - if (stream->deviceBuffer) - free(stream->deviceBuffer); - - free(stream); - streams.erase(streamId); + stream_.mode = UNINITIALIZED; } -void RtAudio :: startStream(int streamId) +void RtApiAl :: startStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_RUNNING) return; - if (stream->state == STREAM_RUNNING) - return; + MUTEX_LOCK(&stream_.mutex); // The AL port is ready as soon as it is opened. - stream->state = STREAM_RUNNING; + stream_.state = STREAM_RUNNING; + + MUTEX_UNLOCK(&stream_.mutex); } -void RtAudio :: stopStream(int streamId) +void RtApiAl :: stopStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; - MUTEX_LOCK(&stream->mutex); + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); - if (stream->state == STREAM_STOPPED) - goto unlock; + int result, buffer_size = stream_.bufferSize * stream_.nBuffers; + ALport *handle = (ALport *) stream_.apiHandle; - int result; - int buffer_size = stream->bufferSize * stream->nBuffers; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) + alZeroFrames(handle[0], buffer_size); - if (stream->mode == OUTPUT || stream->mode == DUPLEX) - alZeroFrames(stream->handle[0], buffer_size); - - if (stream->mode == INPUT || stream->mode == DUPLEX) { - result = alDiscardFrames(stream->handle[1], buffer_size); + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + result = alDiscardFrames(handle[1], buffer_size); if (result == -1) { - sprintf(message, "RtAudio: AL error draining stream device (%s): %s.", - devices[stream->device[1]].name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error draining stream device (%s): %s.", + devices_[stream_.device[1]].name.c_str(), alGetErrorString(oserror())); error(RtError::DRIVER_ERROR); } } - stream->state = STREAM_STOPPED; - unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); } -void RtAudio :: abortStream(int streamId) +void RtApiAl :: abortStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); + if (stream_.state == STREAM_STOPPED) return; - MUTEX_LOCK(&stream->mutex); + // Change the state before the lock to improve shutdown response + // when using a callback. + stream_.state = STREAM_STOPPED; + MUTEX_LOCK(&stream_.mutex); - if (stream->state == STREAM_STOPPED) - goto unlock; + ALport *handle = (ALport *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - - int buffer_size = stream->bufferSize * stream->nBuffers; - int result = alDiscardFrames(stream->handle[0], buffer_size); + int buffer_size = stream_.bufferSize * stream_.nBuffers; + int result = alDiscardFrames(handle[0], buffer_size); if (result == -1) { - sprintf(message, "RtAudio: AL error aborting stream device (%s): %s.", - devices[stream->device[0]].name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error aborting stream device (%s): %s.", + devices_[stream_.device[0]].name.c_str(), alGetErrorString(oserror())); error(RtError::DRIVER_ERROR); } } // There is no clear action to take on the input stream, since the // port will continue to run in any event. - stream->state = STREAM_STOPPED; - unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); } -int RtAudio :: streamWillBlock(int streamId) +int RtApiAl :: streamWillBlock() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); - MUTEX_LOCK(&stream->mutex); + if (stream_.state == STREAM_STOPPED) return 0; + + MUTEX_LOCK(&stream_.mutex); int frames = 0; - if (stream->state == STREAM_STOPPED) - goto unlock; - int err = 0; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { - err = alGetFillable(stream->handle[0]); + ALport *handle = (ALport *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + err = alGetFillable(handle[0]); if (err < 0) { - sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.", - devices[stream->device[0]].name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error getting available frames for stream (%s): %s.", + devices_[stream_.device[0]].name.c_str(), alGetErrorString(oserror())); error(RtError::DRIVER_ERROR); } } frames = err; - if (stream->mode == INPUT || stream->mode == DUPLEX) { - err = alGetFilled(stream->handle[1]); + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + err = alGetFilled(handle[1]); if (err < 0) { - sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.", - devices[stream->device[1]].name, alGetErrorString(oserror())); + sprintf(message_, "RtApiAl: error getting available frames for stream (%s): %s.", + devices_[stream_.device[1]].name.c_str(), alGetErrorString(oserror())); error(RtError::DRIVER_ERROR); } if (frames > err) frames = err; } - frames = stream->bufferSize - frames; + frames = stream_.bufferSize - frames; if (frames < 0) frames = 0; - unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); return frames; } -void RtAudio :: tickStream(int streamId) +void RtApiAl :: tickStream() { - RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId); + verifyStream(); int stopStream = 0; - if (stream->state == STREAM_STOPPED) { - if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds + if (stream_.state == STREAM_STOPPED) { + if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds return; } - else if (stream->callbackInfo.usingCallback) { - RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback; - stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData); + else if (stream_.callbackInfo.usingCallback) { + RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; + stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData); } - MUTEX_LOCK(&stream->mutex); + MUTEX_LOCK(&stream_.mutex); // The state might change while waiting on a mutex. - if (stream->state == STREAM_STOPPED) + if (stream_.state == STREAM_STOPPED) goto unlock; char *buffer; int channels; - RTAUDIO_FORMAT format; - if (stream->mode == OUTPUT || stream->mode == DUPLEX) { + RtAudioFormat format; + ALport *handle = (ALport *) stream_.apiHandle; + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { // Setup parameters and do buffer conversion if necessary. - if (stream->doConvertBuffer[0]) { - convertStreamBuffer(stream, OUTPUT); - buffer = stream->deviceBuffer; - channels = stream->nDeviceChannels[0]; - format = stream->deviceFormat[0]; + if (stream_.doConvertBuffer[0]) { + convertStreamBuffer(OUTPUT); + buffer = stream_.deviceBuffer; + channels = stream_.nDeviceChannels[0]; + format = stream_.deviceFormat[0]; } else { - buffer = stream->userBuffer; - channels = stream->nUserChannels[0]; - format = stream->userFormat; + buffer = stream_.userBuffer; + channels = stream_.nUserChannels[0]; + format = stream_.userFormat; } // Do byte swapping if necessary. - if (stream->doByteSwap[0]) - byteSwapBuffer(buffer, stream->bufferSize * channels, format); + if (stream_.doByteSwap[0]) + byteSwapBuffer(buffer, stream_.bufferSize * channels, format); // Write interleaved samples to device. - alWriteFrames(stream->handle[0], buffer, stream->bufferSize); + alWriteFrames(handle[0], buffer, stream_.bufferSize); } - if (stream->mode == INPUT || stream->mode == DUPLEX) { + if (stream_.mode == INPUT || stream_.mode == DUPLEX) { // Setup parameters. - if (stream->doConvertBuffer[1]) { - buffer = stream->deviceBuffer; - channels = stream->nDeviceChannels[1]; - format = stream->deviceFormat[1]; + if (stream_.doConvertBuffer[1]) { + buffer = stream_.deviceBuffer; + channels = stream_.nDeviceChannels[1]; + format = stream_.deviceFormat[1]; } else { - buffer = stream->userBuffer; - channels = stream->nUserChannels[1]; - format = stream->userFormat; + buffer = stream_.userBuffer; + channels = stream_.nUserChannels[1]; + format = stream_.userFormat; } // Read interleaved samples from device. - alReadFrames(stream->handle[1], buffer, stream->bufferSize); + alReadFrames(handle[1], buffer, stream_.bufferSize); // Do byte swapping if necessary. - if (stream->doByteSwap[1]) - byteSwapBuffer(buffer, stream->bufferSize * channels, format); + if (stream_.doByteSwap[1]) + byteSwapBuffer(buffer, stream_.bufferSize * channels, format); // Do buffer conversion if necessary. - if (stream->doConvertBuffer[1]) - convertStreamBuffer(stream, INPUT); + if (stream_.doConvertBuffer[1]) + convertStreamBuffer(INPUT); } unlock: - MUTEX_UNLOCK(&stream->mutex); + MUTEX_UNLOCK(&stream_.mutex); - if (stream->callbackInfo.usingCallback && stopStream) - this->stopStream(streamId); + if (stream_.callbackInfo.usingCallback && stopStream) + this->stopStream(); +} + +void RtApiAl :: setStreamCallback(RtAudioCallback callback, void *userData) +{ + verifyStream(); + + CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; + if ( info->usingCallback ) { + sprintf(message_, "RtApiAl: A callback is already set for this stream!"); + error(RtError::WARNING); + return; + } + + info->callback = (void *) callback; + info->userData = userData; + info->usingCallback = true; + info->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); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + + int err = pthread_create(&info->thread, &attr, callbackHandler, &stream_.callbackInfo); + pthread_attr_destroy(&attr); + if (err) { + info->usingCallback = false; + sprintf(message_, "RtApiAl: error starting callback thread!"); + error(RtError::THREAD_ERROR); + } +} + +void RtApiAl :: cancelStreamCallback() +{ + verifyStream(); + + if (stream_.callbackInfo.usingCallback) { + + if (stream_.state == STREAM_RUNNING) + stopStream(); + + MUTEX_LOCK(&stream_.mutex); + + stream_.callbackInfo.usingCallback = false; + pthread_join(stream_.callbackInfo.thread, NULL); + stream_.callbackInfo.thread = 0; + stream_.callbackInfo.callback = NULL; + stream_.callbackInfo.userData = NULL; + + MUTEX_UNLOCK(&stream_.mutex); + } } extern "C" void *callbackHandler(void *ptr) { - CALLBACK_INFO *info = (CALLBACK_INFO *) ptr; - RtAudio *object = (RtAudio *) info->object; - int stream = info->streamId; + CallbackInfo *info = (CallbackInfo *) ptr; + RtApiAl *object = (RtApiAl *) info->object; bool *usingCallback = &info->usingCallback; while ( *usingCallback ) { - pthread_testcancel(); try { - object->tickStream(stream); + object->tickStream(); } catch (RtError &exception) { - fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n", - exception.getMessage()); + fprintf(stderr, "\nRtApiAl: callback thread error (%s) ... closing thread.\n\n", + exception.getMessageString()); break; } } @@ -6451,46 +7576,44 @@ extern "C" void *callbackHandler(void *ptr) } //******************** End of __IRIX_AL__ *********************// - #endif // *************************************************** // // -// Private common (OS-independent) RtAudio methods. +// Protected common (OS-independent) RtAudio methods. // // *************************************************** // // This method can be modified to control the behavior of error // message reporting and throwing. -void RtAudio :: error(RtError::TYPE type) +void RtApi :: error(RtError::Type type) { if (type == RtError::WARNING) { - fprintf(stderr, "\n%s\n\n", message); + fprintf(stderr, "\n%s\n\n", message_); } else if (type == RtError::DEBUG_WARNING) { #if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\n%s\n\n", message); + fprintf(stderr, "\n%s\n\n", message_); #endif } else { - fprintf(stderr, "\n%s\n\n", message); - throw RtError(message, type); +#if defined(__RTAUDIO_DEBUG__) + fprintf(stderr, "\n%s\n\n", message_); +#endif + throw RtError(std::string(message_), type); } } -void *RtAudio :: verifyStream(int streamId) +void RtApi :: verifyStream() { - // Verify the stream key. - if ( streams.find( streamId ) == streams.end() ) { - sprintf(message, "RtAudio: invalid stream identifier!"); + if ( stream_.mode == UNINITIALIZED ) { + sprintf(message_, "RtAudio: a stream was not previously opened!"); error(RtError::INVALID_STREAM); } - - return streams[streamId]; } -void RtAudio :: clearDeviceInfo(RTAUDIO_DEVICE *info) +void RtApi :: clearDeviceInfo(RtApiDevice *info) { // Don't clear the name or DEVICE_ID fields here ... they are // typically set prior to a call of this function. @@ -6502,13 +7625,30 @@ void RtAudio :: clearDeviceInfo(RTAUDIO_DEVICE *info) info->minInputChannels = 0; info->minDuplexChannels = 0; info->hasDuplexSupport = false; - info->nSampleRates = 0; - for (int i=0; isampleRates[i] = 0; + info->sampleRates.clear(); info->nativeFormats = 0; } -int RtAudio :: formatBytes(RTAUDIO_FORMAT format) +void RtApi :: clearStreamInfo() +{ + stream_.mode = UNINITIALIZED; + stream_.state = STREAM_STOPPED; + stream_.sampleRate = 0; + stream_.bufferSize = 0; + stream_.nBuffers = 0; + stream_.userFormat = 0; + for ( int i=0; i<2; i++ ) { + stream_.device[i] = 0; + stream_.doConvertBuffer[i] = false; + stream_.deInterleave[i] = false; + stream_.doByteSwap[i] = false; + stream_.nUserChannels[i] = 0; + stream_.nDeviceChannels[i] = 0; + stream_.deviceFormat[i] = 0; + } +} + +int RtApi :: formatBytes(RtAudioFormat format) { if (format == RTAUDIO_SINT16) return 2; @@ -6520,42 +7660,42 @@ int RtAudio :: formatBytes(RTAUDIO_FORMAT format) else if (format == RTAUDIO_SINT8) return 1; - sprintf(message,"RtAudio: undefined format in formatBytes()."); + sprintf(message_,"RtApi: undefined format in formatBytes()."); error(RtError::WARNING); return 0; } -void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode) +void RtApi :: convertStreamBuffer( StreamMode mode ) { // This method does format conversion, input/output channel compensation, and // data interleaving/deinterleaving. 24-bit integers are assumed to occupy // the upper three bytes of a 32-bit integer. int j, jump_in, jump_out, channels; - RTAUDIO_FORMAT format_in, format_out; + RtAudioFormat format_in, format_out; char *input, *output; if (mode == INPUT) { // convert device to user buffer - input = stream->deviceBuffer; - output = stream->userBuffer; - jump_in = stream->nDeviceChannels[1]; - jump_out = stream->nUserChannels[1]; - format_in = stream->deviceFormat[1]; - format_out = stream->userFormat; + input = stream_.deviceBuffer; + output = stream_.userBuffer; + jump_in = stream_.nDeviceChannels[1]; + jump_out = stream_.nUserChannels[1]; + format_in = stream_.deviceFormat[1]; + format_out = stream_.userFormat; } else { // convert user to device buffer - input = stream->userBuffer; - output = stream->deviceBuffer; - jump_in = stream->nUserChannels[0]; - jump_out = stream->nDeviceChannels[0]; - format_in = stream->userFormat; - format_out = stream->deviceFormat[0]; + input = stream_.userBuffer; + output = stream_.deviceBuffer; + jump_in = stream_.nUserChannels[0]; + jump_out = stream_.nDeviceChannels[0]; + format_in = stream_.userFormat; + format_out = stream_.deviceFormat[0]; // clear our device buffer when in/out duplex device channels are different - if ( stream->mode == DUPLEX && - stream->nDeviceChannels[0] != stream->nDeviceChannels[1] ) - memset(output, 0, stream->bufferSize * jump_out * formatBytes(format_out)); + if ( stream_.mode == DUPLEX && + stream_.nDeviceChannels[0] != stream_.nDeviceChannels[1] ) + memset(output, 0, stream_.bufferSize * jump_out * formatBytes(format_out)); } channels = (jump_in < jump_out) ? jump_in : jump_out; @@ -6563,17 +7703,17 @@ void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode) // Set up the interleave/deinterleave offsets std::vector offset_in(channels); std::vector offset_out(channels); - if (mode == INPUT && stream->deInterleave[1]) { + if (mode == INPUT && stream_.deInterleave[1]) { for (int k=0; kbufferSize; + offset_in[k] = k * stream_.bufferSize; offset_out[k] = k; jump_in = 1; } } - else if (mode == OUTPUT && stream->deInterleave[0]) { + else if (mode == OUTPUT && stream_.deInterleave[0]) { for (int k=0; kbufferSize; + offset_out[k] = k * stream_.bufferSize; jump_out = 1; } } @@ -6585,15 +7725,15 @@ void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode) } if (format_out == RTAUDIO_FLOAT64) { - FLOAT64 scale; - FLOAT64 *out = (FLOAT64 *)output; + Float64 scale; + Float64 *out = (Float64 *)output; if (format_in == RTAUDIO_SINT8) { signed char *in = (signed char *)input; scale = 1.0 / 128.0; - for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + Float32 *in = (Float32 *)input; + for (int i=0; ibufferSize; i++) { + Float64 *in = (Float64 *)input; + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + Float32 *in = (Float32 *)input; + for (int i=0; ibufferSize; i++) { + Float64 *in = (Float64 *)input; + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + Int16 *in = (Int16 *)input; + for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; ibufferSize; i++) { + Float32 *in = (Float32 *)input; + for (int i=0; ibufferSize; i++) { + Float64 *in = (Float64 *)input; + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + Int16 *in = (Int16 *)input; + for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; ibufferSize; i++) { + Float32 *in = (Float32 *)input; + for (int i=0; ibufferSize; i++) { + Float64 *in = (Float64 *)input; + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + Int16 *in = (Int16 *)input; + for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; i> 16) & 0x0000ffff); + out[offset_out[j]] = (Int16) ((in[offset_in[j]] >> 16) & 0x0000ffff); } in += jump_in; out += jump_out; } } else if (format_in == RTAUDIO_SINT32) { - INT32 *in = (INT32 *)input; - for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; i> 16) & 0x0000ffff); + out[offset_out[j]] = (Int16) ((in[offset_in[j]] >> 16) & 0x0000ffff); } in += jump_in; out += jump_out; } } else if (format_in == RTAUDIO_FLOAT32) { - FLOAT32 *in = (FLOAT32 *)input; - for (int i=0; ibufferSize; i++) { + Float32 *in = (Float32 *)input; + for (int i=0; ibufferSize; i++) { + Float64 *in = (Float64 *)input; + for (int i=0; ibufferSize; i++) { + for (int i=0; ibufferSize; i++) { + Int16 *in = (Int16 *)input; + for (int i=0; i> 8) & 0x00ff); } @@ -6953,8 +8093,8 @@ void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode) } } else if (format_in == RTAUDIO_SINT24) { - INT32 *in = (INT32 *)input; - for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; i> 24) & 0x000000ff); } @@ -6963,8 +8103,8 @@ void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode) } } else if (format_in == RTAUDIO_SINT32) { - INT32 *in = (INT32 *)input; - for (int i=0; ibufferSize; i++) { + Int32 *in = (Int32 *)input; + for (int i=0; i> 24) & 0x000000ff); } @@ -6973,8 +8113,8 @@ void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode) } } else if (format_in == RTAUDIO_FLOAT32) { - FLOAT32 *in = (FLOAT32 *)input; - for (int i=0; ibufferSize; i++) { + Float32 *in = (Float32 *)input; + for (int i=0; ibufferSize; i++) { + Float64 *in = (Float64 *)input; + for (int i=0; igetStreamBuffer(stream); + audio_ = new RtAudio(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } - lastOutput = (MY_FLOAT *) new MY_FLOAT[channels]; - for (unsigned int i=0; iopenStream(device, channels_, device, channels_, format, + (int)sampleRate, &bufferSize_, nBuffers); + data_ = (MY_FLOAT *) audio_->getStreamBuffer(); + } + catch (RtError &error) { + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); + } + + lastOutput_ = (MY_FLOAT *) new MY_FLOAT[channels_]; + for (unsigned int i=0; istopStream(stream); - delete audio; - delete [] lastOutput; - data = 0; // RtAudio deletes the buffer itself. + if ( !stopped_ ) + audio_->stopStream(); + delete audio_; + delete [] lastOutput_; + data_ = 0; // RtAudio deletes the buffer itself. } void RtDuplex :: start() { - if ( stopped ) { - audio->startStream(stream); - stopped = false; + if ( stopped_ ) { + audio_->startStream(); + stopped_ = false; } } void RtDuplex :: stop() { - if ( !stopped ) { - audio->stopStream(stream); - stopped = true; + if ( !stopped_ ) { + audio_->stopStream(); + stopped_ = true; } } MY_FLOAT RtDuplex :: lastOut(void) const { - if ( channels == 1 ) - return *lastOutput; + if ( channels_ == 1 ) + return *lastOutput_; MY_FLOAT output = 0.0; - for (unsigned int i=0; itickStream(stream); + audio_->tickStream(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } } - unsigned long temp = counter * channels; - for (unsigned int i=0; i= (long) bufferSize) - counter = 0; + counter_++; + if (counter_ >= (long) bufferSize_) + counter_ = 0; return lastOut(); } @@ -117,36 +127,36 @@ MY_FLOAT *RtDuplex :: tick(MY_FLOAT *vector, unsigned int vectorSize) const MY_FLOAT *RtDuplex :: lastFrame() const { - return lastOutput; + return lastOutput_; } MY_FLOAT *RtDuplex :: tickFrame(MY_FLOAT *frameVector, unsigned int frames) { - if ( stopped ) + if ( stopped_ ) start(); unsigned int i; unsigned long temp; for (unsigned int j=0; jtickStream(stream); + audio_->tickStream(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } } - temp = counter * channels; - for (i=0; i= (long) bufferSize) - counter = 0; + counter_++; + if (counter_ >= (long) bufferSize_) + counter_ = 0; } return frameVector; diff --git a/src/RtMidi.cpp b/src/RtMidi.cpp index de0dde2..b2369db 100644 --- a/src/RtMidi.cpp +++ b/src/RtMidi.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #define MIDI_BUFFER_SIZE 256 int writeIndex; @@ -328,7 +327,7 @@ int RtMidi :: nextMessage() } -#elif ( defined(__LINUX_OSS__) || defined(__LINUX_ALSA__) ) +#elif ( defined(__LINUX_OSS__) || defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) #include #include @@ -341,6 +340,11 @@ int RtMidi :: nextMessage() #include int midi_in; +#elif ( defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) + +#include +snd_rawmidi_t *midi_in = 0; + #elif defined(__LINUX_OSS__) #include @@ -350,11 +354,6 @@ int midi_in; #include int midi_in; -#else // __LINUX_ALSA__ - -#include -snd_rawmidi_t *midi_in = 0; - #endif typedef unsigned char byte; @@ -389,7 +388,12 @@ void *midiInputThread(void *) for (;;) { -#if defined(__LINUX_OSS__) || defined(__MIDIATOR__) +#if ( defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) + + if ((n = snd_rawmidi_read(midi_in, &newByte, 1)) == -1) + fprintf(stderr, "RtMidi: Error reading ALSA raw MIDI data from device!\n"); + +#elif defined(__LINUX_OSS__) || defined(__MIDIATOR__) // Normally, you should check the return value of this read() call. // A return of -1 usually indicates an error. However, for OSS @@ -397,11 +401,6 @@ void *midiInputThread(void *) n = read(midi_in, &newByte, 1); -#else // ALSA_API - - if ((n = snd_rawmidi_read(midi_in, &newByte, 1)) == -1) - fprintf(stderr, "RtMidi: Error reading ALSA raw MIDI data from device!\n"); - #endif while (n > 0) { @@ -556,85 +555,7 @@ RtMidi :: RtMidi(int device) } } -#elif defined(__LINUX_OSS__) // normal OSS setup - -#define MAX_DEVICES 8 -#define MIDI_NAME "/dev/midi" - -RtMidi :: RtMidi(int device) -{ - char msg[256]; - char name[16]; - char deviceNames[MAX_DEVICES][16]; - midi_in = 0; - - // /dev/midi should be a link to the default midi device under OSS - strcpy(name, MIDI_NAME); - - // The OSS API doesn't really give us a means for probing the - // capabilities of devices. Thus, we'll just pursue a brute - // force method of opening devices until we either find something - // that doesn't complain or we have to give up. We'll start with - // the default device, then try /dev/midi00, /dev/midi01, etc... - int i, nDevices = 0; - for ( i=0; i 0) sprintf(name, "%s%d%d", MIDI_NAME, 0, i-1); - midi_in = open(name, O_RDONLY | O_NONBLOCK, 0); - if ( midi_in != -1 ) { - strncpy( deviceNames[nDevices++], name, 16 ); - close( midi_in ); - } - else if ( errno == EBUSY ) - fprintf(stderr,"RtMidi: MIDI device (%s) is busy and cannot be opened.\n", name); - } - - if (nDevices == 0) { - sprintf(msg, "RtMidi: no OSS MIDI cards reported available."); - handleError(msg, StkError::MIDI_SYSTEM); - } - - // Check device argument and print list if necessary. - int deveyes; - if ( device >= 0 && device < nDevices ) - deveyes = device; - else if ( nDevices == 1 ) - deveyes = 0; - else { - // Invalid device argument ... print list. - printf("\n"); - for ( i=0; i= nDevices ) { - printf("\nType a MIDI device number from above: "); - fgets(choice, 16, stdin); - deveyes = atoi(choice); - } - printf("\n"); - } - - midi_in = open(deviceNames[deveyes], O_RDONLY, 0); - if ( midi_in == -1) { - sprintf(msg, "RtMidi: Unable to open OSS device (%s) for MIDI input!", - deviceNames[deveyes]); - handleError(msg, StkError::MIDI_SYSTEM); - } - - // Set up the circular buffer for the MIDI input messages - midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; - readIndex = 0; - writeIndex = 0; - - int result = pthread_create(&midi_input_thread, NULL, midiInputThread, NULL); - if (result) { - sprintf(msg, "RtMidi: unable to create MIDI input thread."); - handleError(msg, StkError::PROCESS_THREAD); - } -} - -#else // ALSA_API +#elif ( defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) #define MAX_DEVICES 8 @@ -730,6 +651,84 @@ RtMidi :: RtMidi(int device) } } +#elif defined(__LINUX_OSS__) // normal OSS setup + +#define MAX_DEVICES 8 +#define MIDI_NAME "/dev/midi" + +RtMidi :: RtMidi(int device) +{ + char msg[256]; + char name[16]; + char deviceNames[MAX_DEVICES][16]; + midi_in = 0; + + // /dev/midi should be a link to the default midi device under OSS + strcpy(name, MIDI_NAME); + + // The OSS API doesn't really give us a means for probing the + // capabilities of devices. Thus, we'll just pursue a brute + // force method of opening devices until we either find something + // that doesn't complain or we have to give up. We'll start with + // the default device, then try /dev/midi00, /dev/midi01, etc... + int i, nDevices = 0; + for ( i=0; i 0) sprintf(name, "%s%d%d", MIDI_NAME, 0, i-1); + midi_in = open(name, O_RDONLY | O_NONBLOCK, 0); + if ( midi_in != -1 ) { + strncpy( deviceNames[nDevices++], name, 16 ); + close( midi_in ); + } + else if ( errno == EBUSY ) + fprintf(stderr,"RtMidi: MIDI device (%s) is busy and cannot be opened.\n", name); + } + + if (nDevices == 0) { + sprintf(msg, "RtMidi: no OSS MIDI cards reported available."); + handleError(msg, StkError::MIDI_SYSTEM); + } + + // Check device argument and print list if necessary. + int deveyes; + if ( device >= 0 && device < nDevices ) + deveyes = device; + else if ( nDevices == 1 ) + deveyes = 0; + else { + // Invalid device argument ... print list. + printf("\n"); + for ( i=0; i= nDevices ) { + printf("\nType a MIDI device number from above: "); + fgets(choice, 16, stdin); + deveyes = atoi(choice); + } + printf("\n"); + } + + midi_in = open(deviceNames[deveyes], O_RDONLY, 0); + if ( midi_in == -1) { + sprintf(msg, "RtMidi: Unable to open OSS device (%s) for MIDI input!", + deviceNames[deveyes]); + handleError(msg, StkError::MIDI_SYSTEM); + } + + // Set up the circular buffer for the MIDI input messages + midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; + readIndex = 0; + writeIndex = 0; + + int result = pthread_create(&midi_input_thread, NULL, midiInputThread, NULL); + if (result) { + sprintf(msg, "RtMidi: unable to create MIDI input thread."); + handleError(msg, StkError::PROCESS_THREAD); + } +} + #endif RtMidi :: ~RtMidi() @@ -740,11 +739,11 @@ RtMidi :: ~RtMidi() #if defined(__MIDIATOR__) tcdrain(midi_in); if (midi_in != 0) close(midi_in); -#elif defined(__LINUX_OSS__) - if (midi_in != 0) close(midi_in); -#else // ALSA_API +#elif ( defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) if (midi_in != 0) snd_rawmidi_close(midi_in); +#elif defined(__LINUX_OSS__) + if (midi_in != 0) close(midi_in); #endif } diff --git a/src/RtWvIn.cpp b/src/RtWvIn.cpp index 9f5aa32..4eb81d1 100644 --- a/src/RtWvIn.cpp +++ b/src/RtWvIn.cpp @@ -25,44 +25,54 @@ RtWvIn :: RtWvIn(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrame { channels = nChannels; int size = bufferFrames; - RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32; + RtAudioFormat format = ( sizeof(MY_FLOAT) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + + audio_ = 0; try { - audio = new RtAudio(&stream, 0, 0, device, channels, format, - (int)sampleRate, &size, nBuffers); - data = (MY_FLOAT *) audio->getStreamBuffer(stream); + audio_ = new RtAudio(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); + } + + // Now open a stream and get the buffer pointer. + try { + audio_->openStream(0, 0, device, channels, format, + (int)sampleRate, &size, nBuffers); + data = (MY_FLOAT *) audio_->getStreamBuffer(); + } + catch (RtError &error) { + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } bufferSize = size; lastOutput = (MY_FLOAT *) new MY_FLOAT[channels]; for (unsigned int i=0; istopStream(stream); - delete audio; + if ( !stopped_ ) + audio_->stopStream(); + delete audio_; data = 0; // RtAudio deletes the buffer itself. } void RtWvIn :: start() { - if ( stopped ) { - audio->startStream(stream); - stopped = false; + if ( stopped_ ) { + audio_->startStream(); + stopped_ = false; } } void RtWvIn :: stop() { - if ( !stopped ) { - audio->stopStream(stream); - stopped = true; + if ( !stopped_ ) { + audio_->stopStream(); + stopped_ = true; } } @@ -92,25 +102,25 @@ const MY_FLOAT *RtWvIn :: lastFrame() const const MY_FLOAT *RtWvIn :: tickFrame(void) { - if ( stopped ) + if ( stopped_ ) start(); - if (counter == 0) { + if (counter_ == 0) { try { - audio->tickStream(stream); + audio_->tickStream(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } } - long temp = counter * channels; + long temp = counter_ * channels; for (unsigned int i=0; i= (long) bufferSize) - counter = 0; + counter_++; + if (counter_ >= (long) bufferSize) + counter_ = 0; return lastOutput; } diff --git a/src/RtWvOut.cpp b/src/RtWvOut.cpp index e8d7e36..945f6a2 100644 --- a/src/RtWvOut.cpp +++ b/src/RtWvOut.cpp @@ -19,46 +19,57 @@ /***************************************************/ #include "RtWvOut.h" -#include +#include -RtWvOut :: RtWvOut(unsigned int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers ) +RtWvOut :: RtWvOut( unsigned int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers ) { // We'll let RtAudio deal with channel and srate limitations. channels = nChannels; - bufferSize = bufferFrames; - RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32; + bufferSize_ = bufferFrames; + RtAudioFormat format = ( sizeof(MY_FLOAT) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + + audio_ = 0; try { - audio = new RtAudio(&stream, device, (int)channels, 0, 0, format, - (int)sampleRate, &bufferSize, nBuffers); - data = (MY_FLOAT *) audio->getStreamBuffer(stream); + audio_ = new RtAudio(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } - stopped = true; + + // Now open a stream and set the callback function. + try { + audio_->openStream(device, (int)channels, 0, 0, format, + (int)sampleRate, &bufferSize_, nBuffers); + data = (MY_FLOAT *) audio_->getStreamBuffer(); + } + catch (RtError &error) { + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); + } + + stopped_ = true; } RtWvOut :: ~RtWvOut() { - if ( !stopped ) - audio->stopStream(stream); - delete audio; - data = 0; // RtAudio deletes the buffer itself. + if ( !stopped_ ) + audio_->stopStream(); + delete audio_; + data = 0; } void RtWvOut :: start() { - if ( stopped ) { - audio->startStream(stream); - stopped = false; + if ( stopped_ ) { + audio_->startStream(); + stopped_ = false; } } void RtWvOut :: stop() { - if ( !stopped ) { - audio->stopStream(stream); - stopped = true; + if ( !stopped_ ) { + audio_->stopStream(); + stopped_ = true; } } @@ -74,21 +85,21 @@ MY_FLOAT RtWvOut :: getTime( void ) const void RtWvOut :: tick(const MY_FLOAT sample) { - if ( stopped ) + if ( stopped_ ) start(); - + for ( unsigned int j=0; j= (unsigned int )bufferSize ) { + if ( counter >= (unsigned int )bufferSize_ ) { try { - audio->tickStream(stream); + audio_->tickStream(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } counter = 0; } @@ -102,7 +113,7 @@ void RtWvOut :: tick(const MY_FLOAT *vector, unsigned int vectorSize) void RtWvOut :: tickFrame(const MY_FLOAT *frameVector, unsigned int frames) { - if ( stopped ) + if ( stopped_ ) start(); for ( unsigned int i=0; i= (unsigned int)bufferSize ) { + if ( counter >= (unsigned int)bufferSize_ ) { try { - audio->tickStream(stream); + audio_->tickStream(); } catch (RtError &error) { - handleError( error.getMessage(), StkError::AUDIO_SYSTEM ); + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } counter = 0; } diff --git a/src/Saxofony.cpp b/src/Saxofony.cpp index 4b3c885..d930385 100644 --- a/src/Saxofony.cpp +++ b/src/Saxofony.cpp @@ -36,7 +36,6 @@ /***************************************************/ #include "Saxofony.h" -#include #include "SKINI.msg" Saxofony :: Saxofony(MY_FLOAT lowestFrequency) @@ -54,10 +53,8 @@ Saxofony :: Saxofony(MY_FLOAT lowestFrequency) envelope = new Envelope; noise = new Noise; - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char path[128]; - strcpy(path, RAWWAVE_PATH); - vibrato = new WaveLoop( strcat(path,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); vibrato->setFrequency((MY_FLOAT) 5.735); outputGain = (MY_FLOAT) 0.3; @@ -87,7 +84,7 @@ void Saxofony :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Saxofony: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Saxofony: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -133,7 +130,7 @@ void Saxofony :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) outputGain = amplitude + 0.001; #if defined(_STK_DEBUG_) - cerr << "Saxofony: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Saxofony: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -142,7 +139,7 @@ void Saxofony :: noteOff(MY_FLOAT amplitude) this->stopBlowing(amplitude * 0.01); #if defined(_STK_DEBUG_) - cerr << "Saxofony: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Saxofony: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -172,11 +169,11 @@ void Saxofony :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Saxofony: Control value less than zero!" << endl; + std::cerr << "Saxofony: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Saxofony: Control value greater than 128.0!" << endl; + std::cerr << "Saxofony: Control value greater than 128.0!" << std::endl; } if (number == __SK_ReedStiffness_) // 2 @@ -194,10 +191,10 @@ void Saxofony :: controlChange(int number, MY_FLOAT value) else if (number == 26) // reed table offset reedTable->setOffset(0.4 + ( norm * 0.6)); else - cerr << "Saxofony: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Saxofony: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Saxofony: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Saxofony: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Shakers.cpp b/src/Shakers.cpp index 8c06eeb..256b1e0 100644 --- a/src/Shakers.cpp +++ b/src/Shakers.cpp @@ -344,7 +344,7 @@ int Shakers :: setupName(char* instr) } #if defined(_STK_DEBUG_) - cerr << "Shakers: Setting instrument to " << instrs[which] << endl; + std::cerr << "Shakers: Setting instrument to " << instrs[which] << std::endl; #endif return this->setupNum(which); @@ -795,14 +795,14 @@ int Shakers :: setupNum(int inst) void Shakers :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) { // Yep ... pretty kludgey, but it works! - int noteNum = (int) ((12*log(frequency/220)/log(2)) + 57.01) % 32; + int noteNum = (int) ((12*log(frequency/220.0)/log(2.0)) + 57.01) % 32; if (instType != noteNum) instType = this->setupNum(noteNum); shakeEnergy += amplitude * MAX_SHAKE * 0.1; if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE; if (instType==10 || instType==3) ratchetPos += 1; #if defined(_STK_DEBUG_) - cerr << "Shakers: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Shakers: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -891,11 +891,11 @@ void Shakers :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Shakers: Control value less than zero!" << endl; + std::cerr << "Shakers: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Shakers: Control value greater than 128.0!" << endl; + std::cerr << "Shakers: Control value greater than 128.0!" << std::endl; } MY_FLOAT temp; @@ -982,10 +982,10 @@ void Shakers :: controlChange(int number, MY_FLOAT value) this->setupNum(instType); } else - cerr << "Shakers: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Shakers: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Shakers: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Shakers: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Simple.cpp b/src/Simple.cpp index eb58ad2..d6345ac 100644 --- a/src/Simple.cpp +++ b/src/Simple.cpp @@ -19,17 +19,14 @@ #include "Simple.h" #include "SKINI.msg" -#include Simple :: Simple() { adsr = new ADSR; baseFrequency = (MY_FLOAT) 440.0; - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - loop = new WaveLoop( strcat(file,"impuls10.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + loop = new WaveLoop( (Stk::rawwavePath() + "impuls10.raw").c_str(), TRUE ); filter = new OnePole(0.5); noise = new Noise; @@ -64,7 +61,7 @@ void Simple :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) filter->setGain(amplitude); #if defined(_STK_DEBUG_) - cerr << "Simple: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Simple: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } void Simple :: noteOff(MY_FLOAT amplitude) @@ -72,7 +69,7 @@ void Simple :: noteOff(MY_FLOAT amplitude) keyOff(); #if defined(_STK_DEBUG_) - cerr << "Simple: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Simple: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -97,11 +94,11 @@ void Simple :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Clarinet: Control value less than zero!" << endl; + std::cerr << "Clarinet: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Clarinet: Control value greater than 128.0!" << endl; + std::cerr << "Clarinet: Control value greater than 128.0!" << std::endl; } if (number == __SK_Breath_) // 2 @@ -117,9 +114,9 @@ void Simple :: controlChange(int number, MY_FLOAT value) else if (number == __SK_AfterTouch_Cont_) // 128 adsr->setTarget( norm ); else - cerr << "Simple: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Simple: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Simple: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Simple: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Sitar.cpp b/src/Sitar.cpp index c561bc6..605a3cc 100644 --- a/src/Sitar.cpp +++ b/src/Sitar.cpp @@ -56,7 +56,7 @@ void Sitar :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "Sitar: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Sitar: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -79,7 +79,7 @@ void Sitar :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) amGain = 0.1 * amplitude; #if defined(_STK_DEBUG_) - cerr << "Sitar: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Sitar: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -87,16 +87,16 @@ void Sitar :: noteOff(MY_FLOAT amplitude) { loopGain = (MY_FLOAT) 1.0 - amplitude; if ( loopGain < 0.0 ) { - cerr << "Plucked: noteOff amplitude greater than 1.0!" << endl; + std::cerr << "Plucked: noteOff amplitude greater than 1.0!" << std::endl; loopGain = 0.0; } else if ( loopGain > 1.0 ) { - cerr << "Plucked: noteOff amplitude less than or zero!" << endl; + std::cerr << "Plucked: noteOff amplitude less than or zero!" << std::endl; loopGain = (MY_FLOAT) 0.99999; } #if defined(_STK_DEBUG_) - cerr << "Plucked: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Plucked: NoteOff amplitude = " << amplitude << std::endl; #endif } diff --git a/src/StifKarp.cpp b/src/StifKarp.cpp index 09d174b..e5c4718 100644 --- a/src/StifKarp.cpp +++ b/src/StifKarp.cpp @@ -73,7 +73,7 @@ void StifKarp :: setFrequency(MY_FLOAT frequency) { lastFrequency = frequency; if ( frequency <= 0.0 ) { - cerr << "StifKarp: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "StifKarp: setFrequency parameter is less than or equal to zero!" << std::endl; lastFrequency = 220.0; } @@ -116,11 +116,11 @@ void StifKarp :: setStretch(MY_FLOAT stretch) void StifKarp :: setPickupPosition(MY_FLOAT position) { pickupPosition = position; if ( position < 0.0 ) { - cerr << "StifKarp: setPickupPosition parameter is less than zero!" << endl; + std::cerr << "StifKarp: setPickupPosition parameter is less than zero!" << std::endl; pickupPosition = 0.0; } else if ( position > 1.0 ) { - cerr << "StifKarp: setPickupPosition parameter is greater than 1.0!" << endl; + std::cerr << "StifKarp: setPickupPosition parameter is greater than 1.0!" << std::endl; pickupPosition = 1.0; } @@ -139,11 +139,11 @@ void StifKarp :: pluck(MY_FLOAT amplitude) { MY_FLOAT gain = amplitude; if ( gain > 1.0 ) { - cerr << "StifKarp: pluck amplitude greater than 1.0!" << endl; + std::cerr << "StifKarp: pluck amplitude greater than 1.0!" << std::endl; gain = 1.0; } else if ( gain < 0.0 ) { - cerr << "StifKarp: pluck amplitude less than zero!" << endl; + std::cerr << "StifKarp: pluck amplitude less than zero!" << std::endl; gain = 0.0; } @@ -161,7 +161,7 @@ void StifKarp :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) this->pluck(amplitude); #if defined(_STK_DEBUG_) - cerr << "StifKarp: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "StifKarp: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -169,17 +169,17 @@ void StifKarp :: noteOff(MY_FLOAT amplitude) { MY_FLOAT gain = amplitude; if ( gain > 1.0 ) { - cerr << "StifKarp: noteOff amplitude greater than 1.0!" << endl; + std::cerr << "StifKarp: noteOff amplitude greater than 1.0!" << std::endl; gain = 1.0; } else if ( gain < 0.0 ) { - cerr << "StifKarp: noteOff amplitude less than zero!" << endl; + std::cerr << "StifKarp: noteOff amplitude less than zero!" << std::endl; gain = 0.0; } loopGain = (1.0 - gain) * 0.5; #if defined(_STK_DEBUG_) - cerr << "StifPluck: NoteOff amplitude = " << amplitude << endl; + std::cerr << "StifPluck: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -204,11 +204,11 @@ void StifKarp :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "StifKarp: Control value less than zero!" << endl; + std::cerr << "StifKarp: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "StifKarp: Control value greater than 128.0!" << endl; + std::cerr << "StifKarp: Control value greater than 128.0!" << std::endl; } if (number == __SK_PickPosition_) // 4 @@ -218,10 +218,10 @@ void StifKarp :: controlChange(int number, MY_FLOAT value) else if (number == __SK_StringDetune_) // 1 setStretch( 0.9 + (0.1 * (1.0 - norm)) ); else - cerr << "StifKarp: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "StifKarp: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "StifKarp: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "StifKarp: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Stk.cpp b/src/Stk.cpp index bc6fcb3..139650b 100644 --- a/src/Stk.cpp +++ b/src/Stk.cpp @@ -17,11 +17,12 @@ #include MY_FLOAT Stk :: srate = (MY_FLOAT) SRATE; +std::string Stk :: rawwavepath = RAWWAVE_PATH; const Stk::STK_FORMAT Stk :: STK_SINT8 = 1; const Stk::STK_FORMAT Stk :: STK_SINT16 = 2; const Stk::STK_FORMAT Stk :: STK_SINT32 = 8; -const Stk::STK_FORMAT Stk :: STK_FLOAT32 = 16; -const Stk::STK_FORMAT Stk :: STK_FLOAT64 = 32; +const Stk::STK_FORMAT Stk :: MY_FLOAT32 = 16; +const Stk::STK_FORMAT Stk :: MY_FLOAT64 = 32; Stk :: Stk(void) { @@ -42,6 +43,21 @@ void Stk :: setSampleRate(MY_FLOAT newRate) srate = newRate; } +std::string Stk :: rawwavePath(void) +{ + return rawwavepath; +} + +void Stk :: setRawwavePath(std::string newPath) +{ + if ( !newPath.empty() ) + rawwavepath = newPath; + + // Make sure the path includes a "/" + if ( rawwavepath[rawwavepath.length()-1] != '/' ) + rawwavepath += "/"; +} + void Stk :: swap16(unsigned char *ptr) { register unsigned char val; diff --git a/src/Table.cpp b/src/Table.cpp index e9563a3..16bffc0 100644 --- a/src/Table.cpp +++ b/src/Table.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include Table :: Table(char *fileName) { @@ -77,11 +77,11 @@ MY_FLOAT Table :: tick(MY_FLOAT index) long temp; if (index > length-1) { - cerr << "Table: Index (" << index << ") exceeds table length ... sticking at end!" << endl; + std::cerr << "Table: Index (" << index << ") exceeds table length ... sticking at end!" << std::endl; index = length-1; } else if (index < 0.0) { - cerr << "Table: Index (" << index << ") is less than zero ... setting to zero!" << endl; + std::cerr << "Table: Index (" << index << ") is less than zero ... setting to zero!" << std::endl; index = 0.0; } diff --git a/src/TcpWvIn.cpp b/src/TcpWvIn.cpp index 0b4c5f2..4841775 100644 --- a/src/TcpWvIn.cpp +++ b/src/TcpWvIn.cpp @@ -105,8 +105,8 @@ void TcpWvIn :: listen(unsigned int nChannels, Stk::STK_FORMAT format) channels = nChannels; if ( format == STK_SINT16 ) dataSize = 2; - else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataSize = 4; - else if ( format == STK_FLOAT64 ) dataSize = 8; + else if ( format == STK_SINT32 || format == MY_FLOAT32 ) dataSize = 4; + else if ( format == MY_FLOAT64 ) dataSize = 8; else if ( format == STK_SINT8 ) dataSize = 1; else { sprintf( msg, "TcpWvIn: Unknown data type specified (%ld).", format ); @@ -234,7 +234,7 @@ int TcpWvIn :: readData( void ) data[i] *= gain; } } - else if ( dataType == STK_FLOAT32 ) { + else if ( dataType == MY_FLOAT32 ) { FLOAT32 *buf = (FLOAT32 *) (buffer+readPoint); for (int i=0; i Thread :: Thread() { diff --git a/src/TubeBell.cpp b/src/TubeBell.cpp index ae8f774..6db46d6 100644 --- a/src/TubeBell.cpp +++ b/src/TubeBell.cpp @@ -31,25 +31,14 @@ /***************************************************/ #include "TubeBell.h" -#include TubeBell :: TubeBell() : FM() { - int i; - char files[4][128]; - - // Concatenate the STK RAWWAVE_PATH to the rawwave file. - for ( i=0; i<4; i++ ) - strcpy( files[i], RAWWAVE_PATH); - - strcat(files[0], "sinewave.raw"); - strcat(files[1], "sinewave.raw"); - strcat(files[2], "sinewave.raw"); - strcat(files[3], "fwavblnk.raw"); - - for ( i=0; i<4; i++ ) - waves[i] = new WaveLoop( files[i], TRUE ); + // Concatenate the STK rawwave path to the rawwave files + for ( int i=0; i<3; i++ ) + waves[i] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + waves[3] = new WaveLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), TRUE ); this->setRatio(0, 1.0 * 0.995); this->setRatio(1, 1.414 * 0.995); diff --git a/src/VoicForm.cpp b/src/VoicForm.cpp index 292679b..45ce141 100644 --- a/src/VoicForm.cpp +++ b/src/VoicForm.cpp @@ -30,14 +30,11 @@ #include "SKINI.msg" #include -#include VoicForm :: VoicForm() : Instrmnt() { - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char file[128]; - strcpy(file, RAWWAVE_PATH); - voiced = new SingWave( strcat(file,"impuls20.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + voiced = new SingWave( (Stk::rawwavePath() + "impuls20.raw").c_str(), TRUE ); voiced->setGainRate( 0.001 ); voiced->setGainTarget( 0.0 ); @@ -86,7 +83,7 @@ void VoicForm :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency; if ( frequency <= 0.0 ) { - cerr << "VoicForm: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "VoicForm: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -107,14 +104,14 @@ bool VoicForm :: setPhoneme(const char *phoneme ) setVoiced( Phonemes::voiceGain( i ) ); setUnVoiced( Phonemes::noiseGain( i ) ); #if defined(_STK_DEBUG_) - cout << "VoicForm: found formant " << phoneme << " (number " << i << ")" << endl; + cout << "VoicForm: found formant " << phoneme << " (number " << i << ")" << std::endl; #endif } i++; } if ( !found ) - cerr << "VoicForm: phoneme " << phoneme << " not found!" << endl; + std::cerr << "VoicForm: phoneme " << phoneme << " not found!" << std::endl; return found; } @@ -132,7 +129,7 @@ void VoicForm :: setUnVoiced(MY_FLOAT nGain) void VoicForm :: setFilterSweepRate(int whichOne, MY_FLOAT rate) { if ( whichOne < 0 || whichOne > 3 ) { - cerr << "VoicForm: setFilterSweepRate filter argument outside range 0-3!" << endl; + std::cerr << "VoicForm: setFilterSweepRate filter argument outside range 0-3!" << std::endl; return; } @@ -191,11 +188,11 @@ void VoicForm :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "VoicForm: Control value less than zero!" << endl; + std::cerr << "VoicForm: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "VoicForm: Control value greater than 128.0!" << endl; + std::cerr << "VoicForm: Control value greater than 128.0!" << std::endl; } if (number == __SK_Breath_) { // 2 @@ -240,9 +237,9 @@ void VoicForm :: controlChange(int number, MY_FLOAT value) onepole->setPole( 0.97 - ( norm * 0.2) ); } else - cerr << "VoicForm: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "VoicForm: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "VoicForm: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "VoicForm: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Voicer.cpp b/src/Voicer.cpp index 810059d..6681d21 100644 --- a/src/Voicer.cpp +++ b/src/Voicer.cpp @@ -45,14 +45,13 @@ Voicer :: Voicer( int maxInstruments, MY_FLOAT decayTime ) Voicer :: ~Voicer() { delete [] voices; - //free( voices ); } void Voicer :: addInstrument( Instrmnt *instrument, int channel ) { //voices = (Voice *) realloc( (void *) voices, nVoices+1 * sizeof( Voice ) ); if ( nVoices == maxVoices ) { - cerr << "Voicer: Maximum number of voices already added!!" << endl; + std::cerr << "Voicer: Maximum number of voices already added!!" << std::endl; return; } @@ -226,10 +225,13 @@ void Voicer :: silence( void ) MY_FLOAT Voicer :: tick() { - lastOutput = 0.0; + lastOutput = lastOutputLeft = lastOutputRight = 0.0; for ( int i=0; itick(); + lastOutputLeft += voices[i].instrument->lastOutLeft(); + lastOutputRight += voices[i].instrument->lastOutRight(); + } if ( voices[i].sounding < 0 ) { voices[i].sounding++; if ( voices[i].sounding == 0 ) @@ -252,3 +254,13 @@ MY_FLOAT Voicer :: lastOut() const return lastOutput; } +MY_FLOAT Voicer :: lastOutLeft() const +{ + return lastOutputLeft; +} + +MY_FLOAT Voicer :: lastOutRight() const +{ + return lastOutputRight; +} + diff --git a/src/WaveLoop.cpp b/src/WaveLoop.cpp index 2c1f7f8..d7ca434 100644 --- a/src/WaveLoop.cpp +++ b/src/WaveLoop.cpp @@ -24,6 +24,11 @@ WaveLoop :: WaveLoop( const char *fileName, bool raw ) : WvIn( fileName, raw ), phaseOffset(0.0) { + // If at end of file, redo extra sample frame for looping. + if (chunkPointer+bufferSize == fileSize) { + for (unsigned int j=0; j -#include #include #define CAN_RADIUS 100 @@ -43,10 +42,8 @@ Whistle :: Whistle() pea = new Sphere(PEA_RADIUS); bumper = new Sphere(BUMP_RADIUS); - // Concatenate the STK RAWWAVE_PATH to the rawwave file - char path[128]; - strcpy(path, RAWWAVE_PATH); - sine = new WaveLoop( strcat(path,"sinewave.raw"), TRUE ); + // Concatenate the STK rawwave path to the rawwave file + sine = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); sine->setFrequency(2800.0); can->setPosition(0, 0, 0); // set can location @@ -93,7 +90,7 @@ void Whistle :: setFrequency(MY_FLOAT frequency) { MY_FLOAT freakency = frequency * 4; // the whistle is a transposing instrument if ( frequency <= 0.0 ) { - cerr << "Whistle: setFrequency parameter is less than or equal to zero!" << endl; + std::cerr << "Whistle: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } @@ -117,7 +114,7 @@ void Whistle :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) setFrequency(frequency); startBlowing(amplitude*2.0 ,amplitude * 0.2); #if defined(_STK_DEBUG_) - cerr << "Whistle: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + std::cerr << "Whistle: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; #endif } @@ -126,7 +123,7 @@ void Whistle :: noteOff(MY_FLOAT amplitude) this->stopBlowing(amplitude * 0.02); #if defined(_STK_DEBUG_) - cerr << "Whistle: NoteOff amplitude = " << amplitude << endl; + std::cerr << "Whistle: NoteOff amplitude = " << amplitude << std::endl; #endif } @@ -230,11 +227,11 @@ void Whistle :: controlChange(int number, MY_FLOAT value) MY_FLOAT norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - cerr << "Whistle: Control value less than zero!" << endl; + std::cerr << "Whistle: Control value less than zero!" << std::endl; } else if ( norm > 1.0 ) { norm = 1.0; - cerr << "Whistle: Control value greater than 128.0!" << endl; + std::cerr << "Whistle: Control value greater than 128.0!" << std::endl; } if (number == __SK_NoiseLevel_) // 4 @@ -250,10 +247,10 @@ void Whistle :: controlChange(int number, MY_FLOAT value) else if (number == __SK_Sustain_) // 64 if (value < 1.0) subSample = 1; else - cerr << "Whistle: Undefined Control Number (" << number << ")!!" << endl; + std::cerr << "Whistle: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - cerr << "Whistle: controlChange number = " << number << ", value = " << value << endl; + std::cerr << "Whistle: controlChange number = " << number << ", value = " << value << std::endl; #endif } diff --git a/src/Wurley.cpp b/src/Wurley.cpp index 34da04f..4db05af 100644 --- a/src/Wurley.cpp +++ b/src/Wurley.cpp @@ -31,25 +31,14 @@ /***************************************************/ #include "Wurley.h" -#include Wurley :: Wurley() : FM() { - int i; - char files[4][128]; - - // Concatenate the STK RAWWAVE_PATH to the rawwave file. - for ( i=0; i<4; i++ ) - strcpy( files[i], RAWWAVE_PATH); - - strcat(files[0], "sinewave.raw"); - strcat(files[1], "sinewave.raw"); - strcat(files[2], "sinewave.raw"); - strcat(files[3], "fwavblnk.raw"); - - for ( i=0; i<4; i++ ) - waves[i] = new WaveLoop( files[i], TRUE ); + // Concatenate the STK rawwave path to the rawwave files + for ( int i=0; i<3; i++ ) + waves[i] = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + waves[3] = new WaveLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), TRUE ); this->setRatio(0, 1.0); this->setRatio(1, 4.0); diff --git a/src/WvIn.cpp b/src/WvIn.cpp index 3627c9c..14e4d57 100644 --- a/src/WvIn.cpp +++ b/src/WvIn.cpp @@ -42,14 +42,14 @@ #include #include -#include +#include WvIn :: WvIn() { init(); } -WvIn :: WvIn( const char *fileName, bool raw ) +WvIn :: WvIn( const char *fileName, bool raw, bool doNormalize ) { init(); openFile( fileName, raw ); @@ -86,7 +86,7 @@ void WvIn :: closeFile( void ) finished = true; } -void WvIn :: openFile( const char *fileName, bool raw ) +void WvIn :: openFile( const char *fileName, bool raw, bool doNormalize ) { closeFile(); @@ -129,6 +129,11 @@ void WvIn :: openFile( const char *fileName, bool raw ) if ( result == false ) handleError(msg, StkError::FILE_ERROR); + if ( fileSize == 0 ) { + sprintf(msg, "WvIn: File (%s) data size is zero!", fileName); + handleError(msg, StkError::FILE_ERROR); + } + // Allocate new memory if necessary. samples = (bufferSize+1)*channels; if ( lastSamples < samples ) { @@ -144,7 +149,7 @@ void WvIn :: openFile( const char *fileName, bool raw ) chunkPointer = 0; reset(); readData( 0 ); // Load file data. - normalize(); + if ( doNormalize ) normalize(); finished = false; return; @@ -204,10 +209,11 @@ bool WvIn :: getWavInfo( const char *fileName ) // Check that the data is not compressed. SINT16 format_tag; - if ( fseek(fd, 4, SEEK_CUR) == -1 ) goto error; // Jump over chunkSize. + if ( fread(&chunkSize, 4, 1, fd) != 1 ) goto error; // Read fmt chunk size. if ( fread(&format_tag, 2, 1, fd) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ swap16((unsigned char *)&format_tag); + swap32((unsigned char *)&chunkSize); #endif if (format_tag != 1 && format_tag != 3 ) { // PCM = 1, FLOAT = 3 sprintf(msg, "WvIn: %s contains an unsupported data format type (%d).", fileName, format_tag); @@ -250,17 +256,21 @@ bool WvIn :: getWavInfo( const char *fileName ) } else if ( format_tag == 3 ) { if (temp == 32) - dataType = STK_FLOAT32; + dataType = MY_FLOAT32; else if (temp == 64) - dataType = STK_FLOAT64; + dataType = MY_FLOAT64; } if ( dataType == 0 ) { sprintf(msg, "WvIn: %d bits per sample with data format %d are not supported (%s).", temp, format_tag, fileName); return false; } + // Jump over any remaining part of the "fmt" chunk. + if ( fseek(fd, chunkSize-16, SEEK_CUR) == -1 ) goto error; + // Find "data" chunk ... it must come after the "fmt" chunk. if ( fread(&id, 4, 1, fd) != 1 ) goto error; + while ( strncmp(id, "data", 4) ) { if ( fread(&chunkSize, 4, 1, fd) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ @@ -308,8 +318,8 @@ bool WvIn :: getSndInfo( const char *fileName ) if (format == 2) dataType = STK_SINT8; else if (format == 3) dataType = STK_SINT16; else if (format == 5) dataType = STK_SINT32; - else if (format == 6) dataType = STK_FLOAT32; - else if (format == 7) dataType = STK_FLOAT64; + else if (format == 6) dataType = MY_FLOAT32; + else if (format == 7) dataType = MY_FLOAT64; else { sprintf(msg, "WvIn: data format in file %s is not supported.", fileName); return false; @@ -447,8 +457,8 @@ bool WvIn :: getAifInfo( const char *fileName ) } else { if ( fread(&id, 4, 1, fd) != 1 ) goto error; - if ( (!strncmp(id, "fl32", 4) || !strncmp(id, "FL32", 4)) && temp == 32 ) dataType = STK_FLOAT32; - else if ( (!strncmp(id, "fl64", 4) || !strncmp(id, "FL64", 4)) && temp == 64 ) dataType = STK_FLOAT64; + if ( (!strncmp(id, "fl32", 4) || !strncmp(id, "FL32", 4)) && temp == 32 ) dataType = MY_FLOAT32; + else if ( (!strncmp(id, "fl64", 4) || !strncmp(id, "FL64", 4)) && temp == 64 ) dataType = MY_FLOAT64; } if ( dataType == 0 ) { sprintf(msg, "WvIn: %d bits per sample in file %s are not supported.", temp, fileName); @@ -544,8 +554,8 @@ bool WvIn :: getMatInfo( const char *fileName ) if ( tmp == 1 ) dataType = STK_SINT8; else if ( tmp == 3 ) dataType = STK_SINT16; else if ( tmp == 5 ) dataType = STK_SINT32; - else if ( tmp == 7 ) dataType = STK_FLOAT32; - else if ( tmp == 9 ) dataType = STK_FLOAT64; + else if ( tmp == 7 ) dataType = MY_FLOAT32; + else if ( tmp == 9 ) dataType = MY_FLOAT64; else { sprintf(msg, "WvIn: The MAT-file array data format (%d) is not supported.", tmp); return false; @@ -649,7 +659,7 @@ void WvIn :: readData( unsigned long index ) for (i=length*channels-1; i>=0; i--) data[i] = buf[i]; } - else if ( dataType == STK_FLOAT32 ) { + else if ( dataType == MY_FLOAT32 ) { FLOAT32 *buf = (FLOAT32 *)data; if (fseek(fd, dataOffset+(long)(chunkPointer*channels*4), SEEK_SET) == -1) goto error; if (fread(buf, length*channels, 4, fd) != 4 ) goto error; @@ -661,7 +671,7 @@ void WvIn :: readData( unsigned long index ) for (i=length*channels-1; i>=0; i--) data[i] = buf[i]; } - else if ( dataType == STK_FLOAT64 ) { + else if ( dataType == MY_FLOAT64 ) { FLOAT64 *buf = (FLOAT64 *)data; if (fseek(fd, dataOffset+(long)(chunkPointer*channels*8), SEEK_SET) == -1) goto error; if (fread(buf, length*channels, 8, fd) != 8 ) goto error; @@ -674,11 +684,11 @@ void WvIn :: readData( unsigned long index ) data[i] = buf[i]; } else if ( dataType == STK_SINT8 ) { - char *buf = (char *)data; + unsigned char *buf = (unsigned char *)data; if (fseek(fd, dataOffset+(long)(chunkPointer*channels), SEEK_SET) == -1) goto error; if (fread(buf, length*channels, 1, fd) != 1 ) goto error; for (i=length*channels-1; i>=0; i--) - data[i] = buf[i]; + data[i] = buf[i] - 128.0; // 8-bit WAV data is unsigned! } // If at end of file, repeat last sample frame for interpolation. @@ -719,7 +729,7 @@ void WvIn :: normalize(MY_FLOAT peak) if ( dataType == STK_SINT8 ) gain = peak / 128.0; else if ( dataType == STK_SINT16 ) gain = peak / 32768.0; else if ( dataType == STK_SINT32 ) gain = peak / 2147483648.0; - else if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 ) gain = peak; + else if ( dataType == MY_FLOAT32 || dataType == MY_FLOAT64 ) gain = peak; return; } @@ -731,6 +741,7 @@ void WvIn :: normalize(MY_FLOAT peak) if (fabs(data[i]) > max) max = (MY_FLOAT) fabs((double) data[i]); } + if (max > 0.0) { max = (MY_FLOAT) 1.0 / max; max *= peak; diff --git a/src/WvOut.cpp b/src/WvOut.cpp index 9735927..33c95a0 100644 --- a/src/WvOut.cpp +++ b/src/WvOut.cpp @@ -168,8 +168,8 @@ void WvOut :: openFile( const char *fileName, unsigned int nChannels, WvOut::FIL fileType = type; if ( format != STK_SINT8 && format != STK_SINT16 && - format != STK_SINT32 && format != STK_FLOAT32 && - format != STK_FLOAT64 ) { + format != STK_SINT32 && format != MY_FLOAT32 && + format != MY_FLOAT64 ) { sprintf( msg, "WvOut: Unknown data type specified (%ld).", format ); handleError(msg, StkError::FUNCTION_ARGUMENT); } @@ -259,11 +259,11 @@ bool WvOut :: setWavFile( const char *fileName ) hdr.bits_per_samp = 16; else if ( dataType == STK_SINT32 ) hdr.bits_per_samp = 32; - else if ( dataType == STK_FLOAT32 ) { + else if ( dataType == MY_FLOAT32 ) { hdr.format_tag = 3; hdr.bits_per_samp = 32; } - else if ( dataType == STK_FLOAT64 ) { + else if ( dataType == MY_FLOAT64 ) { hdr.format_tag = 3; hdr.bits_per_samp = 64; } @@ -297,9 +297,9 @@ void WvOut :: closeWavFile( void ) int bytes_per_sample = 1; if ( dataType == STK_SINT16 ) bytes_per_sample = 2; - else if ( dataType == STK_SINT32 || dataType == STK_FLOAT32 ) + else if ( dataType == STK_SINT32 || dataType == MY_FLOAT32 ) bytes_per_sample = 4; - else if ( dataType == STK_FLOAT64 ) + else if ( dataType == MY_FLOAT64 ) bytes_per_sample = 8; SINT32 bytes = totalCount * channels * bytes_per_sample; @@ -338,9 +338,9 @@ bool WvOut :: setSndFile( const char *fileName ) hdr.format = 3; else if ( dataType == STK_SINT32 ) hdr.format = 5; - else if ( dataType == STK_FLOAT32 ) + else if ( dataType == MY_FLOAT32 ) hdr.format = 6; - else if ( dataType == STK_FLOAT64 ) + else if ( dataType == MY_FLOAT64 ) hdr.format = 7; byteswap = false; @@ -368,9 +368,9 @@ void WvOut :: closeSndFile( void ) bytes_per_sample = 2; else if ( dataType == STK_SINT32 ) bytes_per_sample = 4; - else if ( dataType == STK_FLOAT32 ) + else if ( dataType == MY_FLOAT32 ) bytes_per_sample = 4; - else if ( dataType == STK_FLOAT64 ) + else if ( dataType == MY_FLOAT64 ) bytes_per_sample = 8; SINT32 bytes = totalCount * bytes_per_sample * channels; @@ -407,12 +407,12 @@ bool WvOut :: setAifFile( const char *fileName ) hdr.sample_size = 16; else if ( dataType == STK_SINT32 ) hdr.sample_size = 32; - else if ( dataType == STK_FLOAT32 ) { + else if ( dataType == MY_FLOAT32 ) { hdr.aiff[3] = 'C'; hdr.sample_size = 32; hdr.comm_size = 24; } - else if ( dataType == STK_FLOAT64 ) { + else if ( dataType == MY_FLOAT64 ) { hdr.aiff[3] = 'C'; hdr.sample_size = 64; hdr.comm_size = 24; @@ -465,13 +465,13 @@ bool WvOut :: setAifFile( const char *fileName ) if ( fwrite(&hdr.sample_size, 2, 1, fd) != 1 ) goto error; if ( fwrite(&hdr.srate, 10, 1, fd) != 1 ) goto error; - if ( dataType == STK_FLOAT32 ) { + if ( dataType == MY_FLOAT32 ) { char type[4] = {'f','l','3','2'}; char zeroes[2] = { 0, 0 }; if ( fwrite(&type, 4, 1, fd) != 1 ) goto error; if ( fwrite(&zeroes, 2, 1, fd) != 1 ) goto error; } - else if ( dataType == STK_FLOAT64 ) { + else if ( dataType == MY_FLOAT64 ) { char type[4] = {'f','l','6','4'}; char zeroes[2] = { 0, 0 }; if ( fwrite(&type, 4, 1, fd) != 1 ) goto error; @@ -500,13 +500,13 @@ void WvOut :: closeAifFile( void ) int bytes_per_sample = 1; if ( dataType == STK_SINT16 ) bytes_per_sample = 2; - else if ( dataType == STK_SINT32 || dataType == STK_FLOAT32 ) + else if ( dataType == STK_SINT32 || dataType == MY_FLOAT32 ) bytes_per_sample = 4; - else if ( dataType == STK_FLOAT64 ) + else if ( dataType == MY_FLOAT64 ) bytes_per_sample = 8; unsigned long bytes = totalCount * bytes_per_sample * channels + 46; - if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 ) bytes += 6; + if ( dataType == MY_FLOAT32 || dataType == MY_FLOAT64 ) bytes += 6; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&bytes); #endif @@ -514,11 +514,11 @@ void WvOut :: closeAifFile( void ) fwrite(&bytes, 4, 1, fd); bytes = totalCount * bytes_per_sample * channels + 8; - if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 ) bytes += 6; + if ( dataType == MY_FLOAT32 || dataType == MY_FLOAT64 ) bytes += 6; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&bytes); #endif - if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 ) + if ( dataType == MY_FLOAT32 || dataType == MY_FLOAT64 ) fseek(fd, 48, SEEK_SET); // jump to "SSND" chunk size else fseek(fd, 42, SEEK_SET); // jump to "SSND" chunk size @@ -538,8 +538,8 @@ bool WvOut :: setMatFile( const char *fileName ) return false; } - if ( dataType != STK_FLOAT64 ) { - dataType = STK_FLOAT64; + if ( dataType != MY_FLOAT64 ) { + dataType = MY_FLOAT64; sprintf(msg, "WvOut: Using 64-bit floating-point data format for file %s", name); handleError(msg, StkError::WARNING); } @@ -665,11 +665,20 @@ MY_FLOAT WvOut :: getTime( void ) const void WvOut :: writeData( unsigned long frames ) { if ( dataType == STK_SINT8 ) { - signed char sample; - for ( unsigned long k=0; k