diff --git a/INSTALL b/INSTALL index 8c67f9e..09e49aa 100644 --- a/INSTALL +++ b/INSTALL @@ -6,7 +6,7 @@ The Synthesis ToolKit in C++ can be used in a variety of ways, depending on your To configure and compile (on Unix systems): -1. Unpack the STK distribution (tar -xzf stk-4.x.tar.gz). +1. Unpack the STK distribution (tar -xzf stk-4.x.x.tar.gz). 2. From within the directory containing this file, run configure: ./configure @@ -18,11 +18,11 @@ 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-alsa = choose native ALSA API support (default, linux only) --with-jack = choose native JACK server API support (linux only) - --enable-midiator = enable native MS-124W MIDI support (linux only) + --with-oss = choose native OSS API support (linux only) -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): +It is now possible to specify more than one Linux audio API. Note however that the ALSA library is required in order to compile the RtMidi class, even if the "--with-oss" option is provided (only the OSS audio API will be used, not the OSS MIDI API). 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/" @@ -33,7 +33,7 @@ If you wish to use a different compiler than that selected by configure, specify ./configure CXX=CC -In addition, a linux RPM is available from the STK WWW site (http://www-ccrma.stanford.edu/software/stk/). +In addition, a linux RPM is available from the Planet CCRMA WWW site (http://ccrma.stanford.edu/planetccrma/software/). For Windows Users: diff --git a/README b/README index a8a494c..75f3a64 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ include: STK class header files src: STK class source files rawwaves: STK audio files (1-channel, 16-bit, big-endian) doc: STK documentation -projects: example STK programs +projects: example STK projects and programs Please read the Legal and Ethical notes near the bottom of this document. @@ -17,7 +17,7 @@ For compiling and installing STK, see the INSTALL file in this directory. OVERVIEW: -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. +The Synthesis ToolKit in C++ (STK) is a set of open source audio signal processing and algorithmic synthesis classes written in the C++ programming language. 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 10 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. The Synthesis ToolKit is free for non-commercial use. The only parts of the Synthesis ToolKit that are platform-dependent concern real-time audio and MIDI input and output, and that is taken care of with a few special classes. The interface for MIDI input and the simple Tcl/Tk graphical user interfaces (GUIs) provided is the same, so it's easy to experiment in real time using either the GUIs or MIDI. The Synthesis ToolKit can generate simultaneous SND (AU), WAV, AIFF, and MAT-file output soundfile formats (as well as realtime sound output), so you can view your results using one of a large variety of sound/signal analysis tools already available (e.g. Snd, Cool Edit, Matlab). @@ -31,13 +31,15 @@ SYSTEM REQUIREMENTS: See the individual README's (eg. README-linux) in the /doc directory for platform specific information and system requirements. In general, you will use the configure script to create Makefiles on unix platforms or the VC++ workspace files to compile the example programs. To use the Tcl/Tk GUIs, you will need Tcl/Tk version 8.0 or higher. -WHAT'S NEW: +WHAT'S NEW (AND NOT SO NEW): -Despite being available in one form or another since 1996, we still consider STK to be alpha software. Thus, backward compatability has not been a priority. Please read the Release Notes to see what has changed since the last release. +Despite being available in one form or another since 1996, we still consider STK to be alpha software. We attempt to maintain backward compatability but changes are sometimes made in an effort to improve the overall design or performance of the software. Please read the Release Notes to see what has changed since the last release. + +A new StkFrames class has been created to facilitate the handling and passing of multichannel, vectorized audio data. All STK classes have been updated to include tick() functions which accept StkFrames arguments. The control message handling scheme has been simplified greatly through the use of the Messager class. It is now possible to have access to simultaneous piped, socketed, and/or MIDI input control messages. In most cases, this should eliminate the use of the Md2Skini program. -Realtime audio input capabilities were added to STK with release 3.0, though the behavior of such is very hardware dependent. Under Linux and Irix, audio input and output are possible with very low latency. Using the Windoze DirectSound API, minimum dependable output sound latency seems to be around 20 milliseconds or so, while input sound latency is on the order of a hundred milliseconds or more! +Realtime audio input capabilities were added to STK with release 3.0, though the behavior of such is very hardware dependent. Under Linux, Macintosh OS-X, and Irix, audio input and output are possible with very low latency. Using the Windoze DirectSound API, minimum dependable output sound latency seems to be around 20 milliseconds or so, while input sound latency is on the order of a hundred milliseconds or more! As mentioned above, it is possible to record the audio ouput of an STK program to .snd, .wav, .raw, .aif, and .mat (Matlab MAT-file) output file types. Though somewhat obsolete, the program Md2Skini can be used to write SKINI scorefiles from realtime MIDI input. Finally, STK should compile with non-realtime functionality on any platform with a generic C++ compiler. @@ -46,7 +48,7 @@ For those who wish to make a library from the core STK classes, the configure sc DISCLAIMER: -You probably already guessed this, but just to be sure, we don't guarantee anything works. :-) It's free ... what do you expect? If you find a bug, please let us know and we'll try to correct it. You can also make suggestions, but again, no guarantees. Send email to prc@cs.princeton.edu and gary@ccrma.stanford.edu. +You probably already guessed this, but just to be sure, we don't guarantee anything works. :-) It's free ... what do you expect? If you find a bug, please let us know and we'll try to correct it. You can also make suggestions, but again, no guarantees. Send email to the mail list. LEGAL AND ETHICAL: @@ -62,53 +64,30 @@ The good news is that large hunks of the techniques used here are public domain. FURTHER READING: -For complete documentation on this ToolKit, the classes, etc., see the doc directory of the distribution or surf to http://www-ccrma.stanford.edu/software/stk/. Also check the platform specific README's for specific system requirements. +For complete documentation on this ToolKit, the classes, etc., see the doc directory of the distribution or surf to http://ccrma.stanford.edu/software/stk/. Also check the platform specific README's for specific system requirements. PERRY'S NOTES FROM THE ORIGINAL DISTRIBUTION: -This whole world was created with no particular hardware in mind. These examples are intended to be tutorial in nature, as a platform for the continuation of my research, and as a possible starting point for a software synthesis system. The basic motivation was to create the necessary unit generators to do the synthesis, processing, and control that I want to do and teach about. Little thought for optimization was given (see Object.cpp), and therefore improvements, especially speed enhancements, should be possible with these classes. It was written with some basic concepts in mind about how to let compilers optimize. +This whole world was created with no particular hardware in mind. These examples are intended to be tutorial in nature, as a platform for the continuation of my research, and as a possible starting point for a software synthesis system. The basic motivation was to create the necessary unit generators to do the synthesis, processing, and control that I want to do and teach about. Little thought for optimization was given and therefore improvements, especially speed enhancements, should be possible with these classes. It was written with some basic concepts in mind about how to let compilers optimize. Your question at this point might be, "But Perry, with CMix, CMusic, CSound, CShells, CMonkeys, etc. already cluttering the landscape, why a new set of stupid C functions for music synthesis and processing?" The answers lie below. -1) I needed to port many of the things I've done - into something which is generic enough to port - further to different machines. +1) I needed to port many of the things I've done into something which is generic enough to port further to different machines. -2) I really plan to document this stuff, so that - you don't have to be me to figure out what's - going on. (I'll probably be sorry I said this - in a couple of years, when even I can't figure - out what I was thinking.) +2) I really plan to document this stuff, so that you don't have to be me to figure out what's going on. (I'll probably be sorry I said this in a couple of years, when even I can't figure out what I was thinking.) -3) The classic difficulties most people have in - trying to implement physical models are: +3) The classic difficulties most people have in trying to implement physical models are: - A) They have trouble understanding the papers, - and/or in turning the theory into practice. + A) They have trouble understanding the papers, and/or in turning the theory into practice. - B) The Physical Model instruments are a pain to get - to oscillate, and coming up with stable and - meaningful parameter values is required to - get the models to work at all. + B) The Physical Model instruments are a pain to get to oscillate, and coming up with stable and meaningful parameter values is required to get the models to work at all. - This set of C++ unit generators and instruments - might help to diminish the scores of emails I - get asking what to do with those block diagrams - I put in my papers. + This set of C++ unit generators and instruments might help to diminish the scores of emails I get asking what to do with those block diagrams I put in my papers. -4) I wanted to try some new stuff with modal synthesis, - and implement some classic FM patches as well. +4) I wanted to try some new stuff with modal synthesis, and implement some classic FM patches as well. -5) I wanted to reimplement, and newly implement - more of the intelligent and physical performer - models I've talked about in some of my papers. - But I wanted to do it in a portable way, and in - such a way that I can hook up modules quickly. - I also wanted to make these instruments connectable - to such player objects, so folks like Brad Garton - who really think a lot about the players can connect - them to my instruments, a lot about which I think. +5) I wanted to reimplement, and newly implement more of the intelligent and physical performer models I've talked about in some of my papers. But I wanted to do it in a portable way, and in such a way that I can hook up modules quickly. I also wanted to make these instruments connectable to such player objects, so folks like Brad Garton who really think a lot about the players can connect them to my instruments, a lot about which I think. 6) More rationalizations to follow . . . diff --git a/configure.ac b/configure.ac index 83767de..99f1f7b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(STK, 4.1.2, gary@ccrma.stanford.edu, stk) +AC_INIT(STK, 4.2.0, gary@music.mcgill.ca, stk) AC_CONFIG_SRCDIR(src/Stk.cpp) AC_CONFIG_FILES(src/Makefile projects/demo/Makefile projects/effects/Makefile projects/ragamatic/Makefile projects/examples/Makefile) @@ -48,12 +48,12 @@ fi AC_MSG_CHECKING(whether to compile debug version) AC_ARG_ENABLE(debug, [ --enable-debug = enable various debug output], - [AC_SUBST( debug, [-D_STK_DEBUG_] ) AC_SUBST( cflags, [-g] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)], - [AC_SUBST( debug, [] ) AC_SUBST( cflags, [-O2] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)]) + [AC_SUBST( debug, ["-D_STK_DEBUG_ -D__RTAUDIO_DEBUG__"] ) AC_SUBST( cflags, ["-g -O2"] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)], + [AC_SUBST( debug, [] ) AC_SUBST( cflags, [-O3] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)]) # Check compiler and use -Wall if gnu. if test $GXX = "yes" ; then - AC_SUBST( warn, ["-Wall -g"] ) + AC_SUBST( warn, ["-Wall -g -Woverloaded-virtual -D__GXX__"] ) fi if test $realtime = yes; then @@ -64,20 +64,22 @@ if test $realtime = yes; then *-*-linux*) AC_SUBST( sound_api, [_NO_API_] ) + # Look for ALSA library because we need it for RtMidi + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(STK in Linux requires the ALSA asound library for RtMidi!)) + audio_apis="-D__LINUX_ALSASEQ__" + # 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__" + audio_apis="-D__LINUX_JACK__ $audio_apis" 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 @@ -87,18 +89,15 @@ if test $realtime = yes; then audio_apis="-D__LINUX_OSS__ $audio_apis" fi - # If no audio api flags specified, use OSS + # If no audio api flags specified, use ALSA 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__] ) + AC_MSG_RESULT(using ALSA) + audio_apis="-D__LINUX_ALSA__ $audio_apis" 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( audio_apis, ["-D__IRIX_AL__ -LANG:std -w"] ) + AC_SUBST( audio_apis, ["-D__IRIX_AL__ -D__IRIX_MD__ -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!) ) @@ -110,8 +109,6 @@ if test $realtime = yes; then [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 7952a3e..6a1f52a 100644 --- a/doc/Hierarchy.txt +++ b/doc/Hierarchy.txt @@ -4,11 +4,13 @@ By Perry R. Cook and Gary P. Scavone, 1995-2004. STK Classes - See the HTML documentation in the html directory for complete information. - .- Envelope - ADSR + + .- Generator - (Modulate, Noise, SingWave, Envelope) + | | | + | SubNoise ADSR + | Asymp | - |- Noise - SubNoise - | - |- Table + |- Function - (Table, BowTable, JetTable, ReedTable) | |- WvIn - (WaveLoop, RtWvIn, TcpWvIn) | @@ -19,24 +21,14 @@ STK Classes - See the HTML documentation in the html directory for complete info | DelayL FormSwep | DelayA | - |- Echo, Chorus, PitShift - | - |- RtAudio, RtMidi, Socket, Thread + |- RtAudio, RtMidi, RtDuplex, Socket, Thread, Mutex Stk -| - |- Reverb - (PRCRev, JCRev, NRev) + |- Effect - (Echo, Chorus, PitShift, PRCRev, JCRev, NRev) | - |- Modulate - | - |- SingWave - | - |- Voicer + |- Voicer, Message, Skini, MidiFileIn, Phonemes, Sphere, Vector3D | |- Messager | - |- SKINI - | - |- ReedTabl, JetTabl, BowTabl - | | .- FM - (HevyMetl, PercFlut, Rhodey, Wurley, TubeBell, BeeThree, FMVoices) | | | |- Modal - ModalBar @@ -66,8 +58,11 @@ Stk -| Master Class: Stk.cpp Sample rate, byte-swapping, error handling functionality -Sources: Envelope.cpp Linearly Goes to Target by Rate +Sources: Generator.cpp Abstract Base Class for Various Source Signal Classes + Function.cpp Abstract Base Class for Various Input/Output Mapping Classes + Envelope.cpp Linearly Goes to Target by Rate ADSR.cpp ADSR Flavor of Envelope + Asymp.cpp Exponentially Approaches Target Noise.cpp Random Number Generator SubNoise.cpp Random Numbers each N samples Table.cpp Lookup Table (assumes given data in big-endian format) @@ -140,7 +135,7 @@ Shakers.cpp PhISM statistical model for shakers and real-world sound effects Mesh2D.cpp Two-dimensional, rectilinear digital waveguide mesh. Whistle.cpp Hybrid physical/spectral model of a police whistle. -Reverb.cpp Reverberator Effects Processor Master Class for reverberators +Effect.cpp Effects Processor Base Class JCRev.cpp Chowning Reverberator 3 series allpass units, 4 parallel combs, 2 stereo delays NRev.cpp Another famous CCRMA Reverb 8 allpass, 6 parallel comb filters PRCRev.cpp Dirt Cheap Reverb by Cook 2 allpass, 2 comb filters @@ -160,7 +155,7 @@ demo.cpp Demonstration program for most synthesis algorithms effects.cpp Effects demonstration program ragamatic.cpp Nirvana just waiting to happen -SKINI.cpp SKINI file/message parser object +Skini.cpp SKINI file/message parser object SKINI.msg #defines for often used and universal MIDI/SKINI symbols SKINI.tbl Table of SKINI messages diff --git a/doc/README-Linux.txt b/doc/README-Linux.txt index 6c53ea2..8f64655 100644 --- a/doc/README-Linux.txt +++ b/doc/README-Linux.txt @@ -4,20 +4,15 @@ 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 Linux is currently using either the Open Sound System (OSS) or the Advanced Linux Sound Architecture (ALSA) sound and MIDI APIs. The free version of OSS works as well (and in some cases better than the commercial OSS version ... such as with my Maestro 2e chipset). In general, the ALSA drivers also seem to perform well. You can read more about ALSA at http://www.alsa-project.org/. ALSA is open source and holds great promise for audio under Linux. The API is selected during compilation using either the __LINUX_ALSA__ or __LINUX_OSS__ definitions. The configure script uses the OSS API by default. The ALSA API can be selected by passing the "--with-alsa" option to configure. +Realtime audio support for Linux currently includes the Advanced Linux Sound Architecture (ALSA), the JACK low-latency audio server, and/or Open Sound System (OSS) APIs. One or more APIs are selected during compilation using the __LINUX_ALSA__, __LINUX_JACK__, and/or __LINUX_OSS__ definitions. Because the ALSA library is now integrated into the standard Linux kernel, it is the default audio/MIDI API with STK versions 4.2 and higher. The __LINUX_ALSASEQ__ definition is required to compile RtMidi with ALSA sequencer support. Native OSS MIDI support no longer exists in RtMidi. If the __LINUX_OSS__ preprocessor definition is specified, only OSS audio support will be compiled and RtMidi will still be compiled using the ALSA API. For this reason, STK now requires the asound library for realtime support. Realtime programs must also link with the pthread library. The OSS audio API can be selected by passing the "--with-oss" option to configure. -STK should compile without much trouble under Linux ... afterall, it is primarily developed on Linux platforms. Since all Linux distributions typically include the GNU makefile utilities, you should be able to use the default Makefile. Typing "make" will initiate the compilation process. +The free version of OSS generally works as well (and in some cases better than the commercial OSS version ... such as with my Maestro 2e chipset). In general, the ALSA drivers also seem to perform well. You can read more about ALSA at http://www.alsa-project.org/. ALSA is open source and holds great promise for audio under Linux. + +STK should compile without much trouble under Linux. Since all Linux distributions typically include the GNU makefile utilities, you should be able to use the default Makefile. Typing "make" will initiate the compilation process. MIDIATOR SERIAL PORT MIDI SUPPORT: -STK now has special support for the MIDIator serial port MIDI interface. This is of primary interest to us laptop users, whose computers usually don't have a gameport. If you want to buy one of these devices, make sure you get the MS-124w model (www.midiator.com). For it to work in STK, you must provide the __MIDIATOR__ definition during compilation (in addition to either __LINUX_ALSA__ or __LINUX_OSS__) or pass the "--enable-midiator" option to configure. - -There are a few things that need to be done on your system to get the MIDIator working. Assuming you wish to attach the MIDIator to serial port 0, add the following lines to your bootup sequence in /etc/rc.d/rc.local: - -setserial /dev/ttyS0 baud_base 57600 -setserial /dev/ttyS0 divisor 1 - -You may need to specify the full path to the setserial function, depending on how your PATH variable is set up. Also, you may need to modify the permissions of /dev/ttyS0 (chmod a+rwx). And finally, the MIDIator should be set for "single addresssed" mode (the S/A switch on S and the A/B switch on A), which puts identical output on all 4 MIDI output ports. It is possible to use the MIDIator in a "multi-port" mode, though I'm not currently supporting that in STK. +MIDIator support has been removed from RtMidi with STK versions 4.2 and higher. If you really need it, you can contact us to get an old distribution. NOTE REGARDING PTHREADS: diff --git a/doc/README-MacOSX.txt b/doc/README-MacOSX.txt index 9856452..70431e3 100644 --- a/doc/README-MacOSX.txt +++ b/doc/README-MacOSX.txt @@ -6,17 +6,16 @@ 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 version 10.2. +It is necessary to install the OS X developer kit in order to compile STK. STK was successfully tested on OS X versions 10.2 and 10.3. -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 internal Macintosh audio hardware typically supports a sample rate of 44100 Hz only. The default STK sample rate is now 44100 Hz and all current example programs use this rate. However, it is possible to manually override this value in some programs from the command-line. The default sample rate is set in Stk.h. In addition, the RT_BUFFER_SIZE, specified in Stk.h, could be increased (to a higher power of two) for more robust performance. There is a potential conflict between the STK Delay class and a Delay() function declared in OSUtils.h (which is included via ). In general, this conflict can be avoided via the use of a namespace (an explicit Delay::Delay declaration), though this made the Windows Visual C++ compiler barf. If you use STK classes within a project that includes the OSUtils.h file, you will likely need to make changes in STK classes that use the Delay class. Tcl/Tk on OS X: -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. +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 10.2 and 10.3 systems. 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. +It appears that socket support in Tcl/Tk on OS X uses the Nagle algorithm, which produces poor response between changes made in the tcl/tk script and the resulting audio updates. Note that this is only a problem when using a socket connection from a Tcl/Tk script. -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-SGI.txt b/doc/README-SGI.txt index 5be5d16..003a932 100644 --- a/doc/README-SGI.txt +++ b/doc/README-SGI.txt @@ -8,6 +8,8 @@ The project Makefiles are created by configure. If you have trouble running "ma Another issue that has crept up with this release is proper compiler support for C++ error handling. If you experience problems, you probably don't have a recent version of the C++ compiler. Otherwise, STK should compile and run on SGI platforms without any problems. Release 4.0 of STK is confirmed to compile (with various warnings) using CC version 7.30. +The __IRIX_AL__ and __IRIX_MD__ preprocessor definitions are required for realtime audio and MIDI support. + NOTE REGARDING PTHREADS: Since release 3.1, STK has used the pthread API under Irix. It appears that pthread functionality is standard on SGI, so this change shouldn't cause any problems. If I'm wrong, let me know! diff --git a/doc/README-Win.txt b/doc/README-Win.txt index b03e66b..db38b3c 100644 --- a/doc/README-Win.txt +++ b/doc/README-Win.txt @@ -15,7 +15,7 @@ Both the DirectSound and Steinberg ASIO audio APIs are supported for realtime au When using the DirectSound API for audio input, latency is typically pretty horrendous (should we be surprised?). Also, there is a slight chance you don't have DirectSoundCapture support on your computer. If not, you should download the DirectX 6.0 (or higher) runtime libraries from Microsoft's WWW site (http://www.microsoft.com/directx/download.asp) in order to run the pre-compiled STK executables for Windoze. The last time I checked, there was no DirectSoundCapture support for WindowsNT ... you'll have to switch to Windows 2000 or XP or use an ASIO driver. I stopped supporting the WinMM audio output code with release 3.2. -Realtime MIDI input is supported using the winmm.lib API. +Realtime MIDI input/output is supported by RtMidi using the winmm.lib API and requires the __WINDOWS_MM__ preprocessor definition. Visual C++ 6.0 workspaces have been created for the various STK projects. Everything has already been configured for you. The intermediate .obj files will be written to either the "Release" or "Debug" directories, but the executable files will be written to the main project directories (where they need to be for proper execution). If you should somehow lose or hose the VC++ workspace file for a project, then you will have to do a LOT of configuring to recreate it ... it's probably easier just to download the distribution again from our WWW sites. Anyway, for your benefit and mine, here is a list of things that need to be added to the various "Project Settings": @@ -27,7 +27,7 @@ Visual C++ 6.0 workspaces have been created for the various STK projects. Every 4. Under C/C++ > Preprocessor: Add "../../include" directory to the "extra include" field. -5. Under C/C++ > Preprocessor: Add "__WINDOWS_DS__" to the definitions field. +5. Under C/C++ > Preprocessor: Add "__WINDOWS_DS__", "__WINDOWS_MM__", and "__LITTLE_ENDIAN__ to the definitions field. 6. Add all the necessary files to the project. @@ -49,7 +49,7 @@ WINDOWS 95/98: PLAY SKINI SCOREFILES IN REALTIME: - demo Clarinet -or < scores/streetsf.ski + demo Clarinet -or -if scores/streetsf.ski USE TCL/TK GUIs FOR REALTIME CONTROL: diff --git a/doc/ReleaseNotes.txt b/doc/ReleaseNotes.txt index 84f1f0d..41590de 100644 --- a/doc/ReleaseNotes.txt +++ b/doc/ReleaseNotes.txt @@ -2,6 +2,28 @@ The Synthesis ToolKit in C++ (STK) By Perry R. Cook and Gary P. Scavone, 1995-2004. +v4.2.0: (4 October 2004) +- simultaneous multiple audio APIs supported at compile time +- fixed hidden overloaded virtual functions +- new Asymp exponential envelope class +- various changes to better conform to standard C++ programming practices +- MY_FLOAT type converted to StkFloat and changed throughout (use treesed utility to search/replace in old files) +- most example programs rewritten to use an audio callback paradigm (which works better in OS-X) +- new StkFrames class for vectorized multichannel data and associated new tick() functions making use of StkFrames +- new RtMidi class with MIDI output capabilities (API changes) +- new MidiFileIn class for reading MIDI files +- revised Filter classes to use std::vectors for coefficients (API changes) +- revised Messager class (now queues messages for retrieval) (API changes) +- new abstract parent Effect class for various effects +- added setT60 function to all reverbs +- new abstract parent Generator class for various signal sources +- new abstract parent Function class for tables and various non-linear functions +- Skini class completely rewritten (simplified) using the C++ STL (API changes) +- WvOut classes now clip to -1.0 to +1.0 and report out of range +- new Mutex class +- turned Nagle algorithm off by default in Socket class +- error reporting standardized in all classes + v4.1.3: (22 March 2004) - bug fix in RtAudio for Windows DirectSound output only support @@ -108,7 +130,7 @@ v3.0: (10 October 1999) - added RawWvOut class - new WvIn class with RawWvIn, SndWvIn, WavWvIn, MatWvIn, and RTWvIn subclasses - removed RawWave, RawShot, RawInterp, and RawLoop classes (supplanted by RawWvIn) -- multi-channel data support in WvIn and WvOut classes using MY_MULTI data type (pointer to MY_FLOAT) and the methods mtick() and mlastOutput() +- multi-channel data support in WvIn and WvOut classes using MY_MULTI data type (pointer to StkFloat) and the methods mtick() and mlastOutput() - now writing to primary buffer under Windoze when allowed by hardware - cleaned up Object.h a bit - pulled various utility and thread functions out of syntmono.cpp (to aid readability of the code) diff --git a/doc/SKINI.txt b/doc/SKINI.txt index d52dd02..d42c731 100644 --- a/doc/SKINI.txt +++ b/doc/SKINI.txt @@ -10,7 +10,7 @@ for the Synthesis Toolkit in C++ by Perry R. Cook. * A SKINI Haiku. * ********************************* -Profound thanks to Dan Trueman, Brad Garton, and +Profound thanks to Dan trueman, Brad Garton, and Gary Scavone for input on this revision. Thanks also to MIDI, the NeXT MusicKit, ZIPI and all the creators and modifiers of these for good bases @@ -120,7 +120,7 @@ upon/from which to build and depart. 4) C Files Used To Implement SKINI - SKINI.cpp is an object which can either open a SKINI file, and + Skini.cpp is an object which can either open a SKINI file, and successively read and parse lines of text as SKINI strings, or accept strings from another object and parse them. The latter functionality would be used by a socket, pipe, or other connection @@ -128,11 +128,11 @@ upon/from which to build and depart. but not restricted to real time. SKINI.msg should be included by anything wanting to use the - SKINI.cpp object. This is not mandatory, but use of the __SK_blah_ + Skini.cpp object. This is not mandatory, but use of the __SK_blah_ symbols which are defined in the .msg file will help to ensure clarity and consistency when messages are added and changed. - SKINI.tbl is used only by the SKINI parser object (SKINI.cpp). + SKINI.tbl is used only by the SKINI parser object (Skini.cpp). In the file SKINI.tbl, an array of structures is declared and assigned values which instruct the parser as to what the message types are, and what the fields mean for those message types. @@ -240,7 +240,7 @@ upon/from which to build and depart. 7) The SKINI.tbl File, How Messages are Parsed: The SKINI.tbl file contains an array of structures which - are accessed by the parser object SKINI.cpp. The struct is: + are accessed by the parser object Skini.cpp. The struct is: struct SKINISpec { char messageString[32]; long type; @@ -322,70 +322,67 @@ upon/from which to build and depart. 8) Objects using SKINI - Here's a simple example of code which uses the SKINI object + Here's a simple example of code which uses the Skini object to read a SKINI file and control a single instrument. + Skini score; + Skini::Message message; instrument = new Mandolin(50.0); - score = new SKINI(argv[1]); - while(score->getType() > 0) { - tempDouble = score->getDelta(); - if (tempDouble < 0) { - tempDouble = - tempDouble; - tempDouble = tempDouble - output.getTime(); - if (tempDouble < 0) { - printf("Bad News Here!!! Backward Absolute Time Required.\n"); - tempDouble = 0.0; - } + score.setFile( argv[1] ); + while ( score.nextMessage( message ) != 0 ) { + tempDouble = message.time; + if (tempDouble < 0) { + tempDouble = - tempDouble; + tempDouble = tempDouble - output.getTime(); + if (tempDouble < 0) { + printf("Bad News Here!!! Backward Absolute Time Required.\n"); + tempDouble = 0.0; } - tempLong = (long) (tempDouble * Stk::sampleRate()); - for (i=0;itick()); + } + tempLong = (long) ( tempDouble * Stk::sampleRate() ); + for ( i=0; itick() ); + } + + tempDouble3 = message.floatValues[1] * NORM_MIDI; + if ( message.type == __SK_NoteOn_ ) { + if ( tempDouble3 == 0.0 ) { + tempDouble3 = 0.5; + instrument->noteOff( tempDouble3 ); } - tempDouble3 = score->getByteThree(); - if (score->getType()== __SK_NoteOn_ ) { - tempDouble3 *= NORM_MIDI; - if (score->getByteThree() == 0) { - tempDouble3 = 0.5; - instrument->noteOff(tempDouble3); - } - else { - tempLong = (int) score->getByteTwo(); - tempDouble2 = Midi2Pitch[tempLong]; - instrument->noteOn(tempDouble2,tempDouble3); - } + else { + tempLong = message.intValues[0]; + tempDouble2 = Midi2Pitch[tempLong]; + instrument->noteOn( tempDouble2, tempDouble3 ); } - else if (score->getType() == __SK_NoteOff_) { - tempDouble3 *= NORM_MIDI; - instrument->noteOff(tempDouble3); - } - else if (score->getType() == __SK_ControlChange_) { - tempLong = score->getByteTwoInt(); - instrument->controlChange(tempLong,temp3.0); - } - score->nextMessage(); + } + else if ( message.type == __SK_NoteOff_ ) { + instrument->noteOff( tempDouble3 ); + } + else if ( message.type == __SK_ControlChange_ ) { + tempLong = message.intValues[0]; + instrument->controlChange( tempLong, tempDouble3 ); + } } - When the score (SKINI object) object is created from the - filename in argv[1], the first valid command line is read - from the file and parsed. + When a SKINI score is passed to a Skini object using the + Skini::setFile() function, valid messages are read from + the file and returned using the Skini::nextMessage() function. - The score->getType() retrieves the messageType. If this is - -1, there are no more valid messages in the file and the - synthesis loop terminates. Otherwise, the message type is - returned. + A Skini::Message structure contains all the information parsed + from a single SKINI message. A returned message type of zero + indicates either an invalid message or the end of a scorefile. - getDelta() retrieves the deltaTime until the current message - should occur. If this is greater than 0, synthesis occurs - until the deltaTime has elapsed. If deltaTime is less than - zero, the time is interpreted as absolute time and the output - device is queried as to what time it is now. That is used to - form a deltaTime, and if it's positive we synthesize. If - it's negative, we print an error and pretend this never - happened and we hang around hoping to eventually catch up. + The "time" member of a Skini::Message is the deltaTime until the + current message should occur. If this is greater than 0, + synthesis occurs until the deltaTime has elapsed. If deltaTime is + less than zero, the time is interpreted as absolute time and the + output device is queried as to what time it is now. That is used + to form a deltaTime, and if it's positive we synthesize. If it's + negative, we print an error, pretend this never happened and we + hang around hoping to eventually catch up. The rest of the code sorts out message types NoteOn, NoteOff (including NoteOn with velocity 0), and ControlChange. The code implicitly takes into account the integer type of the control number, but all other data is treated as double float. - - The last line reads and parses the next message in the file. diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index c829856..f4b1c07 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -4,7 +4,7 @@ # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = STK -PROJECT_NUMBER = +PROJECT_NUMBER = 4.2.0 OUTPUT_DIRECTORY = . OUTPUT_LANGUAGE = English USE_WINDOWS_ENCODING = NO @@ -18,10 +18,10 @@ STRIP_FROM_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO -DETAILS_AT_TOP = NO +DETAILS_AT_TOP = YES INHERIT_DOCS = YES DISTRIBUTE_GROUP_DOC = NO -TAB_SIZE = 8 +TAB_SIZE = 9 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO @@ -67,13 +67,13 @@ WARN_LOGFILE = INPUT = . \ ../../include FILE_PATTERNS = *.txt \ - *.h \ - *.cpp + *.msg \ + *.h RECURSIVE = YES EXCLUDE = ../../src/asio EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = -EXAMPLE_PATH = +EXAMPLE_PATH = ../../projects/examples EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = @@ -83,7 +83,7 @@ FILTER_SOURCE_FILES = NO # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES -INLINE_SOURCES = NO +INLINE_SOURCES = YES STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES @@ -117,7 +117,7 @@ TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- -GENERATE_LATEX = YES +GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex diff --git a/doc/doxygen/compile.txt b/doc/doxygen/compile.txt index 32777d0..8eccfa1 100644 --- a/doc/doxygen/compile.txt +++ b/doc/doxygen/compile.txt @@ -5,7 +5,7 @@ The Synthesis ToolKit can be used in a variety of ways, depending on your partic \section rtvsnonrt "Realtime" vs. "Non-Realtime" -Most of the Synthesis ToolKit classes are platform independent. That means that they should compile on any reasonably current C++ compiler. The functionality needed for realtime audio and MIDI input/output, as well as realtime control message acquistion, is inherently platform and operating-system (OS) dependent. STK classes which require specific platform/OS support include RtAudio, RtWvOut, RtWvIn, RtDuplex, RtMidi, TcpWvIn, TcpWvOut, Socket, and Thread. These classes currently can only be compiled on Linux, Irix, Macintosh OS X, and Windows systems using the __LINUX_OSS__, __LINUX_ALSA__, __IRIX_AL__, __MACOSX_CORE__, __WINDOWS_DS__, or __WINDOWS_ASIO__ preprocessor definitions. +Most of the Synthesis ToolKit classes are platform independent. That means that they should compile on any reasonably current C++ compiler. The functionality needed for realtime audio and MIDI input/output, as well as realtime control message acquistion, is inherently platform and operating-system (OS) dependent. STK classes which require specific platform/OS support include RtAudio, RtWvOut, RtWvIn, RtDuplex, RtMidi, TcpWvIn, TcpWvOut, Socket, Thread, and Mutex. These classes currently can only be compiled on Linux, Irix, Macintosh OS X, and Windows systems. Without the "realtime" classes, it is still possible to read SKINI scorefiles for control input and to read and write to/from a variety of audio file formats (WAV, SND, AIFF, MAT-file, and RAW). If compiling for a "little-endian" host processor, the __LITTLE_ENDIAN__ preprocessor definition should be provided. @@ -25,25 +25,25 @@ STK compiles with realtime support on the following flavors of the Unix operatin Linux ALSA - __LINUX_ALSA__, __LITTLE_ENDIAN__ + __LINUX_ALSA__, __LINUX_ALSASEQ__, __LITTLE_ENDIAN__ asound, pthread Linux - OSS - __LINUX_OSS__, __LITTLE_ENDIAN__ - pthread + OSS (audio only, use ALSA for MIDI support) + __LINUX_OSS__, __LINUX_ALSASEQ__, __LITTLE_ENDIAN__ + asound, pthread Macintosh OS X CoreAudio __MACOSX_CORE__ - pthread, stdc++, CoreAudio, CoreMIDI, CoreFoundation + pthread, CoreAudio, CoreMIDI, CoreFoundation Irix AL - __IRIX_AL__ + __IRIX_AL__, __IRIX_MD__ audio, pthread @@ -51,21 +51,21 @@ STK compiles with realtime support on the following flavors of the Unix operatin The available C++ compilers on any of these systems can vary. -One approach in using STK is to simply copy the class files needed for a particular program into a project directory. Taking the sineosc.cpp example from the previous tutorial chapter, it would be necessary to set up a directory that includes the files sineosc.cpp, the rawwave file sinewave.raw in a subdirectory called rawwaves, and the header and source files for the classes Stk, WvIn, WaveLoop, and WvOut. The program could then be compiled on a Linux system using the GNU g++ compiler as follows: +One approach in using STK is to simply copy the class files needed for a particular program into a project directory. Taking the sineosc.cpp example from the previous tutorial chapter, it would be necessary to set up a directory that includes the files sineosc.cpp, the rawwave file sinewave.raw in a subdirectory called rawwaves, and the header and source files for the classes Stk, WvIn, WaveLoop, and WvOut. The program could then be compiled on a little-endian system, such as a PC running Linux, using the GNU g++ compiler as follows: \code g++ -Wall -D__LITTLE_ENDIAN__ -o sineosc Stk.cpp WvIn.cpp WaveLoop.cpp WvOut.cpp sineosc.cpp \endcode 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). 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. +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. \subsection library Library Use: -The STK distribution provides a Makefile that can be used on Unix systems to build a static library. After unpacking the distribution (tar -xzf stk-4.x.tar.gz), run the configure script by typing ./configure from the top level distribution directory (see the INSTALL file in the same directory for more information). Then from within the src directory, type make. After a successful build, you may wish to move the library (libstk.a) and the contents of the include directory to standard library and include search paths on your system. For example, the linux RPM distribution of STK puts the library in /usr/lib/ and the STK header files in /usr/include/stk/. +The STK distribution provides a Makefile that can be used on Unix systems to build a static library. After unpacking the distribution (tar -xzf stk-4.x.x.tar.gz), run the configure script by typing ./configure from the top level distribution directory (see the INSTALL file in the same directory for more information). Then from within the src directory, type make. After a successful build, you may wish to move the library (libstk.a) and the contents of the include directory to standard library and include search paths on your system. For example, the linux RPM distribution of STK puts the library in /usr/lib/ and the STK header files in /usr/include/stk/. -Assuming the library is located in a standard search path and the header files are located in /usr/include/stk/, the sineosc.cpp example from the previous tutorial chapter can be compiled on a Linux system using the GNU g++ compiler as follows: +Assuming the library is located in a standard search path and the header files are located in /usr/include/stk/, the sineosc.cpp example from the previous tutorial chapter can be compiled on a little-endian system using the GNU g++ compiler as follows: \code g++ -Wall -D__LITTLE_ENDIAN__ -I/usr/include/stk -o sineosc sineosc.cpp -lstk @@ -91,9 +91,9 @@ STK has been tested on Windows platforms using the Visual C++ compiler only. It The approach when using Visual C++ is to build a project which includes the necessary ToolKit files from the distribution src and include directories. For the example program from the previous tutorial chapter, create a VC++ console application project, add the Stk, WvIn, WaveLoop, and WvOut class files, as well as sineosc.cpp, and make sure the sinewave.raw file is in the subdirectory rawwaves. -For programs using any of the STK realtime classes mentioned above, it is necessary to link with the DirectSound (dsound.lib), winmm.lib, and Wsock32.lib libraries, select the multithreaded library, and provide the __LITTLE_ENDIAN__ and __WINDOWS_DS__ preprocessor definitions. +For programs using any of the STK realtime classes mentioned above, it is necessary to link with the DirectSound (dsound.lib), winmm.lib, and Wsock32.lib libraries, select the multithreaded library, and provide the __LITTLE_ENDIAN__, __WINDOWS_DS__, and __WINDOWS_MM__ preprocessor definitions. -For Steinberg ASIO support, use the __WINDOWS_ASIO__ preprocessor definition, include all the files in the src/asio/ directory (i.e., asio.h,cpp, asiodrivers.h,cpp, ...), and link with the winmm.lib, and Wsock32.lib libraries. +For Steinberg ASIO support, use the __WINDOWS_ASIO__ preprocessor definition (and the __WINDOWS_MM__ definition for RtMidi support), include all the files in the src/asio/ directory (i.e., asio.h,cpp, asiodrivers.h,cpp, ...), and link with the winmm.lib, and Wsock32.lib libraries. -[Next tutorial]   [Main tutorial page] +[Next tutorial]   [Main tutorial page] */ diff --git a/doc/doxygen/control.txt b/doc/doxygen/control.txt index 17d6b81..5b963f3 100644 --- a/doc/doxygen/control.txt +++ b/doc/doxygen/control.txt @@ -17,114 +17,27 @@ StringDetune 0.100000 2 12.0 NoteOff 1.000000 2 69.0 64.0 \endcode -MIDI messages (with the exception of Sysex) are easily represented within the SKINI protocol. +MIDI messages are easily represented within the SKINI protocol. -The class Messager can be used to acquire and parse MIDI messages from a MIDI device and SKINI messages from STDIN and socket connections. Many of the example programs included with the ToolKit distribution use a Messager instance to accept control input from the accompanying tcl/tk graphical user interfaces, from external MIDI devices, or from SKINI scorefiles. +The class Messager can be used to acquire and parse MIDI messages from a MIDI device and SKINI messages from STDIN and socket connections. Incoming messages are acquired asynchronously and saved to an internal message queue of Skini::Message types (MIDI messages are converted to the Skini:Message format). The user then uses the Messager:popMessage() function to retrieve incoming control messages. This function does not block, instead returning a message type of zero when no more messages are in the queue. Many of the example programs included with the ToolKit distribution use a Messager instance to accept control input from the accompanying tcl/tk graphical user interfaces, from external MIDI devices, or from SKINI scorefiles. -In the following example, we'll modify the bethree.cpp program from the previous tutorial chapter and incorporate a Messager class to allow control via a SKINI scorefile. +In the following example, we'll modify the bethree.cpp program from the previous tutorial chapter and incorporate a Messager class to allow control via SKINI messages read from a SKINI file. + +\include controlbee.cpp + +A realtime control message will usually have a delta time of zero, in which case it is processed as soon as possible. Non-realtime messages, normally from a scorefile, will usually have non-zero delta times. The scheme used in this example is designed to work for both scorefile and realtime input types. When no message is available from the queue, the instrument is "ticked" for DELTA_CONTROL_TICKS and then the queue is checked again. The value of DELTA_CONTROL_TICKS roughly defines the program "control rate" in a realtime context, though multiple available messages in the queue are processed in immediate succession when their delta time values are zero. + +The \c processMessage() function centralizes the handling of control messages. Other control update schemes can be implemented, perhaps using a separate thread or in the \c main() function, and this function should work in any context. + +Assuming the program is compiled as controlbee and the SKINI scorefile bookert.ski is in the scores directory, the program can be run as: \code -// controlbee.cpp - -#include "BeeThree.h" -#include "RtWvOut.h" -#include "Messager.h" -#include "SKINI.msg" -#include - -int main() -{ - // Set the global sample rate before creating class instances. - Stk::setSampleRate( 44100.0 ); - - Instrmnt *instrument = 0; - RtWvOut *output = 0; - Messager *messager = 0; - bool done = FALSE; - - try { - // Define and load the BeeThree instrument - instrument = new BeeThree(); - - // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); - } - catch (StkError &) { - goto cleanup; - } - - try { - // Create a Messager instance to read from a redirected SKINI scorefile. - messager = new Messager(); - } - catch (StkError &) { - goto cleanup; - } - - // Play the instrument until the end of the scorefile. - int i, nTicks, type; - MY_FLOAT byte2, byte3, frequency; - while (!done) { - - // Look for new messages and return a delta time (in samples). - type = messager->nextMessage(); - if (type < 0) - done = TRUE; - - nTicks = messager->getDelta(); - try { - for ( i=0; itick( instrument->tick() ); - } - catch (StkError &) { - goto cleanup; - } - - if ( type > 0 ) { - // Process the new control message. - byte2 = messager->getByteTwo(); - byte3 = messager->getByteThree(); - - switch(type) { - - case __SK_NoteOn_: - frequency = (MY_FLOAT) 220.0 * pow( 2.0, (byte2 - 57.0) / 12.0 ); - instrument->noteOn( frequency, byte3 * ONE_OVER_128 ); - break; - - case __SK_NoteOff_: - instrument->noteOff( byte3 * ONE_OVER_128 ); - break; - - case __SK_ControlChange_: - instrument->controlChange( (int) byte2, byte3 ); - break; - - case __SK_AfterTouch_: - instrument->controlChange( 128, byte2 ); - break; - } - } - } - - cleanup: - delete instrument; - delete output; - delete messager; - - return 0; -} -\endcode - -Assuming the program is compiled as controlbee and the SKINI scorefile bookert.ski is in the scores directory, the scorefile could be redirected to the program as: - -\code -controlbee < scores/bookert.ski +controlbee scores/bookert.ski \endcode Only a few basic SKINI message type case statements are included in this example. It is easy to extend the program to support a much more elaborate set of instrument control parameters. -This example could also be easily extended to accept "realtime" control input messages via STDIN, socket, or MIDI connections. The Messager class constructor takes an optional argument consisting of a bitmask of the following options: STK_PIPE, STK_SOCKET, and/or STK_MIDI. +This example could also be easily extended to accept "realtime" control input messages via pipe, socket or MIDI connections. The Messager class provides Messager::startStdInput(), Messager::startSocketInput(), and Messager::startMidiInput() functions for this purpose. [Next tutorial]   [Main tutorial page] */ diff --git a/doc/doxygen/crealtime.txt b/doc/doxygen/crealtime.txt new file mode 100644 index 0000000..af70513 --- /dev/null +++ b/doc/doxygen/crealtime.txt @@ -0,0 +1,26 @@ +/*! \page crealtime Realtime Audio (callback) + +The previous section described the use of the RtWvOut class for realtime audio output. The RtWvOut::tick() function periodically pauses program execution in order to send a buffer of audio data to the computer's audio hardware (referred to as blocking functionality). These pauses will effectively limit a program's computations to the correct number of samples per second, which is defined by the sample rate of the hardware. + +An alternative scheme for audio input/output is to define a specific function in which audio computations are performed and to let the audio system call this function when more input/output data can be accepted by the hardware (referred to as a callback scheme). In this section, we show how the previous rtsine.cpp program can be modified to work in a callback scenario. There is no "single-sample" interface for this functionality. The callback function will be invoked automatically by the audio system controller (RtAudio) when new data is needed and it is necessary to compute a full audio buffer of samples at that time (see \ref callback for further information). + +\include crtsine.cpp + +The sinusoidal oscillator is created as before. The instantiation of RtAudio requires quite a few more parameters, including output/input device and channel specifiers, the data format, and the desired buffer length (in frames). In this example, we request a single output channel using the default output device, zero channels of input, the RtAudio data format which corresponds to an StkFloat, and the RT_BUFFER_SIZE defined in Stk.h. The last argument is an API-dependent buffering parameter (see RtAudio for further information). + +After the digital-to-analog converter (dac) and oscillator are successfully created, it is necessary to provide the audio system controller with a pointer to our callback function. The RtAudio::setStreamCallback() function takes a pointer to the callback function and an optional pointer to data that will be made available in the callback. In this example, we need to pass only the pointer to the oscillator. In more complex programs, it is typically necessary to put all shared data in a struct (see the next tutorial program for an example) or make use of global variables. + +Our callback routine is the \c tick() function. %Function arguments include a pointer to the audio data buffer, the buffer size (in frames), and the data pointer passed to the RtAudio::setStreamCallback() function (if it exists). It is necessary to cast these pointers to their corresponding data types before use. Our tick() routine simply "ticks" the oscillator for \c bufferSize counts and writes the result into the audio data buffer before returning. + +The \c main() function blocks at the std::cin.get() call until the user hits the "enter" key, after which the audio controller is shut down and program execution ends. + +\section callback Blocking vs. Callbacks + +Prior to version 4.2.0, all STK example projects and programs used blocking audio input/output functionality (typically with the RtWvIn, RtWvOut, or RtDuplex classes). In many instances, a blocking scheme results in a clearer and more straight forward program structure. Within a graphical user interface (GUI) programming context, however, callback routines are often more natural. + +The RtAudio class provides both blocking and callback routines for all supported audio APIs. It should be noted that it is easy to embed blocking calls within a thread to create "callback-like" functionality. In fact, this is what RtAudio does for those audio APIs which are naturally based on blocking routines (Linux ALSA and OSS, SGI Irix, and Windows DirectSound). It is much more difficult to make an inherently callback-based system work like a blocking scheme. RtAudio attempts to do this with the Linux JACK, Macintosh OS-X CoreAudio, and Windows ASIO APIs, but the result is not fully robust (audio over/underruns are more likely to occur). + +In order to allow all STK programs to function with equal proficiency on all supported computer platforms, a decision was made to modify the example projects to use audio callback routines. The result is a more complicated code structure, which is unfortunate given that we generally strive to make STK code as clear as possible for educational purposes. This was especially an issue with the demo program because it is designed to function in both realtime and non-realtime contexts. The use of global variables has been avoided by defining data structures to hold all variables which must be accessible to the callback routine and other functions. Alternative schemes for making control updates could be designed depending on particular program needs and constraints. + +[Next tutorial]   [Main tutorial page] +*/ diff --git a/doc/doxygen/download.txt b/doc/doxygen/download.txt index fb448f6..751de48 100644 --- a/doc/doxygen/download.txt +++ b/doc/doxygen/download.txt @@ -1,15 +1,39 @@ /*! \page download Download and Release Notes -Version 4.1.3, 22 March 2004

+Version 4.2.0, 4 October 2004

\section notes Release Notes: +\subsection v4dot2dot0 Version 4.2.0 + +
    +
  • Simultaneous multiple audio APIs supported at compile time.
  • +
  • Various changes to better conform to standard C++ programming practices.
  • +
  • Fixed hidden overloaded virtual functions.
  • +
  • New Asymp exponential envelope class.
  • +
  • MY_FLOAT type converted to StkFloat and changed throughout (use \c treesed utility to search/replace in old files).
  • +
  • Most example programs rewritten to use an audio callback paradigm (which works better in OS-X).
  • +
  • New StkFrames class for vectorized multichannel data and associated new tick() functions making use of StkFrames.
  • +
  • New RtMidi class with MIDI output capabilities (API changes).
  • +
  • New MidiFileIn class for reading MIDI files.
  • +
  • Revised Filter classes to use std::vectors for coefficients (API changes).
  • +
  • Revised Messager class (API changes).
  • +
  • New abstract parent Effect class for various effects.
  • +
  • New abstract parent Generator class for various signal sources.
  • +
  • New abstract parent Function class for tables and various non-linear functions.
  • +
  • Skini class completely rewritten (simplified) using the C++ STL (API changes).
  • +
  • WvOut classes now clip to -1.0 to +1.0 and report out of range.
  • +
  • New Mutex class.
  • +
  • Turned Nagle algorithm off by default in Socket class.
  • +
  • Error reporting standardized in all classes.
  • +
+ \subsection v4dot1dot3 Version 4.1.3
    @@ -30,7 +54,7 @@
  • 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.
  • +
  • Updated \ include statements and appended "std::" as necessary throughout for compatibility with gcc 3.
\subsection v4dot1dot1 Version 4.1.1 @@ -138,7 +162,7 @@
  • Added RawWvOut class.
  • New WvIn class with RawWvIn, SndWvIn, WavWvIn, MatWvIn, and RTWvIn subclasses.
  • Removed RawWave, RawShot, RawInterp, and RawLoop classes (supplanted by RawWvIn).
  • -
  • Multi-channel data support in WvIn and WvOut classes using MY_MULTI data type (pointer to MY_FLOAT) and the methods mtick() and mlastOutput().
  • +
  • Multi-channel data support in WvIn and WvOut classes using MY_MULTI data type (pointer to StkFloat) and the methods mtick() and mlastOutput().
  • Now writing to primary buffer under Windoze when allowed by hardware.
  • Cleaned up Object.h a bit.
  • Pulled various utility and thread functions out of syntmono.cpp (to aid readability of the code).
  • @@ -160,11 +184,11 @@
    • Unification of the capabilities of STK across the various platforms. All of the previous SGI functionality has been ported to Linux and Windows, including realtime sound output and MIDI input.
    • MIDI input (with optional time-stamping) supported on SGI, Linux (OSS device drivers only), and Windows operating systems. Time stamping under IRIX and Windows is quantized to milliseconds and under Linux to hundredths of a second.
    • -
    • Various Sound Output Options - .wav, .snd, and .mat (Matlab MAT-file) soundfile outputs are supported on all operating systems. I hacked out the MAT-file structure, so you don't have to include any platform-specific libraries. Realtime sound output is provided as well, except under NeXTStep.
    • +
    • Various Sound Output Options - .wav, .snd, and .mat (Matlab MAT-file) soundfile outputs are supported on all operating systems. I hacked out the MAT-file structure, so you don't have to include any platform-specific libraries. Realtime sound output is provided as well, except under NeXTStep.
    • Multiple Reverberator Implementations - Reverb subclasses of JCRev and NRev (popular reverberator implementations from CCRMA) have been written. Perry's original reverb implementation still exists as PRCRev. All reverberators now take a T60 initializer argument.
    • -
    • MD2SKINI - A program which parses a MIDI input stream and spits out SKINI code. The output of MD2SKINI is typically piped into an STK instrument executable (eg. MD2SKINI | syntmono Clarinet -r -i). In addition, you can supply a filename argument to MD2SKINI and have it simultaneously record a SKINI score file for future reuse. -
    • Modifications to Object.h for OS_TYPE compilation dependencies. Makefile automatically determines OS_TYPE when invoked (if you have the GNU makefile utilities installed on your system). -
    • A single distribution for all platforms. The Unix and Windows versions have been merged into a single set of classes. Makefiles and Visual C++ workspace/project files are provided for compiling. +
    • MD2SKINI - A program which parses a MIDI input stream and spits out SKINI code. The output of MD2SKINI is typically piped into an STK instrument executable (eg. MD2SKINI | syntmono Clarinet -r -i). In addition, you can supply a filename argument to MD2SKINI and have it simultaneously record a SKINI score file for future reuse.
    • +
    • Modifications to Object.h for OS_TYPE compilation dependencies. Makefile automatically determines OS_TYPE when invoked (if you have the GNU makefile utilities installed on your system).
    • +
    • A single distribution for all platforms. The Unix and Windows versions have been merged into a single set of classes. Makefiles and Visual C++ workspace/project files are provided for compiling.
    */ diff --git a/doc/doxygen/filtering.txt b/doc/doxygen/filtering.txt new file mode 100644 index 0000000..3c24ec8 --- /dev/null +++ b/doc/doxygen/filtering.txt @@ -0,0 +1,88 @@ +/*! \page filtering Using Filters + +In this section, we demonstrate the use of a few of the STK filter classes. The Filter class provides functionality to implement a generalized digital filter of any type, similar to the \c filter function in Matlab. In this example, we create a Filter instance and initialize it with specific numerator and denominator coefficients. We then compute its impulse response for 20 samples. + +\code +#include "Filter.h" + +int main() +{ + StkFrames output( 20 ); // initialize StkFrames to 20 elements (defaults: 1 channel, interleaved) + output[0] = 1.0; + + std::vector numerator( 5, 0.1 ); // create and initialize numerator coefficients + std::vector denominator; // create empty denominator coefficients + denominator.push_back( 1.0 ); // populate our denomintor values + denominator.push_back( 0.3 ); + denominator.push_back( -0.5 ); + + Filter filter( numerator, denominator ); + + filter.tick( output ); + for ( unsigned int i=0; ivector, a container object provided by the C++ Standard Library. + +Most STK classes use more specific types of digital filters, such as the OneZero, OnePole, TwoPole, or BiQuad varieties. These classes inherit from the Filter class and provide specific functionality particular to their use, as well as functions to independently control individual coefficient values. + +\section reson Resonances: + +The STK BiQuad and TwoPole classes provide functionality for creating resonance filters. The following example demonstrates how to create a resonance centered at 440 Hz that is used to filter the output of a Noise generator. + +\code +#include "BiQuad.h" +#include "Noise.h" + +int main() +{ + StkFrames output( 20 ); // initialize StkFrames to 20 elements (defaults: 1 channel, interleaved) + Noise noise; + + BiQuad biquad; + biquad.setResonance( 440.0, 0.98, true ); // automatically normalize for unity peak gain + + for ( unsigned int i=0; iNext tutorial]   [Main tutorial page] +*/ diff --git a/doc/doxygen/footer.html b/doc/doxygen/footer.html index 5f091b7..132c535 100644 --- a/doc/doxygen/footer.html +++ b/doc/doxygen/footer.html @@ -1,7 +1,7 @@
    - +
    The Synthesis ToolKit in C++ (STK)
    The Synthesis ToolKit in C++ (STK)
    ©1995-2004 Perry R. Cook and Gary P. Scavone. All Rights Reserved.
    diff --git a/doc/doxygen/fundamentals.txt b/doc/doxygen/fundamentals.txt new file mode 100644 index 0000000..669e97d --- /dev/null +++ b/doc/doxygen/fundamentals.txt @@ -0,0 +1,63 @@ +/*! \page fundamentals STK Fundamentals + +The Synthesis ToolKit is implemented in the C++ programming language. STK does not attempt to provide a new programming environment or paradigm but rather provides a set of objects which can be used within a normal C++ programming framework. Therefore, it is expected that users of STK will have some familiarity with C/C++ programming concepts. That said, the STK classes do have some particular idiosyncrasies that we will mention here. + +\section Signal Computations: + +Audio and control signals throughout STK use a floating-point data type, StkFloat, the exact precision of which can be controlled via a typedef statement in Stk.h. By default, an StkFloat is a double-precision floating-point value. Thus, the ToolKit can use any normalization scheme desired. The base instruments and algorithms are implemented with a general audio sample dynamic maximum of +/-1.0. + +In general, the computation and/or passing of values is performed on a "single-sample" basis. For example, the Noise class outputs random floating-point numbers in the range +/-1.0. The computation of such values occurs in the Noise::tick() function. The following program will generate 20 random floating-point (StkFloat) values in the range -1.0 to +1.0: + +\code +#include "Noise.h" + +int main() +{ + StkFloat output; + Noise noise; + + for ( unsigned int i=0; i<20; i++ ) { + output = noise.tick(); + std::cout << "i = " << i << " : output = " << output << std::endl; + } + + return 0; +} +\endcode + +Nearly all STK classes implement tick() functions which take and/or return sample values. Within the tick() function, the fundamental sample calculations are performed for a given class. Most STK classes consume/generate a single sample per operation and their tick() method takes/returns each sample "by value". In addition, every class implementing a tick() function also provides one or more overloaded tick() functions which can be used for vectorized computations, as shown in the next example. + +\code +#include "Noise.h" + +int main() +{ + StkFrames output(20); // initialize StkFrames to 20 elements (defaults: 1 channel, interleaved) + Noise noise; + + noise.tick( output ); + for ( unsigned int i=0; iNext tutorial]   [Main tutorial page] +*/ diff --git a/doc/doxygen/hello.txt b/doc/doxygen/hello.txt index fed12eb..31096c0 100644 --- a/doc/doxygen/hello.txt +++ b/doc/doxygen/hello.txt @@ -1,6 +1,6 @@ /*! \page hello Hello Sine! -We'll begin our introduction to the Synthesis ToolKit with a simple sine-wave oscillator program. STK does not provide a specific oscillator for sine waves. Instead, it provides a generic waveform oscillator class, WaveLoop, which can load a variety of common file types. In this example, we load a sine "table" from an STK RAW file (defined as monophonic, 16-bit, big-endian data). We use the class WvOut to write the result to a 16-bit, WAV formatted audio file. +We'll continue our introduction to the Synthesis ToolKit with a simple sine-wave oscillator program. STK does not provide a specific oscillator for sine waves. Instead, it provides a generic waveform oscillator class, WaveLoop, which can load a variety of common file types. In this example, we load a sine "table" from an STK RAW file (defined as monophonic, 16-bit, big-endian data). We use the class WvOut to write the result to a 16-bit, WAV formatted audio file. \code @@ -15,11 +15,11 @@ int main() Stk::setSampleRate( 44100.0 ); // Define and load the sine wave file - WaveLoop *input = new WaveLoop( "rawwaves/sinewave.raw", TRUE ); + WaveLoop* input = new WaveLoop( "rawwaves/sinewave.raw", true ); input->setFrequency( 440.0 ); // Define and open a 16-bit, one-channel WAV formatted output file - output = new WvOut( "hellosine.wav", 1, WvOut::WVOUT_WAV, Stk::STK_SINT16 ); + WvOut* output = new WvOut( "hellosine.wav", 1, WvOut::WVOUT_WAV, Stk::STK_SINT16 ); // Run the oscillator for 40000 samples, writing to the output file int i; @@ -39,66 +39,17 @@ WaveLoop is a subclass of WvIn, which supports WAV, SND (AU), AIFF, MAT-file (Ma The WvIn and WvOut classes are complementary, both supporting WAV, SND (AU), AIFF, MAT-file (Matlab), and RAW file formats with 8-, 16-, and 32-bit integer and 32- and 64-bit floating-point data types. However, WvOut does not perform data interpolation. -Nearly all STK classes implement tick() functions which take and/or return sample values. Within the tick() function, the fundamental sample calculations are performed for a given class. Most STK classes consume/generate a single sample per operation and their tick() method takes/returns each sample "by value". In addition, every class implementing a tick() function also provides an overloaded tick() function taking pointer and size arguments which can be used for vectorized computations. - The WvIn and WvOut classes support multi-channel sample frames. To distinguish single-sample frame operations from multi-channel frame operations, these classes also implement tickFrame() functions. When a tick() method is called for multi-channel data, frame averages are returned or the input sample is distributed across all channels of a sample frame. -Nearly all STK classes inherit from the Stk base class. Stk provides a static sample rate which is queried by subclasses as needed. Because many classes use the current sample rate value during instantiation, it is important that the desired value be set at the beginning of a program. The default STK sample rate is 22050 Hz. - -Another primary concept that is somewhat obscurred in this example concerns the data format in which sample values are passed and received. Audio and control signals throughout STK use a floating-point data type, the exact precision of which can be controlled via the MY_FLOAT \#define statement in Stk.h. Thus, the ToolKit can use any normalization scheme desired. The base instruments and algorithms are implemented with a general audio sample dynamic maximum of +/-1.0, and the WvIn and WvOut classes and subclasses scale appropriately for DAC or soundfile input and output. +Nearly all STK classes inherit from the Stk base class. Stk provides a static sample rate which is queried by subclasses as needed. Because many classes use the current sample rate value during instantiation, it is important that the desired value be set at the beginning of a program. The default STK sample rate is 44100 Hz. \section error Error Handling The ToolKit has some basic C++ error handling functionality built in. Classes which access files and/or hardware are most prone to runtime errors. To properly "catch" such errors, the above example should be rewritten as shown below. -\code -// sineosc.cpp - -#include "WaveLoop.h" -#include "WvOut.h" - -int main() -{ - // Set the global sample rate before creating class instances. - Stk::setSampleRate( 44100.0 ); - - WaveLoop *input = 0; - WvOut *output = 0; - - try { - // Define and load the sine wave file - input = new WaveLoop( "rawwaves/sinewave.raw", TRUE ); - - // Define and open a 16-bit, one-channel WAV formatted output file - output = new WvOut( "hellosine.wav", 1, WvOut::WVOUT_WAV, Stk::STK_SINT16 ); - } - catch ( StkError & ) { - goto cleanup; - } - - input->setFrequency( 440.0 ); - - // Run the oscillator for 40000 samples, writing to the output file - for ( int i=0; i<40000; i++ ) { - - try { - output->tick( input->tick() ); - } - catch ( StkError & ) { - goto cleanup; - } - - } - - cleanup: - delete input; - delete output; - - return 0; -} -\endcode +\include sineosc.cpp In this particular case, we simply exit the program if an error occurs (an error message is automatically printed to stderr). A more refined program might attempt to recover from or fix a particular problem and, if successful, continue processing. See the \ref classes to determine which constructors and functions can throw an error. -[Next tutorial]   [Main tutorial page] +[Next tutorial]   [Main tutorial page] */ diff --git a/doc/doxygen/index.txt b/doc/doxygen/index.txt index 63bdde7..2dfbf38 100644 --- a/doc/doxygen/index.txt +++ b/doc/doxygen/index.txt @@ -2,9 +2,11 @@ -

    Perry R. Cook & Gary P. Scavone

    +\htmlonly +

    Perry R. Cook & Gary P. Scavone

    +\endhtmlonly -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. +The Synthesis ToolKit in C++ (STK) is a set of open source audio signal processing and algorithmic synthesis classes written in the C++ programming language. 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 10 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 - \ref classes diff --git a/doc/doxygen/information.txt b/doc/doxygen/information.txt index d28da38..cda37ed 100644 --- a/doc/doxygen/information.txt +++ b/doc/doxygen/information.txt @@ -4,11 +4,11 @@
    • ICMC99 Paper

    • -A somewhat recent paper by Perry and Gary about the Synthesis ToolKit in C++. +A not-so-recent paper by Perry and Gary about the Synthesis ToolKit in C++.

    • SIGGRAPH96 Paper

    • -A not-so-recent paper by Perry about the Synthesis ToolKit in C++. +A very-not-so-recent paper by Perry about the Synthesis ToolKit in C++.

    • Perry's STK Web Page

    • @@ -18,14 +18,14 @@ This is a link to Perry Cook's STK Web page. He has information about the \ref

      What is the Synthesis ToolKit?

      -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. +The Synthesis ToolKit in C++ (STK) is a set of open source audio signal processing and algorithmic synthesis classes written in the C++ programming language. 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 10 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. The Synthesis ToolKit is free for non-commercial use. The only parts of the Synthesis ToolKit that are platform-dependent concern real-time audio and MIDI input and output, and that is taken care of with a few special classes. The interface for MIDI input and the simple Tcl/Tk graphical user interfaces (GUIs) provided is the same, so it's easy to experiment in real time using either the GUIs or MIDI. The Synthesis ToolKit can generate simultaneous SND (AU), WAV, AIFF, and MAT-file output soundfile formats (as well as realtime sound output), so you can view your results using one of a large variety of sound/signal analysis tools already available (e.g. Snd, Cool Edit, Matlab).

      What the Synthesis ToolKit is not.

      -The Synthesis Toolkit is not one particular program. Rather, it is a set of C++ classes that you can use to create your own programs. A few example applications are provided to demonstrate some of the ways to use the classes. If you have specific needs, you will probably have to either modify the example programs or write a new program altogether. Further, the example programs don't have a fancy GUI wrapper. If you feel the need to have a "drag and drop" graphical patching GUI, you probably don't want to use the ToolKit. Spending hundreds of hours making platform-dependent graphics code would go against one of the fundamental design goals of the ToolKit - platform independence. +The Synthesis Toolkit is not one particular program. Rather, it is a set of C++ classes that you can use to create your own programs. A few example applications are provided to demonstrate some of the ways to use the classes. If you have specific needs, you will probably have to either modify the example programs or write a new program altogether. Further, the example programs don't have a fancy GUI wrapper. It is easy to embed STK classes inside a GUI environment but we have chosen to focus our energy on the audio signal processing issues. Spending hundreds of hours making platform-dependent graphical user interfaces would go against one of the fundamental design goals of the ToolKit - platform independence. For those instances where a simple GUI with sliders and buttons is helpful, we use Tcl/Tk (which is freely distributed for all the supported ToolKit platforms). A number of Tcl/Tk GUI scripts are distributed with the ToolKit release. For control, the Synthesis Toolkit uses raw MIDI (on supported platforms), and SKINI (Synthesis ToolKit Instrument Network Interface, a MIDI-like text message synthesis control format). @@ -33,7 +33,7 @@ For those instances where a simple GUI with sliders and buttons is helpful, we u Perry Cook began developing a pre-cursor to the Synthesis ToolKit (also called STK) under NeXTStep at the Center for Computer Research in Music and Acoustics (CCRMA) at Stanford University in the early-1990s. With his move to Princeton University in 1996, he ported everything to C++ on SGI hardware, added real-time capabilities, and greatly expanded the synthesis techniques available. With the help of Bill Putnam, Perry also made a port of STK to Windows95. Gary Scavone began using STK extensively in the summer of 1997 and completed a full port of STK to Linux early in 1998. He finished the fully compatable Windows port (using Direct Sound API) in June 1998. Numerous improvements and extensions have been made since then. -The Toolkit has been distributed continuously since 1996 via the Princeton Sound Kitchen, Perry Cook's home page at Princeton, Gary Scavone's home page at Stanford's Center for Computer Research in Music and Acoustics (CCRMA), and the Synthesis ToolKit home page. The ToolKit has been in included in various collections of software. Much of it has also been ported to MAX/MSP on Macintosh computers by Dan Trueman and Luke Dubois of Columbia University, and is distributed as PeRColate. Help on real-time sound and MIDI has been provided by Tim Stilson, Bill Putnam, and Gabriel Maldonado. +The Toolkit has been distributed continuously since 1996 via the Princeton Sound Kitchen, Perry Cook's home page at Princeton, Gary Scavone's home page at Stanford's Center for Computer Research in Music and Acoustics (CCRMA), and the Synthesis ToolKit home page. The ToolKit has been included in various collections of software. Much of it has also been ported to Max/MSP on Macintosh computers by Dan trueman and Luke Dubois of Columbia University, and is distributed as PeRColate. Help on real-time sound and MIDI has been provided over the years by Tim Stilson, Bill Putnam, and Gabriel Maldonado.

      Legal and Ethical Notes

      @@ -47,4 +47,32 @@ Some of the concepts are covered by various patents, some known to us and likely STK is free and we do not guarantee anything. We've been hacking on this code for a while now and most of it seems to work pretty well. But, there surely are some bugs floating around. Sometimes things work fine on one computer platform but not so fine on another. FPU overflows and underflows cause very weird behavior which also depends on the particular CPU and OS. Let us know about bugs you find and we'll do our best to correct them. -*/ \ No newline at end of file +

      Perry's Notes From the Original Distribution of STK

      + +This whole world was created with no particular hardware in mind. These examples are intended to be tutorial in nature, as a platform for the continuation of my research, and as a possible starting point for a software synthesis system. The basic motivation was to create the necessary unit generators to do the synthesis, processing, and control that I want to do and teach about. Little thought for optimization was given and therefore improvements, especially speed enhancements, should be possible with these classes. It was written with some basic concepts in mind about how to let compilers optimize. + +Your question at this point might be, "But Perry, with CMix, CMusic, CSound, CShells, CMonkeys, etc. already cluttering the landscape, why a new set of stupid C functions for music synthesis and processing?" The answers lie below. + +
        +
      1. I needed to port many of the things I've done into something which is generic enough to port further to different machines.
      2. + +
      3. I really plan to document this stuff, so that you don't have to be me to figure out what's going on. (I'll probably be sorry I said this in a couple of years, when even I can't figure out what I was thinking.)
      4. + +
      5. The classic difficulties most people have in trying to implement physical models are: + +
          +
        • They have trouble understanding the papers, and/or in turning the theory into practice.
        • + +
        • The physical model instruments are a pain to get to oscillate, and coming up with stable and meaningful parameter values is required to get the models to work at all.
        • +
        + +This set of C++ unit generators and instruments might help to diminish the scores of emails I get asking what to do with those block diagrams I put in my papers.
      6. + +
      7. I wanted to try some new stuff with modal synthesis, and implement some classic FM patches as well.
      8. + +
      9. I wanted to reimplement, and newly implement more of the intelligent and physical performer models I've talked about in some of my papers. But I wanted to do it in a portable way, and in such a way that I can hook up modules quickly. I also wanted to make these instruments connectable to such player objects, so folks like Brad Garton who really think a lot about the players can connect them to my instruments, a lot about which I think.
      10. + +
      11. More rationalizations to follow ...
      12. +
      + +*/ diff --git a/doc/doxygen/instruments.txt b/doc/doxygen/instruments.txt index 3c88234..6a75a8e 100644 --- a/doc/doxygen/instruments.txt +++ b/doc/doxygen/instruments.txt @@ -1,66 +1,8 @@ /*! \page instruments Instruments -The ToolKit comes with a wide variety of synthesis algorithms, all of which inherit from the Instrmnt class. In this example, we'll fire up an instance of the BeeThree FM synthesis class and show how it's frequency can be modified over time. +The ToolKit comes with a wide variety of synthesis algorithms, all of which inherit from the Instrmnt class. In this example, we'll fire up an instance of the BeeThree FM synthesis class and show how its frequency can be modified over time. -\code -// bethree.cpp - -#include "BeeThree.h" -#include "RtWvOut.h" - -int main() -{ - // Set the global sample rate before creating class instances. - Stk::setSampleRate( 44100.0 ); - - Instrmnt *instrument = 0; - RtWvOut *output = 0; - MY_FLOAT frequency, amplitude, scaler; - long counter, i; - - try { - // Define and load the BeeThree instrument - instrument = new BeeThree(); - - // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); - } - catch (StkError &) { - goto cleanup; - } - - scaler = 1.0; - frequency = 220.0; - amplitude = 0.5; - instrument->noteOn( frequency, amplitude ); - - // Play the instrument for 80000 samples, changing the frequency every 2000 samples - counter = 0; - while ( counter < 80000 ) { - for ( i=0; i<2000; i++ ) { - try { - output->tick( instrument->tick() ); - } - catch (StkError &) { - goto cleanup; - } - } - - counter += 2000; - scaler += 0.025; - instrument->setFrequency( frequency * scaler ); - } - - // Turn the instrument off with maximum decay envelope. - instrument->noteOff( 1.0 ); - - cleanup: - delete instrument; - delete output; - - return 0; -} -\endcode +\include bethree.cpp We have used an Instrmnt pointer when referencing the BeeThree instance above, so it would be simple to replace the BeeThree class with any other STK instrument class. It should be noted, however, that a few classes do not respond to the setFrequency() function (e.g., Shakers, Drummer). diff --git a/doc/doxygen/links.txt b/doc/doxygen/links.txt index 5a373dc..2ba705f 100644 --- a/doc/doxygen/links.txt +++ b/doc/doxygen/links.txt @@ -1,6 +1,12 @@ /*! \page links Miscellaneous Links -- The RtAudio WWW site +- The %RtAudio WWW site + +- The %RtMidi WWW site + +- ChucK: Concurrent, On-the-fly Audio Programming Language using STK unit generators + +- Paul Lansky's port of STK to SuperCollider - Kern Scores: A Library of Electronic Musical Scores (with automatic conversion to SKINI format) diff --git a/doc/doxygen/multichannel.txt b/doc/doxygen/multichannel.txt index 11517a7..e6dc567 100644 --- a/doc/doxygen/multichannel.txt +++ b/doc/doxygen/multichannel.txt @@ -8,63 +8,7 @@ Multi-channel support for realtime audio input and output is dependent on the au The following example demonstrates the use of the WvOut class for creating a four channel, 16-bit AIFF formatted audio file. We will use four sinewaves of different frequencies for the first two seconds and then a single sinewave for the last two seconds. -\code -// foursine.cpp - -#include "WaveLoop.h" -#include "WvOut.h" - -int main() -{ - // Set the global sample rate before creating class instances. - Stk::setSampleRate( 44100.0 ); - - int i, j; - WvOut *output = 0; - WaveLoop *inputs[4]; - for ( i=0; i<4; i++ ) inputs[i] = 0; - - // Define and load the sine waves - try { - for ( i=0; i<4; i++ ) { - inputs[i] = new WaveLoop( "rawwaves/sinewave.raw", TRUE ); - inputs[i]->setFrequency( 220.0 * (i+1) ); - } - } - catch (StkError &) { - goto cleanup; - } - - // Define and open a 16-bit, four-channel AIFF formatted output file - try { - output = new WvOut( "foursine.aif", 4, WvOut::WVOUT_AIF, Stk::STK_SINT16 ); - } - catch (StkError &) { - goto cleanup; - } - - // Write two seconds of four sines to the output file - MY_FLOAT frame[4]; - for ( j=0; j<88200; j++ ) { - for ( i=0; i<4; i++ ) - frame[i] = inputs[i]->tick(); - - output->tickFrame( frame ); - } - - // Now write the first sine to all four channels for two seconds - for ( j=0; j<88200; j++ ) { - output->tick( inputs[0]->tick() ); - } - - cleanup: - for ( i=0; i<4; i++ ) delete inputs[i]; - delete output; - - return 0; -} -\endcode - +\include foursine.cpp [Next tutorial]   [Main tutorial page] */ diff --git a/doc/doxygen/polyvoices.txt b/doc/doxygen/polyvoices.txt index fd4ce62..07e4a86 100644 --- a/doc/doxygen/polyvoices.txt +++ b/doc/doxygen/polyvoices.txt @@ -1,122 +1,22 @@ /*! \page polyvoices Voice Management -The previous tutorial chapters were concerned only with monophonic ToolKit instrument playback and control. At this point, it should be relatively clear that one can instantiate multiple instruments and perhaps sum together their sounds or even direct their sounds to separate output channels. It is less clear how one might go about controlling a group of instruments. The Voicer class is designed to serve just this purpose. +The previous tutorial chapters were concerned only with monophonic ToolKit instrument playback and control. At this point, it should be relatively clear that one can instantiate multiple instruments and perhaps sum together their outputs or even direct their outputs to separate channels. It is less clear how one might go about controlling a group of instruments. The Voicer class is designed to serve just this purpose. -The STK Voicer class is a relatively simple voice manager. The user can dynamically add and delete instruments from its "control", with the option of controlling specific instruments via unique note tags and/or grouping sets of instruments via a "channel" number. All sounding instrument outputs are summed and returned via the tick() function. The Voicer class responds to noteOn, noteOff, setFrequency, pitchBend, and controlChange messages, automatically assigning incoming messages to the voices in its control. When all voices are sounding and a new noteOn is encountered, the Voicer interrupts the oldest sounding voice. The user is responsible for creating and deleting all instrument instances. +The STK Voicer class is a relatively simple voice manager. The user can dynamically add and delete instruments to/from its "control", with the option of controlling specific instruments via unique note tags and/or grouping sets of instruments via a "channel" number. All sounding instrument outputs are summed and returned via the tick() function. The Voicer class responds to noteOn, noteOff, setFrequency, pitchBend, and controlChange messages, automatically assigning incoming messages to the voices in its control. When all voices are sounding and a new noteOn is encountered, the Voicer interrupts the oldest sounding voice. The user is responsible for creating and deleting all instrument instances. In the following example, we modify the controlbee.cpp program to make use of three BeeThree instruments, all controlled using a Voicer. -\code -// threebees.cpp +\include threebees.cpp -#include "BeeThree.h" -#include "RtWvOut.h" -#include "Messager.h" -#include "Voicer.h" -#include "SKINI.msg" - -int main() -{ - // Set the global sample rate before creating class instances. - Stk::setSampleRate( 44100.0 ); - - int i; - RtWvOut *output = 0; - Messager *messager = 0; - Voicer *voicer = 0; - bool done = FALSE; - Instrmnt *instrument[3]; - for ( i=0; i<3; i++ ) instrument[i] = 0; - - try { - // Define and load the BeeThree instruments - for ( i=0; i<3; i++ ) - instrument[i] = new BeeThree(); - - // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); - } - catch (StkError &) { - goto cleanup; - } - - try { - // Create a Messager instance to read from a redirected SKINI scorefile. - messager = new Messager(); - } - catch (StkError &) { - goto cleanup; - } - - // Instantiate the voicer for a maximum of three voices. - voicer = new Voicer( 3 ); - for ( i=0; i<3; i++ ) - voicer->addInstrument( instrument[i] ); - - // Play the instrument until the end of the scorefile. - int nTicks, type; - MY_FLOAT byte2, byte3; - while (!done) { - - // Look for new messages and return a delta time (in samples). - type = messager->nextMessage(); - if (type < 0) - done = TRUE; - - nTicks = messager->getDelta(); - try { - for ( i=0; itick( voicer->tick() ); - } - catch (StkError &) { - goto cleanup; - } - - if ( type > 0 ) { - // Process the new control message. - byte2 = messager->getByteTwo(); - byte3 = messager->getByteThree(); - - switch(type) { - - case __SK_NoteOn_: - voicer->noteOn( byte2, byte3 ); - break; - - case __SK_NoteOff_: - voicer->noteOff( byte2, byte3 ); - break; - - case __SK_ControlChange_: - voicer->controlChange( (int) byte2, byte3 ); - break; - - case __SK_AfterTouch_: - voicer->controlChange( 128, byte2 ); - break; - } - } - } - - cleanup: - for ( i=0; i<3; i++ ) delete instrument[i]; - delete output; - delete messager; - delete voicer; - - return 0; -} -\endcode - -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: +We have written this program to accept control messages from \c STDIN. Assuming the program is compiled as threebees, the three-voice SKINI scorefile bachfugue.ski (located in the scores directory with the examples) can be redirected to the program as: \code -threebees < bachfugue.ski +threebees < scores/bachfugue.ski \endcode For more fun, surf to Kern Scores for a huge assortment of other scorefiles which can be downloaded in the SKINI format. -Another easy extension would be to use the STK_MIDI constructor argument to the Messager class and then play the instruments via a MIDI keyboard. +Another easy extension would be to add the \c Messager::startMidiInput() function to the program and then play the instruments via a MIDI keyboard. [Main tutorial page] */ diff --git a/doc/doxygen/realtime.txt b/doc/doxygen/realtime.txt index 41073a3..fe51f0b 100644 --- a/doc/doxygen/realtime.txt +++ b/doc/doxygen/realtime.txt @@ -1,72 +1,28 @@ -/*! \page realtime Realtime Audio +/*! \page realtime Realtime Audio (blocking) -In this section, we modify the sineosc.cpp program in order to send the output to the default audio playback device on your system. +In this section, we modify the sineosc.cpp program in order to send the output to the default audio playback device on your computer system. -\code -// rtsine.cpp +\include rtsine.cpp -#include "WaveLoop.h" -#include "RtWvOut.h" - -int main() -{ - // Set the global sample rate before creating class instances. - Stk::setSampleRate( 44100.0 ); - - WaveLoop *input = 0; - RtWvOut *output = 0; - - try { - // Define and load the sine wave file - input = new WaveLoop( "rawwaves/sinewave.raw", TRUE ); - - // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); - } - catch (StkError &) { - goto cleanup; - } - - input->setFrequency(440.0); - - // Play the oscillator for 40000 samples - int i; - for ( i=0; i<40000; i++ ) { - try { - output->tick(input->tick()); - } - catch (StkError &) { - goto cleanup; - } - } - - cleanup: - delete input; - delete output; - - return 0; -} -\endcode - -The class RtWvOut is a protected subclass of WvOut. A number of optional constructor arguments can be used to fine tune its performance for a given system. +The class RtWvOut is a protected subclass of WvOut. A number of optional constructor arguments can be used to fine tune its performance for a given system. RtWvOut provides a "single-sample" interface to the RtAudio class. Note that RtWvOut (as well as the RtWvIn and RtDuplex classes described below) make use of RtAudio's blocking input/output functionality. On systems which implement an inherently callback-based audio API, this blocking functionality will be less robust. An example of audio output using a callback scheme will be discussed in a subsequent tutorial section. Though not used here, an RtWvIn class exists as well which can be used to read realtime audio data from an input device. See the record.cpp example program in the examples project for more information. -It is possible to use an instance of RtWvOut and an instance of RtWvIn to simultaneously read and write realtime audio to and from a hardware device or devices. However, it is recommended to instead use a single instance of RtDuplex to achieve this behavior, in that it guarantees better synchronization between the input and output data. See the effects project or the io.cpp example program in the examples project for more information. +It may be possible to use an instance of RtWvOut and an instance of RtWvIn to simultaneously read and write realtime audio to and from a hardware device or devices. However, it is recommended to instead use a single instance of RtDuplex to achieve this behavior, in that it guarantees better synchronization between the input and output data. See the effects project or the io.cpp example program in the examples project for more information. -When using any realtime STK class (RtAudio, RtWvOut, RtWvIn, RtDuplex, RtMidi, TcpWvIn, TcpWvOut, Socket, and Thread), it is necessary to specify an audio/MIDI API preprocessor definition and link with the appropriate libraries or frameworks. For example, the above program could be compiled on a Linux system using the GNU g++ compiler and the ALSA audio/MIDI API as follows (assuming all necessary files exist in the project directory): +When using any realtime STK class (RtAudio, RtWvOut, RtWvIn, RtDuplex, RtMidi, TcpWvIn, TcpWvOut, Socket, and Thread), it is necessary to specify an audio/MIDI API preprocessor definition and link with the appropriate libraries or frameworks. For example, the above program could be compiled on a Linux system using the GNU g++ compiler and the ALSA audio API as follows (assuming all necessary files exist in the project directory): \code g++ -Wall -D__LINUX_ALSA__ -D__LITTLE_ENDIAN__ -o rtsine Stk.cpp WvIn.cpp WaveLoop.cpp WvOut.cpp \ - RtWvOut.cpp RtAudio.cpp rtsine.cpp -lpthread -lasound -lstk + RtWvOut.cpp RtAudio.cpp rtsine.cpp -lpthread -lasound \endcode On a Macintosh OS X system, the syntax would be: \code -CC -D__MACOSX_CORE__ -o rtsine Stk.cpp WvIn.cpp WaveLoop.cpp WvOut.cpp RtWvOut.cpp RtAudio.cpp \ - rtsine.cpp -lpthread -lstdc++ -lstk -framework CoreAudio -framework CoreMIDI -framework CoreFoundation +g++ -Wall -D__MACOSX_CORE__ -o rtsine Stk.cpp WvIn.cpp WaveLoop.cpp WvOut.cpp RtWvOut.cpp RtAudio.cpp \ + rtsine.cpp -lpthread -framework CoreAudio -framework CoreMIDI -framework CoreFoundation \endcode -[Next tutorial]   [Main tutorial page] +[Next tutorial]   [Main tutorial page] */ diff --git a/doc/doxygen/skini.txt b/doc/doxygen/skini.txt index 55e26ee..44b7fa1 100644 --- a/doc/doxygen/skini.txt +++ b/doc/doxygen/skini.txt @@ -8,7 +8,7 @@ This describes the latest (version 1.1) implementation of SKINI for the Synthesi A SKINI haiku. \endcode -Profound thanks to Dan Trueman, Brad Garton, and Gary Scavone for input on this revision. Thanks also to MIDI, the NeXT MusicKit, ZIPI and all the creators and modifiers of these for good bases upon/from which to build and depart. +Profound thanks to Dan trueman, Brad Garton, and Gary Scavone for input on this revision. Thanks also to MIDI, the NeXT MusicKit, ZIPI and all the creators and modifiers of these for good bases upon/from which to build and depart. \section compatibility MIDI Compatibility @@ -16,7 +16,7 @@ SKINI was designed to be MIDI compatible wherever possible, and extend MIDI in i Differences from MIDI, and motivations, include: -- Text-based messages are used, with meaningful names wherever possible. This allows any language or system capable of formatted printing to generate SKINI. Similarly, any system capable of reading in a string and turning delimited fields into strings, floats, and ints can consume SKINI for control. More importantly, humans can actually read, and even write if they want, SKINI files and streams. Use an editor and search/replace or macros to change a channel or control number. Load a SKINI score into a spread sheet to apply transformations to time, control parameters, MIDI velocities, etc. Put a monkey on a special typewriter and get your next great work. Life's too short to debug bit/nybble packed variable length mumble messages. Disk space gets cheaper, available bandwidth increases, music takes up so little space and bandwidth compared to video and grapics. Live a little. +- Text-based messages are used, with meaningful names wherever possible. This allows any language or system capable of formatted printing to generate SKINI. Similarly, any system capable of reading in a string and turning delimited fields into strings, floats, and ints can consume SKINI for control. More importantly, humans can actually read, and even write if they want, SKINI files and streams. Use an editor and search/replace or macros to change a channel or control number. Load a SKINI score into a spread sheet to apply transformations to time, control parameters, MIDI velocities, etc. Put a monkey on a special typewriter and get your next great work. Life's too short to debug bit/nybble packed variable length mumble messages. Disk space gets cheaper, available bandwidth increases, music takes up so little space and bandwidth compared to video and graphics. Live a little. - Floating point numbers are used wherever possible. Note Numbers, Velocities, Controller Values, and Delta and Absolute Times are all represented and scanned as ASCII double-precision floats. MIDI byte values are preserved, so that incoming MIDI bytes from an interface can be put directly into SKINI messages. 60.0 or 60 is middle C, 127.0 or 127 is maximum velocity etc. But, unlike MIDI, 60.5 can cause a 50 cent sharp middle C to be played. As with MIDI byte values like velocity, use of the integer and SKINI-added fractional parts is up to the implementor of the algorithm being controlled by SKINI messages. But the extra precision is there to be used or ignored. @@ -38,13 +38,13 @@ All fields other than type, time, and channel are optional, and the types and us The other important file used by SKINI is SKINI.msg, which is a set of #defines to make C code more readable, and to allow reasonably quick re-mapping of control numbers, etc.. All of these defined symbols are assigned integer values. For Java, the #defines could be replaced by declaration and assignment statements, preserving the look and behavior of the rest of the code. -\section cfiles C Files Used To Implement SKINI +\section cfiles Files Used To Implement SKINI -SKINI.cpp is an object which can either open a SKINI file, and successively read and parse lines of text as SKINI strings, or accept strings from another object and parse them. The latter functionality would be used by a socket, pipe, or other connection receiving SKINI messages a line at a time, usually in real time, but not restricted to real time. +Skini.cpp is a C++ object which can either open a SKINI file and successively read and parse lines of text as SKINI strings, or accept strings from another object and parse them. The latter functionality would be used by a socket, pipe, or other connection receiving SKINI messages a line at a time, usually in real time, but not restricted to real time. -SKINI.msg should be included by anything wanting to use the SKINI.cpp object. This is not mandatory, but use of the __SK_blah_ symbols which are defined in the .msg file will help to ensure clarity and consistency when messages are added and changed. +SKINI.msg should be included by anything wanting to use the Skini.cpp object. This is not mandatory, but use of the __SK_blah_ symbols which are defined in the .msg file will help to ensure clarity and consistency when messages are added and changed. -SKINI.tbl is used only by the SKINI parser object (SKINI.cpp). In the file SKINI.tbl, an array of structures is declared and assigned values which instruct the parser as to what the message types are, and what the fields mean for those message types. This table is compiled and linked into applications using SKINI, but could be dynamically loaded and changed in a future version of SKINI. +SKINI.tbl is used only by the SKINI parser object (Skini.cpp). In the file SKINI.tbl, an array of structures is declared and assigned values which instruct the parser as to what the message types are, and what the fields mean for those message types. This table is compiled and linked into applications using SKINI, but could be dynamically loaded and changed in a future version of SKINI. \section parser SKINI Messages and the SKINI Parser: @@ -102,7 +102,7 @@ The parser isn't all that smart, but neither am I. Here are the basic rules gov \section table The SKINI.tbl File and Message Parsing: -The SKINI.tbl file contains an array of structures which are accessed by the parser object SKINI.cpp. The struct is: +The SKINI.tbl file contains an array of structures which are accessed by the parser object Skini.cpp. The struct is: \code struct SKINISpec { @@ -164,55 +164,53 @@ The StringDamping and StringDetune messages behave the same as the Volume messag Here's a simple example of code which uses the SKINI object to read a SKINI file and control a single instrument. \code + Skini score; + Skini::Message message; instrument = new Mandolin(50.0); - score = new SKINI(argv[1]); - while(score->getType() > 0) { - tempDouble = score->getDelta(); - if (tempDouble < 0) { - tempDouble = - tempDouble; - tempDouble = tempDouble - output.getTime(); - if (tempDouble < 0) { - printf("Bad News Here!!! Backward Absolute Time Required.\n"); - tempDouble = 0.0; - } + score.setFile( argv[1] ); + while ( score.nextMessage( message ) != 0 ) { + tempDouble = message.time; + if (tempDouble < 0) { + tempDouble = - tempDouble; + tempDouble = tempDouble - output.getTime(); + if (tempDouble < 0) { + printf("Bad News Here!!! Backward Absolute Time Required.\n"); + tempDouble = 0.0; } - tempLong = (long) (tempDouble * Stk::sampleRate()); - for (i=0;itick()); + } + tempLong = (long) ( tempDouble * Stk::sampleRate() ); + for ( i=0; itick() ); + } + + tempDouble3 = message.floatValues[1] * NORM_MIDI; + if ( message.type == __SK_NoteOn_ ) { + if ( tempDouble3 == 0.0 ) { + tempDouble3 = 0.5; + instrument->noteOff( tempDouble3 ); } - tempDouble3 = score->getByteThree(); - if (score->getType()== __SK_NoteOn_ ) { - tempDouble3 *= NORM_MIDI; - if (score->getByteThree() == 0) { - tempDouble3 = 0.5; - instrument->noteOff(tempDouble3); - } - else { - tempLong = (int) score->getByteTwo(); - tempDouble2 = Midi2Pitch[tempLong]; - instrument->noteOn(tempDouble2,tempDouble3); - } + else { + tempLong = message.intValues[0]; + tempDouble2 = Midi2Pitch[tempLong]; + instrument->noteOn( tempDouble2, tempDouble3 ); } - else if (score->getType() == __SK_NoteOff_) { - tempDouble3 *= NORM_MIDI; - instrument->noteOff(tempDouble3); - } - else if (score->getType() == __SK_ControlChange_) { - tempLong = score->getByteTwoInt(); - instrument->controlChange(tempLong,temp3.0); - } - score->nextMessage(); + } + else if ( message.type == __SK_NoteOff_ ) { + instrument->noteOff( tempDouble3 ); + } + else if ( message.type == __SK_ControlChange_ ) { + tempLong = message.intValues[0]; + instrument->controlChange( tempLong, tempDouble3 ); + } } \endcode -When the score (SKINI object) object is created from the filename in argv[1], the first valid command line is read from the file and parsed. +When a SKINI score is passed to a Skini object using the Skini::setFile() function, valid messages are read from the file and returned using the Skini::nextMessage() function. -The score->getType() retrieves the messageType. If this is -1, there are no more valid messages in the file and the synthesis loop terminates. Otherwise, the message type is returned. +A Skini::Message structure contains all the information parsed from a single SKINI message. A returned message type of zero indicates either an invalid message or the end of a scorefile. -getDelta() retrieves the deltaTime until the current message should occur. If this is greater than 0, synthesis occurs until the deltaTime has elapsed. If deltaTime is less than zero, the time is interpreted as absolute time and the output device is queried as to what time it is now. That is used to form a deltaTime, and if it's positive we synthesize. If it's negative, we print an error and pretend this never happened and we hang around hoping to eventually catch up. +The "time" member of a Skini::Message is the deltaTime until the current message should occur. If this is greater than 0, synthesis occurs until the deltaTime has elapsed. If deltaTime is less than zero, the time is interpreted as absolute time and the output device is queried as to what time it is now. That is used to form a deltaTime, and if it's positive we synthesize. If it's negative, we print an error, pretend this never happened and we hang around hoping to eventually catch up. The rest of the code sorts out message types NoteOn, NoteOff (including NoteOn with velocity 0), and ControlChange. The code implicitly takes into account the integer type of the control number, but all other data is treated as double float. -The last line reads and parses the next message in the file. - -*/ \ No newline at end of file +*/ diff --git a/doc/doxygen/system.txt b/doc/doxygen/system.txt index cf4b7e9..408c88f 100644 --- a/doc/doxygen/system.txt +++ b/doc/doxygen/system.txt @@ -2,23 +2,24 @@ General:
        -
      • A MIDI interface to use MIDI input controls. (NOTE: This may be built into the soundcard on your computer.)
      • +
      • A MIDI interface to use MIDI input/output controls. (NOTE: This may be built into the soundcard on your computer.)
      • Tcl/Tk version 8.0 or higher to use the simple Tcl/Tk GUIs provided with the STK distribution (available free over the WWW for all supported realtime platforms).
      Linux (specific):
      • A soundcard to use realtime audio input/output capabilities. In order to use the effects project, the soundcard and drivers must support full duplex mode.
      • -
      • OSS or ALSA device drivers for realtime sound output and MIDI input.
      • +
      • ALSA device drivers and library for realtime sound and MIDI input/output. OSS device drivers can be used for audio input/output, but MIDI support requires the ALSA library to compile.
      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.
      • -
      • 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. +
      • A C++ compiler does install by default with OS X. It is necessary to download the Developer Kit from the Apple WWW site in order to compile STK or load it from the installation CD-ROM.
      • +
      • IMPORTANT:The internal Macintosh audio hardware typically supports a sample rate of 44100 Hz only. The default STK sample rate is now 44100 Hz, but there may be programs which change that value before execution. Check the program code if you have sample rate conflicts. Many of the example project programs allow the sample rate to be specified via the command line.
      • +
      • If you experience frequent audio input/output "glitches", try increasing the RT_BUFFER_SIZE specified in Stk.h.
      • +
      • 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 10.2 and 10.3 systems. 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. 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.
      • +It appears that socket support in Tcl/Tk uses the Nagle algorithm, which produces poor response between changes made in the tcl/tk script and the resulting audio updates. Note that this is only a problem when using a socket connection from a Tcl/Tk script.
      @@ -37,4 +38,4 @@ Initial tests have shown somewhat poor response between changes made in the tcl/

      -*/ \ No newline at end of file +*/ diff --git a/doc/doxygen/tutorial.txt b/doc/doxygen/tutorial.txt index 1ff0089..0123b85 100644 --- a/doc/doxygen/tutorial.txt +++ b/doc/doxygen/tutorial.txt @@ -1,23 +1,26 @@ /*! \page tutorial Tutorial -The Synthesis ToolKit is a set of C++ classes. In order to go beyond the simple example programs we provide, it is necessary to know some basics about programming in C or C++. STK's "target audience" includes people who: +The Synthesis ToolKit is a set of C++ classes. In order to go beyond the simple example programs we provide, it is necessary to know some basics about programming in C and C++. STK's "target audience" includes people who:

      • want to create audio DSP and/or synthesis programs
      • -
      • want to save some time by using our unit generators and input/output routines
      • +
      • want to use our unit generators and input/output routines rather than code their own
      • want to learn about synthesis and processing algorithms
      • -
      • wish to teach real-time synthesis and processing, and wish to use some of our classes and examples
      • +
      • wish to teach real-time synthesis and processing and wish to use some of our classes and examples
      Most ToolKit programmers will likely end up writing a class or two for their own particular needs, but this task is typically simplified by making use of pre-existing STK classes (filters, oscillators, etc.). The following tutorial chapters describe many of the fundamental ToolKit concepts and classes. All tutorial programs are included in the projects/examples directory. +-# \ref fundamentals -# \ref hello -# \ref compile +-# \ref filtering -# \ref realtime +-# \ref crealtime -# \ref instruments -# \ref controlin -# \ref multichannel -# \ref polyvoices -*/ \ No newline at end of file +*/ diff --git a/doc/doxygen/usage.txt b/doc/doxygen/usage.txt index 7905cc0..8723671 100644 --- a/doc/doxygen/usage.txt +++ b/doc/doxygen/usage.txt @@ -21,7 +21,7 @@ The top level distribution contains the following directories:
    • The include directory contains the header files for all the STK unit generator and algorithm classes.
    • -

    • The rawwaves directory contains various raw, monophonic, 16-bit, big-endian soundfiles used with the STK classes.
    • +

    • The rawwaves directory contains various raw, monophonic, 16-bit, big-endian, 22050 Hz soundfiles used with the STK classes.
    • The doc directory contains documentation about STK.
    • @@ -37,13 +37,13 @@ This release of STK comes with four separate "project" directories:

    • The ragamatic project is just cool. Fire it up and be enlightened.

    • -
    • The examples project contains several simple programs which demonstrate audio input/output, as well as the use of the audio internet streaming classes.
    • +
    • The examples project contains several simple programs which demonstrate audio input/output, including the audio internet streaming classes, as well as most of the tutorial programs.
    • \section compiling Compiling:
        -
      • Windows95/98/2000/XP: Realtime support is available using either DirectSound or ASIO audio drivers. For DirectSound support, use the __WINDOWS_DS__ preprocessor definition and link with the dsound.lib, winmm.lib, and Wsock32.lib libraries. For ASIO support, use the __WINDOWS_ASIO__ preprocessor definition, include all the files in the src/asio/ directory (i.e. asio.h,cpp, asiodrivers.h,cpp, ...), and link with the winmm.lib, and Wsock32.lib libraries. In addition, the __LITTLE_ENDIAN__ preprocessor definition is necessary for all Windows systems. A distribution of the release is available with precompiled binaries (using DirectSound) for all the projects. In order for these binaries to function properly, your system must have the DirectX 5.0 (or higher) runtime libraries installed (available from Microsoft). Further, the effects project requires that your soundcard and drivers provide full duplex mode capabilities. Visual C++ 6.0 project files are provided in each project directory as well should you wish to compile your own binaries. It is important to link with the non-debug libraries when compiling "release" program versions and debug libraries when compiling "debug" program versions.
      • +
      • Windows95/98/2000/XP: Realtime support is available using either DirectSound or ASIO audio drivers. For DirectSound support, use the __WINDOWS_DS__ preprocessor definition and link with the dsound.lib, winmm.lib, and Wsock32.lib libraries. For ASIO support, use the __WINDOWS_ASIO__ preprocessor definition, include all the files in the src/asio/ directory (i.e. asio.h,cpp, asiodrivers.h,cpp, ...), and link with the winmm.lib, and Wsock32.lib libraries. In addition, the __LITTLE_ENDIAN__ and __WINDOWS_MM__ preprocessor definitions are necessary for all Windows systems (RtMidi uses the Windows MultiMedia MIDI API). A distribution of the release is available with precompiled binaries (using DirectSound) for all the projects. In order for these binaries to function properly, your system must have the DirectX 5.0 (or higher) runtime libraries installed (available from Microsoft). Further, the effects project requires that your soundcard and drivers provide full duplex mode capabilities. Visual C++ 6.0 project files are provided in each project directory as well should you wish to compile your own binaries. It is important to link with the non-debug libraries when compiling "release" program versions and debug libraries when compiling "debug" program versions.
      • WindowsNT: DirectX support for NT is inadequate, so it is not possible to use STK under WindowsNT with realtime DirectX support. It may be possible to use STK under WindowsNT with realtime ASIO support, though this has not been tested.
      • @@ -53,11 +53,12 @@ Several options can be supplied to the configure script to customize th
        • --disable-realtime to only compile generic non-realtime classes
        • --enable-debug to enable various debug output
        • -
        • --enable-midiator to enable native MS-124W MIDI support (linux only)
        • -
        • --with-alsa to choose native ALSA API support (linux only)
        • +
        • --with-alsa to choose native ALSA API support (default, linux only)
        • +
        • --with-jack to choose native JACK API support (linux only)
        • +
        • --with-oss to choose native OSS audio API support (linux only, no native OSS MIDI support)

        -In addition, it is possible to specify the location of the STK rawwaves and the STK include path as follows: +Note that it is possible to specify as many of the "--with-" options as desired to compile multi-API support. In addition, it is possible to specify the location of the STK rawwaves and the STK include path as follows: \code ./configure RAWWAVE_PATH="/home/gary/rawwaves/" ./configure INCLUDE_PATH="/home/gary/include/" @@ -68,13 +69,13 @@ For novice STK users, the default configuration should be adequate. For those who wish to create their own system-specific Makefiles:

          -
        • Linux: Realtime support is enabled with either the __LINUX_OSS__ or __LINUX_ALSA__ preprocessor definitions, which are used to select the underlying audio/MIDI system API. Realtime programs must also link with the pthread library. When using the ALSA API, it is also necessary to link with the asound library. In addition, the __LITTLE_ENDIAN__ preprocessor definition is necessary if compiling on a little-endian system. Special support exists under Linux for the MIDIator serial MIDI device, enabled using the __MIDIATOR__ preprocessor definition (together with either the __LINUX_ALSA__ or __LINUX_OSS__ definitions). See the README-Linux file for further system configuration information.
        • +
        • Linux: Realtime audio support is enabled with either the __LINUX_ALSA__, __LINUX_JACK__, and/or __LINUX_OSS__ preprocessor definitions, which are used to select the underlying audio system API(s). Because the ALSA library is now integrated into the standard Linux kernel, it is the default audio/MIDI API with STK versions 4.2 and higher. The __LINUX_ALSASEQ__ preprocessor definition must be included for MIDI support. Note that native OSS MIDI support no longer exists in RtMidi. If the __LINUX_OSS__ preprocessor definition is specified, only OSS audio support will be compiled and RtMidi will still be compiled using the ALSA API (assuming the __LINUX_ALSASEQ__ definition is defined). For this reason, STK now requires the asound library for realtime support. Realtime programs must also link with the pthread library. In addition, the __LITTLE_ENDIAN__ preprocessor definition is necessary if compiling on a little-endian system. See the README-Linux file for further system configuration information.
        • Macintosh OS X: Realtime support is enabled with the __MACOSX_CORE__ preprocessor definitions, which incorporates the CoreAudio audio/MIDI API. Realtime programs must also link with the pthread library and the CoreAudio, CoreMIDI, and CoreFoundation frameworks. See the README-MacOSX file for further system configuration information.
        • -
        • SGI: Realtime support is enabled with the __IRIX_AL__ preprocessor definition and linkage with the audio, md, and pthread libraries. STK 4.0 (and higher) is confirmed to compile using CC version 7.30. There may be problems with old compiler versions.
        • +
        • SGI: Realtime audio and MIDI support is enabled with the __IRIX_AL__ and __IRIX_MD__ preprocessor definitions and linkage with the audio, md, and pthread libraries. STK 4.0 (and higher) is confirmed to compile using CC version 7.30. There may be problems with old compiler versions.
        • -
        • Generic (non-realtime): Most STK classes are operating system independent and can be compiled using any current C++ compiler. STK assumes big-endian host byte order by default, so if your system is little-endian (i.e. Intel processor), you must provide the __LITTLE_ENDIAN__ preprocessor definition to your compiler. The demo project will compile without realtime support, allowing the use of SKINI scorefiles for input control and output to a variety of soundfile formats. The following classes cannot be used without realtime support: RtAudio, RtWvIn, RtWvOut, RtDuplex, RtMidi, Socket, Thread, TcpWvIn, TcpWvOut. Because of this, it is not possible to compile the effects, ragamatic, and most of the examples projects for non-realtime use.
        • +
        • Generic (non-realtime): Most STK classes are operating system independent and can be compiled using any current C++ compiler. STK assumes big-endian host byte order by default, so if your system is little-endian (i.e. Intel processor), you must provide the __LITTLE_ENDIAN__ preprocessor definition to your compiler. The demo project will compile without realtime support, allowing the use of SKINI scorefiles for input control and output to a variety of soundfile formats. The following classes cannot be used without realtime support: RtAudio, RtWvIn, RtWvOut, RtDuplex, RtMidi, Socket, Thread, Mutex, TcpWvIn, TcpWvOut. Because of this, it is not possible to compile the effects, ragamatic, and most of the examples projects for non-realtime use.
        @@ -132,16 +133,10 @@ See the information above with respect to compiling STK for non-realtime use. In non-realtime mode, it is assumed that input control messages are provided from a SKINI scorefile and that audio output is written to a soundfile (.snd, .wav, .aif, .mat, .raw). A number of SKINI scorefiles are provided in the scores directory of the demo project. Assuming a successful compilation of the demo program, typing: \code -cat scores/bookert.ski | demo BeeThree -ow myfile.wav +demo BeeThree -ow myfile.wav -if scores/bookert.ski \endcode -or (on WindowsXX and/or Unix) - -\code -demo BeeThree -ow myfile.wav < scores\bookert.ski -\endcode - -from the demo directory will play the scorefile bookert.ski using the STK BeeThree instrument and write the resulting audio data to a WAV formatted soundfile called "myfile.wav". Typing demo without any arguments will provide a full program usage description. +from the demo directory will play the scorefile bookert.ski using the STK BeeThree instrument and write the resulting audio data to a WAV formatted soundfile called "myfile.wav" (note that you may need to append ./ to the program name if your default shell setup is not set to look in the current directory). Typing demo without any arguments will provide a full program usage description. \section rt Demo: Realtime Use @@ -157,27 +152,22 @@ demo instrument flags where instruments include those described above and flags can be any or all of:
        • -or for realtime audio output,
        • -
        • -ow for WAV soundfile output,
        • -
        • -os for SND (AU) soundfile output,
        • -
        • -om for MAT-file output,
        • +
        • -ow \ for WAV soundfile output,
        • +
        • -os \ for SND (AU) soundfile output,
        • +
        • -om \ for MAT-file output,
        • +
        • -if \ for a SKINI formatted control file,
        • -ip for realtime SKINI control input via piping,
        • -
        • -is > for realtime SKINI control input via socketing (with an optional port number),
        • -
        • -im for MIDI control input
        • -
        • -s RATE to specify a sample rate
        • +
        • -is \ for realtime SKINI control input via socketing (with an optional port number),
        • +
        • -im \ for MIDI control input (with optional port, -1 = virtual port where possible),
        • +
        • -s RATE to specify a sample rate,
        • -n NUMBER to specify multivoice polyphony
        -The <-ip> and <-is> flags must be used when piping or socketing realtime SKINI control data to an STK program. The <-im> flag must be used to read MIDI control input from your MIDI port. Note that you can use all three input types simultaneously. +The -ip and -is flags must be used when piping or socketing realtime SKINI control data to an STK program. The -im flag must be used to read MIDI control input from your MIDI port. Note that you can use all three input types simultaneously. Assuming a successful compilation of the demo program, typing: \code -cat scores/bookert.ski | demo BeeThree -or -\endcode - -or (on WindowsXX and/or Unix) - -\code -demo BeeThree -or < scores\bookert.ski +demo BeeThree -or -if scores/bookert.ski \endcode from the demo directory will play the scorefile bookert.ski using the STK BeeThree instrument and stream the resulting audio data in realtime to the audio output channel of your computer. Typing demo without any arguments will provide a full program usage description. @@ -209,6 +199,7 @@ On all supported realtime platforms, you can direct realtime MIDI input to the S demo Clarinet -or -im \endcode +This will attempt to use the default MIDI port for input. An optional MIDI port number can be specified after the -im flag. Valid MIDI ports are numbered from 0 (default) and higher. On Linux and Macintosh OS-X systems, it is possible to open a virtual MIDI input port (which other software applications can connect to) by specifying a port identifier of -1. \section polyphony Polyphony: diff --git a/include/ADSR.h b/include/ADSR.h index 3d3cb61..f9e0e4b 100644 --- a/include/ADSR.h +++ b/include/ADSR.h @@ -11,12 +11,12 @@ envelope value reaches 0.0 in the ADSR::RELEASE state. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__ADSR_H) -#define __ADSR_H +#ifndef STK_ADSR_H +#define STK_ADSR_H #include "Envelope.h" @@ -40,49 +40,58 @@ class ADSR : public Envelope void keyOff(void); //! Set the attack rate. - void setAttackRate(MY_FLOAT aRate); + void setAttackRate(StkFloat rate); //! Set the decay rate. - void setDecayRate(MY_FLOAT aRate); + void setDecayRate(StkFloat rate); //! Set the sustain level. - void setSustainLevel(MY_FLOAT aLevel); + void setSustainLevel(StkFloat level); //! Set the release rate. - void setReleaseRate(MY_FLOAT aRate); + void setReleaseRate(StkFloat rate); //! Set the attack rate based on a time duration. - void setAttackTime(MY_FLOAT aTime); + void setAttackTime(StkFloat time); //! Set the decay rate based on a time duration. - void setDecayTime(MY_FLOAT aTime); + void setDecayTime(StkFloat time); //! Set the release rate based on a time duration. - void setReleaseTime(MY_FLOAT aTime); + void setReleaseTime(StkFloat time); - //! Set sustain level and attack, decay, and release state rates based on time durations. - void setAllTimes(MY_FLOAT aTime, MY_FLOAT dTime, MY_FLOAT sLevel, MY_FLOAT rTime); + //! Set sustain level and attach, decay, and release time durations. + void setAllTimes(StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime); //! Set the target value. - void setTarget(MY_FLOAT aTarget); + void setTarget(StkFloat target); //! Return the current envelope \e state (ATTACK, DECAY, SUSTAIN, RELEASE, DONE). int getState(void) const; //! Set to state = ADSR::SUSTAIN with current and target values of \e aValue. - void setValue(MY_FLOAT aValue); + void setValue(StkFloat value); //! Return one envelope output value. - MY_FLOAT tick(void); + StkFloat tick(void); - //! Return \e vectorSize envelope outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Compute \e vectorSize outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - MY_FLOAT attackRate; - MY_FLOAT decayRate; - MY_FLOAT sustainLevel; - MY_FLOAT releaseRate; + StkFloat attackRate_; + StkFloat decayRate_; + StkFloat sustainLevel_; + StkFloat releaseRate_; }; #endif diff --git a/include/Asymp.h b/include/Asymp.h new file mode 100644 index 0000000..fd708e0 --- /dev/null +++ b/include/Asymp.h @@ -0,0 +1,84 @@ +/***************************************************/ +/*! \class Asymp + \brief STK asymptotic curve envelope class + + This class implements a simple envelope generator + which asymptotically approaches a target value. + The algorithm used is of the form: + + x[n] = a x[n-1] + (1-a) target, + + where a = exp(-T/tau), T is the sample period, and + tau is a time constant. The user can set the time + constant (default value = 0.3) and target value. + Theoretically, this recursion never reaches its + target, though the calculations in this class are + stopped when the current value gets within a small + threshold value of the target (at which time the + current value is set to the target). It responds + to \e keyOn and \e keyOff messages by ramping to + 1.0 on keyOn and to 0.0 on keyOff. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#ifndef STK_ASYMP_H +#define STK_ASYMP_H + +#include "Envelope.h" + +const StkFloat TARGET_THRESHOLD = 0.000001; + +class Asymp : public Envelope +{ + public: + + //! Default constructor. + Asymp(void); + + //! Class destructor. + ~Asymp(void); + + //! Set target = 1. + void keyOn(void); + + //! Set target = 0. + void keyOff(void); + + //! Set the asymptotic rate via the time factor \e tau (must be > 0). + /*! + The rate is computed as described above. The value of \e tau + must be greater than zero. Values of \e tau close to zero produce + fast approach rates, while values greater than 1.0 produce rather + slow rates. + */ + void setTau(StkFloat tau); + + //! Set the asymptotic rate based on a time duration (must be > 0). + void setTime(StkFloat time); + + //! Set the target value. + void setTarget(StkFloat target); + + //! Return one envelope output value. + StkFloat tick(void); + + //! Compute \e vectorSize outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + + protected: + StkFloat factor_; + StkFloat constant_; +}; + +#endif diff --git a/include/BandedWG.h b/include/BandedWG.h index 68b457b..1c613e1 100644 --- a/include/BandedWG.h +++ b/include/BandedWG.h @@ -24,19 +24,19 @@ - Glass Harmonica = 2 - Tibetan Bowl = 3 - by Georg Essl, 1999 - 2002. + by Georg Essl, 1999 - 2004. Modified for Stk 4.0 by Gary Scavone. */ /***************************************************/ -#if !defined(__BANDEDWG_H) -#define __BANDEDWG_H +#ifndef STK_BANDEDWG_H +#define STK_BANDEDWG_H -#define MAX_BANDED_MODES 20 +const int MAX_BANDED_MODES = 20; #include "Instrmnt.h" #include "DelayL.h" -#include "BowTabl.h" +#include "BowTable.h" #include "ADSR.h" #include "BiQuad.h" @@ -53,59 +53,71 @@ class BandedWG : public Instrmnt void clear(); //! Set strike position (0.0 - 1.0). - void setStrikePosition(MY_FLOAT position); + void setStrikePosition(StkFloat position); //! Select a preset. void setPreset(int preset); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Apply bow velocity/pressure to instrument with given amplitude and rate of increase. - void startBowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBowing(StkFloat amplitude, StkFloat rate); //! Decrease bow velocity/breath pressure with given rate of decrease. - void stopBowing(MY_FLOAT rate); + void stopBowing(StkFloat rate); //! Pluck the instrument with given amplitude. - void pluck(MY_FLOAT amp); + void pluck(StkFloat amp); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - bool doPluck; - bool trackVelocity; - int nModes; - int presetModes; - BowTabl *bowTabl; - ADSR *adsr; - BiQuad *bandpass; - DelayL *delay; - MY_FLOAT maxVelocity; - MY_FLOAT modes[MAX_BANDED_MODES]; - MY_FLOAT freakency; - MY_FLOAT baseGain; - MY_FLOAT gains[MAX_BANDED_MODES]; - MY_FLOAT basegains[MAX_BANDED_MODES]; - MY_FLOAT excitation[MAX_BANDED_MODES]; - MY_FLOAT integrationConstant; - MY_FLOAT velocityInput; - MY_FLOAT bowVelocity; - MY_FLOAT bowTarget; - MY_FLOAT bowPosition; - MY_FLOAT strikeAmp; - int strikePosition; + bool doPluck_; + bool trackVelocity_; + int nModes_; + int presetModes_; + BowTable bowTable_; + ADSR adsr_; + BiQuad bandpass_[MAX_BANDED_MODES]; + DelayL delay_[MAX_BANDED_MODES]; + StkFloat maxVelocity_; + StkFloat modes_[MAX_BANDED_MODES]; + StkFloat frequency_; + StkFloat baseGain_; + StkFloat gains_[MAX_BANDED_MODES]; + StkFloat basegains_[MAX_BANDED_MODES]; + StkFloat excitation_[MAX_BANDED_MODES]; + StkFloat integrationConstant_; + StkFloat velocityInput_; + StkFloat bowVelocity_; + StkFloat bowTarget_; + StkFloat bowPosition_; + StkFloat strikeAmp_; + int strikePosition_; }; diff --git a/include/BeeThree.h b/include/BeeThree.h index f8a1d3f..c71148f 100644 --- a/include/BeeThree.h +++ b/include/BeeThree.h @@ -28,12 +28,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__BEETHREE_H) -#define __BEETHREE_H +#ifndef STK_BEETHREE_H +#define STK_BEETHREE_H #include "FM.h" @@ -41,16 +41,31 @@ class BeeThree : public FM { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ BeeThree(); //! Class destructor. ~BeeThree(); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/BiQuad.h b/include/BiQuad.h index c936094..4d5803c 100644 --- a/include/BiQuad.h +++ b/include/BiQuad.h @@ -8,12 +8,12 @@ frequency response while maintaining a constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__BIQUAD_H) -#define __BIQUAD_H +#ifndef STK_BIQUAD_H +#define STK_BIQUAD_H #include "Filter.h" @@ -31,19 +31,19 @@ public: void clear(void); //! Set the b[0] coefficient value. - void setB0(MY_FLOAT b0); + void setB0(StkFloat b0); //! Set the b[1] coefficient value. - void setB1(MY_FLOAT b1); + void setB1(StkFloat b1); //! Set the b[2] coefficient value. - void setB2(MY_FLOAT b2); + void setB2(StkFloat b2); //! Set the a[1] coefficient value. - void setA1(MY_FLOAT a1); + void setA1(StkFloat a1); //! Set the a[2] coefficient value. - void setA2(MY_FLOAT a2); + void setA2(StkFloat a2); //! Sets the filter coefficients for a resonance at \e frequency (in Hz). /*! @@ -57,7 +57,7 @@ public: frequency. The closer the poles are to the unit-circle (\e radius close to one), the narrower the resulting resonance width. */ - void setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize = FALSE); + void setResonance(StkFloat frequency, StkFloat radius, bool normalize = false); //! Set the filter coefficients for a notch at \e frequency (in Hz). /*! @@ -66,7 +66,7 @@ public: and \e radius from the z-plane origin. No filter normalization is attempted. */ - void setNotch(MY_FLOAT frequency, MY_FLOAT radius); + void setNotch(StkFloat frequency, StkFloat radius); //! Sets the filter zeroes for equal resonance gain. /*! @@ -82,19 +82,28 @@ public: The gain is applied at the filter input and does not affect the coefficient values. The default gain value is 1.0. */ - void setGain(MY_FLOAT theGain); + void setGain(StkFloat gain); //! Return the current filter gain. - MY_FLOAT getGain(void) const; + StkFloat getGain(void) const; //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Input one sample to the filter and return one output. - MY_FLOAT tick(MY_FLOAT sample); + virtual StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/BlowBotl.h b/include/BlowBotl.h index f30ffdf..16d8ab2 100644 --- a/include/BlowBotl.h +++ b/include/BlowBotl.h @@ -12,15 +12,15 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__BOTTLE_H) -#define __BOTTLE_H +#ifndef STK_BLOWBOTL_H +#define STK_BLOWBOTL_H #include "Instrmnt.h" -#include "JetTabl.h" +#include "JetTable.h" #include "BiQuad.h" #include "PoleZero.h" #include "Noise.h" @@ -31,6 +31,9 @@ class BlowBotl : public Instrmnt { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ BlowBotl(); //! Class destructor. @@ -40,37 +43,49 @@ class BlowBotl : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Apply breath velocity to instrument with given amplitude and rate of increase. - void startBlowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBlowing(StkFloat amplitude, StkFloat rate); //! Decrease breath velocity with given rate of decrease. - void stopBlowing(MY_FLOAT rate); + void stopBlowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - JetTabl *jetTable; - BiQuad *resonator; - PoleZero *dcBlock; - Noise *noise; - ADSR *adsr; - WaveLoop *vibrato; - MY_FLOAT maxPressure; - MY_FLOAT noiseGain; - MY_FLOAT vibratoGain; - MY_FLOAT outputGain; + JetTable jetTable_; + BiQuad resonator_; + PoleZero dcBlock_; + Noise noise_; + ADSR adsr_; + WaveLoop *vibrato_; + StkFloat maxPressure_; + StkFloat noiseGain_; + StkFloat vibratoGain_; + StkFloat outputGain_; }; diff --git a/include/BlowHole.h b/include/BlowHole.h index 682fcad..83e36dd 100644 --- a/include/BlowHole.h +++ b/include/BlowHole.h @@ -29,16 +29,16 @@ - Register State = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__BLOWHOLE_H) -#define __BLOWHOLE_H +#ifndef STK_BLOWHOLE_H +#define STK_BLOWHOLE_H #include "Instrmnt.h" #include "DelayL.h" -#include "ReedTabl.h" +#include "ReedTable.h" #include "OneZero.h" #include "PoleZero.h" #include "Envelope.h" @@ -49,7 +49,10 @@ class BlowHole : public Instrmnt { public: //! Class constructor. - BlowHole(MY_FLOAT lowestFrequency); + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + BlowHole(StkFloat lowestFrequency); //! Class destructor. ~BlowHole(); @@ -58,50 +61,60 @@ class BlowHole : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Set the tonehole state (0.0 = closed, 1.0 = fully open). - void setTonehole(MY_FLOAT newValue); + void setTonehole(StkFloat newValue); //! Set the register hole state (0.0 = closed, 1.0 = fully open). - void setVent(MY_FLOAT newValue); + void setVent(StkFloat newValue); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBlowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBlowing(StkFloat amplitude, StkFloat rate); //! Decrease breath pressure with given rate of decrease. - void stopBlowing(MY_FLOAT rate); + void stopBlowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - DelayL *delays[3]; - ReedTabl *reedTable; - OneZero *filter; - PoleZero *tonehole; - PoleZero *vent; - Envelope *envelope; - Noise *noise; - WaveLoop *vibrato; - long length; - MY_FLOAT scatter; - MY_FLOAT th_coeff; - MY_FLOAT r_th; - MY_FLOAT rh_coeff; - MY_FLOAT rh_gain; - MY_FLOAT outputGain; - MY_FLOAT noiseGain; - MY_FLOAT vibratoGain; + DelayL delays_[3]; + ReedTable reedTable_; + OneZero filter_; + PoleZero tonehole_; + PoleZero vent_; + Envelope envelope_; + Noise noise_; + WaveLoop *vibrato_; + unsigned long length_; + StkFloat scatter_; + StkFloat thCoeff_; + StkFloat rhGain_; + StkFloat outputGain_; + StkFloat noiseGain_; + StkFloat vibratoGain_; }; diff --git a/include/BowTabl.h b/include/BowTabl.h deleted file mode 100644 index 7886e09..0000000 --- a/include/BowTabl.h +++ /dev/null @@ -1,62 +0,0 @@ -/***************************************************/ -/*! \class BowTabl - \brief STK bowed string table class. - - This class implements a simple bowed string - non-linear function, as described by Smith (1986). - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#if !defined(__BOWTABL_H) -#define __BOWTABL_H - -#include "Stk.h" - -class BowTabl : public Stk -{ -public: - //! Default constructor. - BowTabl(); - - //! Class destructor. - ~BowTabl(); - - //! Set the table offset value. - /*! - The table offset is a bias which controls the - symmetry of the friction. If you want the - friction to vary with direction, use a non-zero - value for the offset. The default value is zero. - */ - void setOffset(MY_FLOAT aValue); - - //! Set the table slope value. - /*! - The table slope controls the width of the friction - pulse, which is related to bow force. - */ - void setSlope(MY_FLOAT aValue); - - //! Return the last output value. - MY_FLOAT lastOut(void) const; - - //! Return the function value for \e input. - /*! - The function input represents differential - string-to-bow velocity. - */ - MY_FLOAT tick(const MY_FLOAT input); - - //! Take \e vectorSize inputs and return the corresponding function values in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); - -protected: - MY_FLOAT offSet; - MY_FLOAT slope; - MY_FLOAT lastOutput; - -}; - -#endif diff --git a/include/BowTable.h b/include/BowTable.h new file mode 100644 index 0000000..7c61708 --- /dev/null +++ b/include/BowTable.h @@ -0,0 +1,67 @@ +/***************************************************/ +/*! \class BowTable + \brief STK bowed string table class. + + This class implements a simple bowed string + non-linear function, as described by Smith (1986). + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#ifndef STK_BOWTABL_H +#define STK_BOWTABL_H + +#include "Function.h" + +class BowTable : public Function +{ +public: + //! Default constructor. + BowTable(); + + //! Class destructor. + ~BowTable(); + + //! Set the table offset value. + /*! + The table offset is a bias which controls the + symmetry of the friction. If you want the + friction to vary with direction, use a non-zero + value for the offset. The default value is zero. + */ + void setOffset(StkFloat offset); + + //! Set the table slope value. + /*! + The table slope controls the width of the friction + pulse, which is related to bow force. + */ + void setSlope(StkFloat slope); + + //! Return the function value for \e input. + /*! + The function input represents differential + string-to-bow velocity. + */ + StkFloat tick( StkFloat input); + + //! Take \e vectorSize inputs from \e vector and replace them with corresponding outputs. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the function and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + +protected: + StkFloat offset_; + StkFloat slope_; + +}; + +#endif diff --git a/include/Bowed.h b/include/Bowed.h index e393d97..2392b14 100644 --- a/include/Bowed.h +++ b/include/Bowed.h @@ -17,16 +17,16 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__BOWED_H) -#define __BOWED_H +#ifndef STK_BOWED_H +#define STK_BOWED_H #include "Instrmnt.h" #include "DelayL.h" -#include "BowTabl.h" +#include "BowTable.h" #include "OnePole.h" #include "BiQuad.h" #include "WaveLoop.h" @@ -36,7 +36,7 @@ class Bowed : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Bowed(MY_FLOAT lowestFrequency); + Bowed(StkFloat lowestFrequency); //! Class destructor. ~Bowed(); @@ -45,41 +45,53 @@ class Bowed : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Set vibrato gain. - void setVibrato(MY_FLOAT gain); + void setVibrato(StkFloat gain); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBowing(StkFloat amplitude, StkFloat rate); //! Decrease breath pressure with given rate of decrease. - void stopBowing(MY_FLOAT rate); + void stopBowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - DelayL *neckDelay; - DelayL *bridgeDelay; - BowTabl *bowTable; - OnePole *stringFilter; - BiQuad *bodyFilter; - WaveLoop *vibrato; - ADSR *adsr; - MY_FLOAT maxVelocity; - MY_FLOAT baseDelay; - MY_FLOAT vibratoGain; - MY_FLOAT betaRatio; + DelayL neckDelay_; + DelayL bridgeDelay_; + BowTable bowTable_; + OnePole stringFilter_; + BiQuad bodyFilter_; + WaveLoop *vibrato_; + ADSR adsr_; + StkFloat maxVelocity_; + StkFloat baseDelay_; + StkFloat vibratoGain_; + StkFloat betaRatio_; }; diff --git a/include/Brass.h b/include/Brass.h index 4c59036..7d03039 100644 --- a/include/Brass.h +++ b/include/Brass.h @@ -16,12 +16,12 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__BRASS_H) -#define __BRASS_H +#ifndef STK_BRASS_H +#define STK_BRASS_H #include "Instrmnt.h" #include "DelayA.h" @@ -34,7 +34,10 @@ class Brass: public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Brass(MY_FLOAT lowestFrequency); + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Brass(StkFloat lowestFrequency); //! Class destructor. ~Brass(); @@ -43,40 +46,52 @@ class Brass: public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Set the lips frequency. - void setLip(MY_FLOAT frequency); + void setLip(StkFloat frequency); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBlowing(MY_FLOAT amplitude,MY_FLOAT rate); + void startBlowing(StkFloat amplitude, StkFloat rate); //! Decrease breath pressure with given rate of decrease. - void stopBlowing(MY_FLOAT rate); + void stopBlowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - DelayA *delayLine; - BiQuad *lipFilter; - PoleZero *dcBlock; - ADSR *adsr; - WaveLoop *vibrato; - long length; - MY_FLOAT lipTarget; - MY_FLOAT slideTarget; - MY_FLOAT vibratoGain; - MY_FLOAT maxPressure; + DelayA delayLine_; + BiQuad lipFilter_; + PoleZero dcBlock_; + ADSR adsr_; + WaveLoop *vibrato_; + unsigned long length_; + StkFloat lipTarget_; + StkFloat slideTarget_; + StkFloat vibratoGain_; + StkFloat maxPressure_; }; diff --git a/include/Chorus.h b/include/Chorus.h index feadbcb..a4daaea 100644 --- a/include/Chorus.h +++ b/include/Chorus.h @@ -4,22 +4,25 @@ This class implements a chorus effect. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__CHORUS_H) -#define __CHORUS_H +#ifndef STK_CHORUS_H +#define STK_CHORUS_H -#include "Stk.h" +#include "Effect.h" #include "DelayL.h" #include "WaveLoop.h" -class Chorus : public Stk +class Chorus : public Effect { public: - //! Class constructor, taking the longest desired delay length. - Chorus(MY_FLOAT baseDelay); + //! Class constructor, taking the median desired delay length. + /*! + An StkError can be thrown if the rawwave path is incorrect. + */ + Chorus( StkFloat baseDelay = 6000 ); //! Class destructor. ~Chorus(); @@ -28,36 +31,31 @@ class Chorus : public Stk void clear(); //! Set modulation depth. - void setModDepth(MY_FLOAT depth); + void setModDepth(StkFloat depth); //! Set modulation frequency. - void setModFrequency(MY_FLOAT frequency); - - //! Set the mixture of input and processed levels in the output (0.0 = input only, 1.0 = processed only). - void setEffectMix(MY_FLOAT mix); - - //! 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; + void setModFrequency(StkFloat frequency); //! Compute one output sample. - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - DelayL *delayLine[2]; - WaveLoop *mods[2]; - MY_FLOAT baseLength; - MY_FLOAT modDepth; - MY_FLOAT lastOutput[2]; - MY_FLOAT effectMix; + DelayL delayLine_[2]; + WaveLoop *mods_[2]; + StkFloat baseLength_; + StkFloat modDepth_; }; diff --git a/include/Clarinet.h b/include/Clarinet.h index cc60108..9aaac2c 100644 --- a/include/Clarinet.h +++ b/include/Clarinet.h @@ -18,16 +18,16 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__CLARINET_H) -#define __CLARINET_H +#ifndef STK_CLARINET_H +#define STK_CLARINET_H #include "Instrmnt.h" #include "DelayL.h" -#include "ReedTabl.h" +#include "ReedTable.h" #include "OneZero.h" #include "Envelope.h" #include "Noise.h" @@ -37,7 +37,10 @@ class Clarinet : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Clarinet(MY_FLOAT lowestFrequency); + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Clarinet(StkFloat lowestFrequency); //! Class destructor. ~Clarinet(); @@ -46,37 +49,49 @@ class Clarinet : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBlowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBlowing(StkFloat amplitude, StkFloat rate); //! Decrease breath pressure with given rate of decrease. - void stopBlowing(MY_FLOAT rate); + void stopBlowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - DelayL *delayLine; - ReedTabl *reedTable; - OneZero *filter; - Envelope *envelope; - Noise *noise; - WaveLoop *vibrato; - long length; - MY_FLOAT outputGain; - MY_FLOAT noiseGain; - MY_FLOAT vibratoGain; + DelayL delayLine_; + ReedTable reedTable_; + OneZero filter_; + Envelope envelope_; + Noise noise_; + WaveLoop *vibrato_; + long length_; + StkFloat outputGain_; + StkFloat noiseGain_; + StkFloat vibratoGain_; }; diff --git a/include/Delay.h b/include/Delay.h index 84dcff0..7ef8cbc 100644 --- a/include/Delay.h +++ b/include/Delay.h @@ -14,12 +14,12 @@ used in fixed delay-length applications, such as for reverberation. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__DELAY_H) -#define __DELAY_H +#ifndef STK_DELAY_H +#define STK_DELAY_H #include "Filter.h" @@ -31,7 +31,12 @@ public: Delay(); //! Overloaded constructor which specifies the current and maximum delay-line lengths. - Delay(long theDelay, long maxDelay); + /*! + An StkError will be thrown if the delay parameter is less than + zero, the maximum delay parameter is less than one, or the delay + parameter is greater than the maxDelay value. + */ + Delay(unsigned long delay, unsigned long maxDelay); //! Class destructor. virtual ~Delay(); @@ -39,17 +44,27 @@ public: //! Clears the internal state of the delay line. void clear(); + //! Set the maximum delay-line length. + /*! + This method should generally only be used during initial setup + of the delay line. If it is used between calls to the tick() + function, without a call to clear(), a signal discontinuity will + likely occur. If the current maximum length is greater than the + new length, no change will be made. + */ + void setMaximumDelay(unsigned long delay); + //! Set the delay-line length. /*! The valid range for \e theDelay is from 0 to the maximum delay-line length. */ - void setDelay(long theDelay); + void setDelay(unsigned long delay); //! Return the current delay-line length. - long getDelay(void) const; + unsigned long getDelay(void) const; //! Calculate and return the signal energy in the delay-line. - MY_FLOAT energy(void) const; + StkFloat energy(void) const; //! Return the value at \e tapDelay samples from the delay-line input. /*! @@ -57,28 +72,36 @@ public: relative to the last input value (i.e., a tapDelay of zero returns the last input value). */ - MY_FLOAT contentsAt(unsigned long tapDelay) const; + StkFloat contentsAt(unsigned long tapDelay); //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Return the value which will be output by the next call to tick(). /*! This method is valid only for delay settings greater than zero! */ - virtual MY_FLOAT nextOut(void) const; + virtual StkFloat nextOut(void); - //! Input one sample to the delay-line and return one output. - virtual MY_FLOAT tick(MY_FLOAT sample); + //! Input one sample to the delayline and return one output. + virtual StkFloat tick(StkFloat sample); - //! Input \e vectorSize samples to the delay-line and return an equal number of outputs in \e vector. - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Input \e vectorSize samples to the delayline and return an equal number of outputs in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the delayline and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - long inPoint; - long outPoint; - long length; - MY_FLOAT delay; + unsigned long inPoint_; + unsigned long outPoint_; + StkFloat delay_; }; #endif diff --git a/include/DelayA.h b/include/DelayA.h index 224273f..9ef6a0b 100644 --- a/include/DelayA.h +++ b/include/DelayA.h @@ -18,12 +18,12 @@ response, the minimum delay possible in this implementation is limited to a value of 0.5. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__DelayA_h) -#define __DelayA_h +#ifndef STK_DELAYA_H +#define STK_DELAYA_H #include "Delay.h" @@ -35,8 +35,12 @@ public: DelayA(); //! Overloaded constructor which specifies the current and maximum delay-line lengths. - - DelayA(MY_FLOAT theDelay, long maxDelay); + /*! + An StkError will be thrown if the delay parameter is less than + zero, the maximum delay parameter is less than one, or the delay + parameter is greater than the maxDelay value. + */ + DelayA(StkFloat delay, unsigned long maxDelay); //! Class destructor. ~DelayA(); @@ -48,26 +52,38 @@ public: /*! The valid range for \e theDelay is from 0.5 to the maximum delay-line length. */ - void setDelay(MY_FLOAT theDelay); + void setDelay(StkFloat delay); //! Return the current delay-line length. - MY_FLOAT getDelay(void); + StkFloat getDelay(void); //! Return the value which will be output by the next call to tick(). /*! This method is valid only for delay settings greater than zero! */ - MY_FLOAT nextOut(void); + StkFloat nextOut(void); - //! Input one sample to the delay-line and return one output. - MY_FLOAT tick(MY_FLOAT sample); + //! Input one sample to the delayline and return one output. + StkFloat tick(StkFloat sample); + + //! Input \e vectorSize samples to the delayline and return an equal number of outputs in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the delayline and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - MY_FLOAT alpha; - MY_FLOAT coeff; - MY_FLOAT apInput; - MY_FLOAT nextOutput; - bool doNextOut; + StkFloat alpha_; + StkFloat coeff_; + StkFloat apInput_; + StkFloat nextOutput_; + bool doNextOut_; }; #endif diff --git a/include/DelayL.h b/include/DelayL.h index 6169b13..722a7da 100644 --- a/include/DelayL.h +++ b/include/DelayL.h @@ -18,12 +18,12 @@ order Lagrange interpolators can typically improve (minimize) this attenuation characteristic. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__DELAYL_H) -#define __DELAYL_H +#ifndef STK_DELAYL_H +#define STK_DELAYL_H #include "Delay.h" @@ -35,8 +35,12 @@ public: DelayL(); //! Overloaded constructor which specifies the current and maximum delay-line lengths. - - DelayL(MY_FLOAT theDelay, long maxDelay); + /*! + An StkError will be thrown if the delay parameter is less than + zero, the maximum delay parameter is less than one, or the delay + parameter is greater than the maxDelay value. + */ + DelayL(StkFloat delay, unsigned long maxDelay); //! Class destructor. ~DelayL(); @@ -45,25 +49,37 @@ public: /*! The valid range for \e theDelay is from 0 to the maximum delay-line length. */ - void setDelay(MY_FLOAT theDelay); + void setDelay(StkFloat delay); //! Return the current delay-line length. - MY_FLOAT getDelay(void) const; + StkFloat getDelay(void) const; //! Return the value which will be output by the next call to tick(). /*! This method is valid only for delay settings greater than zero! */ - MY_FLOAT nextOut(void); + StkFloat nextOut(void); //! Input one sample to the delay-line and return one output. - MY_FLOAT tick(MY_FLOAT sample); + StkFloat tick(StkFloat sample); + + //! Input \e vectorSize samples to the delay-line and return an equal number of outputs in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the delayline and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - MY_FLOAT alpha; - MY_FLOAT omAlpha; - MY_FLOAT nextOutput; - bool doNextOut; + StkFloat alpha_; + StkFloat omAlpha_; + StkFloat nextOutput_; + bool doNextOut_; }; #endif diff --git a/include/Drummer.h b/include/Drummer.h index 5cd66e3..b081ae8 100644 --- a/include/Drummer.h +++ b/include/Drummer.h @@ -11,24 +11,27 @@ of simultaneous voices) via a #define in the Drummer.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__DRUMMER_H) -#define __DRUMMER_H +#ifndef STK_DRUMMER_H +#define STK_DRUMMER_H #include "Instrmnt.h" #include "WvIn.h" #include "OnePole.h" -#define DRUM_NUMWAVES 11 -#define DRUM_POLYPHONY 4 +const int DRUM_NUMWAVES = 11; +const int DRUM_POLYPHONY = 4; class Drummer : public Instrmnt { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ Drummer(); //! Class destructor. @@ -37,23 +40,35 @@ class Drummer : public Instrmnt //! Start a note with the given drum type and amplitude. /*! Use general MIDI drum instrument numbers, converted to - frequency values as if MIDI note numbers, to select a - particular instrument. + frequency values as if MIDI note numbers, to select a particular + instrument. An StkError will be thrown if the rawwave path is + incorrectly set. */ - void noteOn(MY_FLOAT instrument, MY_FLOAT amplitude); + void noteOn(StkFloat instrument, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - WvIn *waves[DRUM_POLYPHONY]; - OnePole *filters[DRUM_POLYPHONY]; - int sounding[DRUM_POLYPHONY]; - int nSounding; - + WvIn *waves_[DRUM_POLYPHONY]; + OnePole *filters_[DRUM_POLYPHONY]; + int sounding_[DRUM_POLYPHONY]; + int nSounding_; }; #endif diff --git a/include/Echo.h b/include/Echo.h index 62740e7..57d7f11 100644 --- a/include/Echo.h +++ b/include/Echo.h @@ -2,23 +2,26 @@ /*! \class Echo \brief STK echo effect class. - This class implements a echo effect. + This class implements an echo effect. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__ECHO_H) -#define __ECHO_H +#ifndef STK_ECHO_H +#define STK_ECHO_H -#include "Stk.h" +#include "Effect.h" #include "Delay.h" -class Echo : public Stk +class Echo : public Effect { public: - //! Class constructor, taking the longest desired delay length. - Echo(MY_FLOAT longestDelay); + //! Class constructor, taking the longest desired delay length (one second default value). + /*! + The default delay value is set to 1/2 the maximum delay length. + */ + Echo( unsigned long maximumDelay = (unsigned long) Stk::sampleRate() ); //! Class destructor. ~Echo(); @@ -26,26 +29,30 @@ class Echo : public Stk //! Reset and clear all internal state. void clear(); + //! Set the maximum delay line length in samples. + void setMaximumDelay( unsigned long delay ); + //! Set the delay line length in samples. - void setDelay(MY_FLOAT delay); - - //! Set the mixture of input and processed levels in the output (0.0 = input only, 1.0 = processed only). - void setEffectMix(MY_FLOAT mix); - - //! Return the last output value. - MY_FLOAT lastOut() const; + void setDelay( unsigned long delay ); //! Compute one output sample. - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); - //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - Delay *delayLine; - long length; - MY_FLOAT lastOutput; - MY_FLOAT effectMix; + Delay delayLine_; + unsigned long length_; }; diff --git a/include/Effect.h b/include/Effect.h new file mode 100644 index 0000000..60d40e9 --- /dev/null +++ b/include/Effect.h @@ -0,0 +1,67 @@ +/***************************************************/ +/*! \class Effect + \brief STK abstract effects parent class. + + This class provides common functionality for + STK effects subclasses. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#include "Stk.h" + +#ifndef STK_EFFECT_H +#define STK_EFFECT_H + +class Effect : public Stk +{ + public: + //! Class constructor. + Effect(); + + //! Class destructor. + virtual ~Effect(); + + //! Reset and clear all internal state. + virtual void clear() = 0; + + //! Set the mixture of input and "effected" levels in the output (0.0 = input only, 1.0 = reverb only). + void setEffectMix(StkFloat mix); + + //! Return the last output value. + StkFloat lastOut() const; + + //! Return the last left output value. + StkFloat lastOutLeft() const; + + //! Return the last right output value. + StkFloat lastOutRight() const; + + //! Abstract tick function ... must be implemented in subclasses. + virtual StkFloat tick( StkFloat input ) = 0; + + //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. + virtual StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + + protected: + + // Returns true if argument value is prime. + bool isPrime( int number ); + + StkFloat lastOutput_[2]; + StkFloat effectMix_; + +}; + +#endif + diff --git a/include/Envelope.h b/include/Envelope.h index a1abbf7..dc5b2c7 100644 --- a/include/Envelope.h +++ b/include/Envelope.h @@ -9,16 +9,16 @@ \e keyOff messages, ramping to 1.0 on keyOn and to 0.0 on keyOff. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__ENVELOPE_H) -#define __ENVELOPE_H +#ifndef STK_ENVELOPE_H +#define STK_ENVELOPE_H -#include "Stk.h" +#include "Generator.h" -class Envelope : public Stk +class Envelope : public Generator { public: @@ -35,34 +35,40 @@ class Envelope : public Stk virtual void keyOff(void); //! Set the \e rate. - void setRate(MY_FLOAT aRate); + void setRate(StkFloat rate); //! Set the \e rate based on a time duration. - void setTime(MY_FLOAT aTime); + void setTime(StkFloat time); //! Set the target value. - virtual void setTarget(MY_FLOAT aTarget); + virtual void setTarget(StkFloat target); //! Set current and target values to \e aValue. - virtual void setValue(MY_FLOAT aValue); + virtual void setValue(StkFloat value); //! Return the current envelope \e state (0 = at target, 1 otherwise). virtual int getState(void) const; //! Return one envelope output value. - virtual MY_FLOAT tick(void); + virtual StkFloat tick(void); - //! Return \e vectorSize envelope outputs in \e vector. - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Compute \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); - //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - MY_FLOAT value; - MY_FLOAT target; - MY_FLOAT rate; - int state; + StkFloat value_; + StkFloat target_; + StkFloat rate_; + int state_; }; #endif diff --git a/include/FM.h b/include/FM.h index f912fca..d09d2fa 100644 --- a/include/FM.h +++ b/include/FM.h @@ -19,12 +19,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__FM_H) -#define __FM_H +#ifndef STK_FM_H +#define STK_FM_H #include "Instrmnt.h" #include "ADSR.h" @@ -35,7 +35,10 @@ class FM : public Instrmnt { public: //! Class constructor, taking the number of wave/envelope operators to control. - FM( int operators = 4 ); + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + FM( unsigned int operators = 4 ); //! Class destructor. virtual ~FM(); @@ -47,25 +50,25 @@ class FM : public Instrmnt void loadWaves(const char **filenames); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + virtual void setFrequency(StkFloat frequency); //! Set the frequency ratio for the specified wave. - void setRatio(int waveIndex, MY_FLOAT ratio); + void setRatio(unsigned int waveIndex, StkFloat ratio); //! Set the gain for the specified wave. - void setGain(int waveIndex, MY_FLOAT gain); + void setGain(unsigned int waveIndex, StkFloat gain); //! Set the modulation speed in Hz. - void setModulationSpeed(MY_FLOAT mSpeed); + void setModulationSpeed(StkFloat mSpeed); //! Set the modulation depth. - void setModulationDepth(MY_FLOAT mDepth); + void setModulationDepth(StkFloat mDepth); //! Set the value of control1. - void setControl1(MY_FLOAT cVal); + void setControl1(StkFloat cVal); //! Set the value of control1. - void setControl2(MY_FLOAT cVal); + void setControl2(StkFloat cVal); //! Start envelopes toward "on" targets. void keyOn(); @@ -74,29 +77,41 @@ class FM : public Instrmnt void keyOff(); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Pure virtual function ... must be defined in subclasses. - virtual MY_FLOAT tick() = 0; + virtual StkFloat tick() = 0; + + //! Computer \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize) = 0; + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ) = 0; //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + virtual void controlChange(int number, StkFloat value); protected: - ADSR **adsr; - WaveLoop **waves; - WaveLoop *vibrato; - TwoZero *twozero; - int nOperators; - MY_FLOAT baseFrequency; - MY_FLOAT *ratios; - MY_FLOAT *gains; - MY_FLOAT modDepth; - MY_FLOAT control1; - MY_FLOAT control2; - MY_FLOAT __FM_gains[100]; - MY_FLOAT __FM_susLevels[16]; - MY_FLOAT __FM_attTimes[32]; + std::vector adsr_; + std::vector waves_; + WaveLoop *vibrato_; + TwoZero twozero_; + unsigned int nOperators_; + StkFloat baseFrequency_; + std::vector ratios_; + std::vector gains_; + StkFloat modDepth_; + StkFloat control1_; + StkFloat control2_; + StkFloat fmGains_[100]; + StkFloat fmSusLevels_[16]; + StkFloat fmAttTimes_[32]; }; diff --git a/include/FMVoices.h b/include/FMVoices.h index 9c5b3c7..e771e3d 100644 --- a/include/FMVoices.h +++ b/include/FMVoices.h @@ -26,12 +26,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__FMVOICES_H) -#define __FMVOICES_H +#ifndef STK_FMVOICES_H +#define STK_FMVOICES_H #include "FM.h" @@ -39,27 +39,42 @@ class FMVoices : public FM { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ FMVoices(); //! Class destructor. ~FMVoices(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + virtual void setFrequency(StkFloat frequency); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + virtual void controlChange(int number, StkFloat value); protected: - int currentVowel; - MY_FLOAT tilt[3]; - MY_FLOAT mods[3]; + int currentVowel_; + StkFloat tilt_[3]; + StkFloat mods_[3]; }; #endif diff --git a/include/Filter.h b/include/Filter.h index cb9fd5a..365acec 100644 --- a/include/Filter.h +++ b/include/Filter.h @@ -23,14 +23,16 @@ results in one extra multiply per computed sample, but allows easy control of the overall filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__FILTER_H) -#define __FILTER_H +#ifndef STK_FILTER_H +#define STK_FILTER_H #include "Stk.h" +#include +#include class Filter : public Stk { @@ -40,72 +42,81 @@ public: //! Overloaded constructor which takes filter coefficients. /*! - An StkError can be thrown if either \e nb or \e na is less than - one, or if the a[0] coefficient is equal to zero. + An StkError can be thrown if either of the coefficient vector + sizes is zero, or if the a[0] coefficient is equal to zero. */ - Filter(int nb, MY_FLOAT *bCoefficients, int na, MY_FLOAT *aCoefficients); + Filter( std::vector &bCoefficients, std::vector &aCoefficients ); //! Class destructor. virtual ~Filter(void); - //! Clears all internal states of the filter. + //! Sets all internal states of the filter to zero. void clear(void); //! Set filter coefficients. /*! - An StkError can be thrown if either \e nb or \e na is less than - one, or if the a[0] coefficient is equal to zero. If a[0] is not - equal to 1, the filter coeffcients are normalized by a[0]. + An StkError can be thrown if either of the coefficient vector + sizes is zero, or if the a[0] coefficient is equal to zero. If + a[0] is not equal to 1, the filter coeffcients are normalized by + a[0]. The internal state of the filter is cleared. */ - void setCoefficients(int nb, MY_FLOAT *bCoefficients, int na, MY_FLOAT *aCoefficients); + void setCoefficients( std::vector &bCoefficients, std::vector &aCoefficients ); //! Set numerator coefficients. /*! - An StkError can be thrown if \e nb is less than one. Any - previously set denominator coefficients are left unaffected. - Note that the default constructor sets the single denominator - coefficient a[0] to 1.0. + An StkError can be thrown if coefficient vector is empty. Any + previously set denominator coefficients are left unaffected. Note + that the default constructor sets the single denominator + coefficient a[0] to 1.0. The internal state of the filter is + cleared. */ - void setNumerator(int nb, MY_FLOAT *bCoefficients); + void setNumerator( std::vector &bCoefficients ); //! Set denominator coefficients. /*! - An StkError can be thrown if \e na is less than one or if the - a[0] coefficient is equal to zero. Previously set numerator - coefficients are unaffected unless a[0] is not equal to 1, in - which case all coeffcients are normalized by a[0]. Note that the - default constructor sets the single numerator coefficient b[0] - to 1.0. + An StkError can be thrown if the coefficient vector is empty or + if the a[0] coefficient is equal to zero. Previously set + numerator coefficients are unaffected unless a[0] is not equal to + 1, in which case all coeffcients are normalized by a[0]. Note + that the default constructor sets the single numerator coefficient + b[0] to 1.0. The internal state of the filter is cleared. */ - void setDenominator(int na, MY_FLOAT *aCoefficients); + void setDenominator( std::vector &aCoefficients ); //! Set the filter gain. /*! The gain is applied at the filter input and does not affect the coefficient values. The default gain value is 1.0. */ - virtual void setGain(MY_FLOAT theGain); + virtual void setGain(StkFloat gain); //! Return the current filter gain. - virtual MY_FLOAT getGain(void) const; + virtual StkFloat getGain(void) const; //! Return the last computed output value. - virtual MY_FLOAT lastOut(void) const; + virtual StkFloat lastOut(void) const; //! Input one sample to the filter and return one output. - virtual MY_FLOAT tick(MY_FLOAT sample); + virtual StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - MY_FLOAT gain; - int nB; - int nA; - MY_FLOAT *b; - MY_FLOAT *a; - MY_FLOAT *outputs; - MY_FLOAT *inputs; + StkFloat gain_; + std::vector b_; + std::vector a_; + std::vector outputs_; + std::vector inputs_; }; diff --git a/include/Flute.h b/include/Flute.h index 93becad..abe3bf1 100644 --- a/include/Flute.h +++ b/include/Flute.h @@ -18,15 +18,15 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__FLUTE_H) -#define __FLUTE_H +#ifndef STK_FLUTE_H +#define STK_FLUTE_H #include "Instrmnt.h" -#include "JetTabl.h" +#include "JetTable.h" #include "DelayL.h" #include "OnePole.h" #include "PoleZero.h" @@ -38,7 +38,10 @@ class Flute : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Flute(MY_FLOAT lowestFrequency); + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Flute(StkFloat lowestFrequency); //! Class destructor. ~Flute(); @@ -47,53 +50,65 @@ class Flute : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Set the reflection coefficient for the jet delay (-1.0 - 1.0). - void setJetReflection(MY_FLOAT coefficient); + void setJetReflection(StkFloat coefficient); //! Set the reflection coefficient for the air column delay (-1.0 - 1.0). - void setEndReflection(MY_FLOAT coefficient); + void setEndReflection(StkFloat coefficient); //! Set the length of the jet delay in terms of a ratio of jet delay to air column delay lengths. - void setJetDelay(MY_FLOAT aRatio); + void setJetDelay(StkFloat aRatio); //! Apply breath velocity to instrument with given amplitude and rate of increase. - void startBlowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBlowing(StkFloat amplitude, StkFloat rate); //! Decrease breath velocity with given rate of decrease. - void stopBlowing(MY_FLOAT rate); + void stopBlowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - DelayL *jetDelay; - DelayL *boreDelay; - JetTabl *jetTable; - OnePole *filter; - PoleZero *dcBlock; - Noise *noise; - ADSR *adsr; - WaveLoop *vibrato; - long length; - MY_FLOAT lastFrequency; - MY_FLOAT maxPressure; - MY_FLOAT jetReflection; - MY_FLOAT endReflection; - MY_FLOAT noiseGain; - MY_FLOAT vibratoGain; - MY_FLOAT outputGain; - MY_FLOAT jetRatio; + DelayL jetDelay_; + DelayL boreDelay_; + JetTable jetTable_; + OnePole filter_; + PoleZero dcBlock_; + Noise noise_; + ADSR adsr_; + WaveLoop *vibrato_; + unsigned long length_; + StkFloat lastFrequency_; + StkFloat maxPressure_; + StkFloat jetReflection_; + StkFloat endReflection_; + StkFloat noiseGain_; + StkFloat vibratoGain_; + StkFloat outputGain_; + StkFloat jetRatio_; }; diff --git a/include/FormSwep.h b/include/FormSwep.h index fd1e718..332a8f7 100644 --- a/include/FormSwep.h +++ b/include/FormSwep.h @@ -8,12 +8,12 @@ It provides methods for controlling the sweep rate and target frequency. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__FORMSWEP_H) -#define __FORMSWEP_H +#ifndef STK_FORMSWEP_H +#define STK_FORMSWEP_H #include "BiQuad.h" @@ -39,13 +39,13 @@ class FormSwep : public BiQuad the unit-circle (\e radius close to one), the narrower the resulting resonance width. */ - void setResonance(MY_FLOAT aFrequency, MY_FLOAT aRadius); + void setResonance(StkFloat frequency, StkFloat radius); //! Set both the current and target resonance parameters. - void setStates(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain = 1.0); + void setStates(StkFloat frequency, StkFloat radius, StkFloat gain = 1.0); //! Set target resonance parameters. - void setTargets(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain = 1.0); + void setTargets(StkFloat frequency, StkFloat radius, StkFloat gain = 1.0); //! Set the sweep rate (between 0.0 - 1.0). /*! @@ -56,7 +56,7 @@ class FormSwep : public BiQuad target values. A sweep rate of 0.0 will produce no change in resonance parameters. */ - void setSweepRate(MY_FLOAT aRate); + void setSweepRate(StkFloat rate); //! Set the sweep rate in terms of a time value in seconds. /*! @@ -64,29 +64,38 @@ class FormSwep : public BiQuad given time for the formant parameters to reach their target values. */ - void setSweepTime(MY_FLOAT aTime); + void setSweepTime(StkFloat time); //! Input one sample to the filter and return one output. - MY_FLOAT tick(MY_FLOAT sample); + StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - bool dirty; - MY_FLOAT frequency; - MY_FLOAT radius; - MY_FLOAT startFrequency; - MY_FLOAT startRadius; - MY_FLOAT startGain; - MY_FLOAT targetFrequency; - MY_FLOAT targetRadius; - MY_FLOAT targetGain; - MY_FLOAT deltaFrequency; - MY_FLOAT deltaRadius; - MY_FLOAT deltaGain; - MY_FLOAT sweepState; - MY_FLOAT sweepRate; + bool dirty_; + StkFloat frequency_; + StkFloat radius_; + StkFloat startFrequency_; + StkFloat startRadius_; + StkFloat startGain_; + StkFloat targetFrequency_; + StkFloat targetRadius_; + StkFloat targetGain_; + StkFloat deltaFrequency_; + StkFloat deltaRadius_; + StkFloat deltaGain_; + StkFloat sweepState_; + StkFloat sweepRate_; }; diff --git a/include/Function.h b/include/Function.h new file mode 100644 index 0000000..c58f822 --- /dev/null +++ b/include/Function.h @@ -0,0 +1,52 @@ +/***************************************************/ +/*! \class Function + \brief STK abstract function parent class. + + This class provides common functionality for STK classes which + implement tables or other types of input to output function + mappings. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#include "Stk.h" + +#ifndef STK_FUNCTION_H +#define STK_FUNCTION_H + +class Function : public Stk +{ + public: + //! Class constructor. + Function(); + + //! Class destructor. + virtual ~Function(); + + //! Return the last output value. + virtual StkFloat lastOut() const { return lastOutput_; }; + + //! Abstract tick function ... must be implemented in subclasses. + virtual StkFloat tick( StkFloat input ) = 0; + + //! Take \e vectorSize inputs from \e vector and replace them with corresponding outputs. + virtual StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the function and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + + protected: + + StkFloat lastOutput_; + +}; + +#endif + diff --git a/include/Generator.h b/include/Generator.h new file mode 100644 index 0000000..212f355 --- /dev/null +++ b/include/Generator.h @@ -0,0 +1,51 @@ +/***************************************************/ +/*! \class Generator + \brief STK abstract unit generator parent class. + + This class provides common functionality for + STK unit generator source subclasses. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#ifndef STK_GENERATOR_H +#define STK_GENERATOR_H + +#include "Stk.h" + +class Generator : public Stk +{ + public: + //! Class constructor. + Generator(); + + //! Class destructor. + virtual ~Generator(); + + //! Return the last output value. + virtual StkFloat lastOut() const { return lastOutput_; }; + + //! Abstract tick function ... must be implemented in subclasses. + virtual StkFloat tick( void ) = 0; + + //! Compute \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + + protected: + + StkFloat lastOutput_; + +}; + +#endif + diff --git a/include/HevyMetl.h b/include/HevyMetl.h index 3876a65..1495cd5 100644 --- a/include/HevyMetl.h +++ b/include/HevyMetl.h @@ -24,12 +24,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__HEVYMETL_H) -#define __HEVYMETL_H +#ifndef STK_HEVYMETL_H +#define STK_HEVYMETL_H #include "FM.h" @@ -37,16 +37,31 @@ class HevyMetl : public FM { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ HevyMetl(); //! Class destructor. ~HevyMetl(); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/Instrmnt.h b/include/Instrmnt.h index e954baf..e710e33 100644 --- a/include/Instrmnt.h +++ b/include/Instrmnt.h @@ -5,15 +5,14 @@ This class provides a common interface for all STK instruments. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__INSTRMNT_H) -#define __INSTRMNT_H +#ifndef STK_INSTRMNT_H +#define STK_INSTRMNT_H #include "Stk.h" -#include class Instrmnt : public Stk { @@ -25,34 +24,43 @@ class Instrmnt : public Stk virtual ~Instrmnt(); //! Start a note with the given frequency and amplitude. - virtual void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) = 0; + virtual void noteOn(StkFloat frequency, StkFloat amplitude) = 0; //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(MY_FLOAT amplitude) = 0; + virtual void noteOff(StkFloat amplitude) = 0; //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + virtual void setFrequency(StkFloat frequency); //! Return the last output value. - MY_FLOAT lastOut() const; + StkFloat lastOut() const; //! Return the last left output value. - MY_FLOAT lastOutLeft() const; + StkFloat lastOutLeft() const; //! Return the last right output value. - MY_FLOAT lastOutRight() const; + StkFloat lastOutRight() const; //! Compute one output sample. - virtual MY_FLOAT tick() = 0; + virtual StkFloat tick() = 0; //! Computer \e vectorSize outputs and return them in \e vector. - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); - + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + virtual void controlChange(int number, StkFloat value); protected: - MY_FLOAT lastOutput; + StkFloat lastOutput_; }; diff --git a/include/JCRev.h b/include/JCRev.h index 5ef1bb5..312002c 100644 --- a/include/JCRev.h +++ b/include/JCRev.h @@ -10,21 +10,21 @@ filters, and two decorrelation delay lines in parallel at the output. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__JCREV_H) -#define __JCREV_H +#ifndef STK_JCREV_H +#define STK_JCREV_H -#include "Reverb.h" +#include "Effect.h" #include "Delay.h" -class JCRev : public Reverb +class JCRev : public Effect { public: - //! Class constructor taking a T60 decay time argument. - JCRev(MY_FLOAT T60); + //! Class constructor taking a T60 decay time argument (one second default value). + JCRev( StkFloat T60 = 1.0 ); //! Class destructor. ~JCRev(); @@ -32,16 +32,31 @@ class JCRev : public Reverb //! Reset and clear all internal state. void clear(); + //! Set the reverberation T60 decay time. + void setT60( StkFloat T60 ); + //! Compute one output sample. - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); + + //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - Delay *allpassDelays[3]; - Delay *combDelays[4]; - Delay *outLeftDelay; - Delay *outRightDelay; - MY_FLOAT allpassCoefficient; - MY_FLOAT combCoefficient[4]; + Delay allpassDelays_[3]; + Delay combDelays_[4]; + Delay outLeftDelay_; + Delay outRightDelay_; + StkFloat allpassCoefficient_; + StkFloat combCoefficient_[4]; }; diff --git a/include/JetTabl.h b/include/JetTabl.h deleted file mode 100644 index 0698b3c..0000000 --- a/include/JetTabl.h +++ /dev/null @@ -1,44 +0,0 @@ -/***************************************************/ -/*! \class JetTabl - \brief STK jet table class. - - This class implements a flue jet non-linear - function, computed by a polynomial calculation. - Contrary to the name, this is not a "table". - - Consult Fletcher and Rossing, Karjalainen, - Cook, and others for more information. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#if !defined(__JETTABL_H) -#define __JETTABL_H - -#include "Stk.h" - -class JetTabl : public Stk -{ -public: - //! Default constructor. - JetTabl(); - - //! Class destructor. - ~JetTabl(); - - //! Return the last output value. - MY_FLOAT lastOut() const; - - //! Return the function value for \e input. - MY_FLOAT tick(MY_FLOAT input); - - //! Take \e vectorSize inputs and return the corresponding function values in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); - -protected: - MY_FLOAT lastOutput; - -}; - -#endif diff --git a/include/JetTable.h b/include/JetTable.h new file mode 100644 index 0000000..15c9515 --- /dev/null +++ b/include/JetTable.h @@ -0,0 +1,49 @@ +/***************************************************/ +/*! \class JetTable + \brief STK jet table class. + + This class implements a flue jet non-linear + function, computed by a polynomial calculation. + Contrary to the name, this is not a "table". + + Consult Fletcher and Rossing, Karjalainen, + Cook, and others for more information. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#ifndef STK_JETTABL_H +#define STK_JETTABL_H + +#include "Function.h" + +class JetTable : public Function +{ +public: + //! Default constructor. + JetTable(); + + //! Class destructor. + ~JetTable(); + + //! Return the function value for \e input. + StkFloat tick(StkFloat input); + + //! Take \e vectorSize inputs from \e vector and replace them with corresponding outputs. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the function and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + +protected: + +}; + +#endif diff --git a/include/Mandolin.h b/include/Mandolin.h index 06f041a..f628612 100644 --- a/include/Mandolin.h +++ b/include/Mandolin.h @@ -23,12 +23,12 @@ - String Detuning = 1 - Microphone Position = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__MANDOLIN_H) -#define __MANDOLIN_H +#ifndef STK_MANDOLIN_H +#define STK_MANDOLIN_H #include "PluckTwo.h" #include "WvIn.h" @@ -37,35 +37,46 @@ class Mandolin : public PluckTwo { public: //! Class constructor, taking the lowest desired playing frequency. - Mandolin(MY_FLOAT lowestFrequency); + Mandolin(StkFloat lowestFrequency); //! Class destructor. - virtual ~Mandolin(); + ~Mandolin(); //! Pluck the strings with the given amplitude (0.0 - 1.0) using the current frequency. - void pluck(MY_FLOAT amplitude); + void pluck(StkFloat amplitude); //! Pluck the strings with the given amplitude (0.0 - 1.0) and position (0.0 - 1.0). - void pluck(MY_FLOAT amplitude,MY_FLOAT position); + void pluck(StkFloat amplitude,StkFloat position); //! Start a note with the given frequency and amplitude (0.0 - 1.0). - virtual void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Set the body size (a value of 1.0 produces the "default" size). - void setBodySize(MY_FLOAT size); + void setBodySize(StkFloat size); //! Compute one output sample. - virtual MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - WvIn *soundfile[12]; - MY_FLOAT directBody; - int mic; - long dampTime; - bool waveDone; + WvIn *soundfile_[12]; + int mic_; + long dampTime_; + bool waveDone_; }; #endif diff --git a/include/Mesh2D.h b/include/Mesh2D.h index 1237b48..43ac3b1 100644 --- a/include/Mesh2D.h +++ b/include/Mesh2D.h @@ -24,14 +24,14 @@ */ /***************************************************/ -#if !defined(__MESH2D_H) -#define __MESH2D_H +#ifndef STK_MESH2D_H +#define STK_MESH2D_H #include "Instrmnt.h" #include "OnePole.h" -#define NXMAX ((short)(12)) -#define NYMAX ((short)(12)) +const short NXMAX = 12; +const short NYMAX = 12; class Mesh2D : public Instrmnt { @@ -52,54 +52,64 @@ class Mesh2D : public Instrmnt void setNY(short lenY); //! Set the x, y input position on a 0.0 - 1.0 scale. - void setInputPosition(MY_FLOAT xFactor, MY_FLOAT yFactor); + void setInputPosition(StkFloat xFactor, StkFloat yFactor); //! Set the loss filters gains (0.0 - 1.0). - void setDecay(MY_FLOAT decayFactor); + void setDecay(StkFloat decayFactor); //! Impulse the mesh with the given amplitude (frequency ignored). - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay) ... currently ignored. - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Calculate and return the signal energy stored in the mesh. - MY_FLOAT energy(); + StkFloat energy(); //! Compute one output sample, without adding energy to the mesh. - MY_FLOAT tick(); + StkFloat tick(); //! Input a sample to the mesh and compute one output sample. - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - MY_FLOAT tick0(); - MY_FLOAT tick1(); + StkFloat tick0(); + StkFloat tick1(); void clearMesh(); - short NX, NY; - short xInput, yInput; - OnePole *filterX[NXMAX]; - OnePole *filterY[NYMAX]; - MY_FLOAT v[NXMAX-1][NYMAX-1]; // junction velocities - MY_FLOAT vxp[NXMAX][NYMAX]; // positive-x velocity wave - MY_FLOAT vxm[NXMAX][NYMAX]; // negative-x velocity wave - MY_FLOAT vyp[NXMAX][NYMAX]; // positive-y velocity wave - MY_FLOAT vym[NXMAX][NYMAX]; // negative-y velocity wave + short NX_, NY_; + short xInput_, yInput_; + OnePole filterX_[NXMAX]; + OnePole filterY_[NYMAX]; + StkFloat v_[NXMAX-1][NYMAX-1]; // junction velocities + StkFloat vxp_[NXMAX][NYMAX]; // positive-x velocity wave + StkFloat vxm_[NXMAX][NYMAX]; // negative-x velocity wave + StkFloat vyp_[NXMAX][NYMAX]; // positive-y velocity wave + StkFloat vym_[NXMAX][NYMAX]; // negative-y velocity wave // Alternate buffers - MY_FLOAT vxp1[NXMAX][NYMAX]; // positive-x velocity wave - MY_FLOAT vxm1[NXMAX][NYMAX]; // negative-x velocity wave - MY_FLOAT vyp1[NXMAX][NYMAX]; // positive-y velocity wave - MY_FLOAT vym1[NXMAX][NYMAX]; // negative-y velocity wave - - int counter; // time in samples - + StkFloat vxp1_[NXMAX][NYMAX]; // positive-x velocity wave + StkFloat vxm1_[NXMAX][NYMAX]; // negative-x velocity wave + StkFloat vyp1_[NXMAX][NYMAX]; // positive-y velocity wave + StkFloat vym1_[NXMAX][NYMAX]; // negative-y velocity wave + int counter_; // time in samples }; #endif diff --git a/include/Messager.h b/include/Messager.h index c71429c..8d37b5b 100644 --- a/include/Messager.h +++ b/include/Messager.h @@ -2,146 +2,165 @@ /*! \class Messager \brief STK input control message parser. - This class reads and parses control messages - from a variety of sources, such as a MIDI - port, scorefile, socket connection, or pipe. - MIDI messages are retrieved using the RtMidi - class. All other input sources (scorefile, - socket, or pipe) are assumed to provide SKINI - formatted messages. + This class reads and parses control messages from a variety of + sources, such as a scorefile, MIDI port, socket connection, or + stdin. MIDI messages are retrieved using the RtMidi class. All + other input sources (scorefile, socket, or stdin) are assumed to + provide SKINI formatted messages. This class can be compiled with + generic, non-realtime support, in which case only scorefile + reading is possible. - For each call to nextMessage(), the active - input sources are queried to see if a new - control message is available. + The various \e realtime message acquisition mechanisms (from MIDI, + socket, or stdin) take place asynchronously, filling the message + queue. A call to popMessage() will pop the next available control + message from the queue and return it via the referenced Message + structure. When a \e non-realtime scorefile is set, it is not + possible to start reading realtime input messages (from MIDI, + socket, or stdin). Likewise, it is not possible to read from a + scorefile when a realtime input mechanism is running. - This class is primarily for use in STK main() - event loops. + When MIDI input is started, input is also automatically read from + stdin. This allows for program termination via the terminal + window. An __SK_Exit_ message is pushed onto the stack whenever + an "exit" or "Exit" message is received from stdin or when all + socket connections close and no stdin thread is running. - One of the original goals in creating this - class was to simplify the message acquisition - process by removing all threads. If the - windoze select() function behaved just like - the unix one, that would have been possible. - Since it does not (it can't be used to poll - STDIN), I am using a thread to acquire - messages from STDIN, which sends these - messages via a socket connection to the - message socket server. Perhaps in the future, - it will be possible to simplify things. + This class is primarily for use in STK example programs but it is + generic enough to work in many other contexts. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__MESSAGER_H) -#define __MESSSAGER_H +#ifndef STK_MESSAGER_H +#define STK_MESSAGER_H #include "Stk.h" -#include "SKINI.h" +#include "Skini.h" +#include -#define MESSAGE_LENGTH 128 -#define MAX_MESSAGES 25 -#define STK_MIDI 0x0001 -#define STK_PIPE 0x0002 -#define STK_SOCKET 0x0004 +const int DEFAULT_QUEUE_LIMIT = 200; #if defined(__STK_REALTIME__) +#include "Mutex.h" #include "Thread.h" #include "Socket.h" #include "RtMidi.h" extern "C" THREAD_RETURN THREAD_TYPE stdinHandler(void * ptr); -#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) - #include - #include -#endif +extern "C" THREAD_RETURN THREAD_TYPE socketHandler(void * ptr); #endif // __STK_REALTIME__ class Messager : public Stk { public: - //! Constructor performs initialization based on an input mask and an optional socket port. - /*! - The default constructor is set to read input from a SKINI - scorefile. The flags STK_MIDI, STK_PIPE, and STK_SOCKET can be - OR'ed together in any combination for multiple "realtime" input - source parsing. An optional socket port number can be specified - for use when the STK_SOCKET flag is set. For realtime input - types, an StkError can be thrown during instantiation. - */ - Messager(int inputMask = 0, int port = 2001); + + // This structure is used to share data among the various realtime + // messager threads. It must be public. + struct MessagerData { + Skini skini; + std::queue queue; + unsigned int queueLimit; + int sources; + +#if defined(__STK_REALTIME__) + Mutex mutex; + RtMidiIn *midi; + Socket *socket; + std::vector fd; + fd_set mask; +#endif + + // Default constructor. + MessagerData() + :queueLimit(0), sources(0) {} + }; + + //! Default constructor. + Messager(); //! Class destructor. ~Messager(); - //! Check for a new input message and return the message type. + //! Pop the next message from the queue and write it to the referenced message structure. /*! - Return type values greater than zero represent valid messages. - If an input scorefile has been completely read or all realtime - input sources have closed, a negative value is returned. If the - return type is zero, no valid messages are present. + Invalid messages (or an empty queue) are indicated by type + values of zero, in which case all other message structure values + are undefined. The user MUST verify the returned message type is + valid before reading other message values. */ - long nextMessage(void); + void popMessage( Skini::Message& message ); - //! Set the delta time (in samples) returned between valid realtime messages. This setting has no affect for scorefile messages. - void setRtDelta(long nSamples); + //! Push the referenced message onto the message stack. + void pushMessage( Skini::Message& message ); - //! Return the current message "delta time" in samples. - long getDelta(void) const; + //! Specify a SKINI formatted scorefile from which messages should be read. + /*! + A return value of \c true indicates the call was successful. A + return value of \c false can occur if the file is not found, + cannot be opened, another file is currently still open, or if a + realtime input mechanism is running. Scorefile input is + considered to be a non-realtime control mechanism that cannot run + concurrently with realtime input. + */ + bool setScoreFile( const char* filename ); - //! Return the current message type. - long getType() const; +#if defined(__STK_REALTIME__) + //! Initiate the "realtime" retreival from stdin of control messages into the queue. + /*! + This function initiates a thread for asynchronous retrieval of + SKINI formatted messages from stdin. A return value of \c true + indicates the call was successful. A return value of \c false can + occur if a scorefile is being read, a stdin thread is already + running, or a thread error occurs during startup. Stdin input is + considered to be a realtime control mechanism that cannot run + concurrently with non-realtime scorefile input. + */ + bool startStdInput(); - //! Return the byte two value for the current message. - MY_FLOAT getByteTwo() const; + //! Start a socket server, accept connections, and read "realtime" control messages into the message queue. + /*! + This function creates a socket server on the optional port + (default = 2001) and starts a thread for asynchronous retrieval of + SKINI formatted messages from socket connections. A return value + of \c true indicates the call was successful. A return value of + \c false can occur if a scorefile is being read, a socket thread + is already running, or an error occurs during the socket server + or thread initialization stages. Socket input is considered to be + a realtime control mechanism that cannot run concurrently with + non-realtime scorefile input. + */ + bool startSocketInput( int port=2001 ); - //! Return the byte three value for the current message. - MY_FLOAT getByteThree() const; + //! Start MIDI input, with optional device and port identifiers. + /*! + This function creates an RtMidiIn instance for MIDI input. The + RtMidiIn class invokes a local callback function to read incoming + messages into the queue. If \c port = -1, RtMidiIn will open a + virtual port to which other software applications can connect (OS + X and Linux only). A return value of \c true indicates the call + was successful. A return value of \c false can occur if a + scorefile is being read, MIDI input is already running, or an + error occurs during RtMidiIn construction. Midi input is + considered to be a realtime control mechanism that cannot run + concurrently with non-realtime scorefile input. + */ + bool startMidiInput( int port=0 ); - //! Return the channel number for the current message. - long getChannel() const; +#endif protected: - SKINI *skini; - long type; - long channel; - MY_FLOAT byte2; - MY_FLOAT byte3; - int sources; - long delta; - long rtDelta; - char message[MAX_MESSAGES][MESSAGE_LENGTH]; - unsigned int messageIndex; - int nMessages; + MessagerData data_; #if defined(__STK_REALTIME__) - - // Check MIDI source for new messages. - bool midiMessage(void); - - // Check socket sources for new messages. - bool socketMessage(void); - - // Receive and parse socket data. - bool readSocket(int fd); - - RtMidi *midi; - Thread *thread; - Socket *soket; - - unsigned int nSockets; - fd_set mask; - int maxfd; - int pipefd; - int fd[16]; - char error[256]; - -#endif // __STK_REALTIME__ + Thread stdinThread_; + Thread socketThread_; +#endif }; -#endif // defined(__MESSAGER_H) +#endif diff --git a/include/MidiFileIn.h b/include/MidiFileIn.h new file mode 100644 index 0000000..b687b65 --- /dev/null +++ b/include/MidiFileIn.h @@ -0,0 +1,131 @@ +/**********************************************************************/ +/*! \class MidiFileIn + \brief A standard MIDI file reading/parsing class. + + This class can be used to read events from a standard MIDI file. + Event bytes are copied to a C++ vector and must be subsequently + interpreted by the user. The function getNextMidiEvent() skips + meta and sysex events, returning only MIDI channel messages. + Event delta-times are returned in the form of "ticks" and a + function is provided to determine the current "seconds per tick". + Tempo changes are internally tracked by the class and reflected in + the values returned by the function getTickSeconds(). + + by Gary P. Scavone, 2003. +*/ +/**********************************************************************/ + +#ifndef STK_MIDIFILEIN_H +#define STK_MIDIFILEIN_H + +#include "Stk.h" +#include +#include +#include +#include + +class MidiFileIn : public Stk +{ + public: + //! Default constructor. + /*! + If an error occurs while opening or parsing the file header, an + StkError exception will be thrown. + */ + MidiFileIn( std::string fileName ); + + //! Class destructor. + ~MidiFileIn(); + + //! Return the MIDI file format (0, 1, or 2). + int getFileFormat() const; + + //! Return the number of tracks in the MIDI file. + unsigned int getNumberOfTracks() const; + + //! Return the MIDI file division value from the file header. + /*! + Note that this value must be "parsed" in accordance with the + MIDI File Specification. In particular, if the MSB is set, the + file uses time-code representations for delta-time values. + */ + int getDivision() const; + + //! Move the specified track event reader to the beginning of its track. + /*! + The relevant track tempo value is reset as well. If an invalid + track number is specified, an StkError exception will be thrown. + */ + void rewindTrack( unsigned int track = 0 ); + + //! Get the current value, in seconds, of delta-time ticks for the specified track. + /*! + This value can change as events are read (via "Set Tempo" + Meta-Events). Therefore, one should call this function after + every call to getNextEvent() or getNextMidiEvent(). If an + invalid track number is specified, an StkError exception will be + thrown. + */ + double getTickSeconds( unsigned int track = 0 ); + + //! Fill the user-provided vector with the next event in the specified track and return the event delta-time in ticks. + /*! + MIDI File events consist of a delta time and a sequence of event + bytes. This function returns the delta-time value and writes + the subsequent event bytes directly to the event vector. The + user must parse the event bytes in accordance with the MIDI File + Specification. All returned MIDI channel events are complete + ... a status byte is provided even when running status is used + in the file. If the track has reached its end, no bytes will be + written and the event vector size will be zero. If an invalid + track number is specified or an error occurs while reading the + file, an StkError exception will be thrown. + */ + unsigned long getNextEvent( std::vector *event, unsigned int track = 0 ); + + //! Fill the user-provided vector with the next MIDI channel event in the specified track and return the event delta time in ticks. + /*! + All returned MIDI events are complete ... a status byte is + provided even when running status is used in the file. Meta and + sysex events in the track are skipped though "Set Tempo" events + are properly parsed for use by the getTickSeconds() function. + If the track has reached its end, no bytes will be written and + the event vector size will be zero. If an invalid track number + is specified or an error occurs while reading the file, an + StkError exception will be thrown. + */ + unsigned long getNextMidiEvent( std::vector *midiEvent, unsigned int track = 0 ); + + protected: + + // This protected class function is used for reading variable-length + // MIDI file values. It is assumed that this function is called with + // the file read pointer positioned at the start of a + // variable-length value. The function returns true if the value is + // successfully parsed. Otherwise, it returns false. + bool readVariableLength( unsigned long *value ); + + std::ifstream file_; + unsigned int nTracks_; + int format_; + int division_; + bool usingTimeCode_; + std::vector tickSeconds_; + std::vector trackPointers_; + std::vector trackOffsets_; + std::vector trackLengths_; + std::vector trackStatus_; + + // This structure and the following variables are used to save and + // keep track of a format 1 tempo map (and the initial tickSeconds + // parameter for formats 0 and 2). + struct TempoChange { + unsigned long count; + double tickSeconds; + }; + std::vector tempoEvents_; + std::vector trackCounters_; + std::vector trackTempoIndex_; +}; + +#endif diff --git a/include/Modal.h b/include/Modal.h index cca8e78..413ed58 100644 --- a/include/Modal.h +++ b/include/Modal.h @@ -7,12 +7,12 @@ (non-sweeping BiQuad filters), where N is set during instantiation. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__MODAL_H) -#define __MODAL_H +#ifndef STK_MODAL_H +#define STK_MODAL_H #include "Instrmnt.h" #include "Envelope.h" @@ -24,7 +24,10 @@ class Modal : public Instrmnt { public: //! Class constructor, taking the desired number of modes to create. - Modal( int modes = 4 ); + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Modal( unsigned int modes = 4 ); //! Class destructor. virtual ~Modal(); @@ -33,54 +36,67 @@ public: void clear(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + virtual void setFrequency(StkFloat frequency); //! Set the ratio and radius for a specified mode filter. - void setRatioAndRadius(int modeIndex, MY_FLOAT ratio, MY_FLOAT radius); + void setRatioAndRadius(unsigned int modeIndex, StkFloat ratio, StkFloat radius); //! Set the master gain. - void setMasterGain(MY_FLOAT aGain); + void setMasterGain(StkFloat aGain); //! Set the direct gain. - void setDirectGain(MY_FLOAT aGain); + void setDirectGain(StkFloat aGain); //! Set the gain for a specified mode filter. - void setModeGain(int modeIndex, MY_FLOAT gain); + void setModeGain(unsigned int modeIndex, StkFloat gain); //! Initiate a strike with the given amplitude (0.0 - 1.0). - virtual void strike(MY_FLOAT amplitude); + virtual void strike(StkFloat amplitude); //! Damp modes with a given decay factor (0.0 - 1.0). - void damp(MY_FLOAT amplitude); + void damp(StkFloat amplitude); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - virtual MY_FLOAT tick(); + virtual StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value) = 0; + virtual void controlChange(int number, StkFloat value) = 0; protected: - Envelope *envelope; - WvIn *wave; - BiQuad **filters; - OnePole *onepole; - WaveLoop *vibrato; - int nModes; - MY_FLOAT vibratoGain; - MY_FLOAT masterGain; - MY_FLOAT directGain; - MY_FLOAT stickHardness; - MY_FLOAT strikePosition; - MY_FLOAT baseFrequency; - MY_FLOAT *ratios; - MY_FLOAT *radii; + Envelope envelope_; + WvIn *wave_; + BiQuad **filters_; + OnePole onepole_; + WaveLoop *vibrato_; + unsigned int nModes_; + std::vector ratios_; + std::vector radii_; + + StkFloat vibratoGain_; + StkFloat masterGain_; + StkFloat directGain_; + StkFloat stickHardness_; + StkFloat strikePosition_; + StkFloat baseFrequency_; }; #endif diff --git a/include/ModalBar.h b/include/ModalBar.h index 343c0fe..56677fa 100644 --- a/include/ModalBar.h +++ b/include/ModalBar.h @@ -9,8 +9,9 @@ Control Change Numbers: - Stick Hardness = 2 - Stick Position = 4 - - Vibrato Gain = 11 - - Vibrato Frequency = 7 + - Vibrato Gain = 1 + - Vibrato Frequency = 11 + - Direct Stick Mix = 8 - Volume = 128 - Modal Presets = 16 - Marimba = 0 @@ -23,12 +24,12 @@ - Two Fixed = 7 - Clump = 8 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__MODALBAR_H) -#define __MODALBAR_H +#ifndef STK_MODALBAR_H +#define STK_MODALBAR_H #include "Modal.h" @@ -42,19 +43,19 @@ public: ~ModalBar(); //! Set stick hardness (0.0 - 1.0). - void setStickHardness(MY_FLOAT hardness); + void setStickHardness(StkFloat hardness); //! Set stick position (0.0 - 1.0). - void setStrikePosition(MY_FLOAT position); + void setStrikePosition(StkFloat position); //! Select a bar preset (currently modulo 9). void setPreset(int preset); //! Set the modulation (vibrato) depth. - void setModulationDepth(MY_FLOAT mDepth); + void setModulationDepth(StkFloat mDepth); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); }; #endif diff --git a/include/Modulate.h b/include/Modulate.h index 11bf660..092c0a0 100644 --- a/include/Modulate.h +++ b/include/Modulate.h @@ -6,22 +6,25 @@ modulations to give a nice, natural human modulation function. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__MODULATE_H) -#define __MODULATE_H +#ifndef STK_MODULATE_H +#define STK_MODULATE_H -#include "Stk.h" +#include "Generator.h" #include "WaveLoop.h" #include "SubNoise.h" #include "OnePole.h" -class Modulate : public Stk +class Modulate : public Generator { public: //! Class constructor. + /*! + An StkError can be thrown if the rawwave path is incorrect. + */ Modulate(); //! Class destructor. @@ -31,30 +34,35 @@ class Modulate : public Stk void reset(); //! Set the periodic (vibrato) rate or frequency in Hz. - void setVibratoRate(MY_FLOAT aRate); + void setVibratoRate(StkFloat rate); //! Set the periodic (vibrato) gain. - void setVibratoGain(MY_FLOAT aGain); + void setVibratoGain(StkFloat gain); //! Set the random modulation gain. - void setRandomGain(MY_FLOAT aGain); + void setRandomGain(StkFloat gain); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); - //! Return \e vectorSize outputs in \e vector. - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Compute \e vectorSize outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); - //! Return the last computed output value. - MY_FLOAT lastOut() const; + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - WaveLoop *vibrato; - SubNoise *noise; - OnePole *filter; - MY_FLOAT vibratoGain; - MY_FLOAT randomGain; - MY_FLOAT lastOutput; + WaveLoop *vibrato_; + SubNoise noise_; + OnePole filter_; + StkFloat vibratoGain_; + StkFloat randomGain_; }; diff --git a/include/Moog.h b/include/Moog.h index 252b02e..e7bcbf2 100644 --- a/include/Moog.h +++ b/include/Moog.h @@ -14,12 +14,12 @@ - Vibrato Gain = 1 - Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__MOOG_H) -#define __MOOG_H +#ifndef STK_MOOG_H +#define STK_MOOG_H #include "Sampler.h" #include "FormSwep.h" @@ -28,34 +28,49 @@ class Moog : public Sampler { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ Moog(); //! Class destructor. ~Moog(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Start a note with the given frequency and amplitude. - virtual void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Set the modulation (vibrato) speed in Hz. - void setModulationSpeed(MY_FLOAT mSpeed); + void setModulationSpeed(StkFloat mSpeed); //! Set the modulation (vibrato) depth. - void setModulationDepth(MY_FLOAT mDepth); + void setModulationDepth(StkFloat mDepth); //! Compute one output sample. - virtual MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - FormSwep *filters[2]; - MY_FLOAT modDepth; - MY_FLOAT filterQ; - MY_FLOAT filterRate; + FormSwep filters_[2]; + StkFloat modDepth_; + StkFloat filterQ_; + StkFloat filterRate_; }; diff --git a/include/Mutex.h b/include/Mutex.h new file mode 100644 index 0000000..e072718 --- /dev/null +++ b/include/Mutex.h @@ -0,0 +1,70 @@ +/***************************************************/ +/*! \class Mutex + \brief STK mutex class. + + This class provides a uniform interface for + cross-platform mutex use. On Linux and IRIX + systems, the pthread library is used. Under + Windows, critical sections are used. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#ifndef STK_MUTEX_H +#define STK_MUTEX_H + +#include "Stk.h" + +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + + #include + typedef pthread_mutex_t MUTEX; + typedef pthread_cond_t CONDITION; + +#elif defined(__OS_WINDOWS__) + + #include + #include + typedef CRITICAL_SECTION MUTEX; + typedef HANDLE CONDITION; + +#endif + +class Mutex : public Stk +{ + public: + //! Default constructor. + Mutex(); + + //! Class destructor. + ~Mutex(); + + //! Lock the mutex. + void lock(void); + + //! Unlock the mutex. + void unlock(void); + + //! Wait indefinitely on the mutex condition variable. + /*! + The mutex must be locked before calling this function, and then + subsequently unlocked after this function returns. + */ + void wait(void); + + //! Signal the condition variable. + /*! + The mutex must be locked before calling this function, and then + subsequently unlocked after this function returns. + */ + void signal(void); + + protected: + + MUTEX mutex_; + CONDITION condition_; + +}; + +#endif diff --git a/include/NRev.h b/include/NRev.h index 0ae0ddc..731688b 100644 --- a/include/NRev.h +++ b/include/NRev.h @@ -12,21 +12,21 @@ filters in parallel with corresponding right and left outputs. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__NREV_H) -#define __NREV_H +#ifndef STK_NREV_H +#define STK_NREV_H -#include "Reverb.h" +#include "Effect.h" #include "Delay.h" -class NRev : public Reverb +class NRev : public Effect { public: - //! Class constructor taking a T60 decay time argument. - NRev(MY_FLOAT T60); + //! Class constructor taking a T60 decay time argument (one second default value). + NRev( StkFloat T60 = 1.0 ); //! Class destructor. ~NRev(); @@ -34,15 +34,30 @@ class NRev : public Reverb //! Reset and clear all internal state. void clear(); + //! Set the reverberation T60 decay time. + void setT60( StkFloat T60 ); + //! Compute one output sample. - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); + + //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - Delay *allpassDelays[8]; - Delay *combDelays[6]; - MY_FLOAT allpassCoefficient; - MY_FLOAT combCoefficient[6]; - MY_FLOAT lowpassState; + Delay allpassDelays_[8]; + Delay combDelays_[6]; + StkFloat allpassCoefficient_; + StkFloat combCoefficient_[6]; + StkFloat lowpassState_; }; diff --git a/include/Noise.h b/include/Noise.h index 326b881..881b49d 100644 --- a/include/Noise.h +++ b/include/Noise.h @@ -6,16 +6,16 @@ C rand() function. The quality of the rand() function varies from one OS to another. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__NOISE_H) -#define __NOISE_H +#ifndef STK_NOISE_H +#define STK_NOISE_H -#include "Stk.h" +#include "Generator.h" -class Noise : public Stk +class Noise : public Generator { public: @@ -40,17 +40,21 @@ public: void setSeed( unsigned int seed = 0 ); //! Return a random number between -1.0 and 1.0 using rand(). - virtual MY_FLOAT tick(); + virtual StkFloat tick(); - //! Return \e vectorSize random numbers between -1.0 and 1.0 in \e vector. - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Compute \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); - //! Return the last computed value. - MY_FLOAT lastOut() const; + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); -protected: - - MY_FLOAT lastOutput; +protected: }; diff --git a/include/OnePole.h b/include/OnePole.h index 7aa72ac..2252ca4 100644 --- a/include/OnePole.h +++ b/include/OnePole.h @@ -8,12 +8,12 @@ the real axis of the z-plane while maintaining a constant peak filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__ONEPOLE_H) -#define __ONEPOLE_H +#ifndef STK_ONEPOLE_H +#define STK_ONEPOLE_H #include "Filter.h" @@ -25,7 +25,7 @@ public: OnePole(); //! Overloaded constructor which sets the pole position during instantiation. - OnePole(MY_FLOAT thePole); + OnePole(StkFloat thePole); //! Class destructor. ~OnePole(); @@ -34,10 +34,10 @@ public: void clear(void); //! Set the b[0] coefficient value. - void setB0(MY_FLOAT b0); + void setB0(StkFloat b0); //! Set the a[1] coefficient value. - void setA1(MY_FLOAT a1); + void setA1(StkFloat a1); //! Set the pole position in the z-plane. /*! @@ -47,26 +47,36 @@ public: pole value produces a high-pass filter. This method does not affect the filter \e gain value. */ - void setPole(MY_FLOAT thePole); + void setPole(StkFloat thePole); //! Set the filter gain. /*! The gain is applied at the filter input and does not affect the coefficient values. The default gain value is 1.0. */ - void setGain(MY_FLOAT theGain); + void setGain(StkFloat gain); //! Return the current filter gain. - MY_FLOAT getGain(void) const; + StkFloat getGain(void) const; //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Input one sample to the filter and return one output. - MY_FLOAT tick(MY_FLOAT sample); + StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + }; #endif diff --git a/include/OneZero.h b/include/OneZero.h index 0ee129f..92d69b0 100644 --- a/include/OneZero.h +++ b/include/OneZero.h @@ -8,12 +8,12 @@ along the real axis of the z-plane while maintaining a constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__ONEZERO_H) -#define __ONEZERO_H +#ifndef STK_ONEZERO_H +#define STK_ONEZERO_H #include "Filter.h" @@ -25,7 +25,7 @@ class OneZero : protected Filter OneZero(); //! Overloaded constructor which sets the zero position during instantiation. - OneZero(MY_FLOAT theZero); + OneZero(StkFloat theZero); //! Class destructor. ~OneZero(); @@ -34,10 +34,10 @@ class OneZero : protected Filter void clear(void); //! Set the b[0] coefficient value. - void setB0(MY_FLOAT b0); + void setB0(StkFloat b0); //! Set the b[1] coefficient value. - void setB1(MY_FLOAT b1); + void setB1(StkFloat b1); //! Set the zero position in the z-plane. /*! @@ -47,26 +47,36 @@ class OneZero : protected Filter negative zero value produces a low-pass filter. This method does not affect the filter \e gain value. */ - void setZero(MY_FLOAT theZero); + void setZero(StkFloat theZero); //! Set the filter gain. /*! The gain is applied at the filter input and does not affect the coefficient values. The default gain value is 1.0. */ - void setGain(MY_FLOAT theGain); + void setGain(StkFloat gain); //! Return the current filter gain. - MY_FLOAT getGain(void) const; + StkFloat getGain(void) const; //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Input one sample to the filter and return one output. - MY_FLOAT tick(MY_FLOAT sample); + StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + }; #endif diff --git a/include/PRCRev.h b/include/PRCRev.h index 8e3d262..568e353 100644 --- a/include/PRCRev.h +++ b/include/PRCRev.h @@ -10,21 +10,21 @@ two series allpass units and two parallel comb filters. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__PRCREV_H) -#define __PRCREV_H +#ifndef STK_PRCREV_H +#define STK_PRCREV_H -#include "Reverb.h" +#include "Effect.h" #include "Delay.h" -class PRCRev : public Reverb +class PRCRev : public Effect { public: - //! Class constructor taking a T60 decay time argument. - PRCRev(MY_FLOAT T60); + //! Class constructor taking a T60 decay time argument (one second default value). + PRCRev( StkFloat T60 = 1.0 ); //! Class destructor. ~PRCRev(); @@ -32,14 +32,29 @@ public: //! Reset and clear all internal state. void clear(); + //! Set the reverberation T60 decay time. + void setT60( StkFloat T60 ); + //! Compute one output sample. - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); + + //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - Delay *allpassDelays[2]; - Delay *combDelays[2]; - MY_FLOAT allpassCoefficient; - MY_FLOAT combCoefficient[2]; + Delay allpassDelays_[2]; + Delay combDelays_[2]; + StkFloat allpassCoefficient_; + StkFloat combCoefficient_[2]; }; diff --git a/include/PercFlut.h b/include/PercFlut.h index 204eaf7..7eb0ccb 100644 --- a/include/PercFlut.h +++ b/include/PercFlut.h @@ -22,12 +22,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__PERCFLUT_H) -#define __PERCFLUT_H +#ifndef STK_PERCFLUT_H +#define STK_PERCFLUT_H #include "FM.h" @@ -35,19 +35,34 @@ class PercFlut : public FM { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ PercFlut(); //! Class destructor. ~PercFlut(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/Phonemes.h b/include/Phonemes.h index 63d8bde..0dd0463 100644 --- a/include/Phonemes.h +++ b/include/Phonemes.h @@ -6,16 +6,16 @@ set of 32 static phoneme formant parameters and provide access to those values. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__PHONEMES_H) -#define __PHONEMES_H +#ifndef STK_PHONEMES_H +#define STK_PHONEMES_H #include "Stk.h" -class Phonemes +class Phonemes : public Stk { public: @@ -26,26 +26,25 @@ public: static const char *name( unsigned int index ); //! Returns the voiced component gain for the given phoneme index (0-31). - static MY_FLOAT voiceGain( unsigned int index ); + static StkFloat voiceGain( unsigned int index ); //! Returns the unvoiced component gain for the given phoneme index (0-31). - static MY_FLOAT noiseGain( unsigned int index ); + static StkFloat noiseGain( unsigned int index ); //! Returns the formant frequency for the given phoneme index (0-31) and partial (0-3). - static MY_FLOAT formantFrequency( unsigned int index, unsigned int partial ); + static StkFloat formantFrequency( unsigned int index, unsigned int partial ); //! Returns the formant radius for the given phoneme index (0-31) and partial (0-3). - static MY_FLOAT formantRadius( unsigned int index, unsigned int partial ); + static StkFloat formantRadius( unsigned int index, unsigned int partial ); //! Returns the formant gain for the given phoneme index (0-31) and partial (0-3). - static MY_FLOAT formantGain( unsigned int index, unsigned int partial ); + static StkFloat formantGain( unsigned int index, unsigned int partial ); private: static const char phonemeNames[][4]; - static const MY_FLOAT phonemeGains[][2]; - static const MY_FLOAT phonemeParameters[][4][3]; - + static const StkFloat phonemeGains[][2]; + static const StkFloat phonemeParameters[][4][3]; }; #endif diff --git a/include/PitShift.h b/include/PitShift.h index 6cf8ad4..78bd410 100644 --- a/include/PitShift.h +++ b/include/PitShift.h @@ -5,17 +5,17 @@ This class implements a simple pitch shifter using delay lines. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__PITSHIFT_H) -#define __PITSHIFT_H +#ifndef STK_PITSHIFT_H +#define STK_PITSHIFT_H -#include "Stk.h" +#include "Effect.h" #include "DelayL.h" -class PitShift : public Stk +class PitShift : public Effect { public: //! Class constructor. @@ -28,27 +28,30 @@ class PitShift : public Stk void clear(); //! Set the pitch shift factor (1.0 produces no shift). - void setShift(MY_FLOAT shift); - - //! Set the mixture of input and processed levels in the output (0.0 = input only, 1.0 = processed only). - void setEffectMix(MY_FLOAT mix); - - //! Return the last output value. - MY_FLOAT lastOut() const; + void setShift(StkFloat shift); //! Compute one output sample. - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); - //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - DelayL *delayLine[2]; - MY_FLOAT lastOutput; - MY_FLOAT delay[2]; - MY_FLOAT env[2]; - MY_FLOAT effectMix; - MY_FLOAT rate; + DelayL delayLine_[2]; + StkFloat delay_[2]; + StkFloat env_[2]; + StkFloat rate_; + unsigned long delayLength; + unsigned long halfLength; }; diff --git a/include/PluckTwo.h b/include/PluckTwo.h index 4e7a2b4..49fc955 100644 --- a/include/PluckTwo.h +++ b/include/PluckTwo.h @@ -14,12 +14,12 @@ use possibly subject to patents held by Stanford University, Yamaha, and others. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__PLUCKTWO_H) -#define __PLUCKTWO_H +#ifndef STK_PLUCKTWO_H +#define STK_PLUCKTWO_H #include "Instrmnt.h" #include "DelayL.h" @@ -30,7 +30,7 @@ class PluckTwo : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - PluckTwo(MY_FLOAT lowestFrequency); + PluckTwo(StkFloat lowestFrequency); //! Class destructor. virtual ~PluckTwo(); @@ -39,16 +39,16 @@ class PluckTwo : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + virtual void setFrequency(StkFloat frequency); //! Detune the two strings by the given factor. A value of 1.0 produces unison strings. - void setDetune(MY_FLOAT detune); + void setDetune(StkFloat detune); //! Efficient combined setting of frequency and detuning. - void setFreqAndDetune(MY_FLOAT frequency, MY_FLOAT detune); + void setFreqAndDetune(StkFloat frequency, StkFloat detune); //! Set the pluck or "excitation" position along the string (0.0 - 1.0). - void setPluckPosition(MY_FLOAT position); + void setPluckPosition(StkFloat position); //! Set the base loop gain. /*! @@ -56,28 +56,41 @@ class PluckTwo : public Instrmnt Because of high-frequency loop filter roll-off, higher frequency settings have greater loop gains. */ - void setBaseLoopGain(MY_FLOAT aGain); + void setBaseLoopGain(StkFloat aGain); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(MY_FLOAT amplitude); + virtual void noteOff(StkFloat amplitude); //! Virtual (abstract) tick function is implemented by subclasses. - virtual MY_FLOAT tick() = 0; + virtual StkFloat tick() = 0; - protected: - DelayA *delayLine; - DelayA *delayLine2; - DelayL *combDelay; - OneZero *filter; - OneZero *filter2; - long length; - MY_FLOAT loopGain; - MY_FLOAT baseLoopGain; - MY_FLOAT lastFrequency; - MY_FLOAT lastLength; - MY_FLOAT detuning; - MY_FLOAT pluckAmplitude; - MY_FLOAT pluckPosition; + //! Computer \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize) = 0; + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ) = 0; + + protected: + DelayA delayLine_; + DelayA delayLine2_; + DelayL combDelay_; + OneZero filter_; + OneZero filter2_; + + unsigned long length_; + StkFloat loopGain_; + StkFloat baseLoopGain_; + StkFloat lastFrequency_; + StkFloat lastLength_; + StkFloat detuning_; + StkFloat pluckAmplitude_; + StkFloat pluckPosition_; }; diff --git a/include/Plucked.h b/include/Plucked.h index 57e6af1..57290db 100644 --- a/include/Plucked.h +++ b/include/Plucked.h @@ -13,12 +13,12 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__PLUCKED_H) -#define __PLUCKED_H +#ifndef STK_PLUCKED_H +#define STK_PLUCKED_H #include "Instrmnt.h" #include "DelayA.h" @@ -30,7 +30,7 @@ class Plucked : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Plucked(MY_FLOAT lowestFrequency); + Plucked(StkFloat lowestFrequency); //! Class destructor. ~Plucked(); @@ -39,27 +39,39 @@ class Plucked : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + virtual void setFrequency(StkFloat frequency); //! Pluck the string with the given amplitude using the current frequency. - void pluck(MY_FLOAT amplitude); + void pluck(StkFloat amplitude); //! Start a note with the given frequency and amplitude. - virtual void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + virtual void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(MY_FLOAT amplitude); + virtual void noteOff(StkFloat amplitude); //! Compute one output sample. - virtual MY_FLOAT tick(); + virtual StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - DelayA *delayLine; - OneZero *loopFilter; - OnePole *pickFilter; - Noise *noise; - long length; - MY_FLOAT loopGain; + DelayA delayLine_; + OneZero loopFilter_; + OnePole pickFilter_; + Noise noise_; + StkFloat loopGain_; + unsigned long length_; }; diff --git a/include/PoleZero.h b/include/PoleZero.h index e3e0593..ba35e6d 100644 --- a/include/PoleZero.h +++ b/include/PoleZero.h @@ -8,12 +8,12 @@ filter with a given coefficient. Another method is provided to create a DC blocking filter. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__POLEZERO_H) -#define __POLEZERO_H +#ifndef STK_POLEZERO_H +#define STK_POLEZERO_H #include "Filter.h" @@ -31,13 +31,13 @@ class PoleZero : protected Filter void clear(void); //! Set the b[0] coefficient value. - void setB0(MY_FLOAT b0); + void setB0(StkFloat b0); //! Set the b[1] coefficient value. - void setB1(MY_FLOAT b1); + void setB1(StkFloat b1); //! Set the a[1] coefficient value. - void setA1(MY_FLOAT a1); + void setA1(StkFloat a1); //! Set the filter for allpass behavior using \e coefficient. /*! @@ -45,7 +45,7 @@ class PoleZero : protected Filter which has unity gain at all frequencies. Note that the \e coefficient magnitude must be less than one to maintain stability. */ - void setAllpass(MY_FLOAT coefficient); + void setAllpass(StkFloat coefficient); //! Create a DC blocking filter with the given pole position in the z-plane. /*! @@ -54,26 +54,36 @@ class PoleZero : protected Filter close to one to minimize low-frequency attenuation. */ - void setBlockZero(MY_FLOAT thePole = 0.99); + void setBlockZero(StkFloat thePole = 0.99); //! Set the filter gain. /*! The gain is applied at the filter input and does not affect the coefficient values. The default gain value is 1.0. */ - void setGain(MY_FLOAT theGain); + void setGain(StkFloat gain); //! Return the current filter gain. - MY_FLOAT getGain(void) const; + StkFloat getGain(void) const; //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Input one sample to the filter and return one output. - MY_FLOAT tick(MY_FLOAT sample); + StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + }; #endif diff --git a/include/ReedTabl.h b/include/ReedTable.h similarity index 55% rename from include/ReedTabl.h rename to include/ReedTable.h index e6f97a5..c090bd3 100644 --- a/include/ReedTabl.h +++ b/include/ReedTable.h @@ -1,5 +1,5 @@ /***************************************************/ -/*! \class ReedTabl +/*! \class ReedTable \brief STK reed table class. This class implements a simple one breakpoint, @@ -13,23 +13,23 @@ Smith (1986), Hirschman, Cook, Scavone, and others for more information. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__REEDTABL_H) -#define __REEDTABL_H +#ifndef STK_REEDTABLE_H +#define STK_REEDTABLE_H -#include "Stk.h" +#include "Function.h" -class ReedTabl : public Stk +class ReedTable : public Function { public: //! Default constructor. - ReedTabl(); + ReedTable(); //! Class destructor. - ~ReedTabl(); + ~ReedTable(); //! Set the table offset value. /*! @@ -37,7 +37,7 @@ public: of the initial reed tip opening (a greater offset represents a smaller opening). */ - void setOffset(MY_FLOAT aValue); + void setOffset(StkFloat offset); //! Set the table slope value. /*! @@ -45,25 +45,30 @@ public: stiffness (a greater slope represents a harder reed). */ - void setSlope(MY_FLOAT aValue); - - //! Return the last output value. - MY_FLOAT lastOut() const; + void setSlope(StkFloat slope); //! Return the function value for \e input. /*! The function input represents the differential pressure across the reeds. */ - MY_FLOAT tick(MY_FLOAT input); + StkFloat tick(StkFloat input); - //! Take \e vectorSize inputs and return the corresponding function values in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Take \e vectorSize inputs from \e vector and replace them with corresponding outputs. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the function and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - MY_FLOAT offSet; - MY_FLOAT slope; - MY_FLOAT lastOutput; + StkFloat offset_; + StkFloat slope_; }; diff --git a/include/Resonate.h b/include/Resonate.h index 6b35f2e..8922a9d 100644 --- a/include/Resonate.h +++ b/include/Resonate.h @@ -13,12 +13,12 @@ - Zero Radii = 1 - Envelope Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__RESONATE_H) -#define __RESONATE_H +#ifndef STK_RESONATE_H +#define STK_RESONATE_H #include "Instrmnt.h" #include "ADSR.h" @@ -38,10 +38,10 @@ class Resonate : public Instrmnt void clear(); //! Set the filter for a resonance at the given frequency (Hz) and radius. - void setResonance(MY_FLOAT frequency, MY_FLOAT radius); + void setResonance(StkFloat frequency, StkFloat radius); //! Set the filter for a notch at the given frequency (Hz) and radius. - void setNotch(MY_FLOAT frequency, MY_FLOAT radius); + void setNotch(StkFloat frequency, StkFloat radius); //! Set the filter zero coefficients for contant resonance gain. void setEqualGainZeroes(); @@ -53,25 +53,37 @@ class Resonate : public Instrmnt void keyOff(); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - ADSR *adsr; - BiQuad *filter; - Noise *noise; - MY_FLOAT poleFrequency; - MY_FLOAT poleRadius; - MY_FLOAT zeroFrequency; - MY_FLOAT zeroRadius; + ADSR adsr_; + BiQuad filter_; + Noise noise_; + StkFloat poleFrequency_; + StkFloat poleRadius_; + StkFloat zeroFrequency_; + StkFloat zeroRadius_; }; diff --git a/include/Reverb.h b/include/Reverb.h deleted file mode 100644 index 0f996d7..0000000 --- a/include/Reverb.h +++ /dev/null @@ -1,58 +0,0 @@ -/***************************************************/ -/*! \class Reverb - \brief STK abstract reverberator parent class. - - This class provides common functionality for - STK reverberator subclasses. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#include "Stk.h" - -#if !defined(__REVERB_H) -#define __REVERB_H - -class Reverb : public Stk -{ - public: - //! Class constructor. - Reverb(); - - //! Class destructor. - virtual ~Reverb(); - - //! Reset and clear all internal state. - virtual void clear() = 0; - - //! Set the mixture of input and "reverberated" levels in the output (0.0 = input only, 1.0 = reverb only). - void setEffectMix(MY_FLOAT mix); - - //! 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; - - //! Abstract tick function ... must be implemented in subclasses. - virtual MY_FLOAT tick(MY_FLOAT input) = 0; - - //! Take \e vectorSize inputs, compute the same number of outputs and return them in \e vector. - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); - - protected: - - // Returns true if argument value is prime. - bool isPrime(int number); - - MY_FLOAT lastOutput[2]; - MY_FLOAT effectMix; - -}; - -#endif // defined(__REVERB_H) - diff --git a/include/Rhodey.h b/include/Rhodey.h index 1e22fb0..05f7b51 100644 --- a/include/Rhodey.h +++ b/include/Rhodey.h @@ -26,12 +26,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__RHODEY_H) -#define __RHODEY_H +#ifndef STK_RHODEY_H +#define STK_RHODEY_H #include "FM.h" @@ -39,19 +39,34 @@ class Rhodey : public FM { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ Rhodey(); //! Class destructor. ~Rhodey(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/RtAudio.h b/include/RtAudio.h index fa2d494..98fce3e 100644 --- a/include/RtAudio.h +++ b/include/RtAudio.h @@ -9,7 +9,7 @@ RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/ - RtAudio: a realtime audio i/o C++ class + RtAudio: realtime audio i/o C++ classes Copyright (c) 2001-2004 Gary P. Scavone Permission is hereby granted, free of charge, to any person @@ -37,7 +37,7 @@ */ /************************************************************************/ -// RtAudio: Version 3.0.1, 22 March 2004 +// RtAudio: Version 3.0.2, pre-release for STK 4.2.0 #ifndef __RTAUDIO_H #define __RTAUDIO_H @@ -127,17 +127,27 @@ class RtApi { public: + enum StreamState { + STREAM_STOPPED, + STREAM_RUNNING + }; + RtApi(); virtual ~RtApi(); void openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat 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 ); virtual void setStreamCallback( RtAudioCallback callback, void *userData ) = 0; virtual void cancelStreamCallback() = 0; int getDeviceCount(void); RtAudioDeviceInfo getDeviceInfo( int device ); char * const getStreamBuffer(); + RtApi::StreamState getStreamState() const; virtual void tickStream() = 0; virtual void closeStream(); virtual void startStream() = 0; @@ -158,9 +168,13 @@ protected: UNINITIALIZED = -75 }; - enum StreamState { - STREAM_STOPPED, - STREAM_RUNNING + // A protected structure used for buffer conversion. + struct ConvertInfo { + int channels; + int inJump, outJump; + RtAudioFormat inFormat, outFormat; + std::vector inOffset; + std::vector outOffset; }; // A protected structure for audio streams. @@ -183,10 +197,10 @@ protected: RtAudioFormat deviceFormat[2]; // Playback and record, respectively. StreamMutex mutex; CallbackInfo callbackInfo; + ConvertInfo convertInfo[2]; RtApiStream() :apiHandle(0), userBuffer(0), deviceBuffer(0) {} - // mode(UNINITIALIZED), state(STREAM_STOPPED), }; // A protected device structure for audio devices. @@ -217,7 +231,7 @@ protected: typedef float Float32; typedef double Float64; - char message_[256]; + char message_[1024]; int nDevices_; std::vector devices_; RtApiStream stream_; @@ -281,7 +295,7 @@ protected: Protected method used to perform format, channel number, and/or interleaving conversions between the user and device buffers. */ - void convertStreamBuffer( StreamMode mode ); + void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ); //! Protected common method used to perform byte-swapping on buffers. void byteSwapBuffer( char *buffer, int samples, RtAudioFormat format ); @@ -350,6 +364,20 @@ public: RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers, RtAudioApi api=UNSPECIFIED ); + //! An overloaded constructor which opens a stream and also returns \c numberOfBuffers parameter via pointer argument. + /*! + See the previous constructor call for details. This overloaded + version differs only in that it takes a pointer argument for the + \c numberOfBuffers parameter and returns the value used by the + audio device (which may be different from that requested). Note + that the \c numberofBuffers parameter is not used with the Linux + Jack, Macintosh CoreAudio, and Windows ASIO APIs. + */ + 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 an open stream and devices and deallocates @@ -389,6 +417,20 @@ public: RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers ); + //! A public method for opening a stream and also returning \c numberOfBuffers parameter via pointer argument. + /*! + See the previous function call for details. This overloaded + version differs only in that it takes a pointer argument for the + \c numberOfBuffers parameter and returns the value used by the + audio device (which may be different from that requested). Note + that the \c numberofBuffers parameter is not used with the Linux + Jack, Macintosh CoreAudio, and Windows ASIO APIs. + */ + 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 previously opened diff --git a/include/RtDuplex.h b/include/RtDuplex.h index 1dd3fe8..c43a790 100644 --- a/include/RtDuplex.h +++ b/include/RtDuplex.h @@ -1,28 +1,35 @@ /***************************************************/ /*! \class RtDuplex - \brief STK realtime audio input/output class. + \brief STK realtime audio (blocking) input/output class. This class provides a simplified interface to RtAudio for realtime audio input/output. It - is also possible to achieve duplex operation - using separate RtWvIn and RtWvOut classes, but - this class ensures better input/output - syncronization. + may also be possible to achieve duplex + operation using separate RtWvIn and RtWvOut + classes, but this class ensures better + input/output synchronization. + + Because this class makes use of RtAudio's + blocking input/output routines, its + performance is less robust on systems where + the audio API is callback-based (Macintosh + CoreAudio and Windows ASIO). RtDuplex supports multi-channel data in interleaved format. It is important to distinguish the tick() methods, which output - single samples to all channels in a sample frame - and return samples produced by averaging across - sample frames, from the tickFrame() methods, which - take/return pointers to multi-channel sample frames. + single samples to all channels in a sample + frame and return samples produced by averaging + across sample frames, from the tickFrame() + methods, which take/return pointers to + multi-channel sample frames. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__RTDUPLEX_H) -#define __RTDUPLEX_H +#ifndef STK_RTDUPLEX_H +#define STK_RTDUPLEX_H #include "Stk.h" #include "RtAudio.h" @@ -42,7 +49,7 @@ public: is defined in Stk.h. An StkError will be thrown if an error occurs duing instantiation. */ - RtDuplex(int nChannels = 1, MY_FLOAT sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 2); + RtDuplex(int nChannels = 1, StkFloat sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 2); //! Class destructor. ~RtDuplex(); @@ -60,34 +67,53 @@ public: void stop(void); //! Return the average across the last output sample frame. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Output a single sample to all channels in a sample frame and return the average across one new input sample frame of data. /*! An StkError will be thrown if an error occurs during input/output. */ - MY_FLOAT tick(const MY_FLOAT sample); + StkFloat tick(const StkFloat sample); - //! Output each sample in \vector to all channels per frame and return averaged input sample frames of new data in \e vector. + //! Output each sample in \e vector to all channels per frame and return averaged input sample frames of new data in \e vector. /*! An StkError will be thrown if an error occurs during input/output. */ - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Output a channel of the StkFrames object to all channels and replace with averaged sample frames of input. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if an + error occurs during input/outpu or the \c channel argument is zero + or it is greater than the number of channels in the StkFrames + object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Return a pointer to the last output sample frame. - const MY_FLOAT *lastFrame(void) const; + const StkFloat *lastFrame(void) const; //! Output sample \e frames from \e frameVector and return new input frames in \e frameVector. /*! An StkError will be thrown if an error occurs during input/output. */ - MY_FLOAT *tickFrame(MY_FLOAT *frameVector, unsigned int frames = 1); + StkFloat *tickFrame(StkFloat *frameVector, unsigned int frames = 1); + + //! Output the StkFrames data and replace with new input frames. + /*! + An StkError will be thrown if an error occurs during + input/output or if there is an incompatability between the number + of channels in the RtDuplex object and that in the StkFrames + object. + */ + StkFrames& tickFrame( StkFrames& frames ); protected: RtAudio *audio_; - MY_FLOAT *data_; - MY_FLOAT *lastOutput_; + StkFloat *data_; + StkFloat *lastOutput_; int bufferSize_; bool stopped_; long counter_; diff --git a/include/RtError.h b/include/RtError.h index 465cd75..cb1283d 100644 --- a/include/RtError.h +++ b/include/RtError.h @@ -39,7 +39,7 @@ protected: public: //! The constructor. - RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type){} + RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type) {} //! The destructor. virtual ~RtError(void) {}; diff --git a/include/RtMidi.h b/include/RtMidi.h index cd52cf5..c33bd40 100644 --- a/include/RtMidi.h +++ b/include/RtMidi.h @@ -1,80 +1,282 @@ -/***************************************************/ +/**********************************************************************/ /*! \class RtMidi - \brief STK realtime MIDI class. + \brief An abstract base class for realtime MIDI input/output. - At the moment, this object only handles MIDI - input, though MIDI output code can go here - when someone decides they need it (and writes - it). + This class implements some common functionality for the realtime + MIDI input/output subclasses RtMidiIn and RtMidiOut. - This object opens a MIDI input device and - parses MIDI messages into a MIDI buffer. Time - stamp info is converted to a delta-time - value. MIDI data is stored as MY_FLOAT to - conform with SKINI. System exclusive messages - are currently ignored. + RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ - An optional argument to the constructor can be - used to specify a device or card. When no - argument is given, a default device is opened. - If a device argument fails, a list of available - devices is printed to allow selection by the user. + RtMidi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2004 Gary P. Scavone - This code is based in part on work of Perry - Cook (SGI), Paul Leonard (Linux), the - RoseGarden team (Linux), and Bill Putnam - (Windows). + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/***************************************************/ +/**********************************************************************/ -#if !defined(__RTMIDI_H) -#define __RTMIDI_H +// RtMidi: Version 1.0.2, 21 September 2004 -#include "Stk.h" +#ifndef RTMIDI_H +#define RTMIDI_H -class RtMidi : public Stk +#include "RtError.h" +#include + +class RtMidi +{ + protected: + + RtMidi(); + + virtual ~RtMidi() {}; + + // A basic error reporting function for internal use in the RtMidi + // subclasses. The behavior of this function can be modified to + // suit specific needs. + void error( RtError::Type type ); + + virtual void openPort( unsigned int portNumber = 0 ) = 0; + + void *apiData_; + bool connected_; + std::string errorString_; +}; + +/**********************************************************************/ +/*! \class RtMidiIn + \brief A realtime MIDI input class. + + This class provides a common, platform-independent API for + realtime MIDI input. It allows access to a single MIDI input + port. Incoming MIDI messages are either saved to a queue for + retrieval using the getMessage() function or immediately passed to + a user-specified callback function. Create multiple instances of + this class to connect to more than one MIDI device at the same + time. With the OS-X and Linux ALSA MIDI APIs, it is also possible + to open a virtual input port to which other MIDI software clients + can connect. + + by Gary P. Scavone, 2003-2004. +*/ +/**********************************************************************/ + +#include +#include + +class RtMidiIn : public RtMidi { public: - //! Default constructor with optional device argument. - RtMidi(int device = 0); - //! Class destructor. - ~RtMidi(); + //! User callback function type definition. + typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); - //! Print out the current message values. - void printMessage(void) const; - - //! Check for and parse a new MIDI message in the queue, returning its type. + //! Default constructor. /*! - If a new message is found, the return value is greater than zero. + An exception will be thrown if a MIDI system initialization error occurs. */ - int nextMessage(void); + RtMidiIn(); - //! Return the current message type. - int getType() const; + //! If a MIDI connection is still open, it will be closed by the destructor. + ~RtMidiIn(); - //! Return the current message channel value. - int getChannel() const; + //! Open a MIDI input connection. + /*! + An optional port number greater than 0 can be specified. + Otherwise, the default or first port found is opened. + */ + void openPort( unsigned int portNumber = 0 ); - //! Return the current message byte two value. - MY_FLOAT getByteTwo() const; + //! Create a virtual input port to allow software connections (OS X and ALSA only). + /*! + This function creates a virtual MIDI input port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X and Linux ALSA + APIs (the function does nothing for the other APIs). + */ + void openVirtualPort(); - //! Return the current message byte three value. - MY_FLOAT getByteThree() const; + //! Set a callback function to be invoked for incoming MIDI messages. + /*! + The callback function will be called whenever an incoming MIDI + message is received. While not absolutely necessary, it is best + to set the callback function before opening a MIDI port to avoid + leaving some messages in the queue. + */ + void setCallback( RtMidiCallback callback, void *userData = 0 ); - //! Return the current message delta time value in seconds. - MY_FLOAT getDeltaTime() const; + //! Cancel use of the current callback function (if one exists). + /*! + Subsequent incoming MIDI messages will be written to the queue + and can be retrieved with the \e getMessage function. + */ + void cancelCallback(); - protected: - int messageType; - int channel; - float byteTwo; - float byteThree; - MY_FLOAT deltaTime; - int readIndex; + //! Close an open MIDI connection (if one exists). + void closePort( void ); + + //! Return the number of available MIDI input ports. + unsigned int getPortCount(); + + //! Return a string identifier for the specified MIDI input port number. + /*! + An exception is thrown if an invalid port specifier is provided. + */ + std::string getPortName( unsigned int portNumber = 0 ); + + //! Set the maximum number of MIDI messages to be saved in the queue. + /*! + If the queue size limit is reached, incoming messages will be + ignored. The default limit is 1024. + */ + void setQueueSizeLimit( unsigned int queueSize ); + + //! Specify whether certain MIDI message types should be queued or ignored during input. + /*! + By default, MIDI timing and active sensing messages are ignored + during message input because of their relative high data rates. + MIDI sysex messages are ignored by default as well. Variable + values of "true" imply that the respective message type will be + ignored. + */ + void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); + + //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. + /*! + This function returns immediately whether a new message is + available or not. A valid message is indicated by a non-zero + vector size. An exception is thrown if an error occurs during + message retrieval or an input connection was not previously + established. + */ + double getMessage( std::vector *message ); + + // A MIDI structure used internally by the class to store incoming + // messages. Each message represents one and only one MIDI message. + struct MidiMessage { + std::vector bytes; + double timeStamp; + + // Default constructor. + MidiMessage() + :bytes(3), timeStamp(0.0) {} + }; + + // The RtMidiInData structure is used to pass private class data to + // the MIDI input handling function or thread. + struct RtMidiInData { + std::queue queue; + unsigned int queueLimit; + unsigned char ignoreFlags; + bool doInput; + bool firstMessage; + void *apiData; + bool usingCallback; + void *userCallback; + void *userData; + + // Default constructor. + RtMidiInData() + : queueLimit(1024), ignoreFlags(7), doInput(false), firstMessage(true), + apiData(0), usingCallback(false), userCallback(0), userData(0) {} + }; + + private: + + void initialize( void ); + RtMidiInData inputData_; }; +/**********************************************************************/ +/*! \class RtMidiOut + \brief A realtime MIDI output class. + + This class provides a common, platform-independent API for MIDI + output. It allows one to probe available MIDI output ports, to + connect to one such port, and to send MIDI bytes immediately over + the connection. Create multiple instances of this class to + connect to more than one MIDI device at the same time. + + by Gary P. Scavone, 2003-2004. +*/ +/**********************************************************************/ + +class RtMidiOut : public RtMidi +{ + public: + + //! Default constructor. + /*! + An exception will be thrown if a MIDI system initialization error occurs. + */ + RtMidiOut(); + + //! The destructor closes any open MIDI connections. + ~RtMidiOut(); + + //! Open a MIDI output connection. + /*! + An optional port number greater than 0 can be specified. + Otherwise, the default or first port found is opened. An + exception is thrown if an error occurs while attempting to make + the port connection. + */ + void openPort( unsigned int portNumber = 0 ); + + //! Close an open MIDI connection (if one exists). + void closePort(); + + //! Create a virtual output port to allow software connections (OS X and ALSA only). + /*! + This function creates a virtual MIDI output port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X and Linux ALSA + APIs (the function does nothing with the other APIs). An + exception is thrown if an error occurs while attempting to create + the virtual port. + */ + void openVirtualPort(); + + //! Return the number of available MIDI output ports. + unsigned int getPortCount(); + + //! Return a string identifier for the specified MIDI port type and number. + /*! + An exception is thrown if an invalid port specifier is provided. + */ + std::string getPortName( unsigned int portNumber ); + + //! Immediately send a single message out an open MIDI output port. + /*! + An exception is thrown if an error occurs during output or an + output connection was not previously established. + */ + void sendMessage( std::vector *message ); + + private: + + void initialize( void ); +}; + #endif diff --git a/include/RtWvIn.h b/include/RtWvIn.h index 5fa9195..26c25a6 100644 --- a/include/RtWvIn.h +++ b/include/RtWvIn.h @@ -1,10 +1,14 @@ /***************************************************/ /*! \class RtWvIn - \brief STK realtime audio input class. + \brief STK realtime audio (blocking) input class. This class provides a simplified interface to RtAudio for realtime audio input. It is a - protected subclass of WvIn. + protected subclass of WvIn. Because this + class makes use of RtAudio's blocking output + routines, its performance is less robust on + systems where the audio API is callback-based + (Macintosh CoreAudio and Windows ASIO). RtWvIn supports multi-channel data in interleaved format. It is important to @@ -15,14 +19,13 @@ For single-channel data, these methods return equivalent values. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__RTWVIN_H) -#define __RTWVIN_H +#ifndef STK_RTWVIN_H +#define STK_RTWVIN_H -#include "Stk.h" #include "WvIn.h" #include "RtAudio.h" @@ -41,7 +44,7 @@ public: is defined in Stk.h. An StkError will be thrown if an error occurs duing instantiation. */ - RtWvIn(int nChannels = 1, MY_FLOAT sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 2); + RtWvIn(int nChannels = 1, StkFloat sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 2); //! Class destructor. ~RtWvIn(); @@ -59,34 +62,51 @@ public: void stop(void); //! Return the average across the last output sample frame. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Read out the average across one sample frame of data. /*! An StkError will be thrown if an error occurs during input. */ - MY_FLOAT tick(void); + StkFloat tick(void); //! Read out vectorSize averaged sample frames of data in \e vector. /*! An StkError will be thrown if an error occurs during input. */ - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with averaged sample frames. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if an + error occurs during input or the \c channel argument is zero or it + is greater than the number of channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Return a pointer to the last output sample frame. - const MY_FLOAT *lastFrame(void) const; + const StkFloat *lastFrame(void) const; //! Return a pointer to the next sample frame of data. /*! An StkError will be thrown if an error occurs during input. */ - const MY_FLOAT *tickFrame(void); + const StkFloat *tickFrame(void); //! Read out sample \e frames of data to \e frameVector. /*! An StkError will be thrown if an error occurs during input. */ - MY_FLOAT *tickFrame(MY_FLOAT *frameVector, unsigned int frames); + StkFloat *tickFrame(StkFloat *frameVector, unsigned int frames); + + //! Fill the StkFrames object with sample frames of data and return the same reference. + /*! + An StkError will be thrown if an error occurs during input or + there is an incompatability between the number of channels in the + RtWvIn object and that in the StkFrames object. + */ + StkFrames& tickFrame( StkFrames& frames ); protected: diff --git a/include/RtWvOut.h b/include/RtWvOut.h index 16e1b56..3668706 100644 --- a/include/RtWvOut.h +++ b/include/RtWvOut.h @@ -1,10 +1,14 @@ /***************************************************/ /*! \class RtWvOut - \brief STK realtime audio output class. + \brief STK realtime audio (blocking) output class. This class provides a simplified interface to RtAudio for realtime audio output. It is a - protected subclass of WvOut. + protected subclass of WvOut. Because this + class makes use of RtAudio's blocking output + routines, its performance is less robust on + systems where the audio API is callback-based + (Macintosh CoreAudio and Windows ASIO). RtWvOut supports multi-channel data in interleaved format. It is important to @@ -14,16 +18,15 @@ takes a pointer to multi-channel sample frame data. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__RTWVOUT_H) -#define __RTWVOUT_H +#ifndef STK_RTWVOUT_H +#define STK_RTWVOUT_H #include "WvOut.h" #include "RtAudio.h" -#include "Thread.h" class RtWvOut : protected WvOut { @@ -41,7 +44,7 @@ class RtWvOut : protected WvOut is defined in Stk.h. An StkError will be thrown if an error occurs duing instantiation. */ - RtWvOut(unsigned int nChannels = 1, MY_FLOAT sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 4 ); + RtWvOut(unsigned int nChannels = 1, StkFloat sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 4 ); //! Class destructor. ~RtWvOut(); @@ -62,32 +65,50 @@ class RtWvOut : protected WvOut unsigned long getFrames( void ) const; //! Return the number of seconds of data output. - MY_FLOAT getTime( void ) const; + StkFloat getTime( void ) const; //! Output a single sample to all channels in a sample frame. /*! An StkError will be thrown if an error occurs during output. */ - void tick(const MY_FLOAT sample); + void tick( const StkFloat sample ); //! Output each sample in \e vector to all channels in \e vectorSize sample frames. /*! An StkError will be thrown if an error occurs during output. */ - void tick(const MY_FLOAT *vector, unsigned int vectorSize); + void tick( const StkFloat *vector, unsigned int vectorSize ); + + //! Output a channel of the StkFrames object to all channels of the RtWvOut object. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if an + error occurs during output or the \c channel argument is zero or + it is greater than the number of channels in the StkFrames object. + */ + void tick( const StkFrames& frames, unsigned int channel = 1 ); //! Output the \e frameVector of sample frames of the given length. /*! An StkError will be thrown if an error occurs during output. */ - void tickFrame(const MY_FLOAT *frameVector, unsigned int frames = 1); + void tickFrame( const StkFloat *frameVector, unsigned int frames = 1 ); + + //! Output the StkFrames data to the RtWvOut object. + /*! + An StkError will be thrown if an error occurs during output or + if there is an incompatability between the number of channels in + the WvOut object and that in the StkFrames object. + */ + void tickFrame( const StkFrames& frames ); protected: - RtAudio *audio_; + RtAudio *audio_; + StkFloat *dataPtr_; bool stopped_; int bufferSize_; }; -#endif // defined(__RTWVOUT_H) +#endif diff --git a/include/SKINI.h b/include/SKINI.h deleted file mode 100644 index 138fd4c..0000000 --- a/include/SKINI.h +++ /dev/null @@ -1,127 +0,0 @@ -/***************************************************/ -/*! \class SKINI - \brief STK SKINI parsing class - - This class parses SKINI formatted text - messages. It can be used to parse individual - messages or it can be passed an entire file. - The file specification is Perry's and his - alone, but it's all text so it shouldn't be to - hard to figure out. - - SKINI (Synthesis toolKit Instrument Network - Interface) is like MIDI, but allows for - floating-point control changes, note numbers, - etc. The following example causes a sharp - middle C to be played with a velocity of 111.132: - - \code - noteOn 60.01 111.13 - \endcode - - \sa \ref skini - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#if !defined(__SKINI_H) -#define __SKINI_H - -#include "Stk.h" -#include - -class SKINI : public Stk -{ - public: - //! Default constructor used for parsing messages received externally. - SKINI(); - - //! Overloaded constructor taking a SKINI formatted scorefile. - SKINI(char *fileName); - - //! Class destructor - ~SKINI(); - - //! Attempt to parse the given string, returning the message type. - /*! - A type value equal to zero indicates an invalid message. - */ - long parseThis(char* aString); - - //! Parse the next message (if a file is loaded) and return the message type. - /*! - A negative value is returned when the file end is reached. - */ - long nextMessage(); - - //! Return the current message type. - long getType() const; - - //! Return the current message channel value. - long getChannel() const; - - //! Return the current message delta time value (in seconds). - MY_FLOAT getDelta() const; - - //! Return the current message byte two value. - MY_FLOAT getByteTwo() const; - - //! Return the current message byte three value. - MY_FLOAT getByteThree() const; - - //! Return the current message byte two value (integer). - long getByteTwoInt() const; - - //! Return the current message byte three value (integer). - long getByteThreeInt() const; - - //! Return remainder string after parsing. - const char* getRemainderString(); - - //! Return the message type as a string. - const char* getMessageTypeString(); - - //! Return the SKINI type string for the given type value. - const char* whatsThisType(long type); - - //! Return the SKINI controller string for the given controller number. - const char* whatsThisController(long number); - - protected: - - FILE *myFile; - long messageType; - char msgTypeString[64]; - long channel; - MY_FLOAT deltaTime; - MY_FLOAT byteTwo; - MY_FLOAT byteThree; - long byteTwoInt; - long byteThreeInt; - char remainderString[1024]; - char whatString[1024]; -}; - -static const double Midi2Pitch[129] = { -8.18,8.66,9.18,9.72,10.30,10.91,11.56,12.25, -12.98,13.75,14.57,15.43,16.35,17.32,18.35,19.45, -20.60,21.83,23.12,24.50,25.96,27.50,29.14,30.87, -32.70,34.65,36.71,38.89,41.20,43.65,46.25,49.00, -51.91,55.00,58.27,61.74,65.41,69.30,73.42,77.78, -82.41,87.31,92.50,98.00,103.83,110.00,116.54,123.47, -130.81,138.59,146.83,155.56,164.81,174.61,185.00,196.00, -207.65,220.00,233.08,246.94,261.63,277.18,293.66,311.13, -329.63,349.23,369.99,392.00,415.30,440.00,466.16,493.88, -523.25,554.37,587.33,622.25,659.26,698.46,739.99,783.99, -830.61,880.00,932.33,987.77,1046.50,1108.73,1174.66,1244.51, -1318.51,1396.91,1479.98,1567.98,1661.22,1760.00,1864.66,1975.53, -2093.00,2217.46,2349.32,2489.02,2637.02,2793.83,2959.96,3135.96, -3322.44,3520.00,3729.31,3951.07,4186.01,4434.92,4698.64,4978.03, -5274.04,5587.65,5919.91,6271.93,6644.88,7040.00,7458.62,7902.13, -8372.02,8869.84,9397.27,9956.06,10548.08,11175.30,11839.82,12543.85, -13289.75}; - -#endif - - diff --git a/include/SKINI.msg b/include/SKINI.msg index 1ec95db..72bacf1 100644 --- a/include/SKINI.msg +++ b/include/SKINI.msg @@ -3,22 +3,25 @@ Definition of SKINI Message Types and Special Symbols Synthesis toolKit Instrument Network Interface - These symbols should have the form __SK__ + These symbols should have the form: - Where is the string used in the SKINI stream. + \c __SK__ - by Perry R. Cook, 1995 - 2002. + where is the string used in the SKINI stream. + + by Perry R. Cook, 1995 - 2004. */ /*********************************************************/ -/***** MIDI COMPATIBLE MESSAGES *****/ -/***** Status Bytes Have Channel=0 **/ +#define NOPE -32767 +#define YEP 1 +#define SK_DBL -32766 +#define SK_INT -32765 +#define SK_STR -32764 +#define __SK_Exit_ 999 -#define NOPE -32767 -#define YEP 1 -#define SK_DBL -32766 -#define SK_INT -32765 -#define SK_STR -32764 +/***** MIDI COMPATIBLE MESSAGES *****/ +/*** (Status bytes for channel=0) ***/ #define __SK_NoteOff_ 128 #define __SK_NoteOn_ 144 diff --git a/include/SKINI.tbl b/include/SKINI.tbl index f12be83..e2a1877 100644 --- a/include/SKINI.tbl +++ b/include/SKINI.tbl @@ -1,128 +1,128 @@ #include "SKINI.msg" -#define __SK_MaxMsgTypes_ 128 +#define __SK_MaxMsgTypes_ 80 -struct SKINISpec { char messageString[32]; +struct SkiniSpec { char messageString[32]; long type; long data2; long data3; }; -/* SEE COMMENT BLOCK AT BOTTOM FOR FIELDS AND USES */ -/* MessageString ,type, ch?, data2 , data3 */ +/* SEE COMMENT BLOCK AT BOTTOM FOR FIELDS AND USES */ +/* MessageString , type, data2, data3 */ -struct SKINISpec skini_msgs[__SK_MaxMsgTypes_] = +struct SkiniSpec skini_msgs[__SK_MaxMsgTypes_] = { - {"NoteOff" , __SK_NoteOff_, SK_DBL, SK_DBL}, - {"NoteOn" , __SK_NoteOn_, SK_DBL, SK_DBL}, - {"PolyPressure" , __SK_PolyPressure_, SK_DBL, SK_DBL}, - {"ControlChange" , __SK_ControlChange_, SK_INT, SK_DBL}, - {"ProgramChange" , __SK_ProgramChange_, SK_DBL, SK_DBL}, - {"AfterTouch" , __SK_AfterTouch_, SK_DBL, NOPE}, - {"ChannelPressure" ,__SK_ChannelPressure_, SK_DBL, NOPE}, - {"PitchWheel" , __SK_PitchWheel_, SK_DBL, NOPE}, - {"PitchBend" , __SK_PitchBend_, SK_DBL, NOPE}, - {"PitchChange" , __SK_PitchChange_, SK_DBL, NOPE}, - - {"Clock" , __SK_Clock_, NOPE, NOPE}, - {"Undefined" , 249, NOPE, NOPE}, - {"SongStart" , __SK_SongStart_, NOPE, NOPE}, - {"Continue" , __SK_Continue_, NOPE, NOPE}, - {"SongStop" , __SK_SongStop_, NOPE, NOPE}, - {"Undefined" , 253, NOPE, NOPE}, - {"ActiveSensing" , __SK_ActiveSensing_, NOPE, NOPE}, - {"SystemReset" , __SK_SystemReset_, NOPE, NOPE}, - - {"Volume" , __SK_ControlChange_, __SK_Volume_ , SK_DBL}, - {"ModWheel" , __SK_ControlChange_, __SK_ModWheel_ , SK_DBL}, - {"Modulation" , __SK_ControlChange_, __SK_Modulation_ , SK_DBL}, - {"Breath" , __SK_ControlChange_, __SK_Breath_ , SK_DBL}, - {"FootControl" , __SK_ControlChange_, __SK_FootControl_ , SK_DBL}, - {"Portamento" , __SK_ControlChange_, __SK_Portamento_ , SK_DBL}, - {"Balance" , __SK_ControlChange_, __SK_Balance_ , SK_DBL}, - {"Pan" , __SK_ControlChange_, __SK_Pan_ , SK_DBL}, - {"Sustain" , __SK_ControlChange_, __SK_Sustain_ , SK_DBL}, - {"Damper" , __SK_ControlChange_, __SK_Damper_ , SK_DBL}, - {"Expression" , __SK_ControlChange_, __SK_Expression_ , SK_DBL}, - - {"NoiseLevel" , __SK_ControlChange_, __SK_NoiseLevel_ , SK_DBL}, - {"PickPosition" , __SK_ControlChange_, __SK_PickPosition_ , SK_DBL}, - {"StringDamping" , __SK_ControlChange_, __SK_StringDamping_ , SK_DBL}, - {"StringDetune" , __SK_ControlChange_, __SK_StringDetune_ , SK_DBL}, - {"BodySize" , __SK_ControlChange_, __SK_BodySize_ , SK_DBL}, - {"BowPressure" , __SK_ControlChange_, __SK_BowPressure_ , SK_DBL}, - {"BowPosition" , __SK_ControlChange_, __SK_BowPosition_ , SK_DBL}, - {"BowBeta" , __SK_ControlChange_, __SK_BowBeta_ , SK_DBL}, + {"NoteOff" , __SK_NoteOff_, SK_DBL, SK_DBL}, + {"NoteOn" , __SK_NoteOn_, SK_DBL, SK_DBL}, + {"PolyPressure" , __SK_PolyPressure_, SK_DBL, SK_DBL}, + {"ControlChange" , __SK_ControlChange_, SK_INT, SK_DBL}, + {"ProgramChange" , __SK_ProgramChange_, SK_DBL, NOPE}, + {"AfterTouch" , __SK_AfterTouch_, SK_DBL, NOPE}, + {"ChannelPressure" ,__SK_ChannelPressure_, SK_DBL, NOPE}, + {"PitchWheel" , __SK_PitchWheel_, SK_DBL, NOPE}, + {"PitchBend" , __SK_PitchBend_, SK_DBL, NOPE}, + {"PitchChange" , __SK_PitchChange_, SK_DBL, NOPE}, + + {"Clock" , __SK_Clock_, NOPE, NOPE}, + {"Undefined" , 249, NOPE, NOPE}, + {"SongStart" , __SK_SongStart_, NOPE, NOPE}, + {"Continue" , __SK_Continue_, NOPE, NOPE}, + {"SongStop" , __SK_SongStop_, NOPE, NOPE}, + {"Undefined" , 253, NOPE, NOPE}, + {"ActiveSensing" , __SK_ActiveSensing_, NOPE, NOPE}, + {"SystemReset" , __SK_SystemReset_, NOPE, NOPE}, + + {"Volume" , __SK_ControlChange_, __SK_Volume_ , SK_DBL}, + {"ModWheel" , __SK_ControlChange_, __SK_ModWheel_ , SK_DBL}, + {"Modulation" , __SK_ControlChange_, __SK_Modulation_ , SK_DBL}, + {"Breath" , __SK_ControlChange_, __SK_Breath_ , SK_DBL}, + {"FootControl" , __SK_ControlChange_, __SK_FootControl_ , SK_DBL}, + {"Portamento" , __SK_ControlChange_, __SK_Portamento_ , SK_DBL}, + {"Balance" , __SK_ControlChange_, __SK_Balance_ , SK_DBL}, + {"Pan" , __SK_ControlChange_, __SK_Pan_ , SK_DBL}, + {"Sustain" , __SK_ControlChange_, __SK_Sustain_ , SK_DBL}, + {"Damper" , __SK_ControlChange_, __SK_Damper_ , SK_DBL}, + {"Expression" , __SK_ControlChange_, __SK_Expression_ , SK_DBL}, + + {"NoiseLevel" , __SK_ControlChange_, __SK_NoiseLevel_ , SK_DBL}, + {"PickPosition" , __SK_ControlChange_, __SK_PickPosition_ , SK_DBL}, + {"StringDamping" , __SK_ControlChange_, __SK_StringDamping_ , SK_DBL}, + {"StringDetune" , __SK_ControlChange_, __SK_StringDetune_ , SK_DBL}, + {"BodySize" , __SK_ControlChange_, __SK_BodySize_ , SK_DBL}, + {"BowPressure" , __SK_ControlChange_, __SK_BowPressure_ , SK_DBL}, + {"BowPosition" , __SK_ControlChange_, __SK_BowPosition_ , SK_DBL}, + {"BowBeta" , __SK_ControlChange_, __SK_BowBeta_ , SK_DBL}, - {"ReedStiffness" , __SK_ControlChange_, __SK_ReedStiffness_ , SK_DBL}, - {"ReedRestPos" , __SK_ControlChange_, __SK_ReedRestPos_ , SK_DBL}, - {"FluteEmbouchure" , __SK_ControlChange_, __SK_FluteEmbouchure_, SK_DBL}, - {"LipTension" , __SK_ControlChange_, __SK_LipTension_ , SK_DBL}, - {"StrikePosition" , __SK_ControlChange_, __SK_StrikePosition_, SK_DBL}, - {"StickHardness" , __SK_ControlChange_, __SK_StickHardness_ , SK_DBL}, + {"ReedStiffness" , __SK_ControlChange_, __SK_ReedStiffness_ , SK_DBL}, + {"ReedRestPos" , __SK_ControlChange_, __SK_ReedRestPos_ , SK_DBL}, + {"FluteEmbouchure" , __SK_ControlChange_, __SK_FluteEmbouchure_ , SK_DBL}, + {"LipTension" , __SK_ControlChange_, __SK_LipTension_ , SK_DBL}, + {"StrikePosition" , __SK_ControlChange_, __SK_StrikePosition_ , SK_DBL}, + {"StickHardness" , __SK_ControlChange_, __SK_StickHardness_ , SK_DBL}, - {"TrillDepth" , __SK_ControlChange_, __SK_TrillDepth_ , SK_DBL}, - {"TrillSpeed" , __SK_ControlChange_, __SK_TrillSpeed_ , SK_DBL}, + {"TrillDepth" , __SK_ControlChange_, __SK_TrillDepth_ , SK_DBL}, + {"TrillSpeed" , __SK_ControlChange_, __SK_TrillSpeed_ , SK_DBL}, - {"Strumming" , __SK_ControlChange_, __SK_Strumming_ , 127 }, - {"NotStrumming" , __SK_ControlChange_, __SK_Strumming_ , 0 }, + {"Strumming" , __SK_ControlChange_, __SK_Strumming_ , 127 }, + {"NotStrumming" , __SK_ControlChange_, __SK_Strumming_ , 0 }, - {"PlayerSkill" , __SK_ControlChange_, __SK_PlayerSkill_ , SK_DBL}, + {"PlayerSkill" , __SK_ControlChange_, __SK_PlayerSkill_ , SK_DBL}, - {"Chord" , __SK_Chord_ , SK_DBL , SK_STR }, - {"ChordOff" , __SK_ChordOff_ , SK_DBL , NOPE }, + {"Chord" , __SK_Chord_ , SK_DBL, SK_STR}, + {"ChordOff" , __SK_ChordOff_ , SK_DBL, NOPE}, - {"ShakerInst" , __SK_ControlChange_, __SK_ShakerInst_ , SK_DBL}, - {"Maraca" , __SK_ControlChange_, __SK_ShakerInst_ , 0 }, - {"Sekere" , __SK_ControlChange_, __SK_ShakerInst_ , 1 }, - {"Cabasa" , __SK_ControlChange_, __SK_ShakerInst_ , 2 }, - {"Bamboo" , __SK_ControlChange_, __SK_ShakerInst_ , 3 }, - {"Waterdrp" , __SK_ControlChange_, __SK_ShakerInst_ , 4 }, - {"Tambourn" , __SK_ControlChange_, __SK_ShakerInst_ , 5 }, - {"Sleighbl" , __SK_ControlChange_, __SK_ShakerInst_ , 6 }, - {"Guiro" , __SK_ControlChange_, __SK_ShakerInst_ , 7 }, + {"ShakerInst" , __SK_ControlChange_, __SK_ShakerInst_ , SK_DBL}, + {"Maraca" , __SK_ControlChange_, __SK_ShakerInst_ , 0 }, + {"Sekere" , __SK_ControlChange_, __SK_ShakerInst_ , 1 }, + {"Cabasa" , __SK_ControlChange_, __SK_ShakerInst_ , 2 }, + {"Bamboo" , __SK_ControlChange_, __SK_ShakerInst_ , 3 }, + {"Waterdrp" , __SK_ControlChange_, __SK_ShakerInst_ , 4 }, + {"Tambourn" , __SK_ControlChange_, __SK_ShakerInst_ , 5 }, + {"Sleighbl" , __SK_ControlChange_, __SK_ShakerInst_ , 6 }, + {"Guiro" , __SK_ControlChange_, __SK_ShakerInst_ , 7 }, - {"OpenFile" , 256, SK_STR , NOPE}, - {"SetPath" , 257, SK_STR , NOPE}, + {"OpenFile" , 256, SK_STR, NOPE}, + {"SetPath" , 257, SK_STR, NOPE}, - {"FilePath" , __SK_SINGER_FilePath_, SK_STR , NOPE}, - {"Frequency" , __SK_SINGER_Frequency_, SK_STR , NOPE}, - {"NoteName" , __SK_SINGER_NoteName_, SK_STR , NOPE}, - {"VocalShape" , __SK_SINGER_Shape_ , SK_STR , NOPE}, - {"Glottis" , __SK_SINGER_Glot_ , SK_STR , NOPE}, - {"VoicedUnVoiced" , __SK_SINGER_VoicedUnVoiced_, SK_DBL , SK_STR}, - {"Synthesize" , __SK_SINGER_Synthesize_, SK_STR , NOPE}, - {"Silence" , __SK_SINGER_Silence_, SK_STR , NOPE}, - {"VibratoAmt" , __SK_ControlChange_ ,__SK_SINGER_VibratoAmt_,SK_DBL}, - {"RndVibAmt" , __SK_SINGER_RndVibAmt_ ,SK_STR, NOPE}, - {"VibFreq" , __SK_ControlChange_ ,__SK_SINGER_VibFreq_ ,SK_DBL} + {"FilePath" , __SK_SINGER_FilePath_ , SK_STR, NOPE}, + {"Frequency" , __SK_SINGER_Frequency_ , SK_STR, NOPE}, + {"NoteName" , __SK_SINGER_NoteName_ , SK_STR, NOPE}, + {"VocalShape" , __SK_SINGER_Shape_ , SK_STR, NOPE}, + {"Glottis" , __SK_SINGER_Glot_ , SK_STR, NOPE}, + {"VoicedUnVoiced" , __SK_SINGER_VoicedUnVoiced_, SK_DBL, SK_STR}, + {"Synthesize" , __SK_SINGER_Synthesize_ , SK_STR, NOPE}, + {"Silence" , __SK_SINGER_Silence_ , SK_STR, NOPE}, + {"RndVibAmt" , __SK_SINGER_RndVibAmt_ , SK_STR, NOPE}, + {"VibratoAmt" , __SK_ControlChange_ ,__SK_SINGER_VibratoAmt_,SK_DBL}, + {"VibFreq" , __SK_ControlChange_ ,__SK_SINGER_VibFreq_ ,SK_DBL} }; /** FORMAT: *************************************************************/ /* */ -/* MessageStr$ ,type, data2, data3, */ +/* MessageStr$ , type, data2, data3, */ /* */ /* type is the message type sent back from the SKINI line parser. */ /* data is either */ /* NOPE : field not used, specifically, there aren't going */ /* to be any more fields on this line. So if there */ -/* is is NOPE in data2, data3 won't even be checked */ +/* is NOPE in data2, data3 won't even be checked */ /* SK_INT : byte (actually scanned as 32 bit signed integer) */ -/* If it's a MIDI data field which is required to */ -/* be an integer, like a controller number, it's */ -/* 0-127. Otherwise) get creative with SK_INTs */ +/* If it's a MIDI data field which is required to */ +/* be an integer, like a controller number, it's */ +/* 0-127. Otherwise, get creative with SK_INTs. */ /* SK_DBL : double precision floating point. SKINI uses these */ /* in the MIDI context for note numbers with micro */ /* tuning, velocities, controller values, etc. */ /* SK_STR : only valid in final field. This allows (nearly) */ /* arbitrary message types to be supported by simply */ /* scanning the string to EndOfLine and then passing */ -/* it to a more intellegent handler. For example, */ +/* it to a more intelligent handler. For example, */ /* MIDI SYSEX (system exclusive) messages of up to */ -/* 256bytes can be read as space-delimited integers */ -/* into the 1K SK_STR buffer. Longer bulk dumps, */ +/* 256 bytes can be read as space-delimited integers */ +/* into the SK_STR buffer. Longer bulk dumps, */ /* soundfiles, etc. should be handled as a new */ /* message type pointing to a FileName stored in the */ /* SK_STR field, or as a new type of multi-line */ diff --git a/include/Sampler.h b/include/Sampler.h index d3e4b23..edd3f09 100644 --- a/include/Sampler.h +++ b/include/Sampler.h @@ -2,15 +2,15 @@ /*! \class Sampler \brief STK sampling synthesis abstract base class. - This instrument contains up to 5 attack waves, - 5 looped waves, and an ADSR envelope. + This instrument provides an ADSR envelope, a one-pole filter, and + structures for an arbitrary number of attack and loop waves. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__SAMPLER_H) -#define __SAMPLER_H +#ifndef STK_SAMPLER_H +#define STK_SAMPLER_H #include "Instrmnt.h" #include "ADSR.h" @@ -31,7 +31,7 @@ class Sampler : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency) = 0; + virtual void setFrequency(StkFloat frequency) = 0; //! Initiate the envelopes with a key-on event and reset the attack waves. void keyOn(); @@ -40,25 +40,36 @@ class Sampler : public Instrmnt void keyOff(); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(MY_FLOAT amplitude); + virtual void noteOff(StkFloat amplitude); //! Compute one output sample. - virtual MY_FLOAT tick(); + virtual StkFloat tick() = 0; + + //! Computer \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize) = 0; + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ) = 0; //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value) = 0; + virtual void controlChange(int number, StkFloat value) = 0; protected: - ADSR *adsr; - WvIn *attacks[5]; - WaveLoop *loops[5]; - OnePole *filter; - MY_FLOAT baseFrequency; - MY_FLOAT attackRatios[5]; - MY_FLOAT loopRatios[5]; - MY_FLOAT attackGain; - MY_FLOAT loopGain; - int whichOne; + ADSR adsr_; + std::vector attacks_; + std::vector loops_; + OnePole filter_; + StkFloat baseFrequency_; + std::vector attackRatios_; + std::vector loopRatios_; + StkFloat attackGain_; + StkFloat loopGain_; }; diff --git a/include/Saxofony.h b/include/Saxofony.h index 0b64e01..ef9e046 100644 --- a/include/Saxofony.h +++ b/include/Saxofony.h @@ -31,16 +31,16 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__SAXOFONY_H) -#define __SAXOFONY_H +#ifndef STK_SAXOFONY_H +#define STK_SAXOFONY_H #include "Instrmnt.h" #include "DelayL.h" -#include "ReedTabl.h" +#include "ReedTable.h" #include "OneZero.h" #include "Envelope.h" #include "Noise.h" @@ -50,7 +50,10 @@ class Saxofony : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Saxofony(MY_FLOAT lowestFrequency); + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Saxofony(StkFloat lowestFrequency); //! Class destructor. ~Saxofony(); @@ -59,41 +62,53 @@ class Saxofony : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Set the "blowing" position between the air column terminations (0.0 - 1.0). - void setBlowPosition(MY_FLOAT aPosition); + void setBlowPosition(StkFloat aPosition); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBlowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBlowing(StkFloat amplitude, StkFloat rate); //! Decrease breath pressure with given rate of decrease. - void stopBlowing(MY_FLOAT rate); + void stopBlowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - DelayL *delays[2]; - ReedTabl *reedTable; - OneZero *filter; - Envelope *envelope; - Noise *noise; - WaveLoop *vibrato; - long length; - MY_FLOAT outputGain; - MY_FLOAT noiseGain; - MY_FLOAT vibratoGain; - MY_FLOAT position; + DelayL delays_[2]; + ReedTable reedTable_; + OneZero filter_; + Envelope envelope_; + Noise noise_; + WaveLoop *vibrato_; + unsigned long length_; + StkFloat outputGain_; + StkFloat noiseGain_; + StkFloat vibratoGain_; + StkFloat position_; }; diff --git a/include/Shakers.h b/include/Shakers.h index 4802692..91f6025 100644 --- a/include/Shakers.h +++ b/include/Shakers.h @@ -48,17 +48,17 @@ - Little Rocks = 21 - Tuned Bamboo Chimes = 22 - by Perry R. Cook, 1996 - 1999. + by Perry R. Cook, 1996 - 2004. */ /***************************************************/ -#if !defined(__SHAKERS_H) -#define __SHAKERS_H +#ifndef STK_SHAKERS_H +#define STK_SHAKERS_H #include "Instrmnt.h" -#define MAX_FREQS 8 -#define NUM_INSTR 24 +const int MAX_FREQS = 8; +const int NUM_INSTR = 24; class Shakers : public Instrmnt { @@ -74,54 +74,65 @@ class Shakers : public Instrmnt Use the instrument numbers above, converted to frequency values as if MIDI note numbers, to select a particular instrument. */ - virtual void noteOn(MY_FLOAT instrument, MY_FLOAT amplitude); + void noteOn(StkFloat instrument, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: int setupName(char* instr); int setupNum(int inst); - int setFreqAndReson(int which, MY_FLOAT freq, MY_FLOAT reson); - void setDecays(MY_FLOAT sndDecay, MY_FLOAT sysDecay); - void setFinalZs(MY_FLOAT z0, MY_FLOAT z1, MY_FLOAT z2); - MY_FLOAT wuter_tick(); - MY_FLOAT tbamb_tick(); - MY_FLOAT ratchet_tick(); + int setFreqAndReson(int which, StkFloat freq, StkFloat reson); + void setDecays(StkFloat sndDecay, StkFloat sysDecay); + void setFinalZs(StkFloat z0, StkFloat z1, StkFloat z2); + StkFloat wuter_tick(); + StkFloat tbamb_tick(); + StkFloat ratchet_tick(); - int instType; - int ratchetPos, lastRatchetPos; - MY_FLOAT shakeEnergy; - MY_FLOAT inputs[MAX_FREQS]; - MY_FLOAT outputs[MAX_FREQS][2]; - MY_FLOAT coeffs[MAX_FREQS][2]; - MY_FLOAT sndLevel; - MY_FLOAT baseGain; - MY_FLOAT gains[MAX_FREQS]; - int nFreqs; - MY_FLOAT t_center_freqs[MAX_FREQS]; - MY_FLOAT center_freqs[MAX_FREQS]; - MY_FLOAT resons[MAX_FREQS]; - MY_FLOAT freq_rand[MAX_FREQS]; - int freqalloc[MAX_FREQS]; - MY_FLOAT soundDecay; - MY_FLOAT systemDecay; - MY_FLOAT nObjects; - MY_FLOAT collLikely; - MY_FLOAT totalEnergy; - MY_FLOAT ratchet,ratchetDelta; - MY_FLOAT finalZ[3]; - MY_FLOAT finalZCoeffs[3]; - MY_FLOAT defObjs[NUM_INSTR]; - MY_FLOAT defDecays[NUM_INSTR]; - MY_FLOAT decayScale[NUM_INSTR]; + int instType_; + int ratchetPos_, lastRatchetPos_; + StkFloat shakeEnergy_; + StkFloat inputs_[MAX_FREQS]; + StkFloat outputs_[MAX_FREQS][2]; + StkFloat coeffs_[MAX_FREQS][2]; + StkFloat sndLevel_; + StkFloat baseGain_; + StkFloat gains_[MAX_FREQS]; + int nFreqs_; + StkFloat t_center_freqs_[MAX_FREQS]; + StkFloat center_freqs_[MAX_FREQS]; + StkFloat resons_[MAX_FREQS]; + StkFloat freq_rand_[MAX_FREQS]; + int freqalloc_[MAX_FREQS]; + StkFloat soundDecay_; + StkFloat systemDecay_; + StkFloat nObjects_; + StkFloat totalEnergy_; + StkFloat ratchet_, ratchetDelta_; + StkFloat finalZ_[3]; + StkFloat finalZCoeffs_[3]; + StkFloat defObjs_[NUM_INSTR]; + StkFloat defDecays_[NUM_INSTR]; + StkFloat decayScale_[NUM_INSTR]; }; diff --git a/include/Simple.h b/include/Simple.h index 2834e44..bde74da 100644 --- a/include/Simple.h +++ b/include/Simple.h @@ -13,12 +13,12 @@ - Envelope Rate = 11 - Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__SIMPLE_H) -#define __SIMPLE_H +#ifndef STK_SIMPLE_H +#define STK_SIMPLE_H #include "Instrmnt.h" #include "ADSR.h" @@ -31,16 +31,19 @@ class Simple : public Instrmnt { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ Simple(); //! Class destructor. - virtual ~Simple(); + ~Simple(); //! Clear internal states. void clear(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Start envelope toward "on" target. void keyOn(); @@ -49,25 +52,37 @@ class Simple : public Instrmnt void keyOff(); //! Start a note with the given frequency and amplitude. - virtual void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - virtual MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - ADSR *adsr; - WaveLoop *loop; - OnePole *filter; - BiQuad *biquad; - Noise *noise; - MY_FLOAT baseFrequency; - MY_FLOAT loopGain; + ADSR adsr_; + WaveLoop *loop_; + OnePole filter_; + BiQuad biquad_; + Noise noise_; + StkFloat baseFrequency_; + StkFloat loopGain_; }; diff --git a/include/SingWave.h b/include/SingWave.h index d1ae1ed..17a47bb 100644 --- a/include/SingWave.h +++ b/include/SingWave.h @@ -9,26 +9,26 @@ from pitch shifting. It will be used as an excitation source for other instruments. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__SINGWAVE_H) -#define __SINGWAVE_H +#ifndef STK_SINGWAVE_H +#define STK_SINGWAVE_H #include "WaveLoop.h" #include "Modulate.h" #include "Envelope.h" -class SingWave : public Stk +class SingWave : public Generator { public: //! Class constructor taking filename argument. /*! An StkError will be thrown if the file is not found, its format is - unknown, or a read error occurs. + unknown, a read error occurs, or the rawwave path is incorrectly set. */ - SingWave(const char *fileName, bool raw=FALSE); + SingWave( std::string fileName, bool raw=false); //! Class destructor. ~SingWave(); @@ -40,28 +40,28 @@ class SingWave : public Stk void normalize(); //! Normalize the file to a maximum of \e +- peak. - void normalize(MY_FLOAT peak); + void normalize(StkFloat peak); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Set the vibrato frequency in Hz. - void setVibratoRate(MY_FLOAT aRate); + void setVibratoRate(StkFloat rate); //! Set the vibrato gain. - void setVibratoGain(MY_FLOAT gain); + void setVibratoGain(StkFloat gain); //! Set the random-ness amount. - void setRandomGain(MY_FLOAT gain); + void setRandomGain(StkFloat gain); //! Set the sweep rate. - void setSweepRate(MY_FLOAT aRate); + void setSweepRate(StkFloat rate); //! Set the gain rate. - void setGainRate(MY_FLOAT aRate); + void setGainRate(StkFloat rate); //! Set the gain target value. - void setGainTarget(MY_FLOAT target); + void setGainTarget(StkFloat target); //! Start a note. void noteOn(); @@ -69,21 +69,29 @@ class SingWave : public Stk //! Stop a note. void noteOff(); - //! Return the last output value. - MY_FLOAT lastOut(); - //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Compute \e vectorSize outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - WaveLoop *wave; - Modulate *modulator; - Envelope *envelope; - Envelope *pitchEnvelope; - MY_FLOAT rate; - MY_FLOAT sweepRate; - MY_FLOAT lastOutput; + WaveLoop *wave_; + Modulate *modulator_; + Envelope envelope_; + Envelope pitchEnvelope_; + StkFloat rate_; + StkFloat sweepRate_; }; diff --git a/include/Sitar.h b/include/Sitar.h index cb84216..6fc6242 100644 --- a/include/Sitar.h +++ b/include/Sitar.h @@ -13,12 +13,12 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__SITAR_H) -#define __SITAR_H +#ifndef STK_SITAR_H +#define STK_SITAR_H #include "Instrmnt.h" #include "DelayA.h" @@ -30,7 +30,7 @@ class Sitar : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Sitar(MY_FLOAT lowestFrequency); + Sitar( StkFloat lowestFrequency = 20 ); //! Class destructor. ~Sitar(); @@ -39,30 +39,42 @@ class Sitar : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Pluck the string with the given amplitude using the current frequency. - void pluck(MY_FLOAT amplitude); + void pluck(StkFloat amplitude); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - DelayA *delayLine; - OneZero *loopFilter; - Noise *noise; - ADSR *envelope; - long length; - MY_FLOAT loopGain; - MY_FLOAT amGain; - MY_FLOAT delay; - MY_FLOAT targetDelay; + DelayA delayLine_; + OneZero loopFilter_; + Noise noise_; + ADSR envelope_; + + StkFloat loopGain_; + StkFloat amGain_; + StkFloat delay_; + StkFloat targetDelay_; }; diff --git a/include/Skini.h b/include/Skini.h new file mode 100644 index 0000000..d80399f --- /dev/null +++ b/include/Skini.h @@ -0,0 +1,117 @@ +/***************************************************/ +/*! \class Skini + \brief STK SKINI parsing class + + This class parses SKINI formatted text + messages. It can be used to parse individual + messages or it can be passed an entire file. + The SKINI specification is Perry's and his + alone, but it's all text so it shouldn't be too + hard to figure out. + + SKINI (Synthesis toolKit Instrument Network + Interface) is like MIDI, but allows for + floating-point control changes, note numbers, + etc. The following example causes a sharp + middle C to be played with a velocity of 111.132: + + \code + noteOn 60.01 111.132 + \endcode + + \sa \ref skini + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#ifndef STK_SKINI_H +#define STK_SKINI_H + +#include "Stk.h" +#include +#include +#include + +class Skini : public Stk +{ + public: + + //! A message structure to store and pass parsed SKINI messages. + struct Message { + long type; /*!< The message type, as defined in SKINI.msg. */ + long channel; /*!< The message channel (not limited to 16!). */ + StkFloat time; /*!< The message time stamp in seconds (delta or absolute). */ + std::vector floatValues; /*!< The message values read as floats (values are type-specific). */ + std::vector intValues; /*!< The message values read as ints (number and values are type-specific). */ + std::string remainder; /*!< Any remaining message data, read as ascii text. */ + + // Default constructor. + Message() + :type(0), channel(0), time(0.0), floatValues(2), intValues(2) {} + }; + + //! Default constructor. + Skini(); + + //! Class destructor + ~Skini(); + + //! Set a SKINI formatted file for reading. + /*! + If the file is successfully opened, this function returns \e + true. Otherwise, \e false is returned. + */ + bool setFile( std::string fileName ); + + //! Parse the next file message (if a file is loaded) and return the message type. + /*! + This function skips over lines in a file which cannot be + parsed. A type value equal to zero in the referenced message + structure (and the returned value) indicates the file end is + reached or no file is open for reading. + */ + long nextMessage( Skini::Message& message ); + + //! Attempt to parse the given string and returning the message type. + /*! + A type value equal to zero in the referenced message structure + indicates an invalid message. + */ + long parseString( std::string& line, Skini::Message& message ); + + //! Return the SKINI type string for the given type value. + static std::string whatsThisType(long type); + + //! Return the SKINI controller string for the given controller number. + static std::string whatsThisController(long number); + + protected: + + void tokenize( const std::string& str, std::vector& tokens, const std::string& delimiters ); + + std::ifstream file_; +}; + +static const double Midi2Pitch[129] = { +8.18,8.66,9.18,9.72,10.30,10.91,11.56,12.25, +12.98,13.75,14.57,15.43,16.35,17.32,18.35,19.45, +20.60,21.83,23.12,24.50,25.96,27.50,29.14,30.87, +32.70,34.65,36.71,38.89,41.20,43.65,46.25,49.00, +51.91,55.00,58.27,61.74,65.41,69.30,73.42,77.78, +82.41,87.31,92.50,98.00,103.83,110.00,116.54,123.47, +130.81,138.59,146.83,155.56,164.81,174.61,185.00,196.00, +207.65,220.00,233.08,246.94,261.63,277.18,293.66,311.13, +329.63,349.23,369.99,392.00,415.30,440.00,466.16,493.88, +523.25,554.37,587.33,622.25,659.26,698.46,739.99,783.99, +830.61,880.00,932.33,987.77,1046.50,1108.73,1174.66,1244.51, +1318.51,1396.91,1479.98,1567.98,1661.22,1760.00,1864.66,1975.53, +2093.00,2217.46,2349.32,2489.02,2637.02,2793.83,2959.96,3135.96, +3322.44,3520.00,3729.31,3951.07,4186.01,4434.92,4698.64,4978.03, +5274.04,5587.65,5919.91,6271.93,6644.88,7040.00,7458.62,7902.13, +8372.02,8869.84,9397.27,9956.06,10548.08,11175.30,11839.82,12543.85, +13289.75}; + +#endif + + diff --git a/include/Socket.h b/include/Socket.h index 79f0fba..9b780c1 100644 --- a/include/Socket.h +++ b/include/Socket.h @@ -14,12 +14,12 @@ less than or equal to zero indicate a closed or lost connection or the occurence of an error. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__SOCKET_H) -#define __SOCKET_H +#ifndef STK_SOCKET_H +#define STK_SOCKET_H #include "Stk.h" @@ -56,7 +56,7 @@ class Socket : public Stk void close( void ); //! Return the server/client socket descriptor. - int socket( void ) const; + int id( void ) const; //! Return the server/client port number. int port( void ) const; @@ -76,7 +76,7 @@ class Socket : public Stk //! Close the socket with the given descriptor. static void close( int socket ); - //! Returns TRUE is the socket descriptor is valid. + //! Returns true if the socket descriptor is valid. static bool isValid( int socket ); //! Write a buffer over the socket connection. Returns the number of bytes written or -1 if an error occurs. @@ -93,11 +93,10 @@ class Socket : public Stk protected: - char msg[256]; - int soket; - int poort; - bool server; + int soket_; + int port_; + bool server_; }; -#endif // defined(__SOCKET_H) +#endif // defined(STK_SOCKET_H) diff --git a/include/Sphere.h b/include/Sphere.h index 5e87c48..d399165 100644 --- a/include/Sphere.h +++ b/include/Sphere.h @@ -5,66 +5,67 @@ This class implements a spherical ball with radius, mass, position, and velocity parameters. - by Perry R. Cook, 1995 - 2002. + by Perry R. Cook, 1995 - 2004. */ /***************************************************/ -#if !defined(__SPHERE_H) -#define __SPHERE_H +#ifndef STK_SPHERE_H +#define STK_SPHERE_H +#include "Stk.h" #include "Vector3D.h" -class Sphere +class Sphere : public Stk { public: //! Constructor taking an initial radius value. - Sphere(double initRadius); + Sphere(StkFloat radius = 1.0 ); //! Class destructor. ~Sphere(); //! Set the 3D center position of the sphere. - void setPosition(double anX, double aY, double aZ); + void setPosition(StkFloat x, StkFloat y, StkFloat z); //! Set the 3D velocity of the sphere. - void setVelocity(double anX, double aY, double aZ); + void setVelocity(StkFloat x, StkFloat y, StkFloat z); //! Set the radius of the sphere. - void setRadius(double aRadius); + void setRadius(StkFloat radius); //! Set the mass of the sphere. - void setMass(double aMass); + void setMass(StkFloat mass); //! Get the current position of the sphere as a 3D vector. Vector3D* getPosition(); //! Get the relative position of the given point to the sphere as a 3D vector. - Vector3D* getRelativePosition(Vector3D *aPosition); + Vector3D* getRelativePosition(Vector3D *position); //! Set the velcoity of the sphere as a 3D vector. - double getVelocity(Vector3D* aVelocity); + StkFloat getVelocity(Vector3D* velocity); //! Returns the distance from the sphere boundary to the given position (< 0 if inside). - double isInside(Vector3D *aPosition); + StkFloat isInside(Vector3D *position); //! Get the current sphere radius. - double getRadius(); + StkFloat getRadius(); //! Get the current sphere mass. - double getMass(); + StkFloat getMass(); //! Increase the current sphere velocity by the given 3D components. - void addVelocity(double anX, double aY, double aZ); + void addVelocity(StkFloat x, StkFloat y, StkFloat z); //! Move the sphere for the given time increment. - void tick(double timeIncrement); + void tick(StkFloat timeIncrement); private: - Vector3D *myPosition; - Vector3D *myVelocity; - Vector3D workingVector; - double myRadius; - double myMass; + Vector3D position_; + Vector3D velocity_; + Vector3D workingVector_; + StkFloat radius_; + StkFloat mass_; }; #endif diff --git a/include/StifKarp.h b/include/StifKarp.h index eb4d5ba..769aeac 100644 --- a/include/StifKarp.h +++ b/include/StifKarp.h @@ -17,12 +17,12 @@ - String Sustain = 11 - String Stretch = 1 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__StifKarp_h) -#define __StifKarp_h +#ifndef STK_STIFKARP_H +#define STK_STIFKARP_H #include "Instrmnt.h" #include "DelayL.h" @@ -35,7 +35,7 @@ class StifKarp : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - StifKarp(MY_FLOAT lowestFrequency); + StifKarp(StkFloat lowestFrequency); //! Class destructor. ~StifKarp(); @@ -44,13 +44,13 @@ class StifKarp : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Set the stretch "factor" of the string (0.0 - 1.0). - void setStretch(MY_FLOAT stretch); + void setStretch(StkFloat stretch); //! Set the pluck or "excitation" position along the string (0.0 - 1.0). - void setPickupPosition(MY_FLOAT position); + void setPickupPosition(StkFloat position); //! Set the base loop gain. /*! @@ -58,37 +58,50 @@ class StifKarp : public Instrmnt Because of high-frequency loop filter roll-off, higher frequency settings have greater loop gains. */ - void setBaseLoopGain(MY_FLOAT aGain); + void setBaseLoopGain(StkFloat aGain); //! Pluck the string with the given amplitude using the current frequency. - void pluck(MY_FLOAT amplitude); + void pluck(StkFloat amplitude); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - DelayA *delayLine; - DelayL *combDelay; - OneZero *filter; - Noise *noise; - BiQuad *biQuad[4]; - long length; - MY_FLOAT loopGain; - MY_FLOAT baseLoopGain; - MY_FLOAT lastFrequency; - MY_FLOAT lastLength; - MY_FLOAT stretching; - MY_FLOAT pluckAmplitude; - MY_FLOAT pickupPosition; + DelayA delayLine_; + DelayL combDelay_; + OneZero filter_; + Noise noise_; + BiQuad biquad_[4]; + + unsigned long length_; + StkFloat loopGain_; + StkFloat baseLoopGain_; + StkFloat lastFrequency_; + StkFloat lastLength_; + StkFloat stretching_; + StkFloat pluckAmplitude_; + StkFloat pickupPosition_; }; diff --git a/include/Stk.h b/include/Stk.h index 43c36ed..c15c2b6 100644 --- a/include/Stk.h +++ b/include/Stk.h @@ -8,30 +8,36 @@ this class provides error handling and byte-swapping functions. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__STK_H) -#define __STK_H +#ifndef STK_STK_H +#define STK_STK_H #include +#include +#include +#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; +typedef double StkFloat; + +// The "MY_FLOAT" type was deprecated in STK +// versions higher than 4.1.3 and replaced with the variable +// "StkFloat". +#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) + typedef StkFloat MY_FLOAT; + #pragma deprecated(MY_FLOAT) +#elif defined(__GXX__) + typedef StkFloat MY_FLOAT __attribute__ ((deprecated)); +#else + typedef StkFloat MY_FLOAT; // temporary +#endif -// 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. /*! @@ -42,7 +48,8 @@ typedef double MY_FLOAT; class StkError { public: - enum TYPE { + enum Type { + STATUS, WARNING, DEBUG_WARNING, FUNCTION_ARGUMENT, @@ -58,24 +65,27 @@ public: }; protected: - char message[256]; - TYPE type; + std::string message_; + Type type_; public: //! The constructor. - StkError(const char *p, TYPE tipe = StkError::UNSPECIFIED); + StkError(const std::string& message, Type type = StkError::UNSPECIFIED) : message_(message), type_(type) {} //! The destructor. - virtual ~StkError(void); + virtual ~StkError(void) {}; - //! Prints "thrown" error message to stdout. - virtual void printMessage(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 type. + virtual const Type& getType(void) { return type_; } - //! Returns the "thrown" error message string. - virtual const char *getMessage(void) const { return message; } + //! 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 *getMessageCString(void) { return message_.c_str(); } }; @@ -83,15 +93,16 @@ class Stk { public: - typedef unsigned long STK_FORMAT; - 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 MY_FLOAT32; /*!< Normalized between plus/minus 1.0. */ - static const STK_FORMAT MY_FLOAT64; /*!< Normalized between plus/minus 1.0. */ + typedef unsigned long StkFormat; + static const StkFormat STK_SINT8; /*!< -128 to +127 */ + static const StkFormat STK_SINT16; /*!< -32768 to +32767 */ + static const StkFormat STK_SINT24; /*!< Upper 3 bytes of 32-bit signed integer. */ + static const StkFormat STK_SINT32; /*!< -2147483648 to +2147483647. */ + static const StkFormat STK_FLOAT32; /*!< Normalized between plus/minus 1.0. */ + static const StkFormat STK_FLOAT64; /*!< Normalized between plus/minus 1.0. */ //! Static method which returns the current STK sample rate. - static MY_FLOAT sampleRate(void); + static StkFloat sampleRate(void) { return srate_; } //! Static method which sets the STK sample rate. /*! @@ -102,13 +113,13 @@ public: is different from the default rate, it is imperative that it be set \e BEFORE STK objects are instantiated. */ - static void setSampleRate(MY_FLOAT newRate); + static void setSampleRate(StkFloat rate) { if (rate > 0.0) srate_ = rate; } //! Static method which returns the current rawwave path. - static std::string rawwavePath(void); + static std::string rawwavePath(void) { return rawwavepath_; } //! Static method which sets the STK rawwave path. - static void setRawwavePath(std::string newPath); + static void setRawwavePath(std::string path); //! Static method which byte-swaps a 16-bit data type. static void swap16(unsigned char *ptr); @@ -122,41 +133,119 @@ public: //! Static cross-platform method to sleep for a number of milliseconds. static void sleep(unsigned long milliseconds); + //! Static function for error reporting and handling using c-strings. + static void handleError( const char *message, StkError::Type type ); + + //! Static function for error reporting and handling using c++ strings. + static void handleError( std::string message, StkError::Type type ); + private: - static MY_FLOAT srate; - static std::string rawwavepath; + static StkFloat srate_; + static std::string rawwavepath_; protected: + std::ostringstream errorString_; + //! Default constructor. Stk(void); //! Class destructor. virtual ~Stk(void); - //! Function for error reporting and handling. - static void handleError( const char *message, StkError::TYPE type ); + //! Internal function for error reporting which assumes message in \c errorString_ variable. + void handleError( StkError::Type type ); +}; + + +/***************************************************/ +/*! \class StkFrames + \brief An STK class to handle vectorized audio data. + + This class can hold single- or multi-channel audio data in either + interleaved or non-interleaved formats. The data type is always + StkFloat. + + Possible future improvements in this class could include static + functions to inter- or de-interleave the data and to convert to + and return other data types. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +class StkFrames +{ +public: + + //! The default constructor initializes the frame data structure to size zero. + StkFrames( unsigned int nFrames = 0, unsigned int nChannels = 1, bool interleaved = true ); + + //! Overloaded constructor which initializes the frame data to the specified size with \c value. + StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels, bool interleaved = true ); + + //! Subscript operator which returns a reference to element \c n of self. + /*! + The result can be used as an lvalue . This reference is valid + until the resize function is called or the array is destroyed. The + index \c n must be between 0 and size less one. No range checking + is performed. + */ + StkFloat& operator[]( size_t n ) { return data_[n]; }; + + //! Subscript operator which returns the value at element \c n of self. + /*! + The index \c n must be between 0 and size less one. No range + checking is performed. + */ + StkFloat operator[]( size_t n ) const { return data_[n]; }; + + //! Returns the total number of audio samples represented by the object. + size_t size() const { return size_; }; + + //! Resize self to represent the specified number of channels and frames. + /*! + Changes the size of self based on the number of frames and + channels, and assigns \c value to every element. + */ + void resize( unsigned int nFrames, unsigned int nChannels = 1, StkFloat value = 0.0 ); + + //! Return the number of channels represented by the data. + unsigned int channels( void ) const { return nChannels_; }; + + //! Return the number of sample frames represented by the data. + unsigned int frames( void ) const { return nFrames_; }; + + //! Returns \c true if the data is in interleaved format, \c false if the data is non-interleaved. + bool interleaved( void ) const { return interleaved_; }; + + //! Set the flag to indicate whether the internal data is in interleaved (\c true) or non-interleaved (\c false) format. + void setInterleaved( bool isInterleaved ) { interleaved_ = isInterleaved; }; + +private: + std::valarray data_; + unsigned int nFrames_; + unsigned int nChannels_; + size_t size_; + bool interleaved_; }; + // Here are a few other useful typedefs. typedef signed short SINT16; typedef signed int SINT32; typedef float FLOAT32; typedef double FLOAT64; -// Boolean values -#define FALSE 0 -#define TRUE 1 - // The default sampling rate. -#define SRATE (MY_FLOAT) 44100.0 +const StkFloat SRATE = 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 +const unsigned int RT_BUFFER_SIZE = 512; // The default rawwave path value is set with the preprocessor // definition RAWWAVE_PATH. This can be specified as an argument to @@ -171,10 +260,9 @@ typedef double FLOAT64; #define RAWWAVE_PATH "../../rawwaves/" #endif -#define PI (MY_FLOAT) 3.14159265359 -#define TWO_PI (MY_FLOAT) (MY_FLOAT) (2 * PI) - -#define ONE_OVER_128 (MY_FLOAT) 0.0078125 +const StkFloat PI = 3.14159265359; +const StkFloat TWO_PI = 2 * PI; +const StkFloat ONE_OVER_128 = 0.0078125; #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) #define __OS_WINDOWS__ diff --git a/include/SubNoise.h b/include/SubNoise.h index 73c70e5..8f517cc 100644 --- a/include/SubNoise.h +++ b/include/SubNoise.h @@ -6,12 +6,12 @@ using the C rand() function. The quality of the rand() function varies from one OS to another. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__SUBNOISE_H) -#define __SUBNOISE_H +#ifndef STK_SUBNOISE_H +#define STK_SUBNOISE_H #include "Noise.h" @@ -32,11 +32,23 @@ class SubNoise : public Noise void setRate(int subRate); //! Return a sub-sampled random number between -1.0 and 1.0. - MY_FLOAT tick(); + StkFloat tick(); + + //! Compute \e vectorSize outputs and return them in \e vector. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - int counter; - int rate; + int counter_; + int rate_; }; diff --git a/include/Table.h b/include/Table.h index f777bc5..a35da3c 100644 --- a/include/Table.h +++ b/include/Table.h @@ -10,20 +10,24 @@ An StkError will be thrown if the table file is not found. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__TABLE_H) -#define __TABLE_H +#ifndef STK_TABLE_H +#define STK_TABLE_H -#include "Stk.h" +#include "Function.h" -class Table : public Stk +class Table : public Function { public: - //! Constructor loads the data from \e fileName. - Table(char *fileName); + //! The constructor loads the data from \e fileName. + /*! + An StkError will be thrown in the file cannot be found or + opened. + */ + Table( std::string fileName ); //! Class destructor. ~Table(); @@ -31,23 +35,28 @@ public: //! Return the number of elements in the table. long getLength() const; - //! Return the last output value. - MY_FLOAT lastOut() const; - //! Return the table value at position \e index. /*! Linear interpolation is performed if \e index is fractional. */ - MY_FLOAT tick(MY_FLOAT index); + StkFloat tick(StkFloat index); - //! Take \e vectorSize index positions and return the corresponding table values in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + //! Take \e vectorSize inputs from \e vector and replace them with corresponding outputs. + StkFloat *tick( StkFloat *vector, unsigned int vectorSize ); + + //! Take a channel of the StkFrames object as inputs to the function and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - long length; - MY_FLOAT *data; - MY_FLOAT lastOutput; + long length_; + std::valarray data_; }; diff --git a/include/TcpWvIn.h b/include/TcpWvIn.h index 979d8df..0766487 100644 --- a/include/TcpWvIn.h +++ b/include/TcpWvIn.h @@ -20,18 +20,19 @@ for a single remote connection. The default data type for the incoming stream is signed 16-bit integers, though any of the defined - STK_FORMATs are permissible. + StkFormats are permissible. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__TCPWVIN_H) -#define __TCPWVIN_H +#ifndef STK_TCPWVIN_H +#define STK_TCPWVIN_H #include "WvIn.h" #include "Socket.h" #include "Thread.h" +#include "Mutex.h" typedef struct { bool finished; @@ -54,32 +55,49 @@ public: /*! An StkError will be thrown a socket error or an invalid function argument. */ - void listen(unsigned int nChannels = 1, Stk::STK_FORMAT format = STK_SINT16); + void listen(unsigned int nChannels = 1, Stk::StkFormat format = STK_SINT16); - //! Returns TRUE is an input connection exists or input data remains in the queue. + //! Returns true is an input connection exists or input data remains in the queue. /*! - This method will not return FALSE after an input connection has been closed until + This method will not return false after an input connection has been closed until all buffered input data has been read out. */ bool isConnected(void); //! Return the average across the last output sample frame. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Read out the average across one sample frame of data. - MY_FLOAT tick(void); + StkFloat tick(void); //! Read out vectorSize averaged sample frames of data in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with averaged sample frames. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Return a pointer to the last output sample frame. - const MY_FLOAT *lastFrame(void) const; + const StkFloat *lastFrame(void) const; //! Return a pointer to the next sample frame of data. - const MY_FLOAT *tickFrame(void); + const StkFloat *tickFrame(void); //! Read out sample \e frames of data to \e frameVector. - MY_FLOAT *tickFrame(MY_FLOAT *frameVector, unsigned int frames); + StkFloat *tickFrame(StkFloat *frameVector, unsigned int frames); + + //! Fill the StkFrames object with sample frames of data and return the same reference. + /*! + An StkError will be thrown if there is an incompatability + between the number of channels in the TcpWvIn object and that in + the StkFrames object. + */ + StkFrames& tickFrame( StkFrames& frames ); // Called by the thread routine to receive data via the socket connection // and fill the socket buffer. This is not intended for general use but @@ -94,19 +112,19 @@ protected: // Read buffered socket data into the data buffer ... will block if none available. int readData( void ); - Socket *soket; - Thread *thread; - Mutex mutex; - char *buffer; - long bufferBytes; - long bytesFilled; - long writePoint; - long readPoint; - long counter; - int dataSize; - bool connected; - int fd; - thread_info threadInfo; + Socket *soket_; + Thread *thread_; + Mutex mutex_; + char *buffer_; + long bufferBytes_; + long bytesFilled_; + long writePoint_; + long readPoint_; + long counter_; + int dataSize_; + bool connected_; + int fd_; + thread_info threadInfo_; }; diff --git a/include/TcpWvOut.h b/include/TcpWvOut.h index 437d3d9..57f5b64 100644 --- a/include/TcpWvOut.h +++ b/include/TcpWvOut.h @@ -19,14 +19,14 @@ port and IP address of which must be specified as constructor arguments. The default data type is signed 16-bit integers but any of the - defined STK_FORMATs are permissible. + defined StkFormats are permissible. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__TCPWVOUT_H) -#define __TCPWVOUT_H +#ifndef STK_TCPWVOUT_H +#define STK_TCPWVOUT_H #include "WvOut.h" #include "Socket.h" @@ -41,7 +41,7 @@ class TcpWvOut : protected WvOut /*! An StkError is thrown if a socket error occurs or an invalid argument is specified. */ - TcpWvOut(int port, const char *hostname = "localhost", unsigned int nChannels = 1, Stk::STK_FORMAT format = STK_SINT16); + TcpWvOut(int port, const char *hostname = "localhost", unsigned int nChannels = 1, Stk::StkFormat format = STK_SINT16); //! Class destructor. ~TcpWvOut(); @@ -50,7 +50,7 @@ class TcpWvOut : protected WvOut /*! An StkError is thrown if a socket error occurs or an invalid argument is specified. */ - void connect(int port, const char *hostname = "localhost", unsigned int nChannels = 1, Stk::STK_FORMAT format = STK_SINT16); + void connect(int port, const char *hostname = "localhost", unsigned int nChannels = 1, Stk::StkFormat format = STK_SINT16); //! If a connection is open, write out remaining samples in the queue and then disconnect. void disconnect(void); @@ -59,35 +59,51 @@ class TcpWvOut : protected WvOut unsigned long getFrames( void ) const; //! Return the number of seconds of data output. - MY_FLOAT getTime( void ) const; + StkFloat getTime( void ) const; //! Output a single sample to all channels in a sample frame. /*! An StkError is thrown if a socket write error occurs. */ - void tick(MY_FLOAT sample); + void tick( const StkFloat sample ); //! Output each sample in \e vector to all channels in \e vectorSize sample frames. /*! An StkError is thrown if a socket write error occurs. */ - void tick(const MY_FLOAT *vector, unsigned int vectorSize); + void tick( const StkFloat *vector, unsigned int vectorSize ); + + //! Output a channel of the StkFrames object to all channels of the TcpWvOut object. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if a + socket write error occurs or the \c channel argument is zero or it + is greater than the number of channels in the StkFrames object. + */ + void tick( const StkFrames& frames, unsigned int channel = 1 ); //! Output the \e frameVector of sample frames of the given length. /*! An StkError is thrown if a socket write error occurs. */ - void tickFrame(const MY_FLOAT *frameVector, unsigned int frames = 1); + void tickFrame( const StkFloat *frameVector, unsigned int frames = 1 ); + + //! Output the StkFrames data to the TcpWvOut object. + /*! + An StkError will be thrown if a socket write error occurs or if + there is an incompatability between the number of channels in the + TcpWvOut object and that in the StkFrames object. + */ + virtual void tickFrame( const StkFrames& frames ); protected: // Write a buffer of length \e frames via the socket connection. void writeData( unsigned long frames ); - char msg[256]; - char *buffer; - Socket *soket; - int dataSize; + char *buffer_; + Socket *soket_; + int dataSize_; }; -#endif // defined(__TCPWVOUT_H) +#endif diff --git a/include/Thread.h b/include/Thread.h index a1fee0c..9336bcc 100644 --- a/include/Thread.h +++ b/include/Thread.h @@ -2,17 +2,26 @@ /*! \class Thread \brief STK thread class. - This class provides a uniform interface for - cross-platform threads. On unix systems, - the pthread library is used. Under Windows, - the C runtime threadex functions are used. + This class provides a uniform interface for cross-platform + threads. On unix systems, the pthread library is used. Under + Windows, the C runtime threadex functions are used. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + Each instance of the Thread class can be used to control a single + thread process. Routines are provided to signal cancelation + and/or joining with a thread, though it is not possible for this + class to know the running status of a thread once it is started. + + For cross-platform compatability, thread functions should be + declared as follows: + + THREAD_RETURN THREAD_TYPE thread_function(void *ptr) + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__THREAD_H) -#define __THREAD_H +#ifndef STK_THREAD_H +#define STK_THREAD_H #include "Stk.h" @@ -23,7 +32,6 @@ typedef pthread_t THREAD_HANDLE; typedef void * THREAD_RETURN; typedef void * (*THREAD_FUNCTION)(void *); - typedef pthread_mutex_t MUTEX; #elif defined(__OS_WINDOWS__) @@ -33,7 +41,6 @@ typedef unsigned long THREAD_HANDLE; typedef unsigned THREAD_RETURN; typedef unsigned (__stdcall *THREAD_FUNCTION)(void *); - typedef CRITICAL_SECTION MUTEX; #endif @@ -43,53 +50,45 @@ class Thread : public Stk //! Default constructor. Thread(); - //! The class destructor waits indefinitely for the thread to end before returning. + //! The class destructor does not attempt to cancel or join a thread. ~Thread(); - //! Begin execution of the thread \e routine. Upon success, TRUE is returned. + //! Begin execution of the thread \e routine. Upon success, true is returned. /*! - The thread routine can be passed an argument via \e ptr. If - the thread cannot be created, the return value is FALSE. + A data pointer can be supplied to the thread routine via the + optional \e ptr argument. If the thread cannot be created, the + return value is false. */ bool start( THREAD_FUNCTION routine, void * ptr = NULL ); - //! Wait the specified number of milliseconds for the thread to terminate. Return TRUE on success. + //! Signal cancellation of a thread routine, returning \e true on success. /*! - If the specified time value is negative, the function will - block indefinitely. Otherwise, the function will block up to a - maximum of the specified time. A return value of FALSE indicates - the thread did not terminate within the specified time limit. + This function only signals thread cancellation. It does not + wait to verify actual routine termination. A \e true return value + only signifies that the cancellation signal was properly executed, + not thread cancellation. A thread routine may need to make use of + the testCancel() function to specify a cancellation point. */ - bool wait( long milliseconds = -1 ); + bool cancel(void); - //! Test for a thread cancellation request. - static void test(void); + //! Block the calling routine indefinitely until the thread terminates. + /*! + This function suspends execution of the calling routine until the thread has terminated. It will return immediately if the thread was already terminated. A \e true return value signifies successful termination. A \e false return value indicates a problem with the wait call. + */ + bool wait(void); + + //! Create a cancellation point within a thread routine. + /*! + This function call checks for thread cancellation, allowing the + thread to be terminated if a cancellation request was previously + signaled. + */ + void testCancel(void); protected: - THREAD_HANDLE thread; + THREAD_HANDLE thread_; }; -class Mutex : public Stk -{ - public: - //! Default constructor. - Mutex(); - - //! Class destructor. - ~Mutex(); - - //! Lock the mutex. - void lock(void); - - //! Unlock the mutex. - void unlock(void); - - protected: - - MUTEX mutex; - -}; - -#endif // defined(__THREAD_H) +#endif diff --git a/include/TubeBell.h b/include/TubeBell.h index de203af..206a97a 100644 --- a/include/TubeBell.h +++ b/include/TubeBell.h @@ -26,12 +26,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__TUBEBELL_H) -#define __TUBEBELL_H +#ifndef STK_TUBEBELL_H +#define STK_TUBEBELL_H #include "FM.h" @@ -39,16 +39,31 @@ class TubeBell : public FM { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ TubeBell(); //! Class destructor. ~TubeBell(); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/TwoPole.h b/include/TwoPole.h index f825541..9d15d06 100644 --- a/include/TwoPole.h +++ b/include/TwoPole.h @@ -8,12 +8,12 @@ frequency response while maintaining a nearly constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__TWOPOLE_H) -#define __TWOPOLE_H +#ifndef STK_TWOPOLE_H +#define STK_TWOPOLE_H #include "Filter.h" @@ -31,13 +31,13 @@ class TwoPole : protected Filter void clear(void); //! Set the b[0] coefficient value. - void setB0(MY_FLOAT b0); + void setB0(StkFloat b0); //! Set the a[1] coefficient value. - void setA1(MY_FLOAT a1); + void setA1(StkFloat a1); //! Set the a[2] coefficient value. - void setA2(MY_FLOAT a2); + void setA2(StkFloat a2); //! Sets the filter coefficients for a resonance at \e frequency (in Hz). /*! @@ -53,26 +53,35 @@ class TwoPole : protected Filter An unstable filter will result for \e radius >= 1.0. For a better resonance filter, use a BiQuad filter. \sa BiQuad filter class */ - void setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize = FALSE); + void setResonance(StkFloat frequency, StkFloat radius, bool normalize = false); //! Set the filter gain. /*! The gain is applied at the filter input and does not affect the coefficient values. The default gain value is 1.0. */ - void setGain(MY_FLOAT theGain); + void setGain(StkFloat gain); //! Return the current filter gain. - MY_FLOAT getGain(void) const; + StkFloat getGain(void) const; //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Input one sample to the filter and return one output. - MY_FLOAT tick(MY_FLOAT sample); + StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/TwoZero.h b/include/TwoZero.h index 1449bc7..605772a 100644 --- a/include/TwoZero.h +++ b/include/TwoZero.h @@ -8,12 +8,12 @@ frequency response while maintaining a constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__TWOZERO_H) -#define __TWOZERO_H +#ifndef STK_TWOZERO_H +#define STK_TWOZERO_H #include "Filter.h" @@ -30,13 +30,13 @@ class TwoZero : protected Filter void clear(void); //! Set the b[0] coefficient value. - void setB0(MY_FLOAT b0); + void setB0(StkFloat b0); //! Set the b[1] coefficient value. - void setB1(MY_FLOAT b1); + void setB1(StkFloat b1); //! Set the b[2] coefficient value. - void setB2(MY_FLOAT b2); + void setB2(StkFloat b2); //! Sets the filter coefficients for a "notch" at \e frequency (in Hz). /*! @@ -49,26 +49,35 @@ class TwoZero : protected Filter frequency. The closer the zeros are to the unit-circle (\e radius close to or equal to one), the narrower the resulting notch width. */ - void setNotch(MY_FLOAT frequency, MY_FLOAT radius); + void setNotch(StkFloat frequency, StkFloat radius); //! Set the filter gain. /*! The gain is applied at the filter input and does not affect the coefficient values. The default gain value is 1.0. */ - void setGain(MY_FLOAT theGain); + void setGain(StkFloat gain); //! Return the current filter gain. - MY_FLOAT getGain(void) const; + StkFloat getGain(void) const; //! Return the last computed output value. - MY_FLOAT lastOut(void) const; + StkFloat lastOut(void) const; //! Input one sample to the filter and return one output. - MY_FLOAT tick(MY_FLOAT sample); + StkFloat tick(StkFloat sample); //! Input \e vectorSize samples to the filter and return an equal number of outputs in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/Vector3D.h b/include/Vector3D.h index c031962..db0373a 100644 --- a/include/Vector3D.h +++ b/include/Vector3D.h @@ -4,50 +4,53 @@ This class implements a three-dimensional vector. - by Perry R. Cook, 1995 - 2002. + by Perry R. Cook, 1995 - 2004. */ /***************************************************/ -#if !defined(__VECTOR3D_H) -#define __VECTOR3D_H +#ifndef STK_VECTOR3D_H +#define STK_VECTOR3D_H -class Vector3D { +#include "Stk.h" + +class Vector3D : public Stk +{ public: //! Default constructor taking optional initial X, Y, and Z values. - Vector3D(double initX=0.0, double initY=0.0, double initZ=0.0); + Vector3D(StkFloat initX=0.0, StkFloat initY=0.0, StkFloat initZ=0.0); //! Class destructor. ~Vector3D(); //! Get the current X value. - double getX(); + StkFloat getX(); //! Get the current Y value. - double getY(); + StkFloat getY(); //! Get the current Z value. - double getZ(); + StkFloat getZ(); //! Calculate the vector length. - double getLength(); + StkFloat getLength(); //! Set the X, Y, and Z values simultaniously. - void setXYZ(double anX, double aY, double aZ); + void setXYZ(StkFloat x, StkFloat y, StkFloat z); //! Set the X value. - void setX(double aval); + void setX(StkFloat x); //! Set the Y value. - void setY(double aval); + void setY(StkFloat y); //! Set the Z value. - void setZ(double aval); + void setZ(StkFloat z); protected: - double myX; - double myY; - double myZ; + StkFloat myX_; + StkFloat myY_; + StkFloat myZ_; }; #endif diff --git a/include/VoicForm.h b/include/VoicForm.h index 45cc622..f183a19 100644 --- a/include/VoicForm.h +++ b/include/VoicForm.h @@ -21,12 +21,12 @@ - Vibrato Gain = 1 - Loudness (Spectral Tilt) = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__VOICFORM_H) -#define __VOICFORM_H +#ifndef STK_VOICFORM_H +#define STK_VOICFORM_H #include "Instrmnt.h" #include "Envelope.h" @@ -39,7 +39,10 @@ class VoicForm : public Instrmnt { public: - //! Class constructor, taking the lowest desired playing frequency. + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ VoicForm(); //! Class destructor. @@ -49,22 +52,22 @@ class VoicForm : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); - //! Set instrument parameters for the given phoneme. Returns FALSE if phoneme not found. + //! Set instrument parameters for the given phoneme. Returns false if phoneme not found. bool setPhoneme(const char* phoneme); //! Set the voiced component gain. - void setVoiced(MY_FLOAT vGain); + void setVoiced(StkFloat vGain); //! Set the unvoiced component gain. - void setUnVoiced(MY_FLOAT nGain); + void setUnVoiced(StkFloat nGain); //! Set the sweep rate for a particular formant filter (0-3). - void setFilterSweepRate(int whichOne, MY_FLOAT rate); + void setFilterSweepRate(unsigned int whichOne, StkFloat rate); //! Set voiced component pitch sweep rate. - void setPitchSweepRate(MY_FLOAT rate); + void setPitchSweepRate(StkFloat rate); //! Start the voice. void speak(); @@ -73,24 +76,36 @@ class VoicForm : public Instrmnt void quiet(); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - SingWave *voiced; - Noise *noise; - Envelope *noiseEnv; - FormSwep *filters[4]; - OnePole *onepole; - OneZero *onezero; + SingWave *voiced_; + Noise noise_; + Envelope noiseEnv_; + FormSwep filters_[4]; + OnePole onepole_; + OneZero onezero_; }; diff --git a/include/Voicer.h b/include/Voicer.h index 76912c0..48a90bc 100644 --- a/include/Voicer.h +++ b/include/Voicer.h @@ -25,21 +25,21 @@ an ensemble. Alternately, control changes can be sent to all voices on a given channel. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__VOICER_H) -#define __VOICER_H +#ifndef STK_VOICER_H +#define STK_VOICER_H -#include "Stk.h" #include "Instrmnt.h" +#include class Voicer : public Stk { public: - //! Class constructor taking the maximum number of instruments to control and an optional note decay time (in seconds). - Voicer( int maxInstruments, MY_FLOAT decayTime=0.2 ); + //! Class constructor taking an optional note decay time (in seconds). + Voicer( StkFloat decayTime=0.2 ); //! Class destructor. ~Voicer(); @@ -68,81 +68,93 @@ public: found for a specified non-zero channel value, the function returns -1. The amplitude value should be in the range 0.0 - 128.0. */ - long noteOn( MY_FLOAT noteNumber, MY_FLOAT amplitude, int channel=0 ); + long noteOn( StkFloat noteNumber, StkFloat amplitude, int channel=0 ); //! Send a noteOff to all voices having the given noteNumber and optional channel (default channel = 0). /*! The amplitude value should be in the range 0.0 - 128.0. */ - void noteOff( MY_FLOAT noteNumber, MY_FLOAT amplitude, int channel=0 ); + void noteOff( StkFloat noteNumber, StkFloat amplitude, int channel=0 ); //! Send a noteOff to the voice with the given note tag. /*! The amplitude value should be in the range 0.0 - 128.0. */ - void noteOff( long tag, MY_FLOAT amplitude ); + void noteOff( long tag, StkFloat amplitude ); //! Send a frequency update message to all voices assigned to the optional channel argument (default channel = 0). /*! The \e noteNumber argument corresponds to a MIDI note number, though it is a floating-point value and can range beyond the normal 0-127 range. */ - void setFrequency( MY_FLOAT noteNumber, int channel=0 ); + void setFrequency( StkFloat noteNumber, int channel=0 ); //! Send a frequency update message to the voice with the given note tag. /*! The \e noteNumber argument corresponds to a MIDI note number, though it is a floating-point value and can range beyond the normal 0-127 range. */ - void setFrequency( long tag, MY_FLOAT noteNumber ); + void setFrequency( long tag, StkFloat noteNumber ); //! Send a pitchBend message to all voices assigned to the optional channel argument (default channel = 0). - void pitchBend( MY_FLOAT value, int channel=0 ); + void pitchBend( StkFloat value, int channel=0 ); //! Send a pitchBend message to the voice with the given note tag. - void pitchBend( long tag, MY_FLOAT value ); + void pitchBend( long tag, StkFloat value ); //! Send a controlChange to all instruments assigned to the optional channel argument (default channel = 0). - void controlChange( int number, MY_FLOAT value, int channel=0 ); + void controlChange( int number, StkFloat value, int channel=0 ); //! Send a controlChange to the voice with the given note tag. - void controlChange( long tag, int number, MY_FLOAT value ); + void controlChange( long tag, int number, StkFloat value ); //! Send a noteOff message to all existing voices. void silence( void ); //! Mix the output for all sounding voices. - MY_FLOAT tick(); + StkFloat tick(); //! Compute \e vectorSize output mixes and return them in \e vector. - MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Return the last output value. - MY_FLOAT lastOut() const; + StkFloat lastOut() const; //! Return the last left output value. - MY_FLOAT lastOutLeft() const; + StkFloat lastOutLeft() const; //! Return the last right output value. - MY_FLOAT lastOutRight() const; + StkFloat lastOutRight() const; protected: - typedef struct { + struct Voice { Instrmnt *instrument; long tag; - MY_FLOAT noteNumber; - MY_FLOAT frequency; + StkFloat noteNumber; + StkFloat frequency; int sounding; int channel; - } Voice; - int nVoices; - int maxVoices; - Voice *voices; - long tags; - int muteTime; - MY_FLOAT lastOutput; - MY_FLOAT lastOutputLeft; - MY_FLOAT lastOutputRight; + // Default constructor. + Voice() + :instrument(0), tag(0), noteNumber(-1.0), frequency(0.0), + sounding(0), channel(0) {} + }; + + std::vector voices_; + long tags_; + int muteTime_; + StkFloat lastOutput_; + StkFloat lastOutputLeft_; + StkFloat lastOutputRight_; }; #endif diff --git a/include/WaveLoop.h b/include/WaveLoop.h index 6a67e7f..8576af1 100644 --- a/include/WaveLoop.h +++ b/include/WaveLoop.h @@ -14,21 +14,20 @@ For single-channel data, these methods return equivalent values. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__WAVELOOP_H) -#define __WAVELOOP_H +#ifndef STK_WAVELOOP_H +#define STK_WAVELOOP_H #include "WvIn.h" -#include class WaveLoop : public WvIn { public: //! Class constructor. - WaveLoop( const char *fileName, bool raw = FALSE ); + WaveLoop( std::string fileName, bool raw = false ); //! Class destructor. virtual ~WaveLoop(); @@ -40,10 +39,10 @@ public: corresponds to file cycles per second. The frequency can be negative, in which case the loop is read in reverse order. */ - void setFrequency(MY_FLOAT aFrequency); + void setFrequency(StkFloat frequency); //! Increment the read pointer by \e aTime samples, modulo file size. - void addTime(MY_FLOAT aTime); + void addTime(StkFloat time); //! Increment current read pointer by \e anAngle, relative to a looping frequency. /*! @@ -51,7 +50,7 @@ public: size and the current Stk::sampleRate. The \e anAngle value is a multiple of file size. */ - void addPhase(MY_FLOAT anAngle); + void addPhase(StkFloat angle); //! Add a phase offset to the current read pointer. /*! @@ -59,18 +58,33 @@ public: size and the current Stk::sampleRate. The \e anAngle value is a multiple of file size. */ - void addPhaseOffset(MY_FLOAT anAngle); + void addPhaseOffset(StkFloat angle); //! Return a pointer to the next sample frame of data. - const MY_FLOAT *tickFrame(void); + const StkFloat *tickFrame(void); + + //! Read out sample \e frames of data to \e frameVector. + /*! + An StkError will be thrown if a file is read incrementally and a read error occurs. + */ + StkFloat *tickFrame(StkFloat *frameVector, unsigned int frames); + + //! Fill the StkFrames object with sample frames of data and return the same reference. + /*! + An StkError will be thrown if a file is read incrementally and + a read error occurs or there is an incompatability between the + number of channels in the RtWvIn object and that in the StkFrames + object. + */ + StkFrames& tickFrame( StkFrames& frames ); protected: // Read file data. void readData(unsigned long index); - MY_FLOAT phaseOffset; + StkFloat phaseOffset_; }; -#endif // defined(__WAVELOOP_H) +#endif diff --git a/include/Whistle.h b/include/Whistle.h index 4bfec59..b788f6c 100644 --- a/include/Whistle.h +++ b/include/Whistle.h @@ -12,12 +12,12 @@ - Blowing Frequency Modulation = 2 - Volume = 128 - by Perry R. Cook 1996 - 2002. + by Perry R. Cook 1996 - 2004. */ /***************************************************/ -#if !defined(__WHISTLE_H) -#define __WHISTLE_H +#ifndef STK_WHISTLE_H +#define STK_WHISTLE_H #include "Instrmnt.h" #include "Sphere.h" @@ -31,6 +31,9 @@ class Whistle : public Instrmnt { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ Whistle(); //! Class destructor. @@ -40,46 +43,58 @@ public: void clear(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Apply breath velocity to instrument with given amplitude and rate of increase. - void startBlowing(MY_FLOAT amplitude, MY_FLOAT rate); + void startBlowing(StkFloat amplitude, StkFloat rate); //! Decrease breath velocity with given rate of decrease. - void stopBlowing(MY_FLOAT rate); + void stopBlowing(StkFloat rate); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, MY_FLOAT value); + void controlChange(int number, StkFloat value); protected: - Vector3D *tempVectorP; - Vector3D *tempVector; - OnePole onepole; - Noise noise; - Envelope envelope; - Sphere *can; // Declare a Spherical "can". - Sphere *pea, *bumper; // One spherical "pea", and a spherical "bumper". - WaveLoop *sine; + Vector3D *tempVectorP_; + Vector3D tempVector_; + OnePole onepole_; + Noise noise_; + Envelope envelope_; + Sphere can_; // Declare a Spherical "can". + Sphere pea_, bumper_; // One spherical "pea", and a spherical "bumper". - MY_FLOAT baseFrequency; - MY_FLOAT maxPressure; - MY_FLOAT noiseGain; - MY_FLOAT fippleFreqMod; - MY_FLOAT fippleGainMod; - MY_FLOAT blowFreqMod; - MY_FLOAT tickSize; - MY_FLOAT canLoss; - int subSample, subSampCount; + WaveLoop *sine_; + + StkFloat baseFrequency_; + StkFloat noiseGain_; + StkFloat fippleFreqMod_; + StkFloat fippleGainMod_; + StkFloat blowFreqMod_; + StkFloat tickSize_; + StkFloat canLoss_; + int subSample_, subSampCount_; }; #endif diff --git a/include/Wurley.h b/include/Wurley.h index 1b3ee97..b5a0e47 100644 --- a/include/Wurley.h +++ b/include/Wurley.h @@ -26,12 +26,12 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__WURLEY_H) -#define __WURLEY_H +#ifndef STK_WURLEY_H +#define STK_WURLEY_H #include "FM.h" @@ -39,19 +39,34 @@ class Wurley : public FM { public: //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ Wurley(); //! Class destructor. ~Wurley(); //! Set instrument parameters for a particular frequency. - void setFrequency(MY_FLOAT frequency); + void setFrequency(StkFloat frequency); //! Start a note with the given frequency and amplitude. - void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + void noteOn(StkFloat frequency, StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); }; #endif diff --git a/include/WvIn.h b/include/WvIn.h index ba3d3c3..d60e21d 100644 --- a/include/WvIn.h +++ b/include/WvIn.h @@ -10,44 +10,49 @@ subsequent output. Linear interpolation is used for fractional "read rates". - WvIn supports multi-channel data in interleaved - format. It is important to distinguish the - tick() methods, which return samples produced - by averaging across sample frames, from the - tickFrame() methods, which return pointers to - multi-channel sample frames. For single-channel - data, these methods return equivalent values. + WvIn supports multi-channel data in + interleaved format. It is important to + distinguish the tick() methods, which return + samples produced by averaging across sample + frames, from the tickFrame() methods, which + return pointers to multi-channel sample + frames. For single-channel data, these + methods return equivalent values. - Small files are completely read into local memory - during instantiation. Large files are read - incrementally from disk. The file size threshold - and the increment size values are defined in - WvIn.h. + Small files are completely read into local + memory during instantiation. Large files are + read incrementally from disk. The file size + threshold and the increment size values are + defined in WvIn.h. + + When the end of a file is reached, subsequent + calls to the tick() functions return the data + values at the end of the file. WvIn currently supports WAV, AIFF, SND (AU), MAT-file (Matlab), and STK RAW file formats. Signed integer (8-, 16-, and 32-bit) and floating- point (32- and 64-bit) data types are supported. - Uncompressed data types are not supported. If - using MAT-files, data should be saved in an array - with each data channel filling a matrix row. + Compressed data types are not supported. If using + MAT-files, data should be saved in an array with + each data channel filling a matrix row. The sample + rate for MAT-files is assumed to be 44100 Hz. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__WVIN_H) -#define __WVIN_H +#ifndef STK_WVIN_H +#define STK_WVIN_H // Files larger than CHUNK_THRESHOLD will be copied into memory // in CHUNK_SIZE increments, rather than completely loaded into // a buffer at once. -#define CHUNK_THRESHOLD 5000000 // 5 Mb -#define CHUNK_SIZE 1024 // sample frames +const unsigned long CHUNK_THRESHOLD = 5000000; // 5 Mb +const long CHUNK_SIZE = 1024; // sample frames #include "Stk.h" -#include class WvIn : public Stk { @@ -60,7 +65,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, bool doNormalize = TRUE ); + WvIn( std::string fileName, bool raw = false, bool doNormalize = true ); //! Class destructor. virtual ~WvIn(); @@ -70,7 +75,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, bool doNormalize = TRUE ); + void openFile( std::string fileName, bool raw = false, bool doNormalize = true ); //! If a file is open, close it. void closeFile(void); @@ -94,7 +99,7 @@ public: (\e peak/maximum). For incrementally loaded files with floating- point data types, direct scaling by \e peak is performed. */ - void normalize(MY_FLOAT peak); + void normalize(StkFloat peak); //! Return the file size in sample frames. unsigned long getSize(void) const; @@ -108,7 +113,7 @@ public: their headers. STK RAW files have a sample rate of 22050 Hz by definition. MAT-files are assumed to have a rate of 44100 Hz. */ - MY_FLOAT getFileRate(void) const; + StkFloat getFileRate(void) const; //! Query whether reading is complete. bool isFinished(void) const; @@ -117,10 +122,10 @@ public: /*! If the rate value is negative, the data is read in reverse order. */ - void setRate(MY_FLOAT aRate); + void setRate(StkFloat aRate); //! Increment the read pointer by \e aTime samples. - virtual void addTime(MY_FLOAT aTime); + virtual void addTime(StkFloat aTime); //! Turn linear interpolation on/off. /*! @@ -132,34 +137,53 @@ public: void setInterpolate(bool doInterpolate); //! Return the average across the last output sample frame. - virtual MY_FLOAT lastOut(void) const; + virtual StkFloat lastOut(void) const; //! Read out the average across one sample frame of data. /*! An StkError will be thrown if a file is read incrementally and a read error occurs. */ - virtual MY_FLOAT tick(void); + virtual StkFloat tick(void); //! Read out vectorSize averaged sample frames of data in \e vector. /*! An StkError will be thrown if a file is read incrementally and a read error occurs. */ - virtual MY_FLOAT *tick(MY_FLOAT *vector, unsigned int vectorSize); + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with averaged sample frames. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if a file + is read incrementally and a read error occurs or the \c channel + argument is zero or it is greater than the number of channels in + the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); //! Return a pointer to the last output sample frame. - virtual const MY_FLOAT *lastFrame(void) const; + virtual const StkFloat *lastFrame(void) const; //! Return a pointer to the next sample frame of data. /*! An StkError will be thrown if a file is read incrementally and a read error occurs. */ - virtual const MY_FLOAT *tickFrame(void); + virtual const StkFloat *tickFrame(void); //! Read out sample \e frames of data to \e frameVector. /*! An StkError will be thrown if a file is read incrementally and a read error occurs. */ - virtual MY_FLOAT *tickFrame(MY_FLOAT *frameVector, unsigned int frames); + virtual StkFloat *tickFrame(StkFloat *frameVector, unsigned int frames); + + //! Fill the StkFrames object with sample frames of data and return the same reference. + /*! + An StkError will be thrown if a file is read incrementally and + a read error occurs or if there is an incompatability between the + number of channels in the WvIn object and that in the StkFrames + object. + */ + virtual StkFrames& tickFrame( StkFrames& frames ); protected: @@ -184,24 +208,23 @@ protected: // Get MAT-file header information. bool getMatInfo( const char *fileName ); - char msg[256]; - FILE *fd; - MY_FLOAT *data; - MY_FLOAT *lastOutput; - bool chunking; - bool finished; - bool interpolate; - bool byteswap; - unsigned long fileSize; - unsigned long bufferSize; - unsigned long dataOffset; - unsigned int channels; - long chunkPointer; - STK_FORMAT dataType; - MY_FLOAT fileRate; - MY_FLOAT gain; - MY_FLOAT time; - MY_FLOAT rate; + FILE *fd_; + StkFloat *data_; + StkFloat *lastOutputs_; + bool chunking_; + bool finished_; + bool interpolate_; + bool byteswap_; + unsigned long fileSize_; + unsigned long bufferSize_; + unsigned long dataOffset_; + unsigned int channels_; + long chunkPointer_; + StkFormat dataType_; + StkFloat fileRate_; + StkFloat gain_; + StkFloat time_; + StkFloat rate_; }; -#endif // defined(__WVIN_H) +#endif diff --git a/include/WvOut.h b/include/WvOut.h index 407bc63..815e8cb 100644 --- a/include/WvOut.h +++ b/include/WvOut.h @@ -14,32 +14,31 @@ tickFrame() method, which takes a pointer to multi-channel sample frame data. - WvOut currently supports WAV, AIFF, AIFC, SND - (AU), MAT-file (Matlab), and STK RAW file - formats. Signed integer (8-, 16-, and 32-bit) - and floating- point (32- and 64-bit) data types - are supported. STK RAW files use 16-bit - integers by definition. MAT-files will always - be written as 64-bit floats. If a data type - specification does not match the specified file - type, the data type will automatically be - modified. Uncompressed data types are not - supported. + WvOut currently supports uncompressed WAV, + AIFF, AIFC, SND (AU), MAT-file (Matlab), and + STK RAW file formats. Signed integer (8-, + 16-, and 32-bit) and floating- point (32- and + 64-bit) data types are supported. STK RAW + files use 16-bit integers by definition. + MAT-files will always be written as 64-bit + floats. If a data type specification does not + match the specified file type, the data type + will automatically be modified. Compressed + data types are not supported. Currently, WvOut is non-interpolating and the output rate is always Stk::sampleRate(). - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__WVOUT_H) -#define __WVOUT_H +#ifndef STK_WVOUT_H +#define STK_WVOUT_H #include "Stk.h" -#include -#define BUFFER_SIZE 1024 // sample frames +const unsigned long BUFFER_SIZE = 1024; // sample frames class WvOut : public Stk { @@ -60,7 +59,7 @@ class WvOut : public Stk /*! An StkError is thrown for invalid argument values or if an error occurs when initializing the output file. */ - WvOut( const char *fileName, unsigned int nChannels = 1, FILE_TYPE type = WVOUT_WAV, Stk::STK_FORMAT format = STK_SINT16 ); + WvOut( const char *fileName, unsigned int nChannels = 1, FILE_TYPE type = WVOUT_WAV, Stk::StkFormat format = STK_SINT16 ); //! Class destructor. virtual ~WvOut(); @@ -70,7 +69,7 @@ class WvOut : public Stk An StkError is thrown for invalid argument values or if an error occurs when initializing the output file. */ void openFile( const char *fileName, unsigned int nChannels = 1, - WvOut::FILE_TYPE type = WVOUT_WAV, Stk::STK_FORMAT format = STK_SINT16 ); + WvOut::FILE_TYPE type = WVOUT_WAV, Stk::StkFormat format = STK_SINT16 ); //! If a file is open, write out samples in the queue and then close it. void closeFile( void ); @@ -79,25 +78,48 @@ class WvOut : public Stk unsigned long getFrames( void ) const; //! Return the number of seconds of data output. - MY_FLOAT getTime( void ) const; + StkFloat getTime( void ) const; + + //! Returns \c true if clipping has been detected during output since instantiation or the last reset. + bool getClipStatus( void ) { return clipping_; }; + + //! Reset the clipping status to \c false. + void resetClipStatus( void ) { clipping_ = false; }; //! Output a single sample to all channels in a sample frame. /*! An StkError is thrown if a file write error occurs. */ - virtual void tick(const MY_FLOAT sample); + virtual void tick(const StkFloat sample); //! Output each sample in \e vector to all channels in \e vectorSize sample frames. /*! An StkError is thrown if a file write error occurs. */ - virtual void tick(const MY_FLOAT *vector, unsigned int vectorSize); + virtual void tick( const StkFloat *vector, unsigned int vectorSize ); + + //! Output a channel of the StkFrames object to all channels of the WvOut object. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if a file + write error occurs or the \c channel argument is zero or it is + greater than the number of channels in the StkFrames object. + */ + virtual void tick( const StkFrames& frames, unsigned int channel = 1 ); //! Output the \e frameVector of sample frames of the given length. /*! An StkError is thrown if a file write error occurs. */ - virtual void tickFrame(const MY_FLOAT *frameVector, unsigned int frames = 1); + virtual void tickFrame( const StkFloat *frameVector, unsigned int frames = 1); + + //! Output the StkFrames data to the WvOut object. + /*! + An StkError will be thrown if a file write error occurs or if + there is an incompatability between the number of channels in the + WvOut object and that in the StkFrames object. + */ + virtual void tickFrame( const StkFrames& frames ); protected: @@ -107,6 +129,9 @@ class WvOut : public Stk // Write data to output file; virtual void writeData( unsigned long frames ); + // Check for sample clipping and clamp. + void clipTest( StkFloat& sample ); + // Write STK RAW file header. bool setRawFile( const char *fileName ); @@ -134,16 +159,16 @@ class WvOut : public Stk // Close MAT-file, updating the header. void closeMatFile( void ); - char msg[256]; - FILE *fd; - MY_FLOAT *data; - FILE_TYPE fileType; - STK_FORMAT dataType; - bool byteswap; - unsigned int channels; - unsigned long counter; - unsigned long totalCount; + FILE *fd_; + std::valarray data_; + FILE_TYPE fileType_; + StkFormat dataType_; + unsigned int channels_; + unsigned long counter_; + unsigned long totalCount_; + bool byteswap_; + bool clipping_; }; -#endif // defined(__WVOUT_H) +#endif diff --git a/projects/demo/Drums b/projects/demo/Drums new file mode 100755 index 0000000..31f2f5c --- /dev/null +++ b/projects/demo/Drums @@ -0,0 +1 @@ +wish < tcl/Drums.tcl | ./demo Drummer -or -ip \ No newline at end of file diff --git a/projects/demo/Makefile.in b/projects/demo/Makefile.in index c5fbcdd..ac5d4e5 100644 --- a/projects/demo/Makefile.in +++ b/projects/demo/Makefile.in @@ -6,14 +6,14 @@ SRC_PATH = ../../src OBJECT_PATH = @object_path@ vpath %.o $(OBJECT_PATH) -OBJECTS = Stk.o Noise.o SubNoise.o Envelope.o ADSR.o \ +OBJECTS = Stk.o Generator.o Noise.o SubNoise.o Envelope.o ADSR.o \ + Modulate.o SingWave.o \ WvIn.o WaveLoop.o WvOut.o \ Filter.o OneZero.o OnePole.o PoleZero.o TwoZero.o \ BiQuad.o FormSwep.o Delay.o DelayL.o DelayA.o \ - ReedTabl.o JetTabl.o BowTabl.o \ - Reverb.o PRCRev.o \ - Modulate.o SingWave.o Voicer.o \ - Vector3D.o Sphere.o \ + Function.o ReedTable.o JetTable.o BowTable.o \ + Effect.o PRCRev.o \ + Voicer.o Vector3D.o Sphere.o \ \ Instrmnt.o Clarinet.o BlowHole.o Saxofony.o Flute.o Brass.o BlowBotl.o \ Bowed.o Plucked.o StifKarp.o Sitar.o PluckTwo.o Mandolin.o Mesh2D.o \ @@ -21,7 +21,7 @@ OBJECTS = Stk.o Noise.o SubNoise.o Envelope.o ADSR.o \ Sampler.o Moog.o Simple.o Drummer.o Shakers.o \ Modal.o ModalBar.o BandedWG.o Resonate.o VoicForm.o Phonemes.o Whistle.o \ \ - Messager.o SKINI.o utilities.o + Messager.o Skini.o utilities.o INCLUDE = @include@ ifeq ($(strip $(INCLUDE)), ) @@ -39,9 +39,8 @@ LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) - OBJECTS += RtMidi.o RtAudio.o RtWvOut.o Thread.o Socket.o + OBJECTS += RtMidi.o RtAudio.o Thread.o Mutex.o Socket.o DEFS += @audio_apis@ - DEFS += @midiator@ endif RAWWAVES = @rawwaves@ @@ -58,8 +57,14 @@ all : $(PROGRAMS) demo: demo.cpp $(OBJECTS) $(CC) $(CFLAGS) $(DEFS) -o demo demo.cpp $(OBJECT_PATH)/*.o $(LIBRARY) -Md2Skini: Md2Skini.cpp Stk.o RtMidi.o Thread.o Socket.o - $(CC) $(CFLAGS) $(DEFS) -o Md2Skini Md2Skini.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/RtMidi.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Socket.o $(LIBRARY) +libdemo: demo.cpp + $(CC) $(CFLAGS) $(DEFS) -o demo utilities.cpp demo.cpp -L../../src $(LIBRARY) -lstk + +Md2Skini: Md2Skini.cpp Stk.o RtMidi.o + $(CC) $(CFLAGS) $(DEFS) -o Md2Skini Md2Skini.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/RtMidi.o $(LIBRARY) + +libMd2Skini: Md2Skini.cpp + $(CC) $(CFLAGS) $(DEFS) -o Md2Skini Md2Skini.cpp -L../../src $(LIBRARY) -lstk $(OBJECTS) : Stk.h diff --git a/projects/demo/Md2Skini.cpp b/projects/demo/Md2Skini.cpp index 147f561..f16c087 100644 --- a/projects/demo/Md2Skini.cpp +++ b/projects/demo/Md2Skini.cpp @@ -6,71 +6,171 @@ (via the RtMidi class), parses it, and turns it into SKINI messages. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "RtMidi.h" -#include "Thread.h" -#include "Socket.h" #include "SKINI.msg" #include -#include -#include - -// Exit thread declaration. -extern "C" THREAD_RETURN THREAD_TYPE stdinMonitor(void * ptr); +#include +#include void usage(void) { - printf("\nuseage: Md2Skini \n\n"); - printf(" With no arguments, Md2Skini converts MIDI input to SKINI\n"); - printf(" format and sends the output directly to stdout.\n"); - printf(" With flag = -s , the output is sent over a socket\n"); - printf(" connection (port 2001) to the optional hostname (default = localhost).\n"); - printf(" With flag = -f , the output stream is simultaneously\n"); - printf(" written to the file specified by the optional \n"); - printf(" (default = test.ski).\n\n"); + std::cout << "\nuseage: Md2Skini \n\n"; + std::cout << " With no arguments, Md2Skini converts MIDI input to SKINI\n"; + std::cout << " format and sends the output directly to stdout.\n"; + std::cout << " With flag = -f , the output stream is simultaneously\n"; + std::cout << " written to the file specified by the optional \n"; + std::cout << " (default = test.ski).\n"; + std::cout << " A MIDI input port can be specified with flag = -p portNumber.\n" << std::endl; exit(0); } -int main(int argc,char *argv[]) +void midiCallback( double deltatime, std::vector< unsigned char > *bytes, void *userData ) { - bool done = false, firstMessage = true, writeFile = false, useSocket = false; - FILE *file = NULL; - char fileName[256]; - char hostName[128]; - RtMidi *rtmidi = 0; - Socket *soket = 0; - Thread *thread = 0; + if ( bytes->size() < 2 ) return; - if ( argc>5 ) { - usage(); + // Parse the MIDI bytes ... only keep MIDI channel messages. + if ( bytes->at(0) > 239 ) return; + + register long type = bytes->at(0) & 0xF0; + register long channel = bytes->at(0) & 0x0F; + register long databyte1 = bytes->at(1); + register long databyte2 = 0; + if ( ( type != 0xC0 ) && ( type != 0xD0 ) ) { + if ( bytes->size() < 3 ) return; + databyte2 = bytes->at(2); } + std::string typeName; + switch( type ) { + case __SK_NoteOn_: + if ( databyte2 == 0 ) { + typeName = "NoteOff\t\t"; + databyte2 = 64; + } + else typeName = "NoteOn\t\t"; + break; + + case __SK_NoteOff_: + typeName = "NoteOff\t\t"; + break; + + case __SK_PolyPressure_: + typeName = "PolyPressure\t"; + break; + + case __SK_ProgramChange_: + typeName = "ProgramChange\t"; + break; + + case __SK_ChannelPressure_: + typeName = "ChannelPressure\t"; + break; + + case __SK_PitchBend_: + typeName = "PitchBend\t"; + break; + + case __SK_ControlChange_: + + switch( databyte1 ) { + case __SK_PitchChange_: + typeName = "PitchChange\t"; + break; + + case __SK_Volume_: + typeName = "Volume\t"; + break; + + case __SK_ModWheel_: + typeName = "ModWheel\t"; + break; + + case __SK_Breath_: + typeName = "Breath\t\t"; + break; + + case __SK_FootControl_: + typeName = "FootControl\t"; + break; + + case __SK_Portamento_: + typeName = "Portamento\t"; + break; + + case __SK_Balance_: + typeName = "Balance\t"; + break; + + case __SK_Pan_: + typeName = "Pan\t\t"; + break; + + case __SK_Sustain_: + typeName = "Sustain\t"; + break; + + case __SK_Expression_: + typeName = "Expression\t"; + break; + + default: + typeName = "ControlChange\t"; + break; + } + + default: + typeName = "Unknown\t"; + } + + FILE *file = (FILE *) userData; + if ( type == 0xC0 || type == 0xD0 || type == 0xE0 ) { // program change, channel pressure, or pitchbend + fprintf( stdout, "%s %.3f %d %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1 ); + if ( file != NULL ) + fprintf( file, "%s %.3f %d %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1 ); + } + else if ( type == 0xB0 ) { // control change + fprintf( stdout, "%s %.3f %d %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte2 ); + if ( file != NULL ) + fprintf( file, "%s %.3f %d %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte2 ); + } + else { // noteon, noteoff, aftertouch, and unknown + fprintf( stdout, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1, (float)databyte2 ); + if ( file != NULL ) + fprintf( file, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1, (float)databyte2 ); + } +} + +int main( int argc,char *argv[] ) +{ + FILE *file = NULL; + std::string fileName; + RtMidiIn *midiin = 0; + unsigned int port = 0; + + if ( argc > 5 ) usage(); + // Parse the command-line arguments. int i = 1; - while (i < argc) { + while ( i < argc ) { if (argv[i][0] == '-') { switch(argv[i][1]) { - case 's': - if ((i+1 < argc) && argv[i+1][0] != '-') { - i++; - strncpy(hostName, argv[i], 128); - } - else strcpy(hostName, "localhost"); - useSocket = true; - break; - case 'f': - if ((i+1 < argc) && argv[i+1][0] != '-') { + if ( (i+1 < argc) && argv[i+1][0] != '-' ) { i++; - strncpy(fileName, argv[i], 252); - if ( strstr(fileName,".ski") == NULL ) strcat(fileName, ".ski"); + fileName = argv[i]; + if ( fileName.find( ".ski" ) == std::string::npos ) fileName.append( ".ski" ); } - else strcpy(fileName, "test.ski"); - file = fopen(fileName,"wb"); - writeFile = true; + else fileName = "test.ski"; + file = fopen( fileName.c_str(), "wb" ); + break; + + case 'p': + if ( i++ >= argc) usage(); + port = (unsigned int) atoi( argv[i] ); break; default: @@ -82,234 +182,51 @@ int main(int argc,char *argv[]) i++; } - MY_FLOAT dt=0.0; try { - rtmidi = new RtMidi(); + midiin = new RtMidiIn(); } - catch (StkError &) { - exit(0); + catch (RtError &error) { + error.printMessage(); + if ( file != NULL ) fclose( file ); + exit(EXIT_FAILURE); } - // If using sockets, setup the client socket - if (useSocket) { - try { - soket = new Socket( 2001, hostName ); - } - catch (StkError &) { - exit(0); - } + // Check available ports vs. specified. + unsigned int nPorts = midiin->getPortCount(); + if ( nPorts == 0 ) { + std::cout << "No MIDI ports available!\n"; + goto cleanup; } - - // Start the "exit" thread. - thread = new Thread(); - if ( !thread->start( (THREAD_FUNCTION)&stdinMonitor, (void *) &done ) ) { - fprintf(stderr, "Unable to create exit thread ... aborting.\n"); + else if ( port >= nPorts ) { + std::cout << "Invalid port specifier!\n"; goto cleanup; } - // Write SKINI messages to buffer 's'. This is the easiest way to - // allow this single executable to work for both socketing and - // printf's to stdout. - char s[128]; - int channel, j; - MY_FLOAT byte2, byte3; - while ( !done ) { - if (rtmidi->nextMessage() > 0) { - byte3 = rtmidi->getByteThree(); - byte2 = rtmidi->getByteTwo(); - channel = rtmidi->getChannel(); - if (writeFile) dt = rtmidi->getDeltaTime(); - if (firstMessage) { // first MIDI message time stamp is meaningless - dt = 0.0; - firstMessage = false; - } - - switch(rtmidi->getType()) { - case __SK_NoteOn_: - if (byte3 < 1.0) { - sprintf(s,"NoteOff\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,64.0); - if (writeFile) { - fprintf(file,"NoteOff\t\t%.3f %d %.1f %.1f\n",dt,channel,byte2,64.0); - } - } else { - sprintf(s,"NoteOn\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,byte3); - if (writeFile) { - fprintf(file,"NoteOn\t\t%.3f %d %.1f %.1f\n",dt,channel,byte2,byte3); - } - } - break; - - case __SK_NoteOff_: - if (byte3 < 2.0) byte3 = 64.0; - sprintf(s,"NoteOff\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,byte3); - if (writeFile) { - fprintf(file,"NoteOff\t\t%.3f %d %.1f %.1f\n",dt,channel,byte2,byte3); - } - break; - - case __SK_PolyPressure_: - sprintf(s,"PolyPressure\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,byte3); - if (writeFile) { - fprintf(file,"PolyPressure\t%.3f %d %.1f %.1f\n",dt,channel,byte2,byte3); - } - break; - - 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) { - fprintf(file,"Volume\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_ModWheel_: - sprintf(s,"ModWheel\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"ModWheel\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_Breath_: - sprintf(s,"Breath\t\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"Breath\t\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_FootControl_: - sprintf(s,"FootControl\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"FootControl\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_Portamento_: - sprintf(s,"Portamento\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"Portamento\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_Balance_: - sprintf(s,"Balance\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"Balance\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_Pan_: - sprintf(s,"Pan\t\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"Pan\t\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_Sustain_: - sprintf(s,"Sustain\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"Sustain\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - case __SK_Expression_: - sprintf(s,"Expression\t%.3f %d %.1f\n",0.0,channel,byte3); - if (writeFile) { - fprintf(file,"Expression\t%.3f %d %.1f\n",dt,channel,byte3); - } - break; - default: - sprintf(s,"ControlChange\t%.3f %d %d %.1f\n",0.0,channel,j,byte3); - if (writeFile) { - fprintf(file,"ControlChange\t%.3f %d %d %.1f\n",dt,channel,j,byte3); - } - break; - } - break; - - case __SK_ProgramChange_: - j = (int) byte2; - sprintf(s,"ProgramChange\t%.3f %d %d\n",0.0,channel,j); - if (writeFile) { - fprintf(file,"ProgramChange\t%.3f %d %d\n",dt,channel,j); - } - break; - - case __SK_ChannelPressure_: - sprintf(s,"ChannelPressure\t%.3f %d %.1f\n",0.0,channel,byte2); - if (writeFile) { - fprintf(file,"ChannelPressure\t%.3f %d %.1f\n",dt,channel,byte2); - } - break; - - case __SK_PitchBend_: - sprintf(s,"PitchBend\t%.3f %d %f\n",0.0,channel,byte2); - if (writeFile) { - fprintf(file,"PitchBend\t%.3f %d %f\n",dt,channel,byte2); - } - break; - - default: - sprintf(s,"// Unknown\t%.3f %d %f %f\n",0.0,channel,byte2,byte3); - if (writeFile) { - fprintf(file,"// Unknown\t\t%.3f %d %f %f\n",dt,channel,byte2,byte3); - } - break; - } - - if (useSocket) { - if ( soket->writeBuffer( s, strlen(s), 0 ) < 0 ) { - fprintf(stderr,"Socket connection failed ... aborting.\n"); - goto cleanup; - } - } - else { - printf("%s", s); - fflush(stdout); - } - memset(s, 0, sizeof(s)); - } else { - // Sleep for 10 milliseconds - Stk::sleep( 10 ); - } + // Open the port. + try { + midiin->openPort( port ); + } + catch (RtError &error) { + error.printMessage(); + goto cleanup; } - sprintf(s, "Exiting Md2Skini process ... bye!\n"); - if (useSocket) - soket->writeBuffer( s, strlen(s), 0 ); - else { - printf("%s", s); - fflush(stdout); - } + // Set our callback function. This should be done immediately after + // opening the port to avoid having incoming messages written to the + // queue instead of sent to the callback function. + midiin->setCallback( &midiCallback, file ); - if (writeFile) { - printf("Wrote SKINI output to file %s.\n", fileName); - fclose(file); - } + // We'll ignore sysex, timing, and active sensing messages. + midiin->ignoreTypes( true, true, true ); + + std::cout << "\nReading MIDI input ... press to quit.\n"; + char input; + std::cin.get(input); cleanup: - done = true; - delete rtmidi; - delete soket; - delete thread; + delete midiin; + if ( file != NULL ) fclose( file ); - return 0; -} - -THREAD_RETURN THREAD_TYPE stdinMonitor(void * ptr) -{ - bool *done = (bool *) ptr; - char inputString[128]; - printf("Type 'Exit' to quit.\n"); - while ( !*done ) { - fgets(inputString, 128, stdin); - if (inputString[3] == 't' && inputString[1] == 'x' - && inputString[2] == 'i' && inputString[0] == 'E') { - *done = true; - } - else { - printf(inputString); - fflush(stdout); - } - } + std::cout << "Md2Skini finished ... bye!" << std::endl; return 0; } diff --git a/projects/demo/Md2Skini.dsp b/projects/demo/Md2Skini.dsp index 98867fa..3ab2734 100644 --- a/projects/demo/Md2Skini.dsp +++ b/projects/demo/Md2Skini.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /Od /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /Od /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -99,16 +99,8 @@ SOURCE=..\..\src\SKINI.cpp # End Source File # Begin Source File -SOURCE=..\..\src\Socket.cpp -# End Source File -# Begin Source File - SOURCE=..\..\src\Stk.cpp # End Source File -# Begin Source File - -SOURCE=..\..\src\Thread.cpp -# End Source File # End Group # Begin Group "Header Files" @@ -123,16 +115,8 @@ SOURCE=..\..\include\SKINI.h # End Source File # Begin Source File -SOURCE=..\..\include\Socket.h -# End Source File -# Begin Source File - SOURCE=..\..\include\Stk.h # End Source File -# Begin Source File - -SOURCE=..\..\include\Thread.h -# End Source File # End Group # Begin Group "Resource Files" diff --git a/projects/demo/Modal b/projects/demo/Modal new file mode 100755 index 0000000..474234b --- /dev/null +++ b/projects/demo/Modal @@ -0,0 +1 @@ +wish < tcl/Modal.tcl | ./demo ModalBar -or -ip \ No newline at end of file diff --git a/projects/demo/Modal.bat b/projects/demo/Modal.bat index 474234b..09d16b9 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 b/projects/demo/Physical new file mode 100755 index 0000000..f0e74fd --- /dev/null +++ b/projects/demo/Physical @@ -0,0 +1 @@ +wish < tcl/Physical.tcl | ./demo Clarinet -or -ip \ No newline at end of file diff --git a/projects/demo/Physical.bat b/projects/demo/Physical.bat index f0e74fd..ed11db8 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/Shakers b/projects/demo/Shakers new file mode 100755 index 0000000..7ac5fd2 --- /dev/null +++ b/projects/demo/Shakers @@ -0,0 +1 @@ +wish < tcl/Shakers.tcl | ./demo Shakers -or -ip \ No newline at end of file diff --git a/projects/demo/StkDemo b/projects/demo/StkDemo new file mode 100755 index 0000000..2670636 --- /dev/null +++ b/projects/demo/StkDemo @@ -0,0 +1 @@ +wish < tcl/Demo.tcl | ./demo Clarinet -or -ip diff --git a/projects/demo/StkDemo.bat b/projects/demo/StkDemo.bat index 2a6ee42..c0d025e 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 diff --git a/projects/demo/Voice b/projects/demo/Voice new file mode 100755 index 0000000..ecd3085 --- /dev/null +++ b/projects/demo/Voice @@ -0,0 +1 @@ +wish < tcl/Voice.tcl | ./demo FMVoices -or -ip \ No newline at end of file diff --git a/projects/demo/Voice.bat b/projects/demo/Voice.bat index ecd3085..9714b58 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 2a0d2c0..9ee9fbb 100644 --- a/projects/demo/demo.cpp +++ b/projects/demo/demo.cpp @@ -1,35 +1,193 @@ // demo.cpp // -// An example STK program for voice playback and control. +// An example STK program that allows voice playback and control of +// most of the STK instruments. #include "SKINI.msg" #include "WvOut.h" #include "Instrmnt.h" #include "PRCRev.h" #include "Voicer.h" +#include "Skini.h" + +#if defined(__STK_REALTIME__) + #include "RtAudio.h" + #include "Mutex.h" +#endif // Miscellaneous command-line parsing and instrument allocation // functions are defined in utilites.cpp ... specific to this program. #include "utilities.h" -#include #include -#include #include +#include +#if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0 + using std::min; +#endif bool done; static void finish(int ignore){ done = true; } -int main(int argc, char *argv[]) +// The TickData structure holds all the class instances and data that +// are shared by the various processing functions. +struct TickData { + WvOut **wvout; + Instrmnt **instrument; + Voicer *voicer; + Effect *reverb; + Messager messager; + Skini::Message message; + StkFloat volume; + StkFloat t60; + unsigned int nWvOuts; + int nVoices; + int currentVoice; + int channels; + int counter; + bool realtime; + bool settling; + bool haveMessage; + + // Default constructor. + TickData() + : wvout(0), instrument(0), voicer(0), reverb(0), volume(1.0), t60(1.0), + nWvOuts(0), nVoices(1), currentVoice(0), channels(2), counter(0), + realtime( false ), settling( false ), haveMessage( false ) {} +}; + +#define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks + +// The processMessage() function encapsulates the handling of control +// messages. It can be easily relocated within a program structure +// depending on the desired scheduling scheme. +void processMessage( TickData* data ) { - Instrmnt **instrument = 0; - WvOut **output = 0; - Messager *messager = 0; - Reverb *reverb = 0; - Voicer *voicer = 0; - int i, nVoices = 1; - MY_FLOAT volume = 1.0; - MY_FLOAT t60 = 1.0; // in seconds + register StkFloat value1 = data->message.floatValues[0]; + register StkFloat value2 = data->message.floatValues[1]; + + switch( data->message.type ) { + + case __SK_Exit_: + if ( data->settling == false ) goto settle; + done = true; + return; + + case __SK_NoteOn_: + if ( value2 == 0.0 ) // velocity is zero ... really a NoteOff + data->voicer->noteOff( value1, 64.0 ); + else // a NoteOn + data->voicer->noteOn( value1, value2 ); + break; + + case __SK_NoteOff_: + data->voicer->noteOff( value1, value2 ); + break; + + case __SK_ControlChange_: + if (value1 == 44.0) + data->reverb->setEffectMix(value2 * ONE_OVER_128); + else if (value1 == 7.0) + data->volume = value2 * ONE_OVER_128; + else if (value1 == 49.0) + data->voicer->setFrequency( value2 ); + else + data->voicer->controlChange( (int) value1, value2 ); + break; + + case __SK_AfterTouch_: + data->voicer->controlChange( 128, value1 ); + break; + + case __SK_PitchChange_: + data->voicer->setFrequency( value1 ); + break; + + case __SK_PitchBend_: + data->voicer->pitchBend( value1 ); + break; + + case __SK_Volume_: + data->volume = value1 * ONE_OVER_128; + break; + + case __SK_ProgramChange_: + if ( data->currentVoice == (int) value1 ) break; + + // Two-stage program change process. + if ( data->settling == false ) goto settle; + + // Stage 2: delete and reallocate new voice(s) + for ( int i=0; inVoices; i++ ) { + data->voicer->removeInstrument( data->instrument[i] ); + delete data->instrument[i]; + data->currentVoice = voiceByNumber( (int)value1, &data->instrument[i] ); + if ( data->currentVoice < 0 ) + data->currentVoice = voiceByNumber( 0, &data->instrument[i] ); + data->voicer->addInstrument( data->instrument[i] ); + data->settling = false; + } + + } // end of switch + + data->haveMessage = false; + return; + + settle: + // Exit and program change messages are preceeded with a short settling period. + data->voicer->silence(); + data->counter = (int) (0.3 * data->t60 * Stk::sampleRate()); + data->settling = true; +} + + +// The tick() function handles sample computation and scheduling of +// control updates. If doing realtime audio output, it will be called +// automatically when the system needs a new buffer of audio samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + TickData *data = (TickData *) dataPointer; + register StkFloat sample, *samples = (StkFloat *) buffer; + int counter, nTicks = bufferSize; + + while ( nTicks > 0 && !done ) { + + if ( !data->haveMessage ) { + data->messager.popMessage( data->message ); + if ( data->message.type > 0 ) { + data->counter = (long) (data->message.time * Stk::sampleRate()); + data->haveMessage = true; + } + else + data->counter = DELTA_CONTROL_TICKS; + } + + counter = min( nTicks, data->counter ); + data->counter -= counter; + for ( int i=0; ivolume * data->reverb->tick( data->voicer->tick() ); + for ( unsigned int j=0; jnWvOuts; j++ ) data->wvout[j]->tick(sample); + if ( data->realtime ) + for ( int k=0; kchannels; k++ ) *samples++ = sample; + nTicks--; + } + if ( nTicks == 0 ) break; + + // Process control messages. + if ( data->haveMessage ) processMessage( data ); + } + + return 0; +} + +int main( int argc, char *argv[] ) +{ + TickData data; + int i; + +#if defined(__STK_REALTIME__) + RtAudio *dac = 0; +#endif // If you want to change the default sample rate (set in Stk.h), do // it before instantiating any objects! If the sample rate is @@ -38,151 +196,110 @@ int main(int argc, char *argv[]) // Check the command-line arguments for errors and to determine // the number of WvOut objects to be instantiated (in utilities.cpp). - int nOutputs = checkArgs(argc, argv); - output = (WvOut **) calloc(nOutputs, sizeof(WvOut *)); + data.nWvOuts = checkArgs(argc, argv); + data.wvout = (WvOut **) calloc(data.nWvOuts, sizeof(WvOut *)); // Instantiate the instrument(s) type from the command-line argument // (in utilities.cpp). - nVoices = countVoices(argc, argv); - instrument = (Instrmnt **) calloc(nVoices, sizeof(Instrmnt *)); - int voice = voiceByName(argv[1], &instrument[0]); - if ( voice < 0 ) { - free( output ); - free( instrument ); + data.nVoices = countVoices(argc, argv); + data.instrument = (Instrmnt **) calloc(data.nVoices, sizeof(Instrmnt *)); + data.currentVoice = voiceByName(argv[1], &data.instrument[0]); + if ( data.currentVoice < 0 ) { + free( data.wvout ); + free( data.instrument ); usage(argv[0]); } // If there was no error allocating the first voice, we should be fine for more. - for ( i=1; iaddInstrument( instrument[i] ); + data.voicer = (Voicer *) new Voicer( data.nVoices ); + for ( i=0; iaddInstrument( data.instrument[i] ); // Parse the command-line flags, instantiate WvOut objects, and // instantiate the input message controller (in utilities.cpp). try { - parseArgs(argc, argv, output, &messager); + data.realtime = parseArgs(argc, argv, data.wvout, data.messager); } catch (StkError &) { goto cleanup; } - // Set the number of ticks between realtime messages (default = - // RT_BUFFER_SIZE). - messager->setRtDelta( 64 ); + // If realtime output, allocate the dac here. +#if defined(__STK_REALTIME__) + if ( data.realtime ) { + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + int bufferSize = RT_BUFFER_SIZE; + try { + dac = new RtAudio(0, data.channels, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); + } + catch (RtError& error) { + error.printMessage(); + goto cleanup; + } + } +#endif // Set the reverb parameters. - reverb = new PRCRev( t60 ); - reverb->setEffectMix(0.2); + data.reverb = new PRCRev( data.t60 ); + data.reverb->setEffectMix(0.2); // Install an interrupt handler function. (void) signal(SIGINT, finish); - // The runtime loop begins here: - done = FALSE; - int nTicks, type, j; - MY_FLOAT byte2, byte3, sample; - while (!done) { - - // Look for new messages and return a delta time (in samples). - type = messager->nextMessage(); - if (type < 0) - done = TRUE; - - nTicks = messager->getDelta(); - for ( i=0; itick( voicer->tick() ); - for ( j=0; jtick(sample); + // If realtime output, set our callback function and start the dac. +#if defined(__STK_REALTIME__) + if ( data.realtime ) { + try { + dac->setStreamCallback(&tick, (void *)&data); + dac->startStream(); } - - if ( type > 0 ) { - // Process the new control message. - byte2 = messager->getByteTwo(); - byte3 = messager->getByteThree(); - - switch(type) { - - case __SK_NoteOn_: - if (byte3 == 0.0) // velocity is zero ... really a NoteOff - voicer->noteOff( byte2, 64.0 ); - else // a NoteOn - voicer->noteOn( byte2, byte3 ); - break; - - case __SK_NoteOff_: - voicer->noteOff( byte2, byte3 ); - break; - - 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; - - case __SK_AfterTouch_: - voicer->controlChange( 128, byte2 ); - break; - - case __SK_PitchChange_: - voicer->setFrequency( byte2 ); - break; - - case __SK_PitchBend_: - voicer->pitchBend( byte2 ); - break; - - case __SK_Volume_: - volume = byte2 * ONE_OVER_128; - break; - - case __SK_ProgramChange_: - if ( voice != (int) byte2 ) { - voicer->silence(); - // Let the instrument(s) settle a bit. - for ( i=0; i<4096; i++ ) { - sample = reverb->tick( voicer->tick() ); - for ( j=0; jtick(sample); - } - for ( i=0; iremoveInstrument( instrument[i] ); - delete instrument[i]; - voice = voiceByNumber( (int)byte2, &instrument[i] ); - if ( voice < 0 ) - voice = voiceByNumber( 0, &instrument[i] ); - voicer->addInstrument( instrument[i] ); - } - } - } + catch (RtError &error) { + error.printMessage(); + goto cleanup; } } +#endif - // Let the reverb settle a bit. - nTicks = (long) (t60 * Stk::sampleRate()); - for ( i=0; itick( voicer->tick() ); - for ( j=0; jtick(sample); + // Setup finished. + while ( !done ) { +#if defined(__STK_REALTIME__) + if ( data.realtime ) + // Periodically check "done" status. + Stk::sleep( 200 ); + else +#endif + // Call the "tick" function to process data. + tick( NULL, 256, (void *)&data ); } + // Shut down the callback and output stream. +#if defined(__STK_REALTIME__) + try { + dac->cancelStreamCallback(); + dac->closeStream(); + } + catch (RtError& error) { + error.printMessage(); + } +#endif + cleanup: - for ( i=0; i900 } { + if { $patchnum<700 || ($patchnum>900 && $patchnum<2500) || $patchnum>=2600 } { puts $outID [format "AfterTouch 0.0 1 %3.2f" $value] flush $outID } diff --git a/projects/demo/utilities.cpp b/projects/demo/utilities.cpp index 80d7974..676613e 100644 --- a/projects/demo/utilities.cpp +++ b/projects/demo/utilities.cpp @@ -36,10 +36,6 @@ #include "Resonate.h" #include "Whistle.h" -#if defined(__STK_REALTIME__) - #include "RtWvOut.h" -#endif - #define NUM_INSTS 28 // The order of the following list is important. The location of a particular @@ -123,12 +119,13 @@ void usage(char *function) { printf(" -os for .snd audio output file,\n"); printf(" -om for .mat audio output file,\n"); printf(" -oa for .aif audio output file,\n"); + printf(" -if to read control input from SKINI file,\n"); #if defined(__STK_REALTIME__) printf(" -or for realtime audio output,\n"); printf(" -ip for realtime control input by pipe,\n"); printf(" (won't work under Win95/98),\n"); printf(" -is for realtime control input by socket,\n"); - printf(" -im for realtime control input by MIDI,\n"); + printf(" -im for realtime control input by MIDI (virtual port = 0, default = 1),\n"); #endif printf(" and Instrument = one of these:\n"); for (i=0;i 17) usage(args[0]); + if (numArgs < 3 || numArgs > 22) usage(args[0]); while (i < numArgs) { if (args[i][0] == '-') { if (args[i][1] == 'o') { - if ( (args[i][2] == 'r') || (args[i][2] == 's') || - (args[i][2] == 'w') || (args[i][2] == 'm') - || (args[i][2] == 'a') ) - numOutputs++; + if ( args[i][2] == 'r' ) realtime = true; + if ( (args[i][2] == 's') || (args[i][2] == 'w') || + (args[i][2] == 'm') || (args[i][2] == 'a') ) + nWvOuts++; flags[0][j] = 'o'; flags[1][j++] = args[i][2]; } else if (args[i][1] == 'i') { if ( (args[i][2] != 's') && (args[i][2] != 'p') && - (args[i][2] != 'm') ) usage(args[0]); + (args[i][2] != 'm') && (args[i][2] != 'f') ) usage(args[0]); flags[0][j] = 'i'; flags[1][j++] = args[i][2]; } @@ -196,9 +194,9 @@ int checkArgs(int numArgs, char *args[]) } // Make sure we have at least one output type - if (numOutputs < 1) usage(args[0]); + if ( nWvOuts < 1 && !realtime ) usage(args[0]); - return numOutputs; + return nWvOuts; } int countVoices(int nArgs, char *args[]) @@ -218,20 +216,24 @@ int countVoices(int nArgs, char *args[]) return nInstruments; } -void parseArgs(int numArgs, char *args[], WvOut **output, Messager **messager) +bool parseArgs(int numArgs, char *args[], WvOut **output, Messager& messager) { int i = 2, j = 0; - int inputMask = 0; - int port = -1; + bool realtime = false; char fileName[256]; while (i < numArgs) { if ( (args[i][0] == '-') && (args[i][1] == 'i') ) { switch(args[i][2]) { + case 'f': + strcpy(fileName,args[++i]); + if ( !messager.setScoreFile( fileName ) ) usage(args[0]); + break; + case 'p': #if defined(__STK_REALTIME__) - inputMask |= STK_PIPE; + if ( !messager.startStdInput() ) usage(args[0]); break; #else usage(args[0]); @@ -239,10 +241,12 @@ void parseArgs(int numArgs, char *args[], WvOut **output, Messager **messager) case 's': #if defined(__STK_REALTIME__) - inputMask |= STK_SOCKET; // Check for an optional socket port argument. - if ((i+1 < numArgs) && args[i+1][0] != '-') - port = atoi(args[++i]); + if ((i+1 < numArgs) && args[i+1][0] != '-') { + int port = atoi(args[++i]); + if ( !messager.startSocketInput( port ) ) usage(args[0]); + } + else if ( !messager.startSocketInput() ) usage(args[0]); break; #else usage(args[0]); @@ -250,7 +254,12 @@ void parseArgs(int numArgs, char *args[], WvOut **output, Messager **messager) case 'm': #if defined(__STK_REALTIME__) - inputMask |= STK_MIDI; + // Check for an optional MIDI port argument. + if ((i+1 < numArgs) && args[i+1][0] != '-') { + int port = atoi(args[++i]); + if ( !messager.startMidiInput( port-1 ) ) usage(args[0]); + } + else if ( !messager.startMidiInput() ) usage(args[0]); break; #else usage(args[0]); @@ -266,8 +275,7 @@ void parseArgs(int numArgs, char *args[], WvOut **output, Messager **messager) case 'r': #if defined(__STK_REALTIME__) - output[j] = (WvOut *) new RtWvOut(2); - j++; + realtime = true; break; #else usage(args[0]); @@ -321,10 +329,5 @@ void parseArgs(int numArgs, char *args[], WvOut **output, Messager **messager) i++; } - // Instantiate the messager. - if ( inputMask & STK_SOCKET && port >= 0 ) - *messager = new Messager( inputMask, port ); - else - *messager = new Messager( inputMask ); - + return realtime; } diff --git a/projects/demo/utilities.h b/projects/demo/utilities.h index dabe4db..d868494 100644 --- a/projects/demo/utilities.h +++ b/projects/demo/utilities.h @@ -16,4 +16,4 @@ int checkArgs(int numArgs, char *args[]); int countVoices(int nArgs, char *args[]); -void parseArgs(int numArgs, char *args[], WvOut **output, Messager **messager); +bool parseArgs(int numArgs, char *args[], WvOut **output, Messager& messager); diff --git a/projects/effects/Effects b/projects/effects/Effects new file mode 100755 index 0000000..f67da04 --- /dev/null +++ b/projects/effects/Effects @@ -0,0 +1 @@ +wish < tcl/Effects.tcl | ./effects -ip diff --git a/projects/effects/Effects.bat b/projects/effects/Effects.bat index f67da04..8d3a494 100755 --- a/projects/effects/Effects.bat +++ b/projects/effects/Effects.bat @@ -1 +1 @@ -wish < tcl/Effects.tcl | ./effects -ip +wish < tcl/Effects.tcl | effects -ip diff --git a/projects/effects/Makefile.in b/projects/effects/Makefile.in index d09c6f4..25ec474 100644 --- a/projects/effects/Makefile.in +++ b/projects/effects/Makefile.in @@ -6,11 +6,11 @@ SRC_PATH = ../../src OBJECT_PATH = @object_path@ vpath %.o $(OBJECT_PATH) -OBJECTS = Stk.o Reverb.o PRCRev.o JCRev.o \ - NRev.o Delay.o Filter.o \ - SKINI.o Envelope.o Echo.o \ - PitShift.o DelayL.o Chorus.o \ - WvIn.o WaveLoop.o Messager.o +OBJECTS = Stk.o Generator.o Envelope.o \ + Filter.o Delay.o DelayL.o \ + Effect.o Echo.o PitShift.o Chorus.o \ + PRCRev.o JCRev.o NRev.o \ + WvIn.o WaveLoop.o Skini.o Messager.o INCLUDE = @include@ ifeq ($(strip $(INCLUDE)), ) @@ -28,9 +28,8 @@ LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) - OBJECTS += RtMidi.o RtAudio.o RtDuplex.o Thread.o Socket.o + OBJECTS += RtMidi.o RtAudio.o Thread.o Mutex.o Socket.o DEFS += @audio_apis@ - DEFS += @midiator@ endif RAWWAVES = @rawwaves@ @@ -47,6 +46,9 @@ all : $(PROGRAMS) effects: effects.cpp $(OBJECTS) $(CC) $(CFLAGS) $(DEFS) -o effects effects.cpp $(OBJECT_PATH)/*.o $(LIBRARY) +libeffects: effects.cpp + $(CC) $(CFLAGS) $(DEFS) -o effects effects.cpp -L../../src $(LIBRARY) -lstk + $(OBJECTS) : Stk.h clean : diff --git a/projects/effects/effects.cpp b/projects/effects/effects.cpp index 630948f..310678f 100644 --- a/projects/effects/effects.cpp +++ b/projects/effects/effects.cpp @@ -1,7 +1,6 @@ /************** Effects Program *********************/ -#include "RtDuplex.h" -#include "SKINI.h" +#include "Skini.h" #include "SKINI.msg" #include "Envelope.h" #include "PRCRev.h" @@ -10,183 +9,256 @@ #include "Echo.h" #include "PitShift.h" #include "Chorus.h" - -// The input control handler. #include "Messager.h" +#include "RtAudio.h" + +#include +#include +#include +#if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0 + using std::min; +#endif void usage(void) { - /* Error function in case of incorrect command-line argument specifications */ - printf("\nuseage: effects flags \n"); - printf(" where flag = -s RATE to specify a sample rate,\n"); - printf(" flag = -ip for realtime SKINI input by pipe\n"); - printf(" (won't work under Win95/98),\n"); - printf(" and flag = -is for realtime SKINI input by socket.\n"); + // Error function in case of incorrect command-line argument specifications + std::cout << "\nuseage: effects flags \n"; + std::cout << " where flag = -s RATE to specify a sample rate,\n"; + std::cout << " flag = -ip for realtime SKINI input by pipe\n"; + std::cout << " (won't work under Win95/98),\n"; + std::cout << " and flag = -is for realtime SKINI input by socket.\n"; exit(0); } -int main(int argc,char *argv[]) +bool done; +static void finish(int ignore){ done = true; } + +// The TickData structure holds all the class instances and data that +// are shared by the various processing functions. +struct TickData { + Effect *effect; + PRCRev prcrev; + JCRev jcrev; + NRev nrev; + Echo echo; + PitShift shifter; + Chorus chorus; + Envelope envelope; + Messager messager; + Skini::Message message; + StkFloat lastSample; + StkFloat t60; + int counter; + bool settling; + bool haveMessage; + + // Default constructor. + TickData() + : effect(0), t60(1.0), counter(0), + settling( false ), haveMessage( false ) {} +}; + +#define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks + +// The processMessage() function encapsulates the handling of control +// messages. It can be easily relocated within a program structure +// depending on the desired scheduling scheme. +void processMessage( TickData* data ) { + register unsigned int value1 = data->message.intValues[0]; + register StkFloat value2 = data->message.floatValues[1]; + register StkFloat temp = value2 * ONE_OVER_128; + + switch( data->message.type ) { + + case __SK_Exit_: + if ( data->settling == false ) goto settle; + done = true; + return; + + case __SK_NoteOn_: + if ( value2 == 0.0 ) // velocity is zero ... really a NoteOff + data->envelope.setTarget( 0.0 ); + else // a NoteOn + data->envelope.setTarget( 1.0 ); + break; + + case __SK_NoteOff_: + data->envelope.setTarget( 0.0 ); + break; + + case __SK_ControlChange_: + // Change all effect values so they are "synched" to the interface. + switch ( value1 ) { + + case 20: { // effect type change + int type = data->message.intValues[1]; + if ( type == 0 ) + data->effect = &(data->echo); + else if ( type == 1 ) + data->effect = &(data->shifter); + else if ( type == 2 ) + data->effect = &(data->chorus); + else if ( type == 3 ) + data->effect = &(data->prcrev); + else if ( type == 4 ) + data->effect = &(data->jcrev); + else if ( type == 5 ) + data->effect = &(data->nrev); + break; + } + + case 22: // effect parameter change 1 + data->echo.setDelay( (unsigned long) (temp * Stk::sampleRate() * 0.95) ); + // data->shifter.setShift( temp * 3 + 0.25); + data->shifter.setShift( 1.4 * temp + 0.3); + data->chorus.setModFrequency( temp ); + data->prcrev.setT60( temp * 10.0 ); + data->jcrev.setT60( temp * 10.0 ); + data->nrev.setT60( temp * 10.0 ); + break; + + case 23: // effect parameter change 2 + data->chorus.setModDepth( temp * 0.2 ); + break; + + case 44: // effect mix + data->echo.setEffectMix( temp ); + data->shifter.setEffectMix( temp ); + data->chorus.setEffectMix( temp ); + data->prcrev.setEffectMix( temp ); + data->jcrev.setEffectMix( temp ); + data->nrev.setEffectMix( temp ); + break; + + default: + break; + } + + } // end of type switch + + data->haveMessage = false; + return; + + settle: + // Exit and program change messages are preceeded with a short settling period. + data->envelope.setTarget( 0.0 ); + data->counter = (int) (0.3 * data->t60 * Stk::sampleRate()); + data->settling = true; +} + +// The tick() function handles sample computation and scheduling of +// control updates. It will be called automatically by RtAudio when +// the system needs a new buffer of audio samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + TickData *data = (TickData *) dataPointer; + register StkFloat sample, *samples = (StkFloat *) buffer; + int i, counter, nTicks = bufferSize; + + while ( nTicks > 0 && !done ) { + + if ( !data->haveMessage ) { + data->messager.popMessage( data->message ); + if ( data->message.type > 0 ) { + data->counter = (long) (data->message.time * Stk::sampleRate()); + data->haveMessage = true; + } + else + data->counter = DELTA_CONTROL_TICKS; + } + + counter = min( nTicks, data->counter ); + data->counter -= counter; + for ( i=0; ienvelope.tick() * data->effect->tick( *samples ); + *samples++ = sample; // two channels interleaved + *samples++ = sample; + nTicks--; + } + if ( nTicks == 0 ) break; + + // Process control messages. + if ( data->haveMessage ) processMessage( data ); + } + + return 0; +} + + +int main( int argc, char *argv[] ) +{ + TickData data; + RtAudio *adac = 0; + int i; + if (argc < 2 || argc > 6) usage(); // 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 ); - int port = -1; - int controlMask = 0; - for (int k=1; k= 0 ) - messager = new Messager( controlMask, port ); - else - messager = new Messager( controlMask ); + adac = new RtAudio(0, 2, 0, 2, format, (int)Stk::sampleRate(), &bufferSize, 4); } - catch (StkError &) { + catch (RtError& error) { + error.printMessage(); goto cleanup; } - // The runtime loop begins here: - long i, nTicks; - int type; - lastSample = 0.0; - inSample = 0.0; - MY_FLOAT byte2, byte3; - done = FALSE; - while (!done) { + data.envelope.setRate( 0.001 ); + data.effect = &(data.echo); - // Look for new messages and return a delta time (in samples). - type = messager->nextMessage(); - if (type < 0) - done = TRUE; + // Install an interrupt handler function. + (void) signal( SIGINT, finish ); - nTicks = messager->getDelta(); - - for (i=0; itick(envelope->tick() * echo->tick(lastSample)); - else if (effect == 1) - inSample = inout->tick(envelope->tick() * shifter->tick(lastSample)); - else if (effect == 2) - inSample = inout->tick(envelope->tick() * chorus->tick(lastSample)); - else if (effect == 3) - inSample = inout->tick(envelope->tick() * prcrev->tick(lastSample)); - else if (effect == 4) - inSample = inout->tick(envelope->tick() * jcrev->tick(lastSample)); - else if (effect == 5) - inSample = inout->tick(envelope->tick() * nrev->tick(lastSample)); - lastSample = inSample; - } - - if (type > 0) { - // parse the input control message - - byte2 = messager->getByteTwo(); - byte3 = messager->getByteThree(); - - switch(type) { - - case __SK_NoteOn_: - if (byte3 == 0) { // velocity is zero ... really a NoteOff - envelope->setRate(0.001); - envelope->setTarget(0.0); - } - else { // really a NoteOn - envelope->setRate(0.001); - envelope->setTarget(1.0); - } - break; - - case __SK_NoteOff_: - envelope->setRate(0.001); - envelope->setTarget(0.0); - break; - - case __SK_ControlChange_: - if (byte2 == 20) effect = (int) byte3; // effect change - else if (byte2 == 44) { // effects mix - echo->setEffectMix(byte3*ONE_OVER_128); - shifter->setEffectMix(byte3*ONE_OVER_128); - chorus->setEffectMix(byte3*ONE_OVER_128); - prcrev->setEffectMix(byte3*ONE_OVER_128); - jcrev->setEffectMix(byte3*ONE_OVER_128); - nrev->setEffectMix(byte3*ONE_OVER_128); - } - else if (byte2 == 22) { // effect1 parameter change - echo->setDelay(byte3*ONE_OVER_128*Stk::sampleRate()*0.95); - shifter->setShift(byte3*ONE_OVER_128*3 + 0.25); - chorus->setModFrequency(byte3*ONE_OVER_128); - } - else if (byte2 == 23) { // effect1 parameter change - chorus->setModDepth(byte3*ONE_OVER_128*0.2); - } - break; - } - } + // If realtime output, set our callback function and start the dac. + try { + adac->setStreamCallback( &tick, (void *)&data ); + adac->startStream(); + } + catch (RtError &error) { + error.printMessage(); + goto cleanup; } - envelope->setRate(0.001); - envelope->setTarget(0.0); - nTicks = (long) Stk::sampleRate(); - for (i=0; itick(envelope->tick() * echo->tick(lastSample)); - else if (effect == 1) - inSample = inout->tick(envelope->tick() * shifter->tick(lastSample)); - else if (effect == 2) - inSample = inout->tick(envelope->tick() * chorus->tick(lastSample)); - else if (effect == 3) - inSample = inout->tick(envelope->tick() * prcrev->tick(lastSample)); - else if (effect == 4) - inSample = inout->tick(envelope->tick() * jcrev->tick(lastSample)); - else if (effect == 5) - inSample = inout->tick(envelope->tick() * nrev->tick(lastSample)); - lastSample = inSample; + // Setup finished. + while ( !done ) { + // Periodically check "done" status. + Stk::sleep( 50 ); } - delete echo; - delete shifter; - delete chorus; - delete prcrev; - delete jcrev; - delete nrev; - delete score; - delete envelope; + // Shut down the callback and output stream. + try { + adac->cancelStreamCallback(); + adac->closeStream(); + } + catch (RtError& error) { + error.printMessage(); + } cleanup: - delete messager; - delete inout; - printf("effects finished ... goodbye.\n"); + delete adac; + + std::cout << "\neffects finished ... goodbye.\n\n"; return 0; } diff --git a/projects/effects/effects.dsp b/projects/effects/effects.dsp index 7d6e71d..56a70d8 100644 --- a/projects/effects/effects.dsp +++ b/projects/effects/effects.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -67,7 +67,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -118,6 +118,14 @@ SOURCE=..\..\include\Echo.h # End Source File # Begin Source File +SOURCE=..\..\src\Effect.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\Effect.h +# End Source File +# Begin Source File + SOURCE=.\effects.cpp # End Source File # Begin Source File @@ -138,6 +146,14 @@ SOURCE=..\..\include\Filter.h # End Source File # Begin Source File +SOURCE=..\..\src\Generator.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\Generator.h +# End Source File +# Begin Source File + SOURCE=..\..\src\JCRev.cpp # End Source File # Begin Source File @@ -154,6 +170,14 @@ SOURCE=..\..\include\Messager.h # End Source File # Begin Source File +SOURCE=..\..\src\Mutex.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\Mutex.h +# End Source File +# Begin Source File + SOURCE=..\..\src\NRev.cpp # End Source File # Begin Source File @@ -178,14 +202,6 @@ SOURCE=..\..\include\PRCRev.h # End Source File # Begin Source File -SOURCE=..\..\src\Reverb.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\include\Reverb.h -# End Source File -# Begin Source File - SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File @@ -194,14 +210,6 @@ SOURCE=..\..\include\RtAudio.h # End Source File # Begin Source File -SOURCE=..\..\src\RtDuplex.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\include\RtDuplex.h -# End Source File -# Begin Source File - SOURCE=..\..\src\RtMidi.cpp # End Source File # Begin Source File diff --git a/projects/effects/tcl/Effects.tcl b/projects/effects/tcl/Effects.tcl index 363924a..f8cb927 100644 --- a/projects/effects/tcl/Effects.tcl +++ b/projects/effects/tcl/Effects.tcl @@ -26,7 +26,7 @@ label .title -text "STK Effects Controller" \ -font {Times 14 bold} -background white \ -foreground darkred -relief raised -label .title2 -text "by Gary P. Scavone\n Center for Computer Research in Music & Acoustics (CCRMA) \n Stanford University" \ +label .title2 -text "by Gary P. Scavone\n Music Technology, McGill University" \ -font {Times 12 bold} -background white \ -foreground darkred -relief raised @@ -50,7 +50,7 @@ frame .left -bg black scale .left.effectsmix -from 0 -to 127 -length 400 \ -command {printWhatz "ControlChange 0.0 1 " 44} \ --orient horizontal -label "Effects Mix" \ +-orient horizontal -label "Effects Mix (0% effect - 100% effect)" \ -tickinterval 32 -showvalue true -bg grey66 \ -variable mixlevel @@ -132,7 +132,7 @@ proc changeEffect {tag value1 value2 } { .left.effect2 config -state disabled -label "Disabled" } if ($value2==1) { - .left.effect1 config -state normal -label "Pitch Shift Amount" + .left.effect1 config -state normal -label "Pitch Shift Amount (center = no shift)" .left.effect2 config -state disabled -label "Disabled" } if ($value2==2) { @@ -140,7 +140,7 @@ proc changeEffect {tag value1 value2 } { .left.effect2 config -state normal -label "Chorus Modulation Depth" } if {$value2>=3 && $value2<=5} { - .left.effect1 config -state disabled -label "Disabled" + .left.effect1 config -state normal -label "T60 Decay Time ( 0 - 10 seconds)" .left.effect2 config -state disabled -label "Disabled" } puts $outID [format "%s %i %f" $tag $value1 $value2] diff --git a/projects/examples/Makefile.in b/projects/examples/Makefile.in index 7c04e0b..c242d66 100644 --- a/projects/examples/Makefile.in +++ b/projects/examples/Makefile.in @@ -1,6 +1,6 @@ ### STK examples Makefile - for various flavors of unix -PROGRAMS = sine play record io tcpIn tcpOut sineosc rtsine bethree controlbee foursine threebees +PROGRAMS = sine play record io tcpIn tcpOut sineosc rtsine crtsine bethree controlbee foursine threebees playsmf RM = /bin/rm SRC_PATH = ../../src OBJECT_PATH = @object_path@ @@ -23,7 +23,6 @@ LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) DEFS += @audio_apis@ - DEFS += @midiator@ endif RAWWAVES = @rawwaves@ @@ -46,8 +45,8 @@ clean : strip : strip $(PROGRAMS) -play: play.cpp Stk.o WvIn.o WvOut.o RtWvOut.o RtAudio.o - $(CC) $(CFLAGS) $(DEFS) -o play play.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +play: play.cpp Stk.o WvIn.o RtAudio.o + $(CC) $(CFLAGS) $(DEFS) -o play play.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) record: record.cpp Stk.o WvIn.o WvOut.o RtWvIn.o RtAudio.o $(CC) $(CFLAGS) $(DEFS) -o record record.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvIn.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) @@ -58,11 +57,11 @@ sine: sine.cpp Stk.o WvIn.o WvOut.o WaveLoop.o io: io.cpp Stk.o RtAudio.o RtDuplex.o $(CC) $(CFLAGS) $(DEFS) -o io io.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/RtDuplex.o $(LIBRARY) -tcpIn: tcpIn.cpp Stk.o WvIn.o TcpWvIn.o WvOut.o RtWvOut.o RtAudio.o Socket.o Thread.o - $(CC) $(CFLAGS) $(DEFS) -o tcpIn tcpIn.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/TcpWvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +tcpIn: tcpIn.cpp Stk.o WvIn.o TcpWvIn.o WvOut.o RtWvOut.o RtAudio.o Socket.o Thread.o Mutex.o + $(CC) $(CFLAGS) $(DEFS) -o tcpIn tcpIn.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/TcpWvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -tcpOut: tcpOut.cpp Stk.o WvIn.o WvOut.o TcpWvOut.o Socket.o Thread.o - $(CC) $(CFLAGS) $(DEFS) -o tcpOut tcpOut.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/TcpWvOut.o $(LIBRARY) +tcpOut: tcpOut.cpp Stk.o WvIn.o WvOut.o TcpWvOut.o Socket.o Thread.o Mutex.o + $(CC) $(CFLAGS) $(DEFS) -o tcpOut tcpOut.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/TcpWvOut.o $(LIBRARY) sineosc: sineosc.cpp Stk.o WvIn.o WvOut.o WaveLoop.o $(CC) $(CFLAGS) $(DEFS) -o sineosc sineosc.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/WaveLoop.o $(LIBRARY) @@ -70,14 +69,20 @@ sineosc: sineosc.cpp Stk.o WvIn.o WvOut.o WaveLoop.o rtsine: rtsine.cpp Stk.o WvIn.o WaveLoop.o WvOut.o RtWvOut.o RtAudio.o $(CC) $(CFLAGS) $(DEFS) -o rtsine rtsine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -bethree: bethree.cpp Stk.o WvIn.o WaveLoop.o FM.o WvOut.o RtWvOut.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Envelope.o ADSR.o BeeThree.o - $(CC) $(CFLAGS) $(DEFS) -o bethree bethree.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/Instrmnt.o $(OBJECT_PATH)/Filter.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/Envelope.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(LIBRARY) +crtsine: crtsine.cpp Stk.o WvIn.o WaveLoop.o RtAudio.o + $(CC) $(CFLAGS) $(DEFS) -o crtsine crtsine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -controlbee: controlbee.cpp Stk.o WvIn.o WaveLoop.o FM.o WvOut.o RtWvOut.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Envelope.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o Thread.o SKINI.o - $(CC) $(CFLAGS) $(DEFS) -o controlbee controlbee.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/Instrmnt.o $(OBJECT_PATH)/Filter.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/Envelope.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(OBJECT_PATH)/Messager.o $(OBJECT_PATH)/RtMidi.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/SKINI.o $(LIBRARY) +bethree: bethree.cpp Stk.o WvIn.o WaveLoop.o FM.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Generator.o Envelope.o ADSR.o BeeThree.o + $(CC) $(CFLAGS) $(DEFS) -o bethree bethree.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/Instrmnt.o $(OBJECT_PATH)/Filter.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/Envelope.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(LIBRARY) + +controlbee: controlbee.cpp Stk.o WvIn.o WaveLoop.o FM.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Generator.o Envelope.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o Thread.o Mutex.o Skini.o + $(CC) $(CFLAGS) $(DEFS) -o controlbee controlbee.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/Instrmnt.o $(OBJECT_PATH)/Filter.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/Envelope.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(OBJECT_PATH)/Messager.o $(OBJECT_PATH)/RtMidi.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/Skini.o $(LIBRARY) foursine: foursine.cpp Stk.o WvIn.o WvOut.o WaveLoop.o $(CC) $(CFLAGS) $(DEFS) -o foursine foursine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/WaveLoop.o $(LIBRARY) -threebees: threebees.cpp Stk.o WvIn.o WaveLoop.o FM.o WvOut.o RtWvOut.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Envelope.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o Thread.o SKINI.o Voicer.o - $(CC) $(CFLAGS) $(DEFS) -o threebees threebees.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/Instrmnt.o $(OBJECT_PATH)/Filter.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/Envelope.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(OBJECT_PATH)/Messager.o $(OBJECT_PATH)/RtMidi.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/SKINI.o $(OBJECT_PATH)/Voicer.o $(LIBRARY) +threebees: threebees.cpp Stk.o WvIn.o WaveLoop.o FM.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Generator.o Envelope.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o Thread.o Mutex.o Skini.o Voicer.o + $(CC) $(CFLAGS) $(DEFS) -o threebees threebees.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WaveLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/Instrmnt.o $(OBJECT_PATH)/Filter.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/Envelope.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(OBJECT_PATH)/Messager.o $(OBJECT_PATH)/RtMidi.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/Skini.o $(OBJECT_PATH)/Voicer.o $(LIBRARY) + +playsmf: playsmf.cpp Stk.o MidiFileIn.o RtMidi.o + $(CC) $(CFLAGS) $(DEFS) -o playsmf playsmf.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/MidiFileIn.o $(OBJECT_PATH)/RtMidi.o $(LIBRARY) diff --git a/projects/examples/bethree.cpp b/projects/examples/bethree.cpp index b563084..10db363 100644 --- a/projects/examples/bethree.cpp +++ b/projects/examples/bethree.cpp @@ -1,7 +1,43 @@ // bethree.cpp STK tutorial program #include "BeeThree.h" -#include "RtWvOut.h" +#include "RtAudio.h" + +// The TickData structure holds all the class instances and data that +// are shared by the various processing functions. +struct TickData { + Instrmnt *instrument; + StkFloat frequency; + StkFloat scaler; + long counter; + bool done; + + // Default constructor. + TickData() + : instrument(0), scaler(1.0), counter(0), done( false ) {} +}; + +// This tick() function handles sample computation only. It will be +// called automatically when the system needs a new buffer of audio +// samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + TickData *data = (TickData *) dataPointer; + register StkFloat *samples = (StkFloat *) buffer; + + for ( int i=0; iinstrument->tick(); + if ( ++data->counter % 2000 == 0 ) { + data->scaler += 0.025; + data->instrument->setFrequency( data->frequency * data->scaler ); + } + } + + if ( data->counter > 80000 ) + data->done = true; + + return 0; +} int main() { @@ -9,50 +45,56 @@ int main() Stk::setSampleRate( 44100.0 ); Stk::setRawwavePath( "../../rawwaves/" ); - Instrmnt *instrument = 0; - RtWvOut *output = 0; - MY_FLOAT frequency, amplitude, scaler; - long counter, i; + TickData data; + RtAudio *dac = 0; + + // Figure out how many bytes in an StkFloat and setup the RtAudio object. + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + int bufferSize = RT_BUFFER_SIZE; + try { + dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); + } + catch (RtError& error) { + error.printMessage(); + goto cleanup; + } try { // Define and load the BeeThree instrument - instrument = new BeeThree(); - - // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); + data.instrument = new BeeThree(); } catch (StkError &) { goto cleanup; } - scaler = 1.0; - frequency = 220.0; - amplitude = 0.5; - instrument->noteOn( frequency, amplitude ); + data.frequency = 220.0; + data.instrument->noteOn( data.frequency, 0.5 ); - // Play the instrument for 80000 samples, changing the frequency every 2000 samples - counter = 0; - while ( counter < 80000 ) { - for ( i=0; i<2000; i++ ) { - try { - output->tick( instrument->tick() ); - } - catch (StkError &) { - goto cleanup; - } - } - - counter += 2000; - scaler += 0.025; - instrument->setFrequency( frequency * scaler ); + try { + dac->setStreamCallback(&tick, (void *)&data); + dac->startStream(); + } + catch (RtError &error) { + error.printMessage(); + goto cleanup; } - // Turn the instrument off with maximum decay envelope. - instrument->noteOff( 1.0 ); + // Block waiting until callback signals done. + while ( !data.done ) + Stk::sleep( 100 ); + + // Shut down the callback and output stream. + try { + dac->cancelStreamCallback(); + dac->closeStream(); + } + catch (RtError &error) { + error.printMessage(); + } cleanup: - delete instrument; - delete output; + delete data.instrument; + delete dac; return 0; } diff --git a/projects/examples/bethree.dsp b/projects/examples/bethree.dsp index 5594555..6a749c2 100755 --- a/projects/examples/bethree.dsp +++ b/projects/examples/bethree.dsp @@ -42,14 +42,14 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "bethree - Win32 Debug" @@ -65,15 +65,15 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /GZ /c +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF @@ -111,6 +111,10 @@ SOURCE=..\..\src\FM.cpp # End Source File # Begin Source File +SOURCE=..\..\src\Generator.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\Instrmnt.cpp # End Source File # Begin Source File @@ -167,6 +171,10 @@ SOURCE=..\..\include\FM.h # End Source File # Begin Source File +SOURCE=..\..\include\Generator.h +# End Source File +# Begin Source File + SOURCE=..\..\include\Instrmnt.h # End Source File # Begin Source File diff --git a/projects/examples/controlbee.cpp b/projects/examples/controlbee.cpp index f569c1e..9dae23f 100644 --- a/projects/examples/controlbee.cpp +++ b/projects/examples/controlbee.cpp @@ -1,91 +1,175 @@ // controlbee.cpp STK tutorial program #include "BeeThree.h" -#include "RtWvOut.h" +#include "RtAudio.h" #include "Messager.h" #include "SKINI.msg" #include +#include +#if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0 + using std::min; +#endif -int main() +void usage(void) { + // Error function in case of incorrect command-line + // argument specifications. + std::cout << "\nuseage: controlbee file\n"; + std::cout << " where file = a SKINI scorefile.\n\n"; + exit(0); +} + +// The TickData structure holds all the class instances and data that +// are shared by the various processing functions. +struct TickData { + Instrmnt *instrument; + Messager messager; + Skini::Message message; + int counter; + bool haveMessage; + bool done; + + // Default constructor. + TickData() + : instrument(0), counter(0), haveMessage(false), done( false ) {} +}; + +#define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks + +// The processMessage() function encapsulates the handling of control +// messages. It can be easily relocated within a program structure +// depending on the desired scheduling scheme. +void processMessage( TickData* data ) { + register StkFloat value1 = data->message.floatValues[0]; + register StkFloat value2 = data->message.floatValues[1]; + + switch( data->message.type ) { + + case __SK_Exit_: + data->done = true; + return; + + case __SK_NoteOn_: + if ( value2 == 0.0 ) // velocity is zero ... really a NoteOff + data->instrument->noteOff( 0.5 ); + else { // a NoteOn + StkFloat frequency = 220.0 * pow( 2.0, (value1 - 57.0) / 12.0 ); + data->instrument->noteOn( frequency, value2 * ONE_OVER_128 ); + } + break; + + case __SK_NoteOff_: + data->instrument->noteOff( value2 * ONE_OVER_128 ); + break; + + case __SK_ControlChange_: + data->instrument->controlChange( (int) value1, value2 ); + break; + + case __SK_AfterTouch_: + data->instrument->controlChange( 128, value1 ); + + } // end of switch + + data->haveMessage = false; + return; +} + +// This tick() function handles sample computation and scheduling of +// control updates. It will be called automatically when the system +// needs a new buffer of audio samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + TickData *data = (TickData *) dataPointer; + register StkFloat *samples = (StkFloat *) buffer; + int counter, nTicks = bufferSize; + + while ( nTicks > 0 && !data->done ) { + + if ( !data->haveMessage ) { + data->messager.popMessage( data->message ); + if ( data->message.type > 0 ) { + data->counter = (long) (data->message.time * Stk::sampleRate()); + data->haveMessage = true; + } + else + data->counter = DELTA_CONTROL_TICKS; + } + + counter = min( nTicks, data->counter ); + data->counter -= counter; + + for ( int i=0; iinstrument->tick(); + nTicks--; + } + if ( nTicks == 0 ) break; + + // Process control messages. + if ( data->haveMessage ) processMessage( data ); + } + + return 0; +} + +int main( int argc, char *argv[] ) +{ + if ( argc != 2 ) usage(); + // Set the global sample rate and rawwave path before creating class instances. Stk::setSampleRate( 44100.0 ); Stk::setRawwavePath( "../../rawwaves/" ); - Instrmnt *instrument = 0; - RtWvOut *output = 0; - Messager *messager = 0; - bool done = FALSE; + TickData data; + RtAudio *dac = 0; + + // Figure out how many bytes in an StkFloat and setup the RtAudio object. + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + int bufferSize = RT_BUFFER_SIZE; + try { + dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); + } + catch (RtError& error) { + error.printMessage(); + goto cleanup; + } try { // Define and load the BeeThree instrument - instrument = new BeeThree(); - - // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); + data.instrument = new BeeThree(); } catch (StkError &) { goto cleanup; } + if ( data.messager.setScoreFile( argv[1] ) == false ) + goto cleanup; + try { - // Create a Messager instance to read from a redirected SKINI scorefile. - messager = new Messager(); + dac->setStreamCallback(&tick, (void *)&data); + dac->startStream(); } - catch (StkError &) { + catch (RtError &error) { + error.printMessage(); goto cleanup; } - // Play the instrument until the end of the scorefile. - int i, nTicks, type; - MY_FLOAT byte2, byte3, frequency; - while (!done) { - - // Look for new messages and return a delta time (in samples). - type = messager->nextMessage(); - if (type < 0) - done = TRUE; - - nTicks = messager->getDelta(); - try { - for ( i=0; itick( instrument->tick() ); - } - catch (StkError &) { - goto cleanup; - } - - if ( type > 0 ) { - // Process the new control message. - byte2 = messager->getByteTwo(); - byte3 = messager->getByteThree(); - - switch(type) { - - case __SK_NoteOn_: - frequency = (MY_FLOAT) 220.0 * pow( 2.0, (byte2 - 57.0) / 12.0 ); - instrument->noteOn( frequency, byte3 * ONE_OVER_128 ); - break; - - case __SK_NoteOff_: - instrument->noteOff( byte3 * ONE_OVER_128 ); - break; - - case __SK_ControlChange_: - instrument->controlChange( (int) byte2, byte3 ); - break; - - case __SK_AfterTouch_: - instrument->controlChange( 128, byte2 ); - break; - } - } + // Block waiting until callback signals done. + while ( !data.done ) + Stk::sleep( 100 ); + + // Shut down the callback and output stream. + try { + dac->cancelStreamCallback(); + dac->closeStream(); + } + catch (RtError &error) { + error.printMessage(); } cleanup: - delete instrument; - delete output; - delete messager; + delete data.instrument; + delete dac; return 0; } diff --git a/projects/examples/controlbee.dsp b/projects/examples/controlbee.dsp index 1c8da9d..b773d71 100755 --- a/projects/examples/controlbee.dsp +++ b/projects/examples/controlbee.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -67,7 +67,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -112,6 +112,10 @@ SOURCE=..\..\src\FM.cpp # End Source File # Begin Source File +SOURCE=..\..\src\Generator.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\Instrmnt.cpp # End Source File # Begin Source File @@ -120,6 +124,10 @@ SOURCE=..\..\src\Messager.cpp # End Source File # Begin Source File +SOURCE=..\..\src\Mutex.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File @@ -196,6 +204,10 @@ SOURCE=..\..\include\Messager.h # End Source File # Begin Source File +SOURCE=..\..\include\Mutex.h +# End Source File +# Begin Source File + SOURCE=..\..\include\RtAudio.h # End Source File # Begin Source File diff --git a/projects/examples/crtsine.cpp b/projects/examples/crtsine.cpp new file mode 100644 index 0000000..9989ba5 --- /dev/null +++ b/projects/examples/crtsine.cpp @@ -0,0 +1,78 @@ +// crtsine.cpp STK tutorial program + +#include "WaveLoop.h" +#include "RtAudio.h" + +// This tick() function handles sample computation only. It will be +// called automatically when the system needs a new buffer of audio +// samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + WaveLoop *sine = (WaveLoop *) dataPointer; + register StkFloat *samples = (StkFloat *) buffer; + + for ( int i=0; itick(); + + return 0; +} + +int main() +{ + // Set the global sample rate before creating class instances. + Stk::setSampleRate( 44100.0 ); + + WaveLoop *sine = 0; + RtAudio *dac = 0; + + // Figure out how many bytes in an StkFloat and setup the RtAudio object. + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + int bufferSize = RT_BUFFER_SIZE; + try { + dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); + } + catch (RtError& error) { + error.printMessage(); + goto cleanup; + } + + try { + // Define and load the sine wave file + sine = new WaveLoop( "rawwaves/sinewave.raw", true ); + } + catch (StkError &) { + goto cleanup; + } + + sine->setFrequency(440.0); + + try { + dac->setStreamCallback(&tick, (void *)sine); + dac->startStream(); + } + catch (RtError &error) { + error.printMessage(); + goto cleanup; + } + + // Block waiting here. + char keyhit; + std::cout << "\nPlaying ... press to quit.\n"; + std::cin.get(keyhit); + + // Shut down the callback and output stream. + try { + dac->cancelStreamCallback(); + dac->closeStream(); + } + catch (RtError &error) { + error.printMessage(); + } + + cleanup: + + delete sine; + delete dac; + + return 0; +} diff --git a/projects/examples/crtsine.dsp b/projects/examples/crtsine.dsp new file mode 100755 index 0000000..b1ad486 --- /dev/null +++ b/projects/examples/crtsine.dsp @@ -0,0 +1,134 @@ +# Microsoft Developer Studio Project File - Name="crtsine" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=crtsine - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "crtsine.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "crtsine.mak" CFG="crtsine - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "crtsine - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "crtsine - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "crtsine - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "crtsine___Win32_Release" +# PROP BASE Intermediate_Dir "crtsine___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "crtsine - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "crtsine___Win32_Debug" +# PROP BASE Intermediate_Dir "crtsine___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "crtsine - Win32 Release" +# Name "crtsine - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\crtsine.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\RtAudio.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\Stk.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\WaveLoop.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\WvIn.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\include\RtAudio.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\Stk.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\WaveLoop.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\WvIn.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/projects/examples/examples.dsw b/projects/examples/examples.dsw index edd693c..aa24e97 100644 --- a/projects/examples/examples.dsw +++ b/projects/examples/examples.dsw @@ -27,6 +27,18 @@ Package=<4> ############################################################################### +Project: "crtsine"=".\crtsine.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "foursine"=".\foursine.dsp" - Package Owner=<4> Package=<5> diff --git a/projects/examples/foursine.cpp b/projects/examples/foursine.cpp index 435e89b..3bab6f6 100644 --- a/projects/examples/foursine.cpp +++ b/projects/examples/foursine.cpp @@ -16,7 +16,7 @@ int main() // Define and load the sine waves try { for ( i=0; i<4; i++ ) { - inputs[i] = new WaveLoop( "rawwaves/sinewave.raw", TRUE ); + inputs[i] = new WaveLoop( "rawwaves/sinewave.raw", true ); inputs[i]->setFrequency( 220.0 * (i+1) ); } } @@ -33,7 +33,7 @@ int main() } // Write two seconds of four sines to the output file - MY_FLOAT frame[4]; + StkFloat frame[4]; for ( j=0; j<88200; j++ ) { for ( i=0; i<4; i++ ) frame[i] = inputs[i]->tick(); diff --git a/projects/examples/foursine.dsp b/projects/examples/foursine.dsp index 72d81c2..da625b3 100755 --- a/projects/examples/foursine.dsp +++ b/projects/examples/foursine.dsp @@ -41,15 +41,15 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "foursine - Win32 Debug" @@ -63,16 +63,16 @@ LINK32=link.exe # PROP Output_Dir "" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /YX /FD /GZ /c +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF diff --git a/projects/examples/io.cpp b/projects/examples/io.cpp index 4703953..24e0d40 100644 --- a/projects/examples/io.cpp +++ b/projects/examples/io.cpp @@ -1,15 +1,25 @@ /******************************************/ /* Example program for realtime input/output - by Gary P. Scavone, 2000 + by Gary P. Scavone, 2000. - This program reads N channels of realtime - audio input for a specified amount of time - and immediately play them back in realtime - (duplex mode). This program also demonstrates - the use of FIFO scheduling priority. To be - run with such priority, the program must be - set suid (chmod +s) and owned by root. + NOTE: This program makes use of blocking audio + input/output routines. On systems where the + underlying audio API is based on a callback scheme + (Macintosh OS-X, Windows ASIO, and Linux JACK), these + routines are not fully robust (over/underruns can + happen with some frequency). See the STK tutorial + for example programs using callback schemes and/or + visit the RtAudio tutorial page + (http://music.mcgill.ca/~gary/rtaudio/) for more + information. + + This program reads N channels of realtime audio input + for a specified amount of time and immediately play + them back in realtime (duplex mode). This program + also demonstrates the use of FIFO scheduling + priority. To be run with such priority, the program + must be set suid (chmod +s) and owned by root. */ /******************************************/ @@ -37,7 +47,7 @@ main(int argc, char *argv[]) if (argc != 3) usage(); unsigned int channels = (unsigned int) atoi(argv[1]); - float time = atof(argv[2]); + double time = atof(argv[2]); // If you want to change the default sample rate (set in Stk.h), do // it before instantiating any objects! If the sample rate is @@ -72,8 +82,8 @@ main(int argc, char *argv[]) // Here's the runtime loop unsigned long i, counter = 0; - MY_FLOAT *newFrame = new MY_FLOAT[channels]; - const MY_FLOAT *lastFrame = inout->lastFrame(); + StkFloat *newFrame = new StkFloat[channels]; + const StkFloat *lastFrame = inout->lastFrame(); unsigned long samples = (unsigned long) (time * Stk::sampleRate()); while ( counter < samples ) { for ( i=0; i +#include "RtAudio.h" + +#include +#include + +// Eewww ... global variables! :-) +bool done; +StkFrames frames; +static void finish(int ignore){ done = true; } void usage(void) { // Error function in case of incorrect command-line // argument specifications. - printf("\nuseage: play file \n"); - printf(" where file = the file to play,\n"); - printf(" and rate = an optional playback rate.\n"); - printf(" (default = 1.0, can be negative)\n\n"); + std::cout << "\nuseage: play file sr \n"; + std::cout << " where file = the file to play,\n"; + std::cout << " where sr = sample rate,\n"; + std::cout << " and rate = an optional playback rate.\n"; + std::cout << " (default = 1.0, can be negative)\n\n"; exit(0); } +// This tick() function handles sample computation only. It will be +// called automatically when the system needs a new buffer of audio +// samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + WvIn *input = (WvIn *) dataPointer; + register StkFloat *samples = (StkFloat *) buffer; + + input->tickFrame( frames ); + for ( unsigned int i=0; iisFinished() ) { + done = true; + return 1; + } + else + return 0; +} + int main(int argc, char *argv[]) { // Minimal command-line checking. - if (argc < 2 || argc > 3) usage(); + if (argc < 3 || argc > 4) usage(); - // Initialize our WvIn/WvOut pointers. - RtWvOut *output = 0; + // Set the global sample rate before creating class instances. + Stk::setSampleRate( (StkFloat) atof(argv[2]) ); + + // Initialize our WvIn and RtAudio pointers. + RtAudio *dac = 0; WvIn *input = 0; // Try to load the soundfile. @@ -45,29 +76,57 @@ int main(int argc, char *argv[]) } // Set input read rate based on the default STK sample rate. - float rate = 1.0; + double rate = 1.0; rate = input->getFileRate() / Stk::sampleRate(); - if ( argc == 3 ) rate *= atof(argv[2]); + if ( argc == 4 ) rate *= atof(argv[3]); input->setRate( rate ); // Find out how many channels we have. int channels = input->getChannels(); - // Define and open the realtime output device + // Define and open the realtime output device. + // Figure out how many bytes in an StkFloat and setup the RtAudio object. + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + int bufferSize = RT_BUFFER_SIZE; try { - output = new RtWvOut( channels, Stk::sampleRate(), 0, RT_BUFFER_SIZE, 4 ); + dac = new RtAudio(0, channels, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); } - catch (StkError &) { + catch (RtError &error) { + error.printMessage(); goto cleanup; } - // Here's the runtime loop. - while (!input->isFinished()) { - output->tickFrame( input->tickFrame() ); + // Install an interrupt handler function. + (void) signal(SIGINT, finish); + + // Resize the StkFrames object appropriately. + frames.resize( bufferSize, channels ); + + try { + dac->setStreamCallback(&tick, (void *)input); + dac->startStream(); + } + catch (RtError &error) { + error.printMessage(); + goto cleanup; + } + + // Block waiting until callback signals done. + while ( !done ) + Stk::sleep( 100 ); + + // By returning a non-zero value in the callback above, the stream + // is automatically stopped. But we should still close it. + try { + dac->cancelStreamCallback(); + dac->closeStream(); + } + catch (RtError &error) { + error.printMessage(); } cleanup: delete input; - delete output; + delete dac; return 0; } diff --git a/projects/examples/play.dsp b/projects/examples/play.dsp index 28e7cfb..c5fcb12 100644 --- a/projects/examples/play.dsp +++ b/projects/examples/play.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe diff --git a/projects/examples/playsmf.cpp b/projects/examples/playsmf.cpp new file mode 100644 index 0000000..5dc1466 --- /dev/null +++ b/projects/examples/playsmf.cpp @@ -0,0 +1,122 @@ +// playsmf.cpp +// +// Simple program to test the MidiFileIn class by reading and playing +// a single track from a given Standard MIDI file. +// +// by Gary Scavone, 2003. + +#include "MidiFileIn.h" +#include "RtMidi.h" +#include +#include + +bool done = false; +static void finish(int ignore){ done = true; } + +void usage(void) { + // Error function in case of incorrect command-line + // argument specifications. + std::cout << "\nusage: playsmf file track \n"; + std::cout << " where file = a standard MIDI file,\n"; + std::cout << " track = the track to play (0 = 1st track),\n"; + std::cout << " and an optional port integer identifier can be specified\n"; + std::cout << " (default = 0) or a value of -1 to use a virtual MIDI output port.\n\n"; + exit(0); +} + +int main( int argc, char *argv[] ) +{ + RtMidiOut *midiout = 0; + + if ( argc < 3 || argc > 4 ) usage(); + + // Attempt to instantiate MIDI output class. + try { + midiout = new RtMidiOut(); + } + catch ( RtError& error ) { + error.printMessage(); + exit(0); + } + + // Check command-line arguments. + int port = 0; + if ( argc == 4 ) port = atoi( argv[3] ); + if ( port == -1 ) { + try { + midiout->openVirtualPort(); + } + catch ( RtError& error ) { + error.printMessage(); + goto cleanup; + } + std::cout << "\nVirtual port open.\n\n"; + } + else { + if ( midiout->getPortCount() < 1 ) { + std::cout << "\nThere are no MIDI output destinations available!\n\n"; + goto cleanup; + } + try { + midiout->openPort( port ); + } + catch ( RtError& error ) { + error.printMessage(); + goto cleanup; + } + } + + // Install an interrupt handler function. Type "ctrl-c" to quit the + // program. + (void) signal(SIGINT, finish); + + try { + MidiFileIn midiFile( argv[1] ); + + // Print a little information about the file. + std::cout << "\nThe MIDI file (" << argv[1] << ") information:\n"; + std::cout << " - format = " << midiFile.getFileFormat() << "\n"; + std::cout << " - tracks = " << midiFile.getNumberOfTracks() << "\n"; + std::cout << " - seconds / ticks = " << midiFile.getTickSeconds() << "\n"; + + unsigned int track = (unsigned int) atoi( argv[2] ); + if ( midiFile.getNumberOfTracks() <= track ) { + std::cout << "\nInvalid track number ... playing track 0.\n"; + track = 0; + } + + std::cout << "\nPress to start reading/playing.\n"; + char input; + std::cin.get(input); + + std::vector event; + unsigned long ticks = midiFile.getNextMidiEvent( &event, track ); + while ( !done && event.size() ) { + + // Pause for the MIDI event delta time. + Stk::sleep( (unsigned long) (ticks * midiFile.getTickSeconds() * 1000 ) ); + + midiout->sendMessage( &event ); + + // Get a new event. + ticks = midiFile.getNextMidiEvent( &event, track ); + } + + // Send a "all notes off" to the synthesizer. + event.clear(); + event.push_back( 0xb0 ); + event.push_back( 0x7b ); + event.push_back( 0x0 ); + midiout->sendMessage( &event ); + } + catch ( StkError & ) { + // You might want to do something more useful here. + std::cout << "\nAborting program!\n"; + goto cleanup; + } + + cleanup: + delete midiout; + + return 0; +} diff --git a/projects/examples/record.cpp b/projects/examples/record.cpp index 659bf2f..928bd2f 100644 --- a/projects/examples/record.cpp +++ b/projects/examples/record.cpp @@ -3,6 +3,17 @@ Example program to record N channels of data by Gary P. Scavone, 2000 + NOTE: This program makes use of blocking audio + input/output routines. On systems where the + underlying audio API is based on a callback scheme + (Macintosh OS-X, Windows ASIO, and Linux JACK), these + routines are not fully robust (over/underruns can + happen with some frequency). See the STK tutorial + for example programs using callback schemes and/or + visit the RtAudio tutorial page + (http://music.mcgill.ca/~gary/rtaudio/) for more + information. + This program is currently written to read from a realtime audio input device and to write to a WAV output file. However, it @@ -31,8 +42,8 @@ int main(int argc, char *argv[]) if (argc != 5) usage(); int channels = (int) atoi(argv[1]); - float sample_rate = atof(argv[4]); - float time = atof(argv[3]); + double sample_rate = atof(argv[4]); + double time = atof(argv[3]); long samples, i; // Set the global sample rate. diff --git a/projects/examples/record.dsp b/projects/examples/record.dsp index 292525a..e837bd9 100644 --- a/projects/examples/record.dsp +++ b/projects/examples/record.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe diff --git a/projects/examples/rtsine.cpp b/projects/examples/rtsine.cpp index e20c0d1..40012b4 100644 --- a/projects/examples/rtsine.cpp +++ b/projects/examples/rtsine.cpp @@ -8,27 +8,27 @@ int main() // Set the global sample rate before creating class instances. Stk::setSampleRate( 44100.0 ); - WaveLoop *input = 0; - RtWvOut *output = 0; + WaveLoop *sine = 0; + RtWvOut *dac = 0; try { // Define and load the sine wave file - input = new WaveLoop( "rawwaves/sinewave.raw", TRUE ); + sine = new WaveLoop( "rawwaves/sinewave.raw", true ); // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); + dac = new RtWvOut(1); } catch (StkError &) { goto cleanup; } - input->setFrequency(440.0); + sine->setFrequency(440.0); // Play the oscillator for 40000 samples int i; for ( i=0; i<40000; i++ ) { try { - output->tick(input->tick()); + dac->tick( sine->tick() ); } catch (StkError &) { goto cleanup; @@ -36,8 +36,8 @@ int main() } cleanup: - delete input; - delete output; + delete sine; + delete dac; return 0; } diff --git a/projects/examples/rtsine.dsp b/projects/examples/rtsine.dsp index d409e01..285a933 100755 --- a/projects/examples/rtsine.dsp +++ b/projects/examples/rtsine.dsp @@ -42,14 +42,14 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "rtsine - Win32 Debug" @@ -65,15 +65,15 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF diff --git a/projects/examples/sine.cpp b/projects/examples/sine.cpp index 66d892c..933ad3c 100644 --- a/projects/examples/sine.cpp +++ b/projects/examples/sine.cpp @@ -43,7 +43,7 @@ main(int argc, char *argv[]) // Initialize our object and data pointers. WvOut *output = 0; - MY_FLOAT *vector = 0; + StkFloat *vector = 0; WaveLoop **oscs = (WaveLoop **) malloc( channels * sizeof(WaveLoop *) ); for (i=0; itick(); diff --git a/projects/examples/sine.dsp b/projects/examples/sine.dsp index 3c9d93d..beba689 100644 --- a/projects/examples/sine.dsp +++ b/projects/examples/sine.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe diff --git a/projects/examples/sineosc.cpp b/projects/examples/sineosc.cpp index a01f45f..59d6cf5 100644 --- a/projects/examples/sineosc.cpp +++ b/projects/examples/sineosc.cpp @@ -13,7 +13,7 @@ int main() try { // Define and load the sine wave file - input = new WaveLoop( "rawwaves/sinewave.raw", TRUE ); + input = new WaveLoop( "rawwaves/sinewave.raw", true ); // Define and open a 16-bit, one-channel WAV formatted output file output = new WvOut( "hellosine.wav", 1, WvOut::WVOUT_WAV, Stk::STK_SINT16 ); diff --git a/projects/examples/sineosc.dsp b/projects/examples/sineosc.dsp index 331799a..0f4c9a8 100755 --- a/projects/examples/sineosc.dsp +++ b/projects/examples/sineosc.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /D "__WINDOWS_DS__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -65,7 +65,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "_AFXDLL" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "_AFXDLL" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /D "__WINDOWS_DS__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe diff --git a/projects/examples/tcpIn.cpp b/projects/examples/tcpIn.cpp index a6aa476..187c02d 100644 --- a/projects/examples/tcpIn.cpp +++ b/projects/examples/tcpIn.cpp @@ -6,6 +6,17 @@ by Gary P. Scavone, 2000 + NOTE: This program makes use of blocking audio + input/output routines. On systems where the + underlying audio API is based on a callback scheme + (Macintosh OS-X, Windows ASIO, and Linux JACK), these + routines are not fully robust (over/underruns can + happen with some frequency). See the STK tutorial + for example programs using callback schemes and/or + visit the RtAudio tutorial page + (http://music.mcgill.ca/~gary/rtaudio/) for more + information. + This program is currently written to play the input data in realtime. However, it is simple to replace the instance of @@ -15,7 +26,7 @@ The streamed data format is assumed to be signed 16-bit integers. However, both TcpWvIn and TcpWvOut can be initialized - to read/write any of the defined STK_FORMATs. + to read/write any of the defined StkFormats. The class TcpWvIn sets up a socket server and waits for a connection. Therefore, diff --git a/projects/examples/tcpIn.dsp b/projects/examples/tcpIn.dsp index 8812712..741c8b5 100644 --- a/projects/examples/tcpIn.dsp +++ b/projects/examples/tcpIn.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -87,6 +87,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=..\..\src\Mutex.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File diff --git a/projects/examples/tcpOut.cpp b/projects/examples/tcpOut.cpp index 5919fa4..6bebf8a 100644 --- a/projects/examples/tcpOut.cpp +++ b/projects/examples/tcpOut.cpp @@ -10,7 +10,7 @@ file. The output data format is set for signed 16-bit integers. However, it is easy to change the TcpWvOut setting to - any of the other defined STK_FORMATs. + any of the other defined StkFormats. If using tcpIn, it will be necessary to change the expected data format there as well. diff --git a/projects/examples/tcpOut.dsp b/projects/examples/tcpOut.dsp index fd1f226..ac608ed 100644 --- a/projects/examples/tcpOut.dsp +++ b/projects/examples/tcpOut.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe diff --git a/projects/examples/threebees.cpp b/projects/examples/threebees.cpp index e3aee12..793fe1d 100644 --- a/projects/examples/threebees.cpp +++ b/projects/examples/threebees.cpp @@ -1,11 +1,116 @@ // threebees.cpp STK tutorial program #include "BeeThree.h" -#include "RtWvOut.h" +#include "RtAudio.h" #include "Messager.h" #include "Voicer.h" #include "SKINI.msg" +#include +#if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0 + using std::min; +#endif + +// The TickData structure holds all the class instances and data that +// are shared by the various processing functions. +struct TickData { + Voicer voicer; + Messager messager; + Skini::Message message; + int counter; + bool haveMessage; + bool done; + + // Default constructor. + TickData() + : counter(0), haveMessage(false), done( false ) {} +}; + +#define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks + +// The processMessage() function encapsulates the handling of control +// messages. It can be easily relocated within a program structure +// depending on the desired scheduling scheme. +void processMessage( TickData* data ) +{ + register StkFloat value1 = data->message.floatValues[0]; + register StkFloat value2 = data->message.floatValues[1]; + + switch( data->message.type ) { + + case __SK_Exit_: + data->done = true; + return; + + case __SK_NoteOn_: + if ( value2 == 0.0 ) // velocity is zero ... really a NoteOff + data->voicer.noteOff( value1, 64.0 ); + else { // a NoteOn + data->voicer.noteOn( value1, value2 ); + } + break; + + case __SK_NoteOff_: + data->voicer.noteOff( value1, value2 ); + break; + + case __SK_ControlChange_: + data->voicer.controlChange( (int) value1, value2 ); + break; + + case __SK_AfterTouch_: + data->voicer.controlChange( 128, value1 ); + + case __SK_PitchChange_: + data->voicer.setFrequency( value1 ); + break; + + case __SK_PitchBend_: + data->voicer.pitchBend( value1 ); + + } // end of switch + + data->haveMessage = false; + return; +} + +// This tick() function handles sample computation and scheduling of +// control updates. It will be called automatically when the system +// needs a new buffer of audio samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + TickData *data = (TickData *) dataPointer; + register StkFloat *samples = (StkFloat *) buffer; + int counter, nTicks = bufferSize; + + while ( nTicks > 0 && !data->done ) { + + if ( !data->haveMessage ) { + data->messager.popMessage( data->message ); + if ( data->message.type > 0 ) { + data->counter = (long) (data->message.time * Stk::sampleRate()); + data->haveMessage = true; + } + else + data->counter = DELTA_CONTROL_TICKS; + } + + counter = min( nTicks, data->counter ); + data->counter -= counter; + + for ( int i=0; ivoicer.tick(); + nTicks--; + } + if ( nTicks == 0 ) break; + + // Process control messages. + if ( data->haveMessage ) processMessage( data ); + } + + return 0; +} + int main() { // Set the global sample rate and rawwave path before creating class instances. @@ -13,88 +118,63 @@ int main() Stk::setRawwavePath( "../../rawwaves/" ); int i; - RtWvOut *output = 0; - Messager *messager = 0; - Voicer *voicer = 0; - bool done = FALSE; + TickData data; + RtAudio *dac = 0; Instrmnt *instrument[3]; for ( i=0; i<3; i++ ) instrument[i] = 0; + // Figure out how many bytes in an StkFloat and setup the RtAudio object. + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + int bufferSize = RT_BUFFER_SIZE; + try { + dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); + } + catch (RtError& error) { + error.printMessage(); + goto cleanup; + } + try { // Define and load the BeeThree instruments for ( i=0; i<3; i++ ) instrument[i] = new BeeThree(); - - // Define and open the default realtime output device for one-channel playback - output = new RtWvOut(1); } catch (StkError &) { goto cleanup; } + // "Add" the instruments to the voicer. + for ( i=0; i<3; i++ ) + data.voicer.addInstrument( instrument[i] ); + + if ( data.messager.startStdInput() == false ) + goto cleanup; + try { - // Create a Messager instance to read from a redirected SKINI scorefile. - messager = new Messager(); + dac->setStreamCallback(&tick, (void *)&data); + dac->startStream(); } - catch (StkError &) { + catch (RtError &error) { + error.printMessage(); goto cleanup; } - // Instantiate the voicer for a maximum of three voices. - voicer = new Voicer( 3 ); - for ( i=0; i<3; i++ ) - voicer->addInstrument( instrument[i] ); - - // Play the instrument until the end of the scorefile. - int nTicks, type; - MY_FLOAT byte2, byte3; - while (!done) { - - // Look for new messages and return a delta time (in samples). - type = messager->nextMessage(); - if (type < 0) - done = TRUE; - - nTicks = messager->getDelta(); - try { - for ( i=0; itick( voicer->tick() ); - } - catch (StkError &) { - goto cleanup; - } - - if ( type > 0 ) { - // Process the new control message. - byte2 = messager->getByteTwo(); - byte3 = messager->getByteThree(); - - switch(type) { - - case __SK_NoteOn_: - voicer->noteOn( byte2, byte3 ); - break; - - case __SK_NoteOff_: - voicer->noteOff( byte2, byte3 ); - break; - - case __SK_ControlChange_: - voicer->controlChange( (int) byte2, byte3 ); - break; - - case __SK_AfterTouch_: - voicer->controlChange( 128, byte2 ); - break; - } - } + // Block waiting until callback signals done. + while ( !data.done ) + Stk::sleep( 100 ); + + // Shut down the callback and output stream. + try { + dac->cancelStreamCallback(); + dac->closeStream(); + } + catch (RtError &error) { + error.printMessage(); } cleanup: for ( i=0; i<3; i++ ) delete instrument[i]; - delete output; - delete messager; - delete voicer; + delete dac; return 0; } diff --git a/projects/examples/threebees.dsp b/projects/examples/threebees.dsp index e22b339..b0d3c3f 100755 --- a/projects/examples/threebees.dsp +++ b/projects/examples/threebees.dsp @@ -42,14 +42,14 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../include" /D "NDEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Wsock32.lib winmm.lib dsound.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "threebees - Win32 Debug" @@ -65,15 +65,15 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_DS__" /YX /FD /GZ /c +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../include" /D "_DEBUG" /D "__WINDOWS_DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Wsock32.lib winmm.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF @@ -107,6 +107,10 @@ SOURCE=..\..\src\FM.cpp # End Source File # Begin Source File +SOURCE=..\..\src\Generator.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\Instrmnt.cpp # End Source File # Begin Source File @@ -115,6 +119,10 @@ SOURCE=..\..\src\Messager.cpp # End Source File # Begin Source File +SOURCE=..\..\src\Mutex.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File diff --git a/projects/ragamatic/Drone.cpp b/projects/ragamatic/Drone.cpp index aae463d..836e933 100644 --- a/projects/ragamatic/Drone.cpp +++ b/projects/ragamatic/Drone.cpp @@ -13,90 +13,101 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Drone.h" -Drone :: Drone(MY_FLOAT lowestFrequency) +Drone :: Drone(StkFloat lowestFrequency) { - length = (long) (Stk::sampleRate() / lowestFrequency + 1); - loopGain = (MY_FLOAT) 0.999; - delayLine = new DelayA( (MY_FLOAT)(length / 2.0), length ); - loopFilter = new OneZero; - noise = new Noise; - envelope = new ADSR(); - envelope->setAllTimes(2.0, 0.5, 0.0, 0.5); + length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + loopGain_ = 0.999; + delayLine_.setMaximumDelay( length_ ); + delayLine_.setDelay( 0.5 * length_ ); + envelope_.setAllTimes( 2.0, 0.5, 0.0, 0.5 ); this->clear(); } Drone :: ~Drone() { - delete delayLine; - delete loopFilter; - delete envelope; - delete noise; } void Drone :: clear() { - delayLine->clear(); - loopFilter->clear(); + delayLine_.clear(); + loopFilter_.clear(); } -void Drone :: setFrequency(MY_FLOAT frequency) +void Drone :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Drone: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Drone::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } // Delay = length - approximate filter delay. - MY_FLOAT delay = (Stk::sampleRate() / freakency) - (MY_FLOAT) 0.5; - if (delay <= 0.0) delay = 0.3; - else if (delay > length) delay = length; - delayLine->setDelay(delay); - loopGain = 0.997 + (freakency * 0.000002); - if ( loopGain >= 1.0 ) loopGain = (MY_FLOAT) 0.99999; + StkFloat delay = (Stk::sampleRate() / freakency) - 0.5; + if ( delay <= 0.0 ) + delay = 0.3; + else if (delay > length_) + delay = length_; + delayLine_.setDelay( delay ); + loopGain_ = 0.997 + (freakency * 0.000002); + if ( loopGain_ >= 1.0 ) loopGain_ = 0.99999; } -void Drone :: pluck(MY_FLOAT amplitude) +void Drone :: pluck(StkFloat amplitude) { - envelope->keyOn(); + envelope_.keyOn(); } -void Drone :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Drone :: noteOn(StkFloat frequency, StkFloat amplitude) { - this->setFrequency(frequency); - this->pluck(amplitude); + this->setFrequency( frequency ); + this->pluck( amplitude ); #if defined(_STK_DEBUG_) - std::cerr << "Drone: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Drone::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Drone :: noteOff(MY_FLOAT amplitude) +void Drone :: noteOff(StkFloat amplitude) { - loopGain = (MY_FLOAT) 1.0 - amplitude; - if ( loopGain < 0.0 ) { - std::cerr << "Drone: noteOff amplitude greater than 1.0!" << std::endl; - loopGain = 0.0; + loopGain_ = 1.0 - amplitude; + if ( loopGain_ < 0.0 ) { + errorString_ << "Drone::noteOff: amplitude is greater than 1.0 ... setting to 1.0!"; + handleError( StkError::WARNING ); + loopGain_ = 0.0; } - else if ( loopGain > 1.0 ) { - std::cerr << "Drone: noteOff amplitude less than or zero!" << std::endl; - loopGain = (MY_FLOAT) 0.99999; + else if ( loopGain_ > 1.0 ) { + errorString_ << "Drone::noteOff: amplitude is < 0.0 ... setting to 0.0!"; + handleError( StkError::WARNING ); + loopGain_ = 0.99999; } #if defined(_STK_DEBUG_) - std::cerr << "Drone: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Drone::noteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Drone :: tick() +StkFloat Drone :: tick() { // Here's the whole inner loop of the instrument!! - lastOutput = delayLine->tick( loopFilter->tick( delayLine->lastOut() * loopGain ) + (0.005 * envelope->tick() * noise->tick())); - return lastOutput; + lastOutput_ = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) + (0.005 * envelope_.tick() * noise_.tick())); + return lastOutput_; +} + +StkFloat *Drone :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Drone :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/projects/ragamatic/Drone.h b/projects/ragamatic/Drone.h index a4237d7..04cfa89 100644 --- a/projects/ragamatic/Drone.h +++ b/projects/ragamatic/Drone.h @@ -13,12 +13,12 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__DRONE_H) -#define __DRONE_H +#ifndef STK_DRONE_H +#define STK_DRONE_H #include "Instrmnt.h" #include "DelayA.h" @@ -30,7 +30,7 @@ class Drone : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Drone(MY_FLOAT lowestFrequency); + Drone( StkFloat lowestFrequency = 20 ); //! Class destructor. ~Drone(); @@ -39,27 +39,39 @@ class Drone : public Instrmnt void clear(); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(MY_FLOAT frequency); + virtual void setFrequency(StkFloat frequency); //! Pluck the string with the given amplitude using the current frequency. - void pluck(MY_FLOAT amplitude); + void pluck(StkFloat amplitude); //! Start a note with the given frequency and amplitude. - virtual void noteOn(MY_FLOAT frequency, MY_FLOAT amplitude); + virtual void noteOn(StkFloat frequency, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(MY_FLOAT amplitude); + virtual void noteOff(StkFloat amplitude); //! Compute one output sample. - virtual MY_FLOAT tick(); + virtual StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + virtual StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - DelayA *delayLine; - OneZero *loopFilter; - ADSR *envelope; - Noise *noise; - long length; - MY_FLOAT loopGain; + DelayA delayLine_; + OneZero loopFilter_; + ADSR envelope_; + Noise noise_; + StkFloat loopGain_; + unsigned long length_; }; diff --git a/projects/ragamatic/Makefile.in b/projects/ragamatic/Makefile.in index ee6d260..1fe8d89 100644 --- a/projects/ragamatic/Makefile.in +++ b/projects/ragamatic/Makefile.in @@ -6,12 +6,12 @@ SRC_PATH = ../../src OBJECT_PATH = @object_path@ vpath %.o $(OBJECT_PATH) -OBJECTS = Stk.o Envelope.o ADSR.o Noise.o \ +OBJECTS = Stk.o Generator.o Noise.o Envelope.o ADSR.o \ Filter.o DelayA.o Delay.o \ - OnePole.o OneZero.o SKINI.o \ + OnePole.o OneZero.o Skini.o \ Tabla.o Instrmnt.o Sitar.o \ Drone.o VoicDrum.o WvOut.o WvIn.o \ - Reverb.o JCRev.o Messager.o + Effect.o JCRev.o Messager.o INCLUDE = @include@ ifeq ($(strip $(INCLUDE)), ) @@ -29,9 +29,8 @@ LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) - OBJECTS += RtMidi.o RtAudio.o RtWvOut.o Thread.o Socket.o + OBJECTS += RtMidi.o RtAudio.o RtWvOut.o Thread.o Mutex.o Socket.o DEFS += @audio_apis@ - DEFS += @midiator@ endif RAWWAVES = @rawwaves@ @@ -48,6 +47,9 @@ all : $(PROGRAMS) ragamat: ragamat.cpp $(OBJECTS) $(CC) $(CFLAGS) $(DEFS) -o ragamat ragamat.cpp $(OBJECT_PATH)/*.o $(LIBRARY) +libragamat: ragamat.cpp Tabla.cpp Drone.cpp VoicDrum.cpp + $(CC) $(CFLAGS) $(DEFS) -o ragamat Tabla.cpp Drone.cpp VoicDrum.cpp ragamat.cpp -L../../src $(LIBRARY) -lstk + $(OBJECTS) : Stk.h clean : diff --git a/projects/ragamatic/Raga b/projects/ragamatic/Raga new file mode 100755 index 0000000..e19d574 --- /dev/null +++ b/projects/ragamatic/Raga @@ -0,0 +1 @@ +wish < tcl/Raga.tcl | ./ragamat -ip diff --git a/projects/ragamatic/Raga.bat b/projects/ragamatic/Raga.bat index e19d574..ddd2606 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 7ac9f54..4e284b6 100644 --- a/projects/ragamatic/Tabla.cpp +++ b/projects/ragamatic/Tabla.cpp @@ -8,48 +8,49 @@ at 22050 Hz, but will be appropriately interpolated for other sample rates. You can specify the maximum polyphony (maximum number - of simultaneous voices) via a #define in the - Drummer.h. + of simultaneous voices) in Drummer.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Tabla.h" -#include #include Tabla :: Tabla() : Instrmnt() { - for (int i=0; i= 0 ) { // Reset this sound. - waves[waveIndex]->reset(); - filters[waveIndex]->setPole((MY_FLOAT) 0.999 - (gain * 0.6)); - filters[waveIndex]->setGain(gain); + waves_[waveIndex]->reset(); + filters_[waveIndex]->setPole( 0.999 - (gain * 0.6) ); + filters_[waveIndex]->setGain( gain ); } else { - if (nSounding == TABLA_POLYPHONY) { + if ( nSounding_ == TABLA_POLYPHONY ) { // If we're already at maximum polyphony, then preempt the oldest voice. - delete waves[0]; - filters[0]->clear(); - WvIn *tempWv = waves[0]; - OnePole *tempFilt = filters[0]; + delete waves_[0]; + filters_[0]->clear(); + OnePole *tempFilt = filters_[0]; // Re-order the list. - for (i=0; inormalize(0.4); - filters[nSounding-1]->setPole((MY_FLOAT) 0.999 - (gain * 0.6) ); - filters[nSounding-1]->setGain( gain ); + sounding_[nSounding_-1] = noteNum; + // Concatenate the rawwave path to the file name. + waves_[nSounding_-1] = new WvIn( (std::string("rawwaves/") + tablaWaves[noteNum]).c_str(), true ); + waves_[nSounding_-1]->normalize(0.4); + if ( Stk::sampleRate() != 22050.0 ) + waves_[nSounding_-1]->setRate( 22050.0 / Stk::sampleRate() ); + filters_[nSounding_-1]->setPole( 0.999 - (gain * 0.6) ); + filters_[nSounding_-1]->setGain( gain ); } #if defined(_STK_DEBUG_) - std::cerr << "Number Sounding = " << nSounding << std::endl; - for (i=0; isetGain( amplitude * 0.01 ); - } + while ( i < nSounding_ ) + filters_[i++]->setGain( amplitude * 0.01 ); } -MY_FLOAT Tabla :: tick() +StkFloat Tabla :: tick() { - MY_FLOAT output = 0.0; OnePole *tempFilt; int j, i = 0; - while (i < nSounding) { - if ( waves[i]->isFinished() ) { - delete waves[i]; - tempFilt = filters[i]; + lastOutput_ = 0.0; + while ( i < nSounding_ ) { + if ( waves_[i]->isFinished() ) { + delete waves_[i]; + tempFilt = filters_[i]; // Re-order the list. - for (j=i; jclear(); - sounding[j] = -1; - nSounding -= 1; + filters_[j] = tempFilt; + filters_[j]->clear(); + sounding_[j] = -1; + nSounding_ -= 1; i -= 1; } else - output += filters[i]->tick( waves[i]->tick() ); + lastOutput_ += filters_[i]->tick( waves_[i]->tick() ); i++; } - return output; + return lastOutput_; +} + +StkFloat *Tabla :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Tabla :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/projects/ragamatic/Tabla.h b/projects/ragamatic/Tabla.h index b52b64f..45afc7c 100644 --- a/projects/ragamatic/Tabla.h +++ b/projects/ragamatic/Tabla.h @@ -8,22 +8,21 @@ at 22050 Hz, but will be appropriately interpolated for other sample rates. You can specify the maximum polyphony (maximum number - of simultaneous voices) via a #define in the - Drummer.h. + of simultaneous voices) in Drummer.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__TABLA_H) -#define __TABLA_H +#ifndef STK_TABLA_H +#define STK_TABLA_H #include "Instrmnt.h" #include "WvIn.h" #include "OnePole.h" -#define TABLA_NUMWAVES 15 -#define TABLA_POLYPHONY 4 +const int TABLA_NUMWAVES = 15; +const int TABLA_POLYPHONY = 4; class Tabla : public Instrmnt { @@ -35,19 +34,31 @@ class Tabla : public Instrmnt ~Tabla(); //! Start a note with the given drum type and amplitude. - void noteOn(MY_FLOAT instrument, MY_FLOAT amplitude); + void noteOn(StkFloat instrument, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - WvIn *waves[TABLA_POLYPHONY]; - OnePole *filters[TABLA_POLYPHONY]; - int sounding[TABLA_POLYPHONY]; - int nSounding; + WvIn *waves_[TABLA_POLYPHONY]; + OnePole *filters_[TABLA_POLYPHONY]; + int sounding_[TABLA_POLYPHONY]; + int nSounding_; }; diff --git a/projects/ragamatic/VoicDrum.cpp b/projects/ragamatic/VoicDrum.cpp index 08f17ce..c3a852e 100644 --- a/projects/ragamatic/VoicDrum.cpp +++ b/projects/ragamatic/VoicDrum.cpp @@ -8,48 +8,49 @@ at 22050 Hz, but will be appropriately interpolated for other sample rates. You can specify the maximum polyphony (maximum number - of simultaneous voices) via a #define in the - Drummer.h. + of simultaneous voices) in Drummer.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "VoicDrum.h" -#include #include VoicDrum :: VoicDrum() : Instrmnt() { - for (int i=0; i= 0 ) { // Reset this sound. - waves[waveIndex]->reset(); - filters[waveIndex]->setPole((MY_FLOAT) 0.999 - (gain * 0.6)); - filters[waveIndex]->setGain(gain); + waves_[waveIndex]->reset(); + filters_[waveIndex]->setPole( 0.999 - (gain * 0.6) ); + filters_[waveIndex]->setGain( gain ); } else { - if (nSounding == VOICE_POLYPHONY) { + if ( nSounding_ == VOICE_POLYPHONY ) { // If we're already at maximum polyphony, then preempt the oldest voice. - delete waves[0]; - filters[0]->clear(); - WvIn *tempWv = waves[0]; - OnePole *tempFilt = filters[0]; + delete waves_[0]; + filters_[0]->clear(); + OnePole *tempFilt = filters_[0]; // Re-order the list. - for (i=0; inormalize(0.4); - filters[nSounding-1]->setPole((MY_FLOAT) 0.999 - (gain * 0.6) ); - filters[nSounding-1]->setGain( gain ); + sounding_[nSounding_-1] = noteNum; + // Concatenate the rawwave path to the file name. + waves_[nSounding_-1] = new WvIn( (std::string("rawwaves/") + voiceNames[noteNum]).c_str(), true ); + waves_[nSounding_-1]->normalize(0.4); + if (Stk::sampleRate() != 22050.0) + waves_[nSounding_-1]->setRate( 22050.0 / Stk::sampleRate() ); + filters_[nSounding_-1]->setPole( 0.999 - (gain * 0.6) ); + filters_[nSounding_-1]->setGain( gain ); } #if defined(_STK_DEBUG_) - std::cerr << "Number Sounding = " << nSounding << std::endl; - for (i=0; isetGain( amplitude * 0.01 ); - } + while ( i < nSounding_ ) filters_[i++]->setGain( amplitude * 0.01 ); } -MY_FLOAT VoicDrum :: tick() +StkFloat VoicDrum :: tick() { - MY_FLOAT output = 0.0; OnePole *tempFilt; int j, i = 0; - while (i < nSounding) { - if ( waves[i]->isFinished() ) { - delete waves[i]; - tempFilt = filters[i]; + lastOutput_ = 0.0; + while ( i < nSounding_ ) { + if ( waves_[i]->isFinished() ) { + delete waves_[i]; + tempFilt = filters_[i]; // Re-order the list. - for (j=i; jclear(); - sounding[j] = -1; - nSounding -= 1; + filters_[j] = tempFilt; + filters_[j]->clear(); + sounding_[j] = -1; + nSounding_ -= 1; i -= 1; } else - output += filters[i]->tick( waves[i]->tick() ); + lastOutput_ += filters_[i]->tick( waves_[i]->tick() ); i++; } - return output; + return lastOutput_; +} + +StkFloat *VoicDrum :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& VoicDrum :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/projects/ragamatic/VoicDrum.h b/projects/ragamatic/VoicDrum.h index 05125e7..0a21009 100644 --- a/projects/ragamatic/VoicDrum.h +++ b/projects/ragamatic/VoicDrum.h @@ -8,22 +8,21 @@ at 22050 Hz, but will be appropriately interpolated for other sample rates. You can specify the maximum polyphony (maximum number - of simultaneous voices) via a #define in the - Drummer.h. + of simultaneous voices) in Drummer.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ -#if !defined(__VOICDRUM_H) -#define __VOICDRUM_H +#ifndef STK_VOICDRUM_H +#define STK_VOICDRUM_H #include "Instrmnt.h" #include "WvIn.h" #include "OnePole.h" -#define VOICE_NUMWAVES 11 -#define VOICE_POLYPHONY 4 +const int VOICE_NUMWAVES = 11; +const int VOICE_POLYPHONY = 4; class VoicDrum : public Instrmnt { @@ -35,19 +34,31 @@ class VoicDrum : public Instrmnt ~VoicDrum(); //! Start a note with the given drum type and amplitude. - void noteOn(MY_FLOAT instrument, MY_FLOAT amplitude); + void noteOn(StkFloat instrument, StkFloat amplitude); //! Stop a note with the given amplitude (speed of decay). - void noteOff(MY_FLOAT amplitude); + void noteOff(StkFloat amplitude); //! Compute one output sample. - MY_FLOAT tick(); + StkFloat tick(); + + //! Computer \e vectorSize outputs and return them in \e vector. + StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument should be one or greater (the first + channel is specified by 1). An StkError will be thrown if the \c + channel argument is zero or it is greater than the number of + channels in the StkFrames object. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); protected: - WvIn *waves[VOICE_POLYPHONY]; - OnePole *filters[VOICE_POLYPHONY]; - int sounding[VOICE_POLYPHONY]; - int nSounding; + WvIn *waves_[VOICE_POLYPHONY]; + OnePole *filters_[VOICE_POLYPHONY]; + int sounding_[VOICE_POLYPHONY]; + int nSounding_; }; diff --git a/projects/ragamatic/ragamat.cpp b/projects/ragamatic/ragamat.cpp index e162c17..66201d1 100644 --- a/projects/ragamatic/ragamat.cpp +++ b/projects/ragamatic/ragamat.cpp @@ -1,272 +1,342 @@ /************** Test Main Program Individual Voice *********************/ -#include "RtWvOut.h" #include "SKINI.msg" #include "Instrmnt.h" -#include "Reverb.h" #include "JCRev.h" #include "Drone.h" #include "Sitar.h" #include "Tabla.h" #include "VoicDrum.h" - -// The input control handler. #include "Messager.h" +#include "RtAudio.h" -MY_FLOAT float_random(MY_FLOAT max) // Return random float between 0.0 and max +#include +#include +#include +#if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0 + using std::min; +#endif + +StkFloat float_random(StkFloat max) // Return random float between 0.0 and max { - MY_FLOAT temp = (MY_FLOAT) (max * rand() / (RAND_MAX + 1.0) ); + StkFloat temp = (StkFloat) (max * rand() / (RAND_MAX + 1.0) ); return temp; } void usage(void) { - /* Error function in case of incorrect command-line argument specifications */ - printf("\nuseage: ragamat flags \n"); - printf(" where flag = -s RATE to specify a sample rate,\n"); - printf(" flag = -ip for realtime SKINI input by pipe\n"); - printf(" (won't work under Win95/98),\n"); - printf(" and flag = -is for realtime SKINI input by socket.\n"); + // Error function in case of incorrect command-line argument specifications. + std::cout << "\nuseage: ragamat flags \n"; + std::cout << " where flag = -s RATE to specify a sample rate,\n"; + std::cout << " flag = -ip for realtime SKINI input by pipe\n"; + std::cout << " (won't work under Win95/98),\n"; + std::cout << " and flag = -is for realtime SKINI input by socket.\n"; exit(0); } -int main(int argc,char *argv[]) -{ - bool done; - RtWvOut *output; - Instrmnt *drones[3]; - Instrmnt *sitar; - Instrmnt *voicDrums; - Instrmnt *tabla; - Reverb *reverbs[2]; - SKINI *score; - Messager *messager; - MY_FLOAT t60 = 4.0; // in seconds +bool done; +static void finish(int ignore){ done = true; } - MY_FLOAT drone_prob = 0.01, note_prob = 0.0; - MY_FLOAT drum_prob = 0.0, voic_prob = 0.0; - MY_FLOAT droneFreqs[3] = {55.0,82.5,220.0}; - int tempo = 3000; - int counter = 3000; - int key = 0; - int ragaStep, ragaPoint = 6, voicNote; - int ragaUp[2][13] = {{57, 60, 62, 64, 65, 68, 69, 71, 72, 76, 77, 81}, - {52, 54, 55, 57, 59, 60, 63, 64, 66, 67, 71, 72}}; - int ragaDown[2][13] = {{57, 60, 62, 64, 65, 67, 69, 71, 72, 76, 79, 81}, - {48, 52, 53, 55, 57, 59, 60, 64, 66, 68, 70, 72}}; +// The TickData structure holds all the class instances and data that +// are shared by the various processing functions. +struct TickData { + JCRev reverbs[2]; + Drone drones[3]; + Sitar sitar; + VoicDrum voicDrums; + Tabla tabla; + Messager messager; + Skini::Message message; + StkFloat lastSample; + StkFloat t60; + int counter; + bool settling; + bool haveMessage; + StkFloat droneChance, noteChance; + StkFloat drumChance, voiceChance; + int tempo; + int chanceCounter; + int key; + int ragaStep; + int ragaPoint; + int endPhase; + StkFloat rateScaler; + + // Default constructor. + TickData() + : t60(4.0), counter(0), + settling( false ), haveMessage( false ), droneChance(0.01), noteChance(0.01), + drumChance(0.0), voiceChance(0.0), tempo(3000), chanceCounter(3000), key(0), ragaPoint(6), endPhase(0) {} +}; + +// Raga key numbers and drone frequencies. +const int ragaUp[2][13] = {{57, 60, 62, 64, 65, 68, 69, 71, 72, 76, 77, 81}, + {52, 54, 55, 57, 59, 60, 63, 64, 66, 67, 71, 72}}; + +const int ragaDown[2][13] = {{57, 60, 62, 64, 65, 67, 69, 71, 72, 76, 79, 81}, + {48, 52, 53, 55, 57, 59, 60, 64, 66, 68, 70, 72}}; + +StkFloat droneFreqs[3] = { 55.0, 82.5, 220.0 }; + +#define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks + +// The processMessage() function encapsulates the handling of control +// messages. It can be easily relocated within a program structure +// depending on the desired scheduling scheme. +void processMessage( TickData* data ) +{ + register unsigned int value1 = data->message.intValues[0]; + register StkFloat value2 = data->message.floatValues[1]; + register StkFloat temp = value2 * ONE_OVER_128; + + switch( data->message.type ) { + + case __SK_Exit_: + if ( data->settling == false ) goto settle; + if ( data->endPhase < 5 ) return; + done = true; + return; + + case __SK_ControlChange_: + + switch ( value1 ) { + + case 1: + data->droneChance = temp; + break; + + case 2: + data->noteChance = temp; + break; + + case 4: + data->voiceChance = temp; + break; + + case 7: + data->tempo = (int) (11025 - value2 * 70.0 ); + break; + + case 11: + data->drumChance = temp; + break; + + case 64: + if ( value2 == 0.0 ) { + data->key = 1; + droneFreqs[0] = 55.0; + droneFreqs[1] = 82.5; + droneFreqs[2] = 220.0; + } + else { + data->key = 0; + droneFreqs[0] = 82.5; + droneFreqs[1] = 123.5; + droneFreqs[2] = 330.0; + } + break; + + default: + break; + } + + } // end of type switch + + data->haveMessage = false; + return; + + settle: + // Exit and program change messages are preceeded with a short settling period. + data->counter = (int) (data->t60 * Stk::sampleRate()); + data->drones[1].noteOn( droneFreqs[1], 0.1 ); + data->settling = true; + std::cout << "What Need Have I for This?\n"; +} + +// The tick() function handles sample computation and scheduling of +// control updates. It will be called automatically by RtAudio when +// the system needs a new buffer of audio samples. +int tick(char *buffer, int bufferSize, void *dataPointer) +{ + TickData *data = (TickData *) dataPointer; + register StkFloat temp, outs[2], *samples = (StkFloat *) buffer; + int i, voiceNote, counter, nTicks = bufferSize; + + while ( nTicks > 0 && !done ) { + + if ( !data->haveMessage ) { + data->messager.popMessage( data->message ); + if ( data->message.type > 0 ) { + data->counter = (long) (data->message.time * Stk::sampleRate()); + data->haveMessage = true; + } + else + data->counter = DELTA_CONTROL_TICKS; + } + + counter = min( nTicks, data->counter ); + data->counter -= counter; + for ( i=0; ireverbs[0].tick( data->drones[0].tick() + data->drones[2].tick() + + data->sitar.tick() ); + outs[1] = data->reverbs[1].tick( 1.5 * data->drones[1].tick() + 0.5 * data->voicDrums.tick() + + 0.5 * data->tabla.tick() ); + // Mix a little left to right and back. + *samples++ = outs[0] + 0.3 * outs[1]; + *samples++ = outs[1] + 0.3 * outs[0]; + nTicks--; + + // Do a bunch of random controls unless settling down to end. + if ( data->settling ) { + if ( data->counter == 0 ) { + if ( data->endPhase++ == 0 ) { + data->counter = (int) (data->t60 * Stk::sampleRate()); + data->drones[2].noteOn( droneFreqs[2], 0.1 ); + std::cout << "What Need Have I for This?\n"; + } + else if ( data->endPhase == 1 ) { + data->counter = (int) (data->t60 * Stk::sampleRate()); + data->drones[0].noteOn( droneFreqs[0], 0.1 ); + std::cout << "RagaMatic finished ... \n"; + } + else if ( data->endPhase == 2 ) { + data->counter = (int) (data->t60 * Stk::sampleRate()); + std::cout << "All is Bliss ...\n"; + } + else if ( data->endPhase == 3 ) { + std::cout << "All is Bliss ...\n"; + data->counter = (int) (data->t60 * Stk::sampleRate()); + } + } + } + else { + data->chanceCounter--; + if (data->chanceCounter == 0) { + data->chanceCounter = (int) ( data->tempo / data->rateScaler ); + if ( float_random(1.0) < data->droneChance ) + data->drones[0].noteOn( droneFreqs[0], 0.1 ); + if ( float_random(1.0) < data->droneChance ) + data->drones[1].noteOn( droneFreqs[1], 0.1 ); + if ( float_random(1.0) < data->droneChance ) + data->drones[2].noteOn( droneFreqs[2], 0.1 ); + if ( float_random(1.0) < data->noteChance ) { + temp = float_random(1.0); + if ( temp < 0.1) data->ragaStep = 0; + else if (temp < 0.5) data->ragaStep = 1; + else data->ragaStep = -1; + data->ragaPoint += data->ragaStep; + if ( data->ragaPoint < 0 ) + data->ragaPoint -= ( 2 * data->ragaStep ); + if ( data->ragaPoint > 11 ) data->ragaPoint = 11; + if ( data->ragaStep > 0 ) + data->sitar.noteOn( Midi2Pitch[ragaUp[data->key][data->ragaPoint]], + 0.05 + float_random(0.3) ); + else + data->sitar.noteOn( Midi2Pitch[ragaDown[data->key][data->ragaPoint]], + 0.05 + float_random(0.3) ); + } + if ( float_random(1.0) < data->voiceChance ) { + voiceNote = (int) float_random(11); + data->voicDrums.noteOn( voiceNote, 0.3 + (0.4 * data->drumChance) + + float_random(0.3 * data->voiceChance)); + } + if ( float_random(1.0) < data->drumChance ) { + voiceNote = (int) float_random(TABLA_NUMWAVES); + data->tabla.noteOn( voiceNote, 0.2 + (0.2 * data->drumChance) + + float_random(0.6 * data->drumChance)); + } + } + } + } + if ( nTicks == 0 ) break; + + // Process control messages. + if ( data->haveMessage ) processMessage( data ); + } + + return 0; +} + +int main( int argc, char *argv[] ) +{ + TickData data; + RtAudio *dac = 0; + int i; + + if (argc < 2 || argc > 6) usage(); // 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 ); - if (argc < 2 || argc > 6) usage(); - - int port = -1; - int i, controlMask = 0; + // Parse the command-line arguments. + unsigned int port = 2001; for ( i=1; i= 0 ) - messager = new Messager( controlMask, port ); - else - messager = new Messager( controlMask ); + dac = new RtAudio(0, 2, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); } - catch (StkError &) { - exit(0); + catch (RtError& error) { + error.printMessage(); + goto cleanup; } - drones[0] = new Drone(50.0); - drones[1] = new Drone(50.0); - drones[2] = new Drone(50.0); - sitar = new Sitar(50.0); - voicDrums = new VoicDrum(); - tabla = new Tabla(); + data.reverbs[0].setT60( data.t60 ); + data.reverbs[0].setEffectMix( 0.5 ); + data.reverbs[1].setT60( 2.0 ); + data.reverbs[1].setEffectMix( 0.2 ); - score = new SKINI(); - reverbs[0] = new JCRev(t60); - reverbs[0]->setEffectMix(0.5); - reverbs[1] = new JCRev(2.0); - reverbs[1]->setEffectMix(0.2); + data.drones[0].noteOn( droneFreqs[0], 0.1 ); + data.drones[1].noteOn( droneFreqs[1], 0.1 ); + data.drones[2].noteOn( droneFreqs[2], 0.1 ); - drones[0]->noteOn(droneFreqs[0],0.1); - drones[1]->noteOn(droneFreqs[1],0.1); - drones[2]->noteOn(droneFreqs[2],0.1); + data.rateScaler = 22050.0 / Stk::sampleRate(); - MY_FLOAT outSamples[2]; - for (i=0;itick(drones[0]->tick() + drones[2]->tick()); - outSamples[1] = reverbs[1]->tick(1.5 * drones[1]->tick()); - output->tickFrame(outSamples); + // Install an interrupt handler function. + (void) signal( SIGINT, finish ); + + // If realtime output, set our callback function and start the dac. + try { + dac->setStreamCallback( &tick, (void *)&data ); + dac->startStream(); + } + catch (RtError &error) { + error.printMessage(); + goto cleanup; } - // The runtime loop begins here: - done = FALSE; - MY_FLOAT rateScaler = 22050.0 / Stk::sampleRate(); - int nTicks, type; - MY_FLOAT temp, byte2, byte3; - while (!done) { - - type = messager->nextMessage(); - if (type < 0) - done = TRUE; - - nTicks = messager->getDelta(); - - for (i=0; itick(drones[0]->tick() + drones[2]->tick() - + sitar->tick()); - outSamples[1] = reverbs[1]->tick(1.5 * drones[1]->tick() + 0.5 * voicDrums->tick() - + 0.5 * tabla->tick()); - // mix a little left to right and back - temp = outSamples[0]; - outSamples[0] += 0.3 * outSamples[1]; - outSamples[1] += 0.3 * temp; - output->tickFrame(outSamples); - - counter -= 1; - if (counter == 0) { - counter = (int) (tempo / rateScaler); - if (float_random(1.0) < drone_prob) - drones[0]->noteOn(droneFreqs[0], 0.1); - if (float_random(1.0) < drone_prob) - drones[1]->noteOn(droneFreqs[1], 0.1); - if (float_random(1.0) < drone_prob) - drones[2]->noteOn(droneFreqs[2], 0.1); - if (float_random(1.0) < note_prob) { - if ((temp = float_random(1.0)) < 0.1) - ragaStep = 0; - else if (temp < 0.5) - ragaStep = 1; - else - ragaStep = -1; - ragaPoint += ragaStep; - if (ragaPoint < 0) - ragaPoint -= (2*ragaStep); - if (ragaPoint > 11) - ragaPoint = 11; - if (ragaStep > 0) - sitar->noteOn(Midi2Pitch[ragaUp[key][ragaPoint]], - 0.05 + float_random(0.3)); - else - sitar->noteOn(Midi2Pitch[ragaDown[key][ragaPoint]], - 0.05 + float_random(0.3)); - } - if (float_random(1.0) < voic_prob) { - voicNote = (int) float_random(11); - voicDrums->noteOn(voicNote, 0.3 + (0.4 * drum_prob) + - float_random(0.3 * voic_prob)); - } - if (float_random(1.0) < drum_prob) { - voicNote = (int) float_random(TABLA_NUMWAVES); - tabla->noteOn(voicNote, 0.2 + (0.2 * drum_prob) + - float_random(0.6 * drum_prob)); - } - } - } - - if ( type > 0 ) { - // parse the input control message - - byte2 = messager->getByteTwo(); - byte3 = messager->getByteThree(); - - switch(type) { - - case __SK_ControlChange_: - if (byte2 == 1) { - drone_prob = byte3 * ONE_OVER_128; - } - else if (byte2 == 2) { - note_prob = byte3 * ONE_OVER_128; - } - else if (byte2 == 4) { - voic_prob = byte3 * ONE_OVER_128; - } - else if (byte2 == 11) { - drum_prob = byte3 * ONE_OVER_128; - } - else if (byte2 == 7) { - tempo = (int) (11025 - (byte3 * 70)); - } - else if (byte2 == 64) { - if (byte3 == 0) { - key = 1; - droneFreqs[0] = 55.0; - droneFreqs[1] = 82.5; - droneFreqs[2] = 220.0; - } - else { - key = 0; - droneFreqs[0] = 82.5; - droneFreqs[1] = 123.5; - droneFreqs[2] = 330.0; - } - } - } - } + // Setup finished. + while ( !done ) { + // Periodically check "done" status. + Stk::sleep( 50 ); } - nTicks = (long) (t60 * Stk::sampleRate()); - - printf("What Need Have I for This?\n"); - drones[1]->noteOn(droneFreqs[1],0.1); - for (i=0; itick(drones[0]->tick() + drones[2]->tick()); - outSamples[1] = reverbs[1]->tick(1.5 * drones[1]->tick()); - output->tickFrame(outSamples); + // Shut down the output stream. + try { + dac->cancelStreamCallback(); + dac->closeStream(); } - printf("What Need Have I for This?\n"); - drones[2]->noteOn(droneFreqs[2],0.1); - for (i=0; itick(drones[0]->tick() + drones[2]->tick()); - outSamples[1] = reverbs[1]->tick(1.5 * drones[1]->tick()); - output->tickFrame(outSamples); - } - printf("RagaMatic finished ... \n"); - drones[0]->noteOn(droneFreqs[0],0.1); - for (i=0; itick(drones[0]->tick() + drones[2]->tick()); - outSamples[1] = reverbs[1]->tick(1.5 * drones[1]->tick()); - output->tickFrame(outSamples); - } - printf("All is Bliss ...\n"); - for (i=0; itick(drones[0]->tick() + drones[2]->tick()); - outSamples[1] = reverbs[1]->tick(1.5 * drones[1]->tick()); - output->tickFrame(outSamples); - } - printf("All is Bliss ...\n"); - for (i=0; itick(drones[0]->tick() + drones[2]->tick()); - outSamples[1] = reverbs[1]->tick(1.5 * drones[1]->tick()); - output->tickFrame(outSamples); + catch (RtError& error) { + error.printMessage(); } - delete output; - delete score; - delete drones[0]; - delete drones[1]; - delete drones[2]; - delete sitar; - delete tabla; - delete voicDrums; - delete reverbs[0]; - delete reverbs[1]; - delete messager; + cleanup: + delete dac; return 0; + } diff --git a/projects/ragamatic/ragamat.dsp b/projects/ragamatic/ragamat.dsp index e8d8090..9a362c1 100644 --- a/projects/ragamatic/ragamat.dsp +++ b/projects/ragamatic/ragamat.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /YX /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__LITTLE_ENDIAN__" /D "__WINDOWS_MM__" /YX /FD /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -124,6 +124,14 @@ SOURCE=.\Drone.h # End Source File # Begin Source File +SOURCE=..\..\src\Effect.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\Effect.h +# End Source File +# Begin Source File + SOURCE=..\..\src\Envelope.cpp # End Source File # Begin Source File @@ -140,6 +148,14 @@ SOURCE=..\..\include\Filter.h # End Source File # Begin Source File +SOURCE=..\..\src\Generator.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\Generator.h +# End Source File +# Begin Source File + SOURCE=..\..\src\Instrmnt.cpp # End Source File # Begin Source File @@ -164,6 +180,14 @@ SOURCE=..\..\include\Messager.h # End Source File # Begin Source File +SOURCE=..\..\src\Mutex.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\Mutex.h +# End Source File +# Begin Source File + SOURCE=..\..\src\Noise.cpp # End Source File # Begin Source File @@ -192,14 +216,6 @@ SOURCE=.\ragamat.cpp # End Source File # Begin Source File -SOURCE=..\..\src\Reverb.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\include\Reverb.h -# End Source File -# Begin Source File - SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File @@ -216,14 +232,6 @@ SOURCE=..\..\include\RtMidi.h # End Source File # Begin Source File -SOURCE=..\..\src\RtWvOut.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\include\RtWvOut.h -# End Source File -# Begin Source File - SOURCE=..\..\src\Sitar.cpp # End Source File # Begin Source File diff --git a/src/ADSR.cpp b/src/ADSR.cpp index b2f837f..cd16dfb 100644 --- a/src/ADSR.cpp +++ b/src/ADSR.cpp @@ -11,22 +11,21 @@ envelope value reaches 0.0 in the ADSR::RELEASE state. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "ADSR.h" -#include ADSR :: ADSR() : Envelope() { - target = (MY_FLOAT) 0.0; - value = (MY_FLOAT) 0.0; - attackRate = (MY_FLOAT) 0.001; - decayRate = (MY_FLOAT) 0.001; - sustainLevel = (MY_FLOAT) 0.5; - releaseRate = (MY_FLOAT) 0.01; - state = ATTACK; + target_ = 0.0; + value_ = 0.0; + attackRate_ = 0.001; + decayRate_ = 0.001; + sustainLevel_ = 0.5; + releaseRate_ = 0.01; + state_ = ATTACK; } ADSR :: ~ADSR() @@ -35,82 +34,89 @@ ADSR :: ~ADSR() void ADSR :: keyOn() { - target = (MY_FLOAT) 1.0; - rate = attackRate; - state = ATTACK; + target_ = 1.0; + rate_ = attackRate_; + state_ = ATTACK; } void ADSR :: keyOff() { - target = (MY_FLOAT) 0.0; - rate = releaseRate; - state = RELEASE; + target_ = 0.0; + rate_ = releaseRate_; + state_ = RELEASE; } -void ADSR :: setAttackRate(MY_FLOAT aRate) +void ADSR :: setAttackRate(StkFloat rate) { - if (aRate < 0.0) { - printf("ADSR: negative rates not allowed ... correcting!\n"); - attackRate = -aRate; + if (rate < 0.0) { + errorString_ << "ADSR::setAttackRate: negative rates not allowed ... correcting!"; + handleError( StkError::WARNING ); + attackRate_ = -rate; } - else attackRate = aRate; + else attackRate_ = rate; } -void ADSR :: setDecayRate(MY_FLOAT aRate) +void ADSR :: setDecayRate(StkFloat rate) { - if (aRate < 0.0) { - printf("ADSR: negative rates not allowed ... correcting!\n"); - decayRate = -aRate; + if (rate < 0.0) { + errorString_ << "ADSR::setDecayRate: negative rates not allowed ... correcting!"; + handleError( StkError::WARNING ); + decayRate_ = -rate; } - else decayRate = aRate; + else decayRate_ = rate; } -void ADSR :: setSustainLevel(MY_FLOAT aLevel) +void ADSR :: setSustainLevel(StkFloat level) { - if (aLevel < 0.0 ) { - printf("ADSR: sustain level out of range ... correcting!\n"); - sustainLevel = (MY_FLOAT) 0.0; + if (level < 0.0 ) { + errorString_ << "ADSR::setSustainLevel: level out of range ... correcting!"; + handleError( StkError::WARNING ); + sustainLevel_ = 0.0; } - else sustainLevel = aLevel; + else sustainLevel_ = level; } -void ADSR :: setReleaseRate(MY_FLOAT aRate) +void ADSR :: setReleaseRate(StkFloat rate) { - if (aRate < 0.0) { - printf("ADSR: negative rates not allowed ... correcting!\n"); - releaseRate = -aRate; + if (rate < 0.0) { + errorString_ << "ADSR::setReleaseRate: negative rates not allowed ... correcting!"; + handleError( StkError::WARNING ); + releaseRate_ = -rate; } - else releaseRate = aRate; + else releaseRate_ = rate; } -void ADSR :: setAttackTime(MY_FLOAT aTime) +void ADSR :: setAttackTime(StkFloat time) { - if (aTime < 0.0) { - printf("ADSR: negative rates not allowed ... correcting!\n"); - attackRate = 1.0 / ( -aTime * Stk::sampleRate() ); + if (time < 0.0) { + errorString_ << "ADSR::setAttackTime: negative times not allowed ... correcting!"; + handleError( StkError::WARNING ); + attackRate_ = 1.0 / ( -time * Stk::sampleRate() ); } - else attackRate = 1.0 / ( aTime * Stk::sampleRate() ); + else attackRate_ = 1.0 / ( time * Stk::sampleRate() ); } -void ADSR :: setDecayTime(MY_FLOAT aTime) +void ADSR :: setDecayTime(StkFloat time) { - if (aTime < 0.0) { - printf("ADSR: negative times not allowed ... correcting!\n"); - decayRate = 1.0 / ( -aTime * Stk::sampleRate() ); + if (time < 0.0) { + errorString_ << "ADSR::setDecayTime: negative times not allowed ... correcting!"; + handleError( StkError::WARNING ); + decayRate_ = 1.0 / ( -time * Stk::sampleRate() ); } - else decayRate = 1.0 / ( aTime * Stk::sampleRate() ); + else decayRate_ = 1.0 / ( time * Stk::sampleRate() ); } -void ADSR :: setReleaseTime(MY_FLOAT aTime) +void ADSR :: setReleaseTime(StkFloat time) { - if (aTime < 0.0) { - printf("ADSR: negative times not allowed ... correcting!\n"); - releaseRate = sustainLevel / ( -aTime * Stk::sampleRate() ); + if (time < 0.0) { + errorString_ << "ADSR::setReleaseTime: negative times not allowed ... correcting!"; + handleError( StkError::WARNING ); + releaseRate_ = sustainLevel_ / ( -time * Stk::sampleRate() ); } - else releaseRate = sustainLevel / ( aTime * Stk::sampleRate() ); + else releaseRate_ = sustainLevel_ / ( time * Stk::sampleRate() ); } -void ADSR :: setAllTimes(MY_FLOAT aTime, MY_FLOAT dTime, MY_FLOAT sLevel, MY_FLOAT rTime) +void ADSR :: setAllTimes(StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime) { this->setAttackTime(aTime); this->setDecayTime(dTime); @@ -118,73 +124,76 @@ void ADSR :: setAllTimes(MY_FLOAT aTime, MY_FLOAT dTime, MY_FLOAT sLevel, MY_FLO this->setReleaseTime(rTime); } -void ADSR :: setTarget(MY_FLOAT aTarget) +void ADSR :: setTarget(StkFloat target) { - target = aTarget; - if (value < target) { - state = ATTACK; - this->setSustainLevel(target); - rate = attackRate; + target_ = target; + if (value_ < target_) { + state_ = ATTACK; + this->setSustainLevel(target_); + rate_ = attackRate_; } - if (value > target) { - this->setSustainLevel(target); - state = DECAY; - rate = decayRate; + if (value_ > target_) { + this->setSustainLevel(target_); + state_ = DECAY; + rate_ = decayRate_; } } -void ADSR :: setValue(MY_FLOAT aValue) +void ADSR :: setValue(StkFloat value) { - state = SUSTAIN; - target = aValue; - value = aValue; - this->setSustainLevel(aValue); - rate = (MY_FLOAT) 0.0; + state_ = SUSTAIN; + target_ = value; + value_ = value; + this->setSustainLevel(value); + rate_ = (StkFloat) 0.0; } int ADSR :: getState(void) const { - return state; + return state_; } -MY_FLOAT ADSR :: tick() +StkFloat ADSR :: tick() { - switch (state) { + switch (state_) { case ATTACK: - value += rate; - if (value >= target) { - value = target; - rate = decayRate; - target = sustainLevel; - state = DECAY; + value_ += rate_; + if (value_ >= target_) { + value_ = target_; + rate_ = decayRate_; + target_ = sustainLevel_; + state_ = DECAY; } break; case DECAY: - value -= decayRate; - if (value <= sustainLevel) { - value = sustainLevel; - rate = (MY_FLOAT) 0.0; - state = SUSTAIN; + value_ -= decayRate_; + if (value_ <= sustainLevel_) { + value_ = sustainLevel_; + rate_ = (StkFloat) 0.0; + state_ = SUSTAIN; } break; case RELEASE: - value -= releaseRate; - if (value <= 0.0) { - value = (MY_FLOAT) 0.0; - state = DONE; + value_ -= releaseRate_; + if (value_ <= 0.0) { + value_ = (StkFloat) 0.0; + state_ = DONE; } } - return value; + lastOutput_ = value_; + return value_; } -MY_FLOAT *ADSR :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *ADSR :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i + +Asymp :: Asymp(void) : Envelope() +{ + factor_ = exp( -1.0 / ( 0.3 * Stk::sampleRate() ) ); + constant_ = 0.0; +} + +Asymp :: ~Asymp(void) +{ +} + +void Asymp :: keyOn(void) +{ + Envelope::keyOn(); + constant_ = ( 1.0 - factor_ ) * target_; +} + +void Asymp :: keyOff(void) +{ + Envelope::keyOff(); + constant_ = ( 1.0 - factor_ ) * target_; +} + +void Asymp :: setTau(StkFloat tau) +{ + if (tau <= 0.0) { + errorString_ << "Asymp::setTau: negative or zero tau not allowed ... ignoring!"; + handleError( StkError::WARNING ); + return; + } + + factor_ = exp( -1.0 / ( tau * Stk::sampleRate() ) ); + constant_ = ( 1.0 - factor_ ) * target_; +} + +void Asymp :: setTime(StkFloat time) +{ + if (time <= 0.0) { + errorString_ << "Asymp::setTime: negative or zero times not allowed ... ignoring!"; + handleError( StkError::WARNING ); + return; + } + + StkFloat tau = -time / log( TARGET_THRESHOLD ); + factor_ = exp( -1.0 / ( tau * Stk::sampleRate() ) ); + constant_ = ( 1.0 - factor_ ) * target_; +} + +void Asymp :: setTarget(StkFloat target) +{ + Envelope::setTarget( target ); + constant_ = ( 1.0 - factor_ ) * target_; +} + +StkFloat Asymp :: tick(void) +{ + if (state_) { + + value_ = factor_ * value_ + constant_; + + // Check threshold. + if ( target_ > value_ ) { + if ( target_ - value_ <= TARGET_THRESHOLD ) { + value_ = target_; + state_ = 0; + } + } + else { + if ( value_ - target_ <= TARGET_THRESHOLD ) { + value_ = target_; + state_ = 0; + } + } + } + + lastOutput_ = value_; + return value_; +} + +StkFloat *Asymp :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Generator::tick( vector, vectorSize ); +} + +StkFrames& Asymp :: tick( StkFrames& frames, unsigned int channel ) +{ + return Generator::tick( frames, channel ); +} diff --git a/src/BandedWG.cpp b/src/BandedWG.cpp index 7fca402..af36953 100644 --- a/src/BandedWG.cpp +++ b/src/BandedWG.cpp @@ -24,7 +24,7 @@ - Glass Harmonica = 2 - Tibetan Bowl = 3 - by Georg Essl, 1999 - 2002. + by Georg Essl, 1999 - 2004. Modified for Stk 4.0 by Gary Scavone. */ /***************************************************/ @@ -36,45 +36,35 @@ BandedWG :: BandedWG() { - doPluck = true; + doPluck_ = true; - delay = new DelayL[MAX_BANDED_MODES]; - bandpass = new BiQuad[MAX_BANDED_MODES]; + bowTable_.setSlope( 3.0 ); + adsr_.setAllTimes( 0.02, 0.005, 0.9, 0.01); + + frequency_ = 220.0; + this->setPreset(0); + + bowPosition_ = 0; + baseGain_ = (StkFloat) 0.999; - bowTabl = new BowTabl; - bowTabl->setSlope( 3.0 ); + integrationConstant_ = 0.0; + trackVelocity_ = false; - adsr = new ADSR; - adsr->setAllTimes( 0.02, 0.005, 0.9, 0.01); + bowVelocity_ = 0.0; + bowTarget_ = 0.0; - freakency = 220.0; - setPreset(0); - - bowPosition = 0; - baseGain = (MY_FLOAT) 0.999; - - integrationConstant = 0.0; - trackVelocity = false; - - bowVelocity = 0.0; - bowTarget = 0.0; - - strikeAmp = 0.0; + strikeAmp_ = 0.0; } BandedWG :: ~BandedWG() { - delete bowTabl; - delete adsr; - delete [] bandpass; - delete [] delay; } void BandedWG :: clear() { - for (int i=0; i 1568.0) freakency = 1568.0; + if (frequency_ > 1568.0) frequency_ = 1568.0; - MY_FLOAT radius; - MY_FLOAT base = Stk::sampleRate() / freakency; - MY_FLOAT length; - for (int i=0; i 2.0) { - delay[i].setDelay( length ); - gains[i]=basegains[i]; - // gains[i]=(MY_FLOAT) pow(basegains[i], 1/((MY_FLOAT)delay[i].getDelay())); - // std::cerr << gains[i]; + delay_[i].setDelay( length ); + gains_[i]=basegains_[i]; + // gains_[i]=(StkFloat) pow(basegains_[i], 1/((StkFloat)delay_[i].getDelay())); + // std::cerr << gains_[i]; } else { - nModes = i; + nModes_ = i; break; } // std::cerr << std::endl; // Set the bandpass filter resonances - radius = 1.0 - PI * 32 / Stk::sampleRate(); //freakency * modes[i] / Stk::sampleRate()/32; + radius = 1.0 - PI * 32 / Stk::sampleRate(); //frequency_ * modes_[i] / Stk::sampleRate()/32; if ( radius < 0.0 ) radius = 0.0; - bandpass[i].setResonance(freakency * modes[i], radius, true); + bandpass_[i].setResonance(frequency_ * modes_[i], radius, true); - delay[i].clear(); - bandpass[i].clear(); + delay_[i].clear(); + bandpass_[i].clear(); } - //int olen = (int)(delay[0].getDelay()); - //strikePosition = (int)(strikePosition*(length/modes[0])/olen); + //int olen = (int)(delay_[0].getDelay()); + //strikePosition_ = (int)(strikePosition_*(length/modes_[0])/olen); } -void BandedWG :: setStrikePosition(MY_FLOAT position) +void BandedWG :: setStrikePosition(StkFloat position) { - strikePosition = (int)(delay[0].getDelay() * position / 2.0); + strikePosition_ = (int)(delay_[0].getDelay() * position / 2.0); } -void BandedWG :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate) +void BandedWG :: startBowing(StkFloat amplitude, StkFloat rate) { - adsr->setRate(rate); - adsr->keyOn(); - maxVelocity = 0.03 + (0.1 * amplitude); + adsr_.setRate(rate); + adsr_.keyOn(); + maxVelocity_ = 0.03 + (0.1 * amplitude); } -void BandedWG :: stopBowing(MY_FLOAT rate) +void BandedWG :: stopBowing(StkFloat rate) { - adsr->setRate(rate); - adsr->keyOff(); + adsr_.setRate(rate); + adsr_.keyOff(); } -void BandedWG :: pluck(MY_FLOAT amplitude) +void BandedWG :: pluck(StkFloat amplitude) { int j; - MY_FLOAT min_len = delay[nModes-1].getDelay(); - for (int i=0; isetFrequency(frequency); - if ( doPluck ) + if ( doPluck_ ) this->pluck(amplitude); else this->startBowing(amplitude, amplitude * 0.001); #if defined(_STK_DEBUG_) - std::cerr << "BandedWG: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "BandedWG::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void BandedWG :: noteOff(MY_FLOAT amplitude) +void BandedWG :: noteOff(StkFloat amplitude) { - if ( !doPluck ) + if ( !doPluck_ ) this->stopBowing((1.0 - amplitude) * 0.005); #if defined(_STK_DEBUG_) - std::cerr << "BandedWG: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "BandedWG::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT BandedWG :: tick() +StkFloat BandedWG :: tick() { int k; - MY_FLOAT input = 0.0; - if ( doPluck ) { + StkFloat input = 0.0; + if ( doPluck_ ) { input = 0.0; - // input = strikeAmp/nModes; - // strikeAmp = 0.0; + // input = strikeAmp_/nModes_; + // strikeAmp_ = 0.0; } else { - if (integrationConstant == 0.0) - velocityInput = 0.0; + if (integrationConstant_ == 0.0) + velocityInput_ = 0.0; else - velocityInput = integrationConstant * velocityInput; + velocityInput_ = integrationConstant_ * velocityInput_; - for (k=0; ktick() * maxVelocity; + bowVelocity_ = adsr_.tick() * maxVelocity_; - input = bowVelocity - velocityInput; - input = input * bowTabl->tick(input); - input = input/(MY_FLOAT)nModes; + input = bowVelocity_ - velocityInput_; + input = input * bowTable_.tick(input); + input = input/(StkFloat)nModes_; } - MY_FLOAT data = 0.0; - for (k=0; k 1.0 ) { norm = 1.0; - std::cerr << "BandedWG: Control value greater than 128.0!" << std::endl; + errorString_ << "BandedWG::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_BowPressure_) { // 2 if ( norm == 0.0 ) - doPluck = true; + doPluck_ = true; else { - doPluck = false; - bowTabl->setSlope( 10.0 - (9.0 * norm)); + doPluck_ = false; + bowTable_.setSlope( 10.0 - (9.0 * norm)); } } else if (number == 4) { // 4 - if ( !trackVelocity ) trackVelocity = true; - bowTarget += 0.005 * (norm - bowPosition); - bowPosition = norm; - //adsr->setTarget(bowPosition); + if ( !trackVelocity_ ) trackVelocity_ = true; + bowTarget_ += 0.005 * (norm - bowPosition_); + bowPosition_ = norm; + //adsr_.setTarget(bowPosition_); } else if (number == 8) // 8 this->setStrikePosition( norm ); else if (number == __SK_AfterTouch_Cont_) { // 128 - //bowTarget += 0.02 * (norm - bowPosition); - //bowPosition = norm; - if ( trackVelocity ) trackVelocity = false; - maxVelocity = 0.13 * norm; - adsr->setTarget(norm); + //bowTarget_ += 0.02 * (norm - bowPosition_); + //bowPosition_ = norm; + if ( trackVelocity_ ) trackVelocity_ = false; + maxVelocity_ = 0.13 * norm; + adsr_.setTarget(norm); } else if (number == __SK_ModWheel_) { // 1 - // baseGain = 0.9989999999 + (0.001 * norm ); - baseGain = 0.8999999999999999 + (0.1 * norm); + // baseGain_ = 0.9989999999 + (0.001 * norm ); + baseGain_ = 0.8999999999999999 + (0.1 * norm); // std::cerr << "Yuck!" << std::endl; - for (int i=0; isetPreset((int) value); - else - std::cerr << "BandedWG: Undefined Control Number (" << number << ")!!" << std::endl; + else { + errorString_ << "BandedWG::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "BandedWG: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "BandedWG::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/BeeThree.cpp b/src/BeeThree.cpp index e3f4989..389fce9 100644 --- a/src/BeeThree.cpp +++ b/src/BeeThree.cpp @@ -28,7 +28,7 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -38,66 +38,77 @@ BeeThree :: BeeThree() : FM() { // 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 ); + for ( unsigned 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); this->setRatio(2, 3.006); this->setRatio(3, 6.009); - gains[0] = __FM_gains[95]; - gains[1] = __FM_gains[95]; - gains[2] = __FM_gains[99]; - gains[3] = __FM_gains[95]; + gains_[0] = fmGains_[95]; + gains_[1] = fmGains_[95]; + gains_[2] = fmGains_[99]; + gains_[3] = fmGains_[95]; - adsr[0]->setAllTimes( 0.005, 0.003, 1.0, 0.01); - adsr[1]->setAllTimes( 0.005, 0.003, 1.0, 0.01); - adsr[2]->setAllTimes( 0.005, 0.003, 1.0, 0.01); - adsr[3]->setAllTimes( 0.005, 0.001, 0.4, 0.03); + adsr_[0]->setAllTimes( 0.005, 0.003, 1.0, 0.01); + adsr_[1]->setAllTimes( 0.005, 0.003, 1.0, 0.01); + adsr_[2]->setAllTimes( 0.005, 0.003, 1.0, 0.01); + adsr_[3]->setAllTimes( 0.005, 0.001, 0.4, 0.03); - twozero->setGain( 0.1 ); + twozero_.setGain( 0.1 ); } BeeThree :: ~BeeThree() { } -void BeeThree :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void BeeThree :: noteOn(StkFloat frequency, StkFloat amplitude) { - gains[0] = amplitude * __FM_gains[95]; - gains[1] = amplitude * __FM_gains[95]; - gains[2] = amplitude * __FM_gains[99]; - gains[3] = amplitude * __FM_gains[95]; - this->setFrequency(frequency); + gains_[0] = amplitude * fmGains_[95]; + gains_[1] = amplitude * fmGains_[95]; + gains_[2] = amplitude * fmGains_[99]; + gains_[3] = amplitude * fmGains_[95]; + this->setFrequency( frequency ); this->keyOn(); #if defined(_STK_DEBUG_) - cerr << "BeeThree: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + errorString_ << "BeeThree::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT BeeThree :: tick() +StkFloat BeeThree :: tick() { - register MY_FLOAT temp; + register StkFloat temp; - if (modDepth > 0.0) { - temp = 1.0 + (modDepth * vibrato->tick() * 0.1); - waves[0]->setFrequency(baseFrequency * temp * ratios[0]); - waves[1]->setFrequency(baseFrequency * temp * ratios[1]); - waves[2]->setFrequency(baseFrequency * temp * ratios[2]); - waves[3]->setFrequency(baseFrequency * temp * ratios[3]); + if (modDepth_ > 0.0) { + temp = 1.0 + (modDepth_ * vibrato_->tick() * 0.1); + waves_[0]->setFrequency(baseFrequency_ * temp * ratios_[0]); + waves_[1]->setFrequency(baseFrequency_ * temp * ratios_[1]); + waves_[2]->setFrequency(baseFrequency_ * temp * ratios_[2]); + waves_[3]->setFrequency(baseFrequency_ * temp * ratios_[3]); } - waves[3]->addPhaseOffset(twozero->lastOut()); - temp = control1 * 2.0 * gains[3] * adsr[3]->tick() * waves[3]->tick(); - twozero->tick(temp); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = control1_ * 2.0 * gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); - temp += control2 * 2.0 * gains[2] * adsr[2]->tick() * waves[2]->tick(); - temp += gains[1] * adsr[1]->tick() * waves[1]->tick(); - temp += gains[0] * adsr[0]->tick() * waves[0]->tick(); + temp += control2_ * 2.0 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + temp += gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp += gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); - lastOutput = temp * 0.125; - return lastOutput; + lastOutput_ = temp * 0.125; + return lastOutput_; +} + +StkFloat *BeeThree :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& BeeThree :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/BiQuad.cpp b/src/BiQuad.cpp index b970afb..b2e7f9d 100644 --- a/src/BiQuad.cpp +++ b/src/BiQuad.cpp @@ -8,7 +8,7 @@ frequency response while maintaining a constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -17,9 +17,11 @@ BiQuad :: BiQuad() : Filter() { - MY_FLOAT B[3] = {1.0, 0.0, 0.0}; - MY_FLOAT A[3] = {1.0, 0.0, 0.0}; - Filter::setCoefficients( 3, B, 3, A ); + std::vector b(3, 0.0); + std::vector a(3, 0.0); + b[0] = 1.0; + a[0] = 1.0; + Filter::setCoefficients( b, a ); } BiQuad :: ~BiQuad() @@ -31,90 +33,92 @@ void BiQuad :: clear(void) Filter::clear(); } -void BiQuad :: setB0(MY_FLOAT b0) +void BiQuad :: setB0(StkFloat b0) { - b[0] = b0; + b_[0] = b0; } -void BiQuad :: setB1(MY_FLOAT b1) +void BiQuad :: setB1(StkFloat b1) { - b[1] = b1; + b_[1] = b1; } -void BiQuad :: setB2(MY_FLOAT b2) +void BiQuad :: setB2(StkFloat b2) { - b[2] = b2; + b_[2] = b2; } -void BiQuad :: setA1(MY_FLOAT a1) +void BiQuad :: setA1(StkFloat a1) { - a[1] = a1; + a_[1] = a1; } -void BiQuad :: setA2(MY_FLOAT a2) +void BiQuad :: setA2(StkFloat a2) { - a[2] = a2; + a_[2] = a2; } -void BiQuad :: setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize) +void BiQuad :: setResonance(StkFloat frequency, StkFloat radius, bool normalize) { - a[2] = radius * radius; - a[1] = -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate()); + a_[2] = radius * radius; + a_[1] = -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate()); if ( normalize ) { // Use zeros at +- 1 and normalize the filter peak gain. - b[0] = 0.5 - 0.5 * a[2]; - b[1] = 0.0; - b[2] = -b[0]; + b_[0] = 0.5 - 0.5 * a_[2]; + b_[1] = 0.0; + b_[2] = -b_[0]; } } -void BiQuad :: setNotch(MY_FLOAT frequency, MY_FLOAT radius) +void BiQuad :: setNotch(StkFloat frequency, StkFloat radius) { // This method does not attempt to normalize the filter gain. - b[2] = radius * radius; - b[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate()); + b_[2] = radius * radius; + b_[1] = (StkFloat) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate()); } void BiQuad :: setEqualGainZeroes() { - b[0] = 1.0; - b[1] = 0.0; - b[2] = -1.0; + b_[0] = 1.0; + b_[1] = 0.0; + b_[2] = -1.0; } -void BiQuad :: setGain(MY_FLOAT theGain) +void BiQuad :: setGain(StkFloat gain) { - Filter::setGain(theGain); + Filter::setGain(gain); } -MY_FLOAT BiQuad :: getGain(void) const +StkFloat BiQuad :: getGain(void) const { return Filter::getGain(); } -MY_FLOAT BiQuad :: lastOut(void) const +StkFloat BiQuad :: lastOut(void) const { return Filter::lastOut(); } -MY_FLOAT BiQuad :: tick(MY_FLOAT sample) +StkFloat BiQuad :: tick(StkFloat sample) { - inputs[0] = gain * sample; - outputs[0] = b[0] * inputs[0] + b[1] * inputs[1] + b[2] * inputs[2]; - outputs[0] -= a[2] * outputs[2] + a[1] * outputs[1]; - inputs[2] = inputs[1]; - inputs[1] = inputs[0]; - outputs[2] = outputs[1]; - outputs[1] = outputs[0]; + inputs_[0] = gain_ * sample; + outputs_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2]; + outputs_[0] -= a_[2] * outputs_[2] + a_[1] * outputs_[1]; + inputs_[2] = inputs_[1]; + inputs_[1] = inputs_[0]; + outputs_[2] = outputs_[1]; + outputs_[1] = outputs_[0]; - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *BiQuad :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *BiQuad :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; isetBlockZero(); + dcBlock_.setBlockZero(); // 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; + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency( 5.925 ); + vibratoGain_ = 0.0; - resonator = new BiQuad(); - resonator->setResonance(500.0, __BOTTLE_RADIUS_, true); + resonator_.setResonance(500.0, __BOTTLE_RADIUS_, true); + adsr_.setAllTimes( 0.005, 0.01, 0.8, 0.010); - adsr = new ADSR(); - adsr->setAllTimes( 0.005, 0.01, 0.8, 0.010); - - noise = new Noise(); - noiseGain = 20.0; - - maxPressure = (MY_FLOAT) 0.0; + noiseGain_ = 20.0; + maxPressure_ = (StkFloat) 0.0; } BlowBotl :: ~BlowBotl() { - delete jetTable; - delete resonator; - delete dcBlock; - delete noise; - delete adsr; - delete vibrato; + delete vibrato_; } void BlowBotl :: clear() { - resonator->clear(); + resonator_.clear(); } -void BlowBotl :: setFrequency(MY_FLOAT frequency) +void BlowBotl :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "BlowBotl: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "BlowBotl::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } - resonator->setResonance( freakency, __BOTTLE_RADIUS_, true ); + resonator_.setResonance( freakency, __BOTTLE_RADIUS_, true ); } -void BlowBotl :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +void BlowBotl :: startBlowing(StkFloat amplitude, StkFloat rate) { - adsr->setAttackRate(rate); - maxPressure = amplitude; - adsr->keyOn(); + adsr_.setAttackRate(rate); + maxPressure_ = amplitude; + adsr_.keyOn(); } -void BlowBotl :: stopBlowing(MY_FLOAT rate) +void BlowBotl :: stopBlowing(StkFloat rate) { - adsr->setReleaseRate(rate); - adsr->keyOff(); + adsr_.setReleaseRate(rate); + adsr_.keyOff(); } -void BlowBotl :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void BlowBotl :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); + this->setFrequency(frequency); startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02); - outputGain = amplitude + 0.001; + outputGain_ = amplitude + 0.001; #if defined(_STK_DEBUG_) - std::cerr << "BlowBotl: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "BlowBotl::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void BlowBotl :: noteOff(MY_FLOAT amplitude) +void BlowBotl :: noteOff(StkFloat amplitude) { this->stopBlowing(amplitude * 0.02); #if defined(_STK_DEBUG_) - std::cerr << "BlowBotl: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "BlowBotl::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT BlowBotl :: tick() +StkFloat BlowBotl :: tick() { - MY_FLOAT breathPressure; - MY_FLOAT randPressure; - MY_FLOAT pressureDiff; + StkFloat breathPressure; + StkFloat randPressure; + StkFloat pressureDiff; // Calculate the breath pressure (envelope + vibrato) - breathPressure = maxPressure * adsr->tick(); - breathPressure += vibratoGain * vibrato->tick(); + breathPressure = maxPressure_ * adsr_.tick(); + breathPressure += vibratoGain_ * vibrato_->tick(); - pressureDiff = breathPressure - resonator->lastOut(); + pressureDiff = breathPressure - resonator_.lastOut(); - randPressure = noiseGain * noise->tick(); + randPressure = noiseGain_ * noise_.tick(); randPressure *= breathPressure; randPressure *= (1.0 + pressureDiff); - resonator->tick( breathPressure + randPressure - ( jetTable->tick( pressureDiff ) * pressureDiff ) ); - lastOutput = 0.2 * outputGain * dcBlock->tick( pressureDiff ); + resonator_.tick( breathPressure + randPressure - ( jetTable_.tick( pressureDiff ) * pressureDiff ) ); + lastOutput_ = 0.2 * outputGain_ * dcBlock_.tick( pressureDiff ); - return lastOutput; + return lastOutput_; } -void BlowBotl :: controlChange(int number, MY_FLOAT value) +StkFloat *BlowBotl :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& BlowBotl :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void BlowBotl :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "BlowBotl: Control value less than zero!" << std::endl; + errorString_ << "BlowBotl::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "BlowBotl: Control value greater than 128.0!" << std::endl; + errorString_ << "BlowBotl::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_NoiseLevel_) // 4 - noiseGain = norm * 30.0; + noiseGain_ = norm * 30.0; else if (number == __SK_ModFrequency_) // 11 - vibrato->setFrequency( norm * 12.0 ); + vibrato_->setFrequency( norm * 12.0 ); else if (number == __SK_ModWheel_) // 1 - vibratoGain = norm * 0.4; + vibratoGain_ = norm * 0.4; else if (number == __SK_AfterTouch_Cont_) // 128 - adsr->setTarget( norm ); - else - std::cerr << "BlowBotl: Undefined Control Number (" << number << ")!!" << std::endl; + adsr_.setTarget( norm ); + else { + errorString_ << "BlowBotl::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "BlowBotl: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "BlowBotl::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/BlowHole.cpp b/src/BlowHole.cpp index 7ee7f69..2efb8c5 100644 --- a/src/BlowHole.cpp +++ b/src/BlowHole.cpp @@ -29,7 +29,7 @@ - Register State = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -37,221 +37,232 @@ #include "SKINI.msg" #include -BlowHole :: BlowHole(MY_FLOAT lowestFrequency) +BlowHole :: BlowHole(StkFloat lowestFrequency) { - length = (long) (Stk::sampleRate() / lowestFrequency + 1); + length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); // delays[0] is the delay line between the reed and the register vent. - delays[0] = (DelayL *) new DelayL( 5.0 * Stk::sampleRate() / 22050.0, 100 ); + delays_[0].setDelay( 5.0 * Stk::sampleRate() / 22050.0 ); // delays[1] is the delay line between the register vent and the tonehole. - delays[1] = (DelayL *) new DelayL( length >> 1, length ); + delays_[1].setMaximumDelay( length_ ); + delays_[1].setDelay( length_ >> 1 ); // delays[2] is the delay line between the tonehole and the end of the bore. - delays[2] = (DelayL *) new DelayL( 4.0 * Stk::sampleRate() / 22050.0, 100 ); - reedTable = new ReedTabl(); - reedTable->setOffset((MY_FLOAT) 0.7); - reedTable->setSlope((MY_FLOAT) -0.3); - filter = new OneZero; - envelope = new Envelope; - noise = new Noise; + delays_[2].setDelay( 4.0 * Stk::sampleRate() / 22050.0 ); + + reedTable_.setOffset( 0.7 ); + reedTable_.setSlope( -0.3 ); // Calculate the initial tonehole three-port scattering coefficient - double r_b = 0.0075; // main bore radius - r_th = 0.003; // tonehole radius - scatter = -pow(r_th,2) / ( pow(r_th,2) + 2*pow(r_b,2) ); + StkFloat rb = 0.0075; // main bore radius + StkFloat rth = 0.003; // tonehole radius + scatter_ = -pow(rth,2) / ( pow(rth,2) + 2*pow(rb,2) ); - // Calculate tonehole coefficients - MY_FLOAT te = 1.4 * r_th; // effective length of the open hole - th_coeff = (te*2*Stk::sampleRate() - 347.23) / (te*2*Stk::sampleRate() + 347.23); - tonehole = new PoleZero; - // Start with tonehole open - tonehole->setA1(-th_coeff); - tonehole->setB0(th_coeff); - tonehole->setB1(-1.0); + // Calculate tonehole coefficients and set for initially open. + StkFloat te = 1.4 * rth; // effective length of the open hole + thCoeff_ = (te*2*Stk::sampleRate() - 347.23) / (te*2*Stk::sampleRate() + 347.23); + tonehole_.setA1(-thCoeff_); + tonehole_.setB0(thCoeff_); + tonehole_.setB1(-1.0); // Calculate register hole filter coefficients double r_rh = 0.0015; // register vent radius - te = 1.4 * r_rh; // effective length of the open hole + te = 1.4 * r_rh; // effective length of the open hole double xi = 0.0; // series resistance term - double zeta = 347.23 + 2*PI*pow(r_b,2)*xi/1.1769; - double psi = 2*PI*pow(r_b,2)*te / (PI*pow(r_rh,2)); - rh_coeff = (zeta - 2 * Stk::sampleRate() * psi) / (zeta + 2 * Stk::sampleRate() * psi); - rh_gain = -347.23 / (zeta + 2 * Stk::sampleRate() * psi); - vent = new PoleZero; - vent->setA1(rh_coeff); - vent->setB0(1.0); - vent->setB1(1.0); + double zeta = 347.23 + 2*PI*pow(rb,2)*xi/1.1769; + double psi = 2*PI*pow(rb,2)*te / (PI*pow(r_rh,2)); + StkFloat rhCoeff = (zeta - 2 * Stk::sampleRate() * psi) / (zeta + 2 * Stk::sampleRate() * psi); + rhGain_ = -347.23 / (zeta + 2 * Stk::sampleRate() * psi); + vent_.setA1( rhCoeff ); + vent_.setB0(1.0); + vent_.setB1(1.0); // Start with register vent closed - vent->setGain(0.0); + vent_.setGain(0.0); // 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; - vibratoGain = (MY_FLOAT) 0.01; + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency((StkFloat) 5.735); + outputGain_ = 1.0; + noiseGain_ = 0.2; + vibratoGain_ = 0.01; } BlowHole :: ~BlowHole() { - delete delays[0]; - delete delays[1]; - delete delays[2]; - delete reedTable; - delete filter; - delete tonehole; - delete vent; - delete envelope; - delete noise; - delete vibrato; + delete vibrato_; } void BlowHole :: clear() { - delays[0]->clear(); - delays[1]->clear(); - delays[2]->clear(); - filter->tick((MY_FLOAT) 0.0); - tonehole->tick((MY_FLOAT) 0.0); - vent->tick((MY_FLOAT) 0.0); + delays_[0].clear(); + delays_[1].clear(); + delays_[2].clear(); + filter_.tick( 0.0 ); + tonehole_.tick( 0.0 ); + vent_.tick( 0.0 ); } -void BlowHole :: setFrequency(MY_FLOAT frequency) +void BlowHole :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { std::cerr << "BlowHole: setFrequency parameter is less than or equal to zero!" << std::endl; freakency = 220.0; } // Delay = length - approximate filter delay. - MY_FLOAT delay = (Stk::sampleRate() / freakency) * 0.5 - 3.5; - delay -= delays[0]->getDelay() + delays[2]->getDelay(); + StkFloat delay = (Stk::sampleRate() / freakency) * 0.5 - 3.5; + delay -= delays_[0].getDelay() + delays_[2].getDelay(); if (delay <= 0.0) delay = 0.3; - else if (delay > length) delay = length; - delays[1]->setDelay(delay); + else if (delay > length_) delay = length_; + delays_[1].setDelay(delay); } -void BlowHole :: setVent(MY_FLOAT newValue) +void BlowHole :: setVent(StkFloat newValue) { // This method allows setting of the register vent "open-ness" at // any point between "Open" (newValue = 1) and "Closed" // (newValue = 0). - MY_FLOAT gain; + StkFloat gain; - if (newValue <= 0.0) gain = 0.0; - else if (newValue >= 1.0) gain = rh_gain; - else gain = newValue * rh_gain; - vent->setGain(gain); + if (newValue <= 0.0) + gain = 0.0; + else if (newValue >= 1.0) + gain = rhGain_; + else + gain = newValue * rhGain_; + + vent_.setGain( gain ); } -void BlowHole :: setTonehole(MY_FLOAT newValue) +void BlowHole :: setTonehole(StkFloat newValue) { // This method allows setting of the tonehole "open-ness" at // any point between "Open" (newValue = 1) and "Closed" // (newValue = 0). - MY_FLOAT new_coeff; + StkFloat new_coeff; - if (newValue <= 0.0) new_coeff = 0.9995; - else if (newValue >= 1.0) new_coeff = th_coeff; - else new_coeff = (newValue * (th_coeff - 0.9995)) + 0.9995; - tonehole->setA1(-new_coeff); - tonehole->setB0(new_coeff); + if ( newValue <= 0.0 ) + new_coeff = 0.9995; + else if ( newValue >= 1.0 ) + new_coeff = thCoeff_; + else + new_coeff = (newValue * (thCoeff_ - 0.9995)) + 0.9995; + + tonehole_.setA1( -new_coeff ); + tonehole_.setB0( new_coeff ); } -void BlowHole :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +void BlowHole :: startBlowing(StkFloat amplitude, StkFloat rate) { - envelope->setRate(rate); - envelope->setTarget(amplitude); + envelope_.setRate( rate ); + envelope_.setTarget( amplitude ); } -void BlowHole :: stopBlowing(MY_FLOAT rate) +void BlowHole :: stopBlowing(StkFloat rate) { - envelope->setRate(rate); - envelope->setTarget((MY_FLOAT) 0.0); + envelope_.setRate( rate ); + envelope_.setTarget( 0.0 ); } -void BlowHole :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void BlowHole :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); - startBlowing((MY_FLOAT) 0.55 + (amplitude * 0.30), amplitude * 0.005); - outputGain = amplitude + 0.001; + this->setFrequency( frequency ); + this->startBlowing( 0.55 + (amplitude * 0.30), amplitude * 0.005 ); + outputGain_ = amplitude + 0.001; #if defined(_STK_DEBUG_) - std::cerr << "BlowHole: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "BlowHole::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void BlowHole :: noteOff(MY_FLOAT amplitude) +void BlowHole :: noteOff(StkFloat amplitude) { - this->stopBlowing(amplitude * 0.01); + this->stopBlowing( amplitude * 0.01 ); #if defined(_STK_DEBUG_) - std::cerr << "BlowHole: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "BlowHole::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT BlowHole :: tick() +StkFloat BlowHole :: tick() { - MY_FLOAT pressureDiff; - MY_FLOAT breathPressure; - MY_FLOAT temp; + StkFloat pressureDiff; + StkFloat breathPressure; + StkFloat temp; // Calculate the breath pressure (envelope + noise + vibrato) - breathPressure = envelope->tick(); - breathPressure += breathPressure * noiseGain * noise->tick(); - breathPressure += breathPressure * vibratoGain * vibrato->tick(); + breathPressure = envelope_.tick(); + breathPressure += breathPressure * noiseGain_ * noise_.tick(); + breathPressure += breathPressure * vibratoGain_ * vibrato_->tick(); // Calculate the differential pressure = reflected - mouthpiece pressures - pressureDiff = delays[0]->lastOut() - breathPressure; + pressureDiff = delays_[0].lastOut() - breathPressure; // Do two-port junction scattering for register vent - MY_FLOAT pa = breathPressure + pressureDiff * reedTable->tick(pressureDiff); - MY_FLOAT pb = delays[1]->lastOut(); - vent->tick(pa+pb); + StkFloat pa = breathPressure + pressureDiff * reedTable_.tick( pressureDiff ); + StkFloat pb = delays_[1].lastOut(); + vent_.tick( pa+pb ); - lastOutput = delays[0]->tick(vent->lastOut()+pb); - lastOutput *= outputGain; + lastOutput_ = delays_[0].tick( vent_.lastOut()+pb ); + lastOutput_ *= outputGain_; // Do three-port junction scattering (under tonehole) - pa += vent->lastOut(); - pb = delays[2]->lastOut(); - MY_FLOAT pth = tonehole->lastOut(); - temp = scatter * (pa + pb - 2 * pth); + pa += vent_.lastOut(); + pb = delays_[2].lastOut(); + StkFloat pth = tonehole_.lastOut(); + temp = scatter_ * (pa + pb - 2 * pth); - delays[2]->tick(filter->tick(pa + temp) * -0.95); - delays[1]->tick(pb + temp); - tonehole->tick(pa + pb - pth + temp); + delays_[2].tick( filter_.tick(pa + temp) * -0.95 ); + delays_[1].tick( pb + temp ); + tonehole_.tick( pa + pb - pth + temp ); - return lastOutput; + return lastOutput_; } -void BlowHole :: controlChange(int number, MY_FLOAT value) +StkFloat *BlowHole :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& BlowHole :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void BlowHole :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "BlowHole: Control value less than zero!" << std::endl; + errorString_ << "BlowHole::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "BlowHole: Control value greater than 128.0!" << std::endl; + errorString_ << "BlowHole::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_ReedStiffness_) // 2 - reedTable->setSlope( -0.44 + (0.26 * norm) ); + reedTable_.setSlope( -0.44 + (0.26 * norm) ); else if (number == __SK_NoiseLevel_) // 4 - noiseGain = ( norm * 0.4); + noiseGain_ = ( norm * 0.4); else if (number == __SK_ModFrequency_) // 11 this->setTonehole( norm ); else if (number == __SK_ModWheel_) // 1 this->setVent( norm ); else if (number == __SK_AfterTouch_Cont_) // 128 - envelope->setValue( norm ); - else - std::cerr << "BlowHole: Undefined Control Number (" << number << ")!!" << std::endl; + envelope_.setValue( norm ); + else { + errorString_ << "BlowHole::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "BlowHole: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "BlowHole::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/BowTabl.cpp b/src/BowTabl.cpp deleted file mode 100644 index 089e1ae..0000000 --- a/src/BowTabl.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/***************************************************/ -/*! \class BowTabl - \brief STK bowed string table class. - - This class implements a simple bowed string - non-linear function, as described by Smith (1986). - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#include "BowTabl.h" -#include - -BowTabl :: BowTabl() -{ - offSet = (MY_FLOAT) 0.0; - slope = (MY_FLOAT) 0.1; -} - -BowTabl :: ~BowTabl() -{ -} - -void BowTabl :: setOffset(MY_FLOAT aValue) -{ - offSet = aValue; -} - -void BowTabl :: setSlope(MY_FLOAT aValue) -{ - slope = aValue; -} - -MY_FLOAT BowTabl :: lastOut() const -{ - return lastOutput; -} - -MY_FLOAT BowTabl :: tick(MY_FLOAT input) -{ - // The input represents differential string vs. bow velocity. - MY_FLOAT sample; - sample = input + offSet; // add bias to input - sample *= slope; // then scale it - lastOutput = (MY_FLOAT) fabs((double) sample) + (MY_FLOAT) 0.75; - lastOutput = (MY_FLOAT) pow( lastOutput,(MY_FLOAT) -4.0 ); - - // Set minimum friction to 0.0 - //if (lastOutput < 0.0 ) lastOutput = 0.0; - // Set maximum friction to 1.0. - if (lastOutput > 1.0 ) lastOutput = (MY_FLOAT) 1.0; - - return lastOutput; -} - -MY_FLOAT *BowTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize) -{ - for (unsigned int i=0; i + +BowTable :: BowTable() +{ + offset_ = (StkFloat) 0.0; + slope_ = (StkFloat) 0.1; +} + +BowTable :: ~BowTable() +{ +} + +void BowTable :: setOffset(StkFloat offset) +{ + offset_ = offset; +} + +void BowTable :: setSlope(StkFloat slope) +{ + slope_ = slope; +} + +StkFloat BowTable :: tick(StkFloat input) +{ + // The input represents differential string vs. bow velocity. + StkFloat sample; + sample = input + offset_; // add bias to input + sample *= slope_; // then scale it + lastOutput_ = (StkFloat) fabs( (double) sample ) + (StkFloat) 0.75; + lastOutput_ = (StkFloat) pow( lastOutput_, (StkFloat) -4.0 ); + + // Set minimum friction to 0.0 + // if (lastOutput < 0.0 ) lastOutput = 0.0; + // Set maximum friction to 1.0. + if (lastOutput_ > 1.0 ) lastOutput_ = (StkFloat) 1.0; + + return lastOutput_; +} + +StkFloat *BowTable :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Function::tick( vector, vectorSize ); +} + +StkFrames& BowTable :: tick( StkFrames& frames, unsigned int channel ) +{ + return Function::tick( frames, channel ); +} diff --git a/src/Bowed.cpp b/src/Bowed.cpp index 48208ef..ebd493a 100644 --- a/src/Bowed.cpp +++ b/src/Bowed.cpp @@ -17,173 +17,184 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Bowed.h" #include "SKINI.msg" -Bowed :: Bowed(MY_FLOAT lowestFrequency) +Bowed :: Bowed(StkFloat lowestFrequency) { - long length; - length = (long) (Stk::sampleRate() / lowestFrequency + 1); - neckDelay = new DelayL(100.0, length); - length >>= 1; - bridgeDelay = new DelayL(29.0, length); + unsigned long length; + length = (long) ( Stk::sampleRate() / lowestFrequency + 1 ); + neckDelay_.setMaximumDelay( length ); + neckDelay_.setDelay( 100.0 ); - bowTable = new BowTabl; - bowTable->setSlope((MY_FLOAT) 3.0); + length >>= 1; + bridgeDelay_.setMaximumDelay( length ); + bridgeDelay_.setDelay( 29.0 ); + + bowTable_.setSlope(3.0 ); // 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; + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency( 6.12723 ); + vibratoGain_ = 0.0; - stringFilter = new OnePole; - stringFilter->setPole((MY_FLOAT) (0.6 - (0.1 * 22050.0 / Stk::sampleRate() ) ) ); - stringFilter->setGain((MY_FLOAT) 0.95); + stringFilter_.setPole( 0.6 - (0.1 * 22050.0 / Stk::sampleRate()) ); + stringFilter_.setGain( 0.95 ); - bodyFilter = new BiQuad; - bodyFilter->setResonance( 500.0, 0.85, TRUE ); - bodyFilter->setGain((MY_FLOAT) 0.2); + bodyFilter_.setResonance( 500.0, 0.85, true ); + bodyFilter_.setGain( 0.2 ); - adsr = new ADSR; - adsr->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.005,(MY_FLOAT) 0.9,(MY_FLOAT) 0.01); + adsr_.setAllTimes( 0.02, 0.005, 0.9, 0.01 ); - betaRatio = (MY_FLOAT) 0.127236; + betaRatio_ = 0.127236; // Necessary to initialize internal variables. - setFrequency( 220.0 ); + this->setFrequency( 220.0 ); } Bowed :: ~Bowed() { - delete neckDelay; - delete bridgeDelay; - delete bowTable; - delete stringFilter; - delete bodyFilter; - delete vibrato; - delete adsr; + delete vibrato_; } void Bowed :: clear() { - neckDelay->clear(); - bridgeDelay->clear(); + neckDelay_.clear(); + bridgeDelay_.clear(); } -void Bowed :: setFrequency(MY_FLOAT frequency) +void Bowed :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Bowed: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Bowed::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } // Delay = length - approximate filter delay. - baseDelay = Stk::sampleRate() / freakency - (MY_FLOAT) 4.0; - if ( baseDelay <= 0.0 ) baseDelay = 0.3; - bridgeDelay->setDelay(baseDelay * betaRatio); // bow to bridge length - neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); // bow to nut (finger) length + baseDelay_ = Stk::sampleRate() / freakency - 4.0; + if ( baseDelay_ <= 0.0 ) baseDelay_ = 0.3; + bridgeDelay_.setDelay( baseDelay_ * betaRatio_ ); // bow to bridge length + neckDelay_.setDelay( baseDelay_ * (1.0 - betaRatio_) ); // bow to nut (finger) length } -void Bowed :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate) +void Bowed :: startBowing(StkFloat amplitude, StkFloat rate) { - adsr->setRate(rate); - adsr->keyOn(); - maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.2 * amplitude); + adsr_.setRate( rate ); + adsr_.keyOn(); + maxVelocity_ = 0.03 + ( 0.2 * amplitude ); } -void Bowed :: stopBowing(MY_FLOAT rate) +void Bowed :: stopBowing(StkFloat rate) { - adsr->setRate(rate); - adsr->keyOff(); + adsr_.setRate( rate ); + adsr_.keyOff(); } -void Bowed :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Bowed :: noteOn(StkFloat frequency, StkFloat amplitude) { - this->startBowing(amplitude, amplitude * 0.001); - this->setFrequency(frequency); + this->startBowing( amplitude, amplitude * 0.001 ); + this->setFrequency( frequency ); #if defined(_STK_DEBUG_) - std::cerr << "Bowed: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Bowed::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Bowed :: noteOff(MY_FLOAT amplitude) +void Bowed :: noteOff(StkFloat amplitude) { - this->stopBowing(((MY_FLOAT) 1.0 - amplitude) * (MY_FLOAT) 0.005); + this->stopBowing( (1.0 - amplitude) * 0.005 ); #if defined(_STK_DEBUG_) - std::cerr << "Bowed: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Bowed::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Bowed :: setVibrato(MY_FLOAT gain) +void Bowed :: setVibrato(StkFloat gain) { - vibratoGain = gain; + vibratoGain_ = gain; } -MY_FLOAT Bowed :: tick() +StkFloat Bowed :: tick() { - MY_FLOAT bowVelocity; - MY_FLOAT bridgeRefl; - MY_FLOAT nutRefl; - MY_FLOAT newVel; - MY_FLOAT velDiff; - MY_FLOAT stringVel; + StkFloat bowVelocity; + StkFloat bridgeRefl; + StkFloat nutRefl; + StkFloat newVel; + StkFloat velDiff; + StkFloat stringVel; - bowVelocity = maxVelocity * adsr->tick(); + bowVelocity = maxVelocity_ * adsr_.tick(); - bridgeRefl = -stringFilter->tick( bridgeDelay->lastOut() ); - nutRefl = -neckDelay->lastOut(); + bridgeRefl = -stringFilter_.tick( bridgeDelay_.lastOut() ); + nutRefl = -neckDelay_.lastOut(); stringVel = bridgeRefl + nutRefl; // Sum is String Velocity velDiff = bowVelocity - stringVel; // Differential Velocity - newVel = velDiff * bowTable->tick( velDiff ); // Non-Linear Bow Function - neckDelay->tick(bridgeRefl + newVel); // Do string propagations - bridgeDelay->tick(nutRefl + newVel); + newVel = velDiff * bowTable_.tick( velDiff ); // Non-Linear Bow Function + neckDelay_.tick(bridgeRefl + newVel); // Do string propagations + bridgeDelay_.tick(nutRefl + newVel); - if (vibratoGain > 0.0) { - neckDelay->setDelay((baseDelay * ((MY_FLOAT) 1.0 - betaRatio)) + - (baseDelay * vibratoGain * vibrato->tick())); + if ( vibratoGain_ > 0.0 ) { + neckDelay_.setDelay( (baseDelay_ * (1.0 - betaRatio_) ) + + (baseDelay_ * vibratoGain_ * vibrato_->tick()) ); } - lastOutput = bodyFilter->tick(bridgeDelay->lastOut()); + lastOutput_ = bodyFilter_.tick( bridgeDelay_.lastOut() ); - return lastOutput; + return lastOutput_; } -void Bowed :: controlChange(int number, MY_FLOAT value) +StkFloat *Bowed :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Bowed :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Bowed :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Bowed: Control value less than zero!" << std::endl; + errorString_ << "Bowed::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Bowed: Control value greater than 128.0!" << std::endl; + errorString_ << "Bowed::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_BowPressure_) // 2 - bowTable->setSlope( 5.0 - (4.0 * norm) ); + bowTable_.setSlope( 5.0 - (4.0 * norm) ); else if (number == __SK_BowPosition_) { // 4 - betaRatio = 0.027236 + (0.2 * norm); - bridgeDelay->setDelay(baseDelay * betaRatio); - neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); + betaRatio_ = 0.027236 + (0.2 * norm); + bridgeDelay_.setDelay( baseDelay_ * betaRatio_ ); + neckDelay_.setDelay( baseDelay_ * (1.0 - betaRatio_) ); } else if (number == __SK_ModFrequency_) // 11 - vibrato->setFrequency( norm * 12.0 ); + vibrato_->setFrequency( norm * 12.0 ); else if (number == __SK_ModWheel_) // 1 - vibratoGain = ( norm * 0.4 ); + vibratoGain_ = ( norm * 0.4 ); else if (number == __SK_AfterTouch_Cont_) // 128 - adsr->setTarget(norm); - else - std::cerr << "Bowed: Undefined Control Number (" << number << ")!!" << std::endl; + adsr_.setTarget(norm); + else { + errorString_ << "Bowed::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Bowed: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Bowed::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Brass.cpp b/src/Brass.cpp index a235c06..f5aee41 100644 --- a/src/Brass.cpp +++ b/src/Brass.cpp @@ -16,7 +16,7 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -24,153 +24,167 @@ #include "SKINI.msg" #include -Brass :: Brass(MY_FLOAT lowestFrequency) +Brass :: Brass(StkFloat lowestFrequency) { - length = (long) (Stk::sampleRate() / lowestFrequency + 1); - delayLine = new DelayA( 0.5 * length, length ); + length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + delayLine_.setMaximumDelay( length_ ); + delayLine_.setDelay( 0.5 * length_ ); - lipFilter = new BiQuad(); - lipFilter->setGain( 0.03 ); - dcBlock = new PoleZero(); - dcBlock->setBlockZero(); + lipFilter_.setGain( 0.03 ); + dcBlock_.setBlockZero(); - adsr = new ADSR; - adsr->setAllTimes( 0.005, 0.001, 1.0, 0.010); + adsr_.setAllTimes( 0.005, 0.001, 1.0, 0.010); // 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; + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency( 6.137 ); + vibratoGain_ = 0.0; this->clear(); - maxPressure = (MY_FLOAT) 0.0; - lipTarget = 0.0; + maxPressure_ = 0.0; + lipTarget_ = 0.0; - // Necessary to initialize variables. - setFrequency( 220.0 ); + // This is necessary to initialize variables. + this->setFrequency( 220.0 ); } Brass :: ~Brass() { - delete delayLine; - delete lipFilter; - delete dcBlock; - delete adsr; - delete vibrato; + delete vibrato_; } void Brass :: clear() { - delayLine->clear(); - lipFilter->clear(); - dcBlock->clear(); + delayLine_.clear(); + lipFilter_.clear(); + dcBlock_.clear(); } -void Brass :: setFrequency(MY_FLOAT frequency) +void Brass :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Brass: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Brass::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } // Fudge correction for filter delays. - slideTarget = (Stk::sampleRate() / freakency * 2.0) + 3.0; - delayLine->setDelay(slideTarget); // play a harmonic + slideTarget_ = (Stk::sampleRate() / freakency * 2.0) + 3.0; + delayLine_.setDelay( slideTarget_ ); // play a harmonic - lipTarget = freakency; - lipFilter->setResonance( freakency, 0.997 ); + lipTarget_ = freakency; + lipFilter_.setResonance( freakency, 0.997 ); } -void Brass :: setLip(MY_FLOAT frequency) +void Brass :: setLip(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Brass: setLip parameter is less than or equal to zero!" << std::endl; + errorString_ << "Brass::setLip: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } - lipFilter->setResonance( freakency, 0.997 ); + lipFilter_.setResonance( freakency, 0.997 ); } -void Brass :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +void Brass :: startBlowing(StkFloat amplitude, StkFloat rate) { - adsr->setAttackRate(rate); - maxPressure = amplitude; - adsr->keyOn(); + adsr_.setAttackRate( rate ); + maxPressure_ = amplitude; + adsr_.keyOn(); } -void Brass :: stopBlowing(MY_FLOAT rate) +void Brass :: stopBlowing(StkFloat rate) { - adsr->setReleaseRate(rate); - adsr->keyOff(); + adsr_.setReleaseRate( rate ); + adsr_.keyOff(); } -void Brass :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Brass :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); - this->startBlowing(amplitude, amplitude * 0.001); + this->setFrequency( frequency ); + this->startBlowing( amplitude, amplitude * 0.001 ); #if defined(_STK_DEBUG_) - std::cerr << "Brass: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Brass::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Brass :: noteOff(MY_FLOAT amplitude) +void Brass :: noteOff(StkFloat amplitude) { - this->stopBlowing(amplitude * 0.005); + this->stopBlowing( amplitude * 0.005 ); #if defined(_STK_DEBUG_) - std::cerr << "Brass: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Brass::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Brass :: tick() +StkFloat Brass :: tick() { - MY_FLOAT breathPressure = maxPressure * adsr->tick(); - breathPressure += vibratoGain * vibrato->tick(); + StkFloat breathPressure = maxPressure_ * adsr_.tick(); + breathPressure += vibratoGain_ * vibrato_->tick(); - MY_FLOAT mouthPressure = 0.3 * breathPressure; - MY_FLOAT borePressure = 0.85 * delayLine->lastOut(); - MY_FLOAT deltaPressure = mouthPressure - borePressure; // Differential pressure. - deltaPressure = lipFilter->tick( deltaPressure ); // Force - > position. + StkFloat mouthPressure = 0.3 * breathPressure; + StkFloat borePressure = 0.85 * delayLine_.lastOut(); + StkFloat deltaPressure = mouthPressure - borePressure; // Differential pressure. + deltaPressure = lipFilter_.tick( deltaPressure ); // Force - > position. deltaPressure *= deltaPressure; // Basic position to area mapping. - if ( deltaPressure > 1.0 ) deltaPressure = 1.0; // Non-linear saturation. - // The following input scattering assumes the mouthPressure = area. - lastOutput = deltaPressure * mouthPressure + ( 1.0 - deltaPressure) * borePressure; - lastOutput = delayLine->tick( dcBlock->tick( lastOutput ) ); + if ( deltaPressure > 1.0 ) deltaPressure = 1.0; // Non-linear saturation. - return lastOutput; + // The following input scattering assumes the mouthPressure = area. + lastOutput_ = deltaPressure * mouthPressure + ( 1.0 - deltaPressure) * borePressure; + lastOutput_ = delayLine_.tick( dcBlock_.tick( lastOutput_ ) ); + + return lastOutput_; } -void Brass :: controlChange(int number, MY_FLOAT value) +StkFloat *Brass :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Brass :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Brass :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Brass: Control value less than zero!" << std::endl; + errorString_ << "Brass::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Brass: Control value greater than 128.0!" << std::endl; + errorString_ << "Brass::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_LipTension_) { // 2 - MY_FLOAT temp = lipTarget * pow( 4.0, (2.0 * norm) - 1.0 ); + StkFloat temp = lipTarget_ * pow( 4.0, (2.0 * norm) - 1.0 ); this->setLip(temp); } else if (number == __SK_SlideLength_) // 4 - delayLine->setDelay( slideTarget * (0.5 + norm) ); + delayLine_.setDelay( slideTarget_ * (0.5 + norm) ); else if (number == __SK_ModFrequency_) // 11 - vibrato->setFrequency( norm * 12.0 ); + vibrato_->setFrequency( norm * 12.0 ); else if (number == __SK_ModWheel_ ) // 1 - vibratoGain = norm * 0.4; + vibratoGain_ = norm * 0.4; else if (number == __SK_AfterTouch_Cont_) // 128 - adsr->setTarget( norm ); - else - std::cerr << "Brass: Undefined Control Number (" << number << ")!!" << std::endl; + adsr_.setTarget( norm ); + else { + errorString_ << "Brass::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Brass: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Brass::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Chorus.cpp b/src/Chorus.cpp index e0a3bbf..b779024 100644 --- a/src/Chorus.cpp +++ b/src/Chorus.cpp @@ -4,99 +4,73 @@ This class implements a chorus effect. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Chorus.h" #include -Chorus :: Chorus(MY_FLOAT baseDelay) +Chorus :: Chorus(StkFloat baseDelay) { - delayLine[0] = new DelayL((long) baseDelay, (long) (baseDelay * 1.414) + 2); - delayLine[1] = new DelayL((long) (baseDelay), (long) baseDelay + 2); - baseLength = baseDelay; + delayLine_[0].setMaximumDelay( (unsigned long) (baseDelay * 1.414) + 2); + delayLine_[0].setDelay( baseDelay ); + delayLine_[1].setMaximumDelay( (unsigned long) (baseDelay * 1.414) + 2); + delayLine_[1].setDelay( baseDelay ); + baseLength_ = baseDelay; // 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; - effectMix = 0.5; + 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; + effectMix_ = 0.5; this->clear(); } Chorus :: ~Chorus() { - delete delayLine[0]; - delete delayLine[1]; - delete mods[0]; - delete mods[1]; + delete mods_[0]; + delete mods_[1]; } void Chorus :: clear() { - delayLine[0]->clear(); - delayLine[1]->clear(); - lastOutput[0] = 0.0; - lastOutput[1] = 0.0; + delayLine_[0].clear(); + delayLine_[1].clear(); + lastOutput_[0] = 0.0; + lastOutput_[1] = 0.0; } -void Chorus :: setEffectMix(MY_FLOAT mix) +void Chorus :: setModDepth(StkFloat depth) { - effectMix = mix; - if ( mix < 0.0 ) { - std::cerr << "Chorus: setEffectMix parameter is less than zero!" << std::endl; - effectMix = 0.0; - } - else if ( mix > 1.0 ) { - std::cerr << "Chorus: setEffectMix parameter is greater than 1.0!" << std::endl; - effectMix = 1.0; - } + modDepth_ = depth; } -void Chorus :: setModDepth(MY_FLOAT depth) +void Chorus :: setModFrequency(StkFloat frequency) { - modDepth = depth; + mods_[0]->setFrequency(frequency); + mods_[1]->setFrequency(frequency * 1.1111); } -void Chorus :: setModFrequency(MY_FLOAT frequency) +StkFloat Chorus :: tick(StkFloat input) { - mods[0]->setFrequency(frequency); - mods[1]->setFrequency(frequency * 1.1111); + delayLine_[0].setDelay( baseLength_ * 0.707 * (1.0 + mods_[0]->tick()) ); + delayLine_[1].setDelay( baseLength_ * 0.5 * (1.0 - mods_[1]->tick()) ); + lastOutput_[0] = input * (1.0 - effectMix_); + lastOutput_[0] += effectMix_ * delayLine_[0].tick(input); + lastOutput_[1] = input * (1.0 - effectMix_); + lastOutput_[1] += effectMix_ * delayLine_[1].tick(input); + return Effect::lastOut(); } -MY_FLOAT Chorus :: lastOut() const +StkFloat *Chorus :: tick(StkFloat *vector, unsigned int vectorSize) { - return (lastOutput[0] + lastOutput[1]) * (MY_FLOAT) 0.5; + return Effect::tick( vector, vectorSize ); } -MY_FLOAT Chorus :: lastOutLeft() const +StkFrames& Chorus :: tick( StkFrames& frames, unsigned int channel ) { - return lastOutput[0]; -} - -MY_FLOAT Chorus :: lastOutRight() const -{ - return lastOutput[1]; -} - -MY_FLOAT Chorus :: tick(MY_FLOAT input) -{ - delayLine[0]->setDelay(baseLength * 0.707 * (1.0 + mods[0]->tick())); - delayLine[1]->setDelay(baseLength * 0.5 * (1.0 - mods[1]->tick())); - lastOutput[0] = input * (1.0 - effectMix); - lastOutput[0] += effectMix * delayLine[0]->tick(input); - lastOutput[1] = input * (1.0 - effectMix); - lastOutput[1] += effectMix * delayLine[1]->tick(input); - return (lastOutput[0] + lastOutput[1]) * (MY_FLOAT) 0.5; -} - -MY_FLOAT *Chorus :: tick(MY_FLOAT *vector, unsigned int vectorSize) -{ - for (unsigned int i=0; isetOffset((MY_FLOAT) 0.7); - reedTable->setSlope((MY_FLOAT) -0.3); - filter = new OneZero; - envelope = new Envelope; - noise = new Noise; + length_ = (long) (Stk::sampleRate() / lowestFrequency + 1); + delayLine_.setMaximumDelay( length_ ); + delayLine_.setDelay( length_ / 2.0 ); + reedTable_.setOffset((StkFloat) 0.7); + reedTable_.setSlope((StkFloat) -0.3); // 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; - vibratoGain = (MY_FLOAT) 0.1; + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency((StkFloat) 5.735); + outputGain_ = (StkFloat) 1.0; + noiseGain_ = (StkFloat) 0.2; + vibratoGain_ = (StkFloat) 0.1; } Clarinet :: ~Clarinet() { - delete delayLine; - delete reedTable; - delete filter; - delete envelope; - delete noise; - delete vibrato; + delete vibrato_; } void Clarinet :: clear() { - delayLine->clear(); - filter->tick((MY_FLOAT) 0.0); + delayLine_.clear(); + filter_.tick((StkFloat) 0.0); } -void Clarinet :: setFrequency(MY_FLOAT frequency) +void Clarinet :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Clarinet: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Clarinet::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } // Delay = length - approximate filter delay. - MY_FLOAT delay = (Stk::sampleRate() / freakency) * 0.5 - 1.5; + StkFloat delay = (Stk::sampleRate() / freakency) * 0.5 - 1.5; if (delay <= 0.0) delay = 0.3; - else if (delay > length) delay = length; - delayLine->setDelay(delay); + else if (delay > length_) delay = length_; + delayLine_.setDelay(delay); } -void Clarinet :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +void Clarinet :: startBlowing(StkFloat amplitude, StkFloat rate) { - envelope->setRate(rate); - envelope->setTarget(amplitude); + envelope_.setRate(rate); + envelope_.setTarget(amplitude); } -void Clarinet :: stopBlowing(MY_FLOAT rate) +void Clarinet :: stopBlowing(StkFloat rate) { - envelope->setRate(rate); - envelope->setTarget((MY_FLOAT) 0.0); + envelope_.setRate(rate); + envelope_.setTarget((StkFloat) 0.0); } -void Clarinet :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Clarinet :: noteOn(StkFloat frequency, StkFloat amplitude) { this->setFrequency(frequency); - this->startBlowing((MY_FLOAT) 0.55 + (amplitude * (MY_FLOAT) 0.30), amplitude * (MY_FLOAT) 0.005); - outputGain = amplitude + (MY_FLOAT) 0.001; + this->startBlowing((StkFloat) 0.55 + (amplitude * (StkFloat) 0.30), amplitude * (StkFloat) 0.005); + outputGain_ = amplitude + (StkFloat) 0.001; #if defined(_STK_DEBUG_) - std::cerr << "Clarinet: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Clarinet::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Clarinet :: noteOff(MY_FLOAT amplitude) +void Clarinet :: noteOff(StkFloat amplitude) { - this->stopBlowing(amplitude * (MY_FLOAT) 0.01); + this->stopBlowing( amplitude * 0.01 ); #if defined(_STK_DEBUG_) - std::cerr << "Clarinet: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Clarinet::NoteOff: amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Clarinet :: tick() +StkFloat Clarinet :: tick() { - MY_FLOAT pressureDiff; - MY_FLOAT breathPressure; + StkFloat pressureDiff; + StkFloat breathPressure; // Calculate the breath pressure (envelope + noise + vibrato) - breathPressure = envelope->tick(); - breathPressure += breathPressure * noiseGain * noise->tick(); - breathPressure += breathPressure * vibratoGain * vibrato->tick(); + breathPressure = envelope_.tick(); + breathPressure += breathPressure * noiseGain_ * noise_.tick(); + breathPressure += breathPressure * vibratoGain_ * vibrato_->tick(); // Perform commuted loss filtering. - pressureDiff = -0.95 * filter->tick(delayLine->lastOut()); + pressureDiff = -0.95 * filter_.tick(delayLine_.lastOut()); // Calculate pressure difference of reflected and mouthpiece pressures. pressureDiff = pressureDiff - breathPressure; // Perform non-linear scattering using pressure difference in reed function. - lastOutput = delayLine->tick(breathPressure + pressureDiff * reedTable->tick(pressureDiff)); + lastOutput_ = delayLine_.tick(breathPressure + pressureDiff * reedTable_.tick(pressureDiff)); // Apply output gain. - lastOutput *= outputGain; + lastOutput_ *= outputGain_; - return lastOutput; + return lastOutput_; } -void Clarinet :: controlChange(int number, MY_FLOAT value) +StkFloat *Clarinet :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Clarinet :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Clarinet :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Clarinet: Control value less than zero!" << std::endl; + errorString_ << "Clarinet::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Clarinet: Control value greater than 128.0!" << std::endl; + errorString_ << "Clarinet::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_ReedStiffness_) // 2 - reedTable->setSlope((MY_FLOAT) -0.44 + ( (MY_FLOAT) 0.26 * norm )); + reedTable_.setSlope((StkFloat) -0.44 + ( (StkFloat) 0.26 * norm )); else if (number == __SK_NoiseLevel_) // 4 - noiseGain = (norm * (MY_FLOAT) 0.4); + noiseGain_ = (norm * (StkFloat) 0.4); else if (number == __SK_ModFrequency_) // 11 - vibrato->setFrequency((norm * (MY_FLOAT) 12.0)); + vibrato_->setFrequency((norm * (StkFloat) 12.0)); else if (number == __SK_ModWheel_) // 1 - vibratoGain = (norm * (MY_FLOAT) 0.5); + vibratoGain_ = (norm * (StkFloat) 0.5); else if (number == __SK_AfterTouch_Cont_) // 128 - envelope->setValue(norm); - else - std::cerr << "Clarinet: Undefined Control Number (" << number << ")!!" << std::endl; + envelope_.setValue(norm); + else { + errorString_ << "Clarinet::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Clarinet: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Clarinet::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Delay.cpp b/src/Delay.cpp index c6b2b1d..0bd2b63 100644 --- a/src/Delay.cpp +++ b/src/Delay.cpp @@ -14,40 +14,45 @@ used in fixed delay-length applications, such as for reverberation. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Delay.h" -#include -Delay :: Delay() +Delay :: Delay() : Filter() { - // Default max delay length set to 4095. - length = 4096; - delete [] inputs; - inputs = new MY_FLOAT[length]; + // Default maximum delay length set to 4095. + inputs_.resize( 4096 ); this->clear(); - inPoint = 0; - outPoint = 0; - delay = 0; + inPoint_ = 0; + outPoint_ = 0; + delay_ = 0; } -Delay :: Delay(long theDelay, long maxDelay) +Delay :: Delay(unsigned long delay, unsigned long maxDelay) { // Writing before reading allows delays from 0 to length-1. // If we want to allow a delay of maxDelay, we need a // delay-line of length = maxDelay+1. - length = maxDelay+1; + if ( maxDelay < 1 ) { + errorString_ << "Delay::Delay: maxDelay must be > 0!\n"; + handleError( StkError::FUNCTION_ARGUMENT ); + } - // We need to delete the previously allocated inputs. - delete [] inputs; - inputs = new MY_FLOAT[length]; - this->clear(); + if ( delay > maxDelay ) { + errorString_ << "Delay::Delay: maxDelay must be > than delay argument!\n"; + handleError( StkError::FUNCTION_ARGUMENT ); + } - inPoint = 0; - this->setDelay(theDelay); + if ( maxDelay > inputs_.size()-1 ) { + inputs_.resize( maxDelay+1 ); + this->clear(); + } + + inPoint_ = 0; + this->setDelay( delay ); } Delay :: ~Delay() @@ -56,109 +61,135 @@ Delay :: ~Delay() void Delay :: clear(void) { - long i; - for (i=0;i length-1) { // The value is too big. - std::cerr << "Delay: setDelay(" << theDelay << ") too big!" << std::endl; - // Force delay to maxLength. - outPoint = inPoint + 1; - delay = length - 1; + if ( delay < inputs_.size() ) return; + + if ( delay < 0 ) { + errorString_ << "Delay::setMaximumDelay: argument (" << delay << ") less than zero!\n"; + handleError( StkError::WARNING ); + return; } - else if (theDelay < 0 ) { - std::cerr << "Delay: setDelay(" << theDelay << ") less than zero!" << std::endl; - outPoint = inPoint; - delay = 0; - } - else { - outPoint = inPoint - (long) theDelay; // read chases write - delay = theDelay; + else if (delay < delay_ ) { + errorString_ << "Delay::setMaximumDelay: argument (" << delay << ") less than current delay setting (" << delay_ << ")!\n"; + handleError( StkError::WARNING ); + return; } - while (outPoint < 0) outPoint += length; // modulo maximum length + inputs_.resize( delay + 1 ); } -long Delay :: getDelay(void) const +void Delay :: setDelay(unsigned long delay) { - return (long)delay; + if ( delay > inputs_.size() - 1 ) { // The value is too big. + errorString_ << "Delay::setDelay: argument (" << delay << ") too big ... setting to maximum!\n"; + handleError( StkError::WARNING ); + + // Force delay to maximum length. + outPoint_ = inPoint_ + 1; + if ( outPoint_ == inputs_.size() ) outPoint_ = 0; + delay_ = inputs_.size() - 1; + } + else if ( delay < 0 ) { + errorString_ << "Delay::setDelay: argument (" << delay << ") less than zero ... setting to zero!\n"; + handleError( StkError::WARNING ); + + outPoint_ = inPoint_; + delay_ = 0; + } + else { // read chases write + if ( inPoint_ >= delay ) outPoint_ = inPoint_ - delay; + else outPoint_ = inputs_.size() + inPoint_ - delay; + delay_ = delay; + } } -MY_FLOAT Delay :: energy(void) const +unsigned long Delay :: getDelay(void) const { - int i; - register MY_FLOAT e = 0; - if (inPoint >= outPoint) { - for (i=outPoint; i= outPoint_) { + for (i=outPoint_; i delay) { - std::cerr << "Delay: contentsAt(" << tapDelay << ") too big!" << std::endl; - i = (long) delay; + else if (i > delay_) { + errorString_ << "Delay::contentsAt: argument (" << tapDelay << ") too big!"; + handleError( StkError::WARNING ); + return 0.0; } - long tap = inPoint - i; + long tap = inPoint_ - i; if (tap < 0) // Check for wraparound. - tap += length; + tap += inputs_.size(); - return inputs[tap]; + return inputs_[tap]; } -MY_FLOAT Delay :: lastOut(void) const +StkFloat Delay :: lastOut(void) const { return Filter::lastOut(); } -MY_FLOAT Delay :: nextOut(void) const +StkFloat Delay :: nextOut(void) { - return inputs[outPoint]; + return inputs_[outPoint_]; } -MY_FLOAT Delay :: tick(MY_FLOAT sample) +StkFloat Delay :: tick(StkFloat sample) { - inputs[inPoint++] = sample; + inputs_[inPoint_++] = sample; // Check for end condition - if (inPoint == length) - inPoint -= length; + if (inPoint_ == inputs_.size()) + inPoint_ = 0; // Read out next value - outputs[0] = inputs[outPoint++]; + outputs_[0] = inputs_[outPoint_++]; - if (outPoint>=length) - outPoint -= length; + if (outPoint_ == inputs_.size()) + outPoint_ = 0; - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *Delay :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Delay :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i -DelayA :: DelayA() +DelayA :: DelayA() : Delay() { this->setDelay( 0.5 ); - apInput = 0.0; - doNextOut = true; + apInput_ = 0.0; + doNextOut_ = true; } -DelayA :: DelayA(MY_FLOAT theDelay, long maxDelay) +DelayA :: DelayA(StkFloat delay, unsigned long maxDelay) { - // Writing before reading allows delays from 0 to length-1. - length = maxDelay+1; + if ( delay < 0.0 || maxDelay < 1 ) { + errorString_ << "DelayA::DelayA: delay must be >= 0.0, maxDelay must be > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } - if ( length > 4096 ) { - // We need to delete the previously allocated inputs. - delete [] inputs; - inputs = new MY_FLOAT[length]; + if ( delay > (StkFloat) maxDelay ) { + errorString_ << "DelayA::DelayA: maxDelay must be > than delay argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + // Writing before reading allows delays from 0 to length-1. + if ( maxDelay > inputs_.size()-1 ) { + inputs_.resize( maxDelay+1 ); this->clear(); } - inPoint = 0; - this->setDelay(theDelay); - apInput = 0.0; - doNextOut = true; + inPoint_ = 0; + this->setDelay(delay); + apInput_ = 0.0; + doNextOut_ = true; } DelayA :: ~DelayA() @@ -57,74 +62,90 @@ DelayA :: ~DelayA() void DelayA :: clear() { Delay::clear(); - apInput = 0.0; + apInput_ = 0.0; } -void DelayA :: setDelay(MY_FLOAT theDelay) +void DelayA :: setDelay(StkFloat delay) { - MY_FLOAT outPointer; + StkFloat outPointer; + unsigned long length = inputs_.size(); + + if ( delay > inputs_.size() - 1 ) { // The value is too big. + errorString_ << "DelayA::setDelay: argument (" << delay << ") too big ... setting to maximum!"; + handleError( StkError::WARNING ); - if (theDelay > length-1) { - std::cerr << "DelayA: setDelay(" << theDelay << ") too big!" << std::endl; // Force delay to maxLength - outPointer = inPoint + 1.0; - delay = length - 1; + outPointer = inPoint_ + 1.0; + delay_ = length - 1; } - else if (theDelay < 0.5) { - std::cerr << "DelayA: setDelay(" << theDelay << ") less than 0.5 not possible!" << std::endl; - outPointer = inPoint + 0.4999999999; - delay = 0.5; + else if (delay < 0.5) { + errorString_ << "DelayA::setDelay: argument (" << delay << ") less than 0.5 not possible!"; + handleError( StkError::WARNING ); + + outPointer = inPoint_ + 0.4999999999; + delay_ = 0.5; } else { - outPointer = inPoint - theDelay + 1.0; // outPoint chases inpoint - delay = theDelay; + outPointer = inPoint_ - delay + 1.0; // outPoint chases inpoint + delay_ = delay; } if (outPointer < 0) outPointer += length; // modulo maximum length - outPoint = (long) outPointer; // integer part - alpha = 1.0 + outPoint - outPointer; // fractional part + outPoint_ = (long) outPointer; // integer part + if ( outPoint_ == length ) outPoint_ = 0; + alpha_ = 1.0 + outPoint_ - outPointer; // fractional part - if (alpha < 0.5) { + if (alpha_ < 0.5) { // The optimal range for alpha is about 0.5 - 1.5 in order to // achieve the flattest phase delay response. - outPoint += 1; - if (outPoint >= length) outPoint -= length; - alpha += (MY_FLOAT) 1.0; + outPoint_ += 1; + if (outPoint_ >= length) outPoint_ -= length; + alpha_ += (StkFloat) 1.0; } - coeff = ((MY_FLOAT) 1.0 - alpha) / - ((MY_FLOAT) 1.0 + alpha); // coefficient for all pass + coeff_ = ((StkFloat) 1.0 - alpha_) / + ((StkFloat) 1.0 + alpha_); // coefficient for all pass } -MY_FLOAT DelayA :: nextOut(void) +StkFloat DelayA :: nextOut(void) { - if ( doNextOut ) { + if ( doNextOut_ ) { // Do allpass interpolation delay. - nextOutput = -coeff * outputs[0]; - nextOutput += apInput + (coeff * inputs[outPoint]); - doNextOut = false; + nextOutput_ = -coeff_ * outputs_[0]; + nextOutput_ += apInput_ + (coeff_ * inputs_[outPoint_]); + doNextOut_ = false; } - return nextOutput; + return nextOutput_; } -MY_FLOAT DelayA :: tick(MY_FLOAT sample) +StkFloat DelayA :: tick(StkFloat sample) { - inputs[inPoint++] = sample; + inputs_[inPoint_++] = sample; // Increment input pointer modulo length. - if (inPoint == length) - inPoint -= length; + if (inPoint_ == inputs_.size()) + inPoint_ = 0; - outputs[0] = nextOut(); - doNextOut = true; + outputs_[0] = nextOut(); + doNextOut_ = true; // Save the allpass input and increment modulo length. - apInput = inputs[outPoint++]; - if (outPoint == length) - outPoint -= length; + apInput_ = inputs_[outPoint_++]; + if (outPoint_ == inputs_.size()) + outPoint_ = 0; - return outputs[0]; + return outputs_[0]; +} + +StkFloat *DelayA :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Filter::tick( vector, vectorSize ); +} + +StkFrames& DelayA :: tick( StkFrames& frames, unsigned int channel ) +{ + return Filter::tick( frames, channel ); } diff --git a/src/DelayL.cpp b/src/DelayL.cpp index cbe6139..0313271 100644 --- a/src/DelayL.cpp +++ b/src/DelayL.cpp @@ -18,102 +18,122 @@ order Lagrange interpolators can typically improve (minimize) this attenuation characteristic. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "DelayL.h" -#include -DelayL :: DelayL() +DelayL :: DelayL() : Delay() { - doNextOut = true; + doNextOut_ = true; } -DelayL :: DelayL(MY_FLOAT theDelay, long maxDelay) +DelayL :: DelayL(StkFloat delay, unsigned long maxDelay) { - // Writing before reading allows delays from 0 to length-1. - length = maxDelay+1; + if ( delay < 0.0 || maxDelay < 1 ) { + errorString_ << "DelayL::DelayL: delay must be >= 0.0, maxDelay must be > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } - if ( length > 4096 ) { - // We need to delete the previously allocated inputs. - delete [] inputs; - inputs = new MY_FLOAT[length]; + if ( delay > (StkFloat) maxDelay ) { + errorString_ << "DelayL::DelayL: maxDelay must be > than delay argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + // Writing before reading allows delays from 0 to length-1. + if ( maxDelay > inputs_.size()-1 ) { + inputs_.resize( maxDelay+1 ); this->clear(); } - inPoint = 0; - this->setDelay(theDelay); - doNextOut = true; + inPoint_ = 0; + this->setDelay(delay); + doNextOut_ = true; } DelayL :: ~DelayL() { } -void DelayL :: setDelay(MY_FLOAT theDelay) +void DelayL :: setDelay(StkFloat delay) { - MY_FLOAT outPointer; + StkFloat outPointer; + + if ( delay > inputs_.size() - 1 ) { // The value is too big. + errorString_ << "DelayL::setDelay: argument (" << delay << ") too big ... setting to maximum!"; + handleError( StkError::WARNING ); - if (theDelay > length-1) { - std::cerr << "DelayL: setDelay(" << theDelay << ") too big!" << std::endl; // Force delay to maxLength - outPointer = inPoint + 1.0; - delay = length - 1; + outPointer = inPoint_ + 1.0; + delay_ = inputs_.size() - 1; } - else if (theDelay < 0 ) { - std::cerr << "DelayL: setDelay(" << theDelay << ") less than zero!" << std::endl; - outPointer = inPoint; - delay = 0; + else if (delay < 0 ) { + errorString_ << "DelayL::setDelay: argument (" << delay << ") less than zero ... setting to zero!"; + handleError( StkError::WARNING ); + + outPointer = inPoint_; + delay_ = 0; } else { - outPointer = inPoint - theDelay; // read chases write - delay = theDelay; + outPointer = inPoint_ - delay; // read chases write + delay_ = delay; } while (outPointer < 0) - outPointer += length; // modulo maximum length + outPointer += inputs_.size(); // modulo maximum length - outPoint = (long) outPointer; // integer part - alpha = outPointer - outPoint; // fractional part - omAlpha = (MY_FLOAT) 1.0 - alpha; + outPoint_ = (long) outPointer; // integer part + if ( outPoint_ == inputs_.size() ) outPoint_ = 0; + alpha_ = outPointer - outPoint_; // fractional part + omAlpha_ = (StkFloat) 1.0 - alpha_; } -MY_FLOAT DelayL :: getDelay(void) const +StkFloat DelayL :: getDelay(void) const { - return delay; + return delay_; } -MY_FLOAT DelayL :: nextOut(void) +StkFloat DelayL :: nextOut(void) { - if ( doNextOut ) { + if ( doNextOut_ ) { // First 1/2 of interpolation - nextOutput = inputs[outPoint] * omAlpha; + nextOutput_ = inputs_[outPoint_] * omAlpha_; // Second 1/2 of interpolation - if (outPoint+1 < length) - nextOutput += inputs[outPoint+1] * alpha; + if (outPoint_+1 < inputs_.size()) + nextOutput_ += inputs_[outPoint_+1] * alpha_; else - nextOutput += inputs[0] * alpha; - doNextOut = false; + nextOutput_ += inputs_[0] * alpha_; + doNextOut_ = false; } - return nextOutput; + return nextOutput_; } -MY_FLOAT DelayL :: tick(MY_FLOAT sample) +StkFloat DelayL :: tick(StkFloat sample) { - inputs[inPoint++] = sample; + inputs_[inPoint_++] = sample; // Increment input pointer modulo length. - if (inPoint == length) - inPoint -= length; + if (inPoint_ == inputs_.size()) + inPoint_ = 0; - outputs[0] = nextOut(); - doNextOut = true; + outputs_[0] = nextOut(); + doNextOut_ = true; // Increment output pointer modulo length. - if (++outPoint >= length) - outPoint -= length; + if (++outPoint_ == inputs_.size()) + outPoint_ = 0; - return outputs[0]; + return outputs_[0]; +} + +StkFloat *DelayL :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Filter::tick( vector, vectorSize ); +} + +StkFrames& DelayL :: tick( StkFrames& frames, unsigned int channel ) +{ + return Filter::tick( frames, channel ); } diff --git a/src/Drummer.cpp b/src/Drummer.cpp index 251dc4c..444c9ba 100644 --- a/src/Drummer.cpp +++ b/src/Drummer.cpp @@ -11,14 +11,14 @@ of simultaneous voices) via a #define in the Drummer.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Drummer.h" #include -// Not really General MIDI yet. Coming soon. +// Not really General MIDI yet. unsigned char genMIDIMap[128] = { 0,0,0,0,0,0,0,0, // 0-7 0,0,0,0,0,0,0,0, // 8-15 @@ -55,122 +55,133 @@ char waveNames[DRUM_NUMWAVES][16] = Drummer :: Drummer() : Instrmnt() { - for (int i=0; i= 0 ) { // Reset this sound. - waves[waveIndex]->reset(); - filters[waveIndex]->setPole((MY_FLOAT) 0.999 - (gain * 0.6)); - filters[waveIndex]->setGain(gain); + waves_[waveIndex]->reset(); + filters_[waveIndex]->setPole( 0.999 - (gain * 0.6) ); + filters_[waveIndex]->setGain( gain ); } else { - if (nSounding == DRUM_POLYPHONY) { + if (nSounding_ == DRUM_POLYPHONY) { // If we're already at maximum polyphony, then preempt the oldest voice. - delete waves[0]; - filters[0]->clear(); - WvIn *tempWv = waves[0]; - OnePole *tempFilt = filters[0]; + delete waves_[0]; + filters_[0]->clear(); + OnePole *tempFilt = filters_[0]; // Re-order the list. - for (i=0; isetRate( 22050.0 / Stk::sampleRate() ); - filters[nSounding-1]->setPole((MY_FLOAT) 0.999 - (gain * 0.6) ); - filters[nSounding-1]->setGain( gain ); + waves_[nSounding_-1]->setRate( 22050.0 / Stk::sampleRate() ); + filters_[nSounding_-1]->setPole( 0.999 - (gain * 0.6) ); + filters_[nSounding_-1]->setGain( gain ); } #if defined(_STK_DEBUG_) - std::cerr << "Number Sounding = " << nSounding << std::endl; - for (i=0; isetGain( amplitude * 0.01 ); - } + while ( i < nSounding_ ) filters_[i++]->setGain( amplitude * 0.01 ); } -MY_FLOAT Drummer :: tick() +StkFloat Drummer :: tick() { - MY_FLOAT output = 0.0; OnePole *tempFilt; int j, i = 0; - while (i < nSounding) { - if ( waves[i]->isFinished() ) { - delete waves[i]; - tempFilt = filters[i]; + lastOutput_ = 0.0; + while (i < nSounding_) { + if ( waves_[i]->isFinished() ) { + delete waves_[i]; + tempFilt = filters_[i]; // Re-order the list. - for (j=i; jclear(); - sounding[j] = -1; - nSounding -= 1; + filters_[j] = tempFilt; + filters_[j]->clear(); + sounding_[j] = -1; + nSounding_ -= 1; i -= 1; } else - output += filters[i]->tick( waves[i]->tick() ); + lastOutput_ += filters_[i]->tick( waves_[i]->tick() ); i++; } - return output; + return lastOutput_; +} + +StkFloat *Drummer :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Drummer :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/Echo.cpp b/src/Echo.cpp index 4774389..02e1452 100644 --- a/src/Echo.cpp +++ b/src/Echo.cpp @@ -2,78 +2,72 @@ /*! \class Echo \brief STK echo effect class. - This class implements a echo effect. + This class implements an echo effect. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Echo.h" #include -Echo :: Echo(MY_FLOAT longestDelay) +Echo :: Echo( unsigned long maximumDelay ) : Effect() { - length = (long) longestDelay + 2; - delayLine = new Delay(length>>1, length); - effectMix = 0.5; + this->setMaximumDelay( maximumDelay ); + delayLine_.setDelay( length_ >> 1 ); + effectMix_ = 0.5; this->clear(); } Echo :: ~Echo() { - delete delayLine; } void Echo :: clear() { - delayLine->clear(); - lastOutput = 0.0; + delayLine_.clear(); + lastOutput_[0] = 0.0; + lastOutput_[1] = 0.0; } -void Echo :: setDelay(MY_FLOAT delay) +void Echo :: setMaximumDelay( unsigned long delay ) { - MY_FLOAT size = delay; - if ( delay < 0.0 ) { - std::cerr << "Echo: setDelay parameter is less than zero!" << std::endl; - size = 0.0; - } - else if ( delay > length ) { - std::cerr << "Echo: setDelay parameter is greater than delay length!" << std::endl; - size = length; + length_ = delay; + if ( delay == 0 ) { + errorString_ << "Echo::setMaximumDelay: parameter cannot be zero ... setting to 10!"; + handleError( StkError::WARNING ); + length_ = 10; } - delayLine->setDelay((long)size); + delayLine_.setMaximumDelay( length_ ); } -void Echo :: setEffectMix(MY_FLOAT mix) +void Echo :: setDelay( unsigned long delay ) { - effectMix = mix; - if ( mix < 0.0 ) { - std::cerr << "Echo: setEffectMix parameter is less than zero!" << std::endl; - effectMix = 0.0; - } - else if ( mix > 1.0 ) { - std::cerr << "Echo: setEffectMix parameter is greater than 1.0!" << std::endl; - effectMix = 1.0; + unsigned long size = delay; + if ( delay > length_ ) { + errorString_ << "Echo::setDelay: parameter is greater than maximum delay length ... setting to max!"; + handleError( StkError::WARNING ); + size = length_; } + + delayLine_.setDelay( size ); } -MY_FLOAT Echo :: lastOut() const +StkFloat Echo :: tick(StkFloat input) { - return lastOutput; + lastOutput_[0] = effectMix_ * delayLine_.tick(input); + lastOutput_[0] += input * (1.0 - effectMix_); + lastOutput_[1] = lastOutput_[0]; + return lastOutput_[0]; } -MY_FLOAT Echo :: tick(MY_FLOAT input) +StkFloat *Echo :: tick(StkFloat *vector, unsigned int vectorSize) { - lastOutput = effectMix * delayLine->tick(input); - lastOutput += input * (1.0 - effectMix); - return lastOutput; + return Effect::tick( vector, vectorSize ); } -MY_FLOAT *Echo :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFrames& Echo :: tick( StkFrames& frames, unsigned int channel ) { - for (unsigned int i=0; i + +Effect :: Effect() +{ +} + +Effect :: ~Effect() +{ +} + +void Effect :: setEffectMix(StkFloat mix) +{ + if ( mix < 0.0 ) { + errorString_ << "Effect::setEffectMix: mix parameter is less than zero ... setting to zero!"; + handleError( StkError::WARNING ); + effectMix_ = 0.0; + } + else if ( mix > 1.0 ) { + errorString_ << "Effect::setEffectMix: mix parameter is greater than 1.0 ... setting to one!"; + handleError( StkError::WARNING ); + effectMix_ = 1.0; + } + else + effectMix_ = mix; +} + +StkFloat Effect :: lastOut() const +{ + return (lastOutput_[0] + lastOutput_[1]) * 0.5; +} + +StkFloat Effect :: lastOutLeft() const +{ + return lastOutput_[0]; +} + +StkFloat Effect :: lastOutRight() const +{ + return lastOutput_[1]; +} + +StkFloat *Effect :: tick(StkFloat *vector, unsigned int vectorSize) +{ + for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; i -Envelope :: Envelope(void) : Stk() +Envelope :: Envelope(void) : Generator() { - target = (MY_FLOAT) 0.0; - value = (MY_FLOAT) 0.0; - rate = (MY_FLOAT) 0.001; - state = 0; + target_ = 0.0; + value_ = 0.0; + rate_ = 0.001; + state_ = 0; } Envelope :: ~Envelope(void) @@ -30,85 +29,85 @@ Envelope :: ~Envelope(void) void Envelope :: keyOn(void) { - target = (MY_FLOAT) 1.0; - if (value != target) state = 1; + target_ = 1.0; + if (value_ != target_) state_ = 1; } void Envelope :: keyOff(void) { - target = (MY_FLOAT) 0.0; - if (value != target) state = 1; + target_ = 0.0; + if (value_ != target_) state_ = 1; } -void Envelope :: setRate(MY_FLOAT aRate) +void Envelope :: setRate(StkFloat rate) { - if (aRate < 0.0) { - printf("Envelope: negative rates not allowed ... correcting!\n"); - rate = -aRate; + if (rate < 0.0) { + errorString_ << "Envelope::setRate: negative rates not allowed ... correcting!"; + handleError( StkError::WARNING ); + rate_ = -rate; } else - rate = aRate; + rate_ = rate; } -void Envelope :: setTime(MY_FLOAT aTime) +void Envelope :: setTime(StkFloat time) { - if (aTime < 0.0) { - printf("Envelope: negative times not allowed ... correcting!\n"); - rate = 1.0 / (-aTime * Stk::sampleRate()); + if (time < 0.0) { + errorString_ << "Envelope::setTime: negative times not allowed ... correcting!"; + handleError( StkError::WARNING ); + rate_ = 1.0 / (-time * Stk::sampleRate()); } else - rate = 1.0 / (aTime * Stk::sampleRate()); + rate_ = 1.0 / (time * Stk::sampleRate()); } -void Envelope :: setTarget(MY_FLOAT aTarget) +void Envelope :: setTarget(StkFloat target) { - target = aTarget; - if (value != target) state = 1; + target_ = target; + if (value_ != target_) state_ = 1; } -void Envelope :: setValue(MY_FLOAT aValue) +void Envelope :: setValue(StkFloat value) { - state = 0; - target = aValue; - value = aValue; + state_ = 0; + target_ = value; + value_ = value; } int Envelope :: getState(void) const { - return state; + return state_; } -MY_FLOAT Envelope :: tick(void) +StkFloat Envelope :: tick(void) { - if (state) { - if (target > value) { - value += rate; - if (value >= target) { - value = target; - state = 0; + if (state_) { + if (target_ > value_) { + value_ += rate_; + if (value_ >= target_) { + value_ = target_; + state_ = 0; } } else { - value -= rate; - if (value <= target) { - value = target; - state = 0; + value_ -= rate_; + if (value_ <= target_) { + value_ = target_; + state_ = 0; } } } - return value; + + lastOutput_ = value_; + return value_; } -MY_FLOAT *Envelope :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Envelope :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i -FM :: FM(int operators) - : nOperators(operators) +FM :: FM( unsigned int operators ) + : nOperators_(operators) { - if ( nOperators <= 0 ) { - char msg[256]; - sprintf(msg, "FM: Invalid number of operators (%d) argument to constructor!", operators); - handleError(msg, StkError::FUNCTION_ARGUMENT); + if ( nOperators_ == 0 ) { + errorString_ << "FM: Invalid number of operators (" << operators << ") argument to constructor!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - twozero = new TwoZero(); - twozero->setB2( -1.0 ); - twozero->setGain( 0.0 ); + twozero_.setB2( -1.0 ); + twozero_.setGain( 0.0 ); // Concatenate the STK rawwave path to the rawwave file - vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); - vibrato->setFrequency(6.0); + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency( 6.0 ); - int i; - ratios = (MY_FLOAT *) new MY_FLOAT[nOperators]; - gains = (MY_FLOAT *) new MY_FLOAT[nOperators]; - adsr = (ADSR **) calloc( nOperators, sizeof(ADSR *) ); - waves = (WaveLoop **) calloc( nOperators, sizeof(WaveLoop *) ); - for (i=0; i=0; i--) { - __FM_gains[i] = temp; + fmGains_[i] = temp; temp *= 0.933033; } temp = 1.0; for (i=15; i>=0; i--) { - __FM_susLevels[i] = temp; + fmSusLevels_[i] = temp; temp *= 0.707101; } temp = 8.498186; for (i=0; i<32; i++) { - __FM_attTimes[i] = temp; + fmAttTimes_[i] = temp; temp *= 0.707101; } } FM :: ~FM() { - delete vibrato; - delete twozero; + delete vibrato_; - delete [] ratios; - delete [] gains; - for (int i=0; isetFrequency( baseFrequency * ratios[i] ); + for (unsigned int i=0; isetFrequency( baseFrequency_ * ratios_[i] ); } -void FM :: setRatio(int waveIndex, MY_FLOAT ratio) +void FM :: setRatio(unsigned int waveIndex, StkFloat ratio) { if ( waveIndex < 0 ) { - std::cerr << "FM: setRatio waveIndex parameter is less than zero!" << std::endl; + errorString_ << "FM::setRatio: waveIndex parameter is less than zero!"; + handleError( StkError::WARNING ); return; } - else if ( waveIndex >= nOperators ) { - std::cerr << "FM: setRatio waveIndex parameter is greater than the number of operators!" << std::endl; + else if ( waveIndex >= nOperators_ ) { + errorString_ << "FM:setRatio: waveIndex parameter is greater than the number of operators!"; + handleError( StkError::WARNING ); return; } - ratios[waveIndex] = ratio; + ratios_[waveIndex] = ratio; if (ratio > 0.0) - waves[waveIndex]->setFrequency(baseFrequency * ratio); + waves_[waveIndex]->setFrequency( baseFrequency_ * ratio ); else - waves[waveIndex]->setFrequency(ratio); + waves_[waveIndex]->setFrequency( ratio ); } -void FM :: setGain(int waveIndex, MY_FLOAT gain) +void FM :: setGain(unsigned int waveIndex, StkFloat gain) { if ( waveIndex < 0 ) { - std::cerr << "FM: setGain waveIndex parameter is less than zero!" << std::endl; + errorString_ << "FM::setGain: waveIndex parameter is less than zero!"; + handleError( StkError::WARNING ); return; } - else if ( waveIndex >= nOperators ) { - std::cerr << "FM: setGain waveIndex parameter is greater than the number of operators!" << std::endl; + else if ( waveIndex >= nOperators_ ) { + errorString_ << "FM::setGain: waveIndex parameter is greater than the number of operators!"; + handleError( StkError::WARNING ); return; } - gains[waveIndex] = gain; + gains_[waveIndex] = gain; } -void FM :: setModulationSpeed(MY_FLOAT mSpeed) +void FM :: setModulationSpeed(StkFloat mSpeed) { - vibrato->setFrequency(mSpeed); + vibrato_->setFrequency( mSpeed ); } -void FM :: setModulationDepth(MY_FLOAT mDepth) +void FM :: setModulationDepth(StkFloat mDepth) { - modDepth = mDepth; + modDepth_ = mDepth; } -void FM :: setControl1(MY_FLOAT cVal) +void FM :: setControl1(StkFloat cVal) { - control1 = cVal * (MY_FLOAT) 2.0; + control1_ = cVal * 2.0; } -void FM :: setControl2(MY_FLOAT cVal) +void FM :: setControl2(StkFloat cVal) { - control2 = cVal * (MY_FLOAT) 2.0; + control2_ = cVal * 2.0; } void FM :: keyOn() { - for (int i=0; ikeyOn(); + for (unsigned int i=0; ikeyOn(); } void FM :: keyOff() { - for (int i=0; ikeyOff(); + for (unsigned int i=0; ikeyOff(); } -void FM :: noteOff(MY_FLOAT amplitude) +void FM :: noteOff(StkFloat amplitude) { - keyOff(); + this->keyOff(); #if defined(_STK_DEBUG_) - std::cerr << "FM: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "FM::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void FM :: controlChange(int number, MY_FLOAT value) +void FM :: controlChange(int number, StkFloat value) { - MY_FLOAT norm = value * ONE_OVER_128; + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "FM: Control value less than zero!" << std::endl; + errorString_ << "FM::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "FM: Control value greater than 128.0!" << std::endl; + errorString_ << "FM::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_Breath_) // 2 - setControl1( norm ); + this->setControl1( norm ); else if (number == __SK_FootControl_) // 4 - setControl2( norm ); + this->setControl2( norm ); else if (number == __SK_ModFrequency_) // 11 - setModulationSpeed( norm * 12.0); + this->setModulationSpeed( norm * 12.0); else if (number == __SK_ModWheel_) // 1 - setModulationDepth( norm ); + this->setModulationDepth( norm ); else if (number == __SK_AfterTouch_Cont_) { // 128 - //adsr[0]->setTarget( norm ); - adsr[1]->setTarget( norm ); - //adsr[2]->setTarget( norm ); - adsr[3]->setTarget( norm ); + //adsr_[0]->setTarget( norm ); + adsr_[1]->setTarget( norm ); + //adsr_[2]->setTarget( norm ); + adsr_[3]->setTarget( norm ); + } + else { + errorString_ << "FM::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); } - else - std::cerr << "FM: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - std::cerr << "FM: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "FM::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/FMVoices.cpp b/src/FMVoices.cpp index d893582..4e229dc 100644 --- a/src/FMVoices.cpp +++ b/src/FMVoices.cpp @@ -26,7 +26,7 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -38,146 +38,163 @@ FMVoices :: FMVoices() : FM() { // 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 ); + for ( unsigned 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); this->setRatio(2, 12.0); this->setRatio(3, 1.00); - gains[3] = __FM_gains[80]; + gains_[3] = fmGains_[80]; - adsr[0]->setAllTimes( 0.05, 0.05, __FM_susLevels[15], 0.05); - adsr[1]->setAllTimes( 0.05, 0.05, __FM_susLevels[15], 0.05); - adsr[2]->setAllTimes( 0.05, 0.05, __FM_susLevels[15], 0.05); - adsr[3]->setAllTimes( 0.01, 0.01, __FM_susLevels[15], 0.5); + adsr_[0]->setAllTimes( 0.05, 0.05, fmSusLevels_[15], 0.05); + adsr_[1]->setAllTimes( 0.05, 0.05, fmSusLevels_[15], 0.05); + adsr_[2]->setAllTimes( 0.05, 0.05, fmSusLevels_[15], 0.05); + adsr_[3]->setAllTimes( 0.01, 0.01, fmSusLevels_[15], 0.5); - twozero->setGain( 0.0 ); - modDepth = (MY_FLOAT) 0.005; - currentVowel = 0; - tilt[0] = 1.0; - tilt[1] = 0.5; - tilt[2] = 0.2; - mods[0] = 1.0; - mods[1] = 1.1; - mods[2] = 1.1; - baseFrequency = 110.0; - setFrequency( 110.0 ); + twozero_.setGain( 0.0 ); + modDepth_ = (StkFloat) 0.005; + currentVowel_ = 0; + tilt_[0] = 1.0; + tilt_[1] = 0.5; + tilt_[2] = 0.2; + mods_[0] = 1.0; + mods_[1] = 1.1; + mods_[2] = 1.1; + baseFrequency_ = 110.0; + this->setFrequency( 110.0 ); } FMVoices :: ~FMVoices() { } -void FMVoices :: setFrequency(MY_FLOAT frequency) +void FMVoices :: setFrequency(StkFloat frequency) { - MY_FLOAT temp, temp2 = 0.0; + StkFloat temp, temp2 = 0.0; int tempi = 0; unsigned int i = 0; - if (currentVowel < 32) { - i = currentVowel; - temp2 = (MY_FLOAT) 0.9; + if (currentVowel_ < 32) { + i = currentVowel_; + temp2 = 0.9; } - else if (currentVowel < 64) { - i = currentVowel - 32; - temp2 = (MY_FLOAT) 1.0; + else if (currentVowel_ < 64) { + i = currentVowel_ - 32; + temp2 = 1.0; } - else if (currentVowel < 96) { - i = currentVowel - 64; - temp2 = (MY_FLOAT) 1.1; + else if (currentVowel_ < 96) { + i = currentVowel_ - 64; + temp2 = 1.1; } - else if (currentVowel <= 128) { - i = currentVowel - 96; - temp2 = (MY_FLOAT) 1.2; + else if (currentVowel_ <= 128) { + i = currentVowel_ - 96; + temp2 = 1.2; } - baseFrequency = frequency; - temp = (temp2 * Phonemes::formantFrequency(i, 0) / baseFrequency) + 0.5; + baseFrequency_ = frequency; + temp = (temp2 * Phonemes::formantFrequency(i, 0) / baseFrequency_) + 0.5; tempi = (int) temp; - this->setRatio(0,(MY_FLOAT) tempi); - temp = (temp2 * Phonemes::formantFrequency(i, 1) / baseFrequency) + 0.5; + this->setRatio( 0, (StkFloat) tempi ); + temp = (temp2 * Phonemes::formantFrequency(i, 1) / baseFrequency_) + 0.5; tempi = (int) temp; - this->setRatio(1,(MY_FLOAT) tempi); - temp = (temp2 * Phonemes::formantFrequency(i, 2) / baseFrequency) + 0.5; + this->setRatio( 1, (StkFloat) tempi ); + temp = (temp2 * Phonemes::formantFrequency(i, 2) / baseFrequency_) + 0.5; tempi = (int) temp; - this->setRatio(2, (MY_FLOAT) tempi); - gains[0] = 1.0; - gains[1] = 1.0; - gains[2] = 1.0; + this->setRatio( 2, (StkFloat) tempi ); + gains_[0] = 1.0; + gains_[1] = 1.0; + gains_[2] = 1.0; } -void FMVoices :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void FMVoices :: noteOn(StkFloat frequency, StkFloat amplitude) { - this->setFrequency(frequency); - tilt[0] = amplitude; - tilt[1] = amplitude * amplitude; - tilt[2] = tilt[1] * amplitude; + this->setFrequency( frequency ); + tilt_[0] = amplitude; + tilt_[1] = amplitude * amplitude; + tilt_[2] = tilt_[1] * amplitude; this->keyOn(); #if defined(_STK_DEBUG_) - std::cerr << "FMVoices: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "FMVoices::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT FMVoices :: tick() +StkFloat FMVoices :: tick() { - register MY_FLOAT temp, temp2; + register StkFloat temp, temp2; - temp = gains[3] * adsr[3]->tick() * waves[3]->tick(); - temp2 = vibrato->tick() * modDepth * (MY_FLOAT) 0.1; + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + temp2 = vibrato_->tick() * modDepth_ * 0.1; - waves[0]->setFrequency(baseFrequency * (1.0 + temp2) * ratios[0]); - waves[1]->setFrequency(baseFrequency * (1.0 + temp2) * ratios[1]); - waves[2]->setFrequency(baseFrequency * (1.0 + temp2) * ratios[2]); - waves[3]->setFrequency(baseFrequency * (1.0 + temp2) * ratios[3]); + waves_[0]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[0]); + waves_[1]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[1]); + waves_[2]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[2]); + waves_[3]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[3]); - waves[0]->addPhaseOffset(temp * mods[0]); - waves[1]->addPhaseOffset(temp * mods[1]); - waves[2]->addPhaseOffset(temp * mods[2]); - waves[3]->addPhaseOffset(twozero->lastOut()); - twozero->tick(temp); - temp = gains[0] * tilt[0] * adsr[0]->tick() * waves[0]->tick(); - temp += gains[1] * tilt[1] * adsr[1]->tick() * waves[1]->tick(); - temp += gains[2] * tilt[2] * adsr[2]->tick() * waves[2]->tick(); - - return temp * 0.33; + waves_[0]->addPhaseOffset(temp * mods_[0]); + waves_[1]->addPhaseOffset(temp * mods_[1]); + waves_[2]->addPhaseOffset(temp * mods_[2]); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + twozero_.tick( temp ); + temp = gains_[0] * tilt_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += gains_[1] * tilt_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp += gains_[2] * tilt_[2] * adsr_[2]->tick() * waves_[2]->tick(); + + lastOutput_ = temp * 0.33; + return lastOutput_; } -void FMVoices :: controlChange(int number, MY_FLOAT value) +StkFloat *FMVoices :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& FMVoices :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void FMVoices :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "FMVoices: Control value less than zero!" << std::endl; + errorString_ << "FMVoices::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "FMVoices: Control value greater than 128.0!" << std::endl; + errorString_ << "FMVoices::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_Breath_) // 2 - gains[3] = __FM_gains[(int) ( norm * 99.9 )]; + gains_[3] = fmGains_[(int) ( norm * 99.9 )]; else if (number == __SK_FootControl_) { // 4 - currentVowel = (int) (norm * 128.0); - this->setFrequency(baseFrequency); + currentVowel_ = (int) (norm * 128.0); + this->setFrequency(baseFrequency_); } else if (number == __SK_ModFrequency_) // 11 this->setModulationSpeed( norm * 12.0); else if (number == __SK_ModWheel_) // 1 this->setModulationDepth( norm ); else if (number == __SK_AfterTouch_Cont_) { // 128 - tilt[0] = norm; - tilt[1] = norm * norm; - tilt[2] = tilt[1] * norm; + tilt_[0] = norm; + tilt_[1] = norm * norm; + tilt_[2] = tilt_[1] * norm; + } + else { + errorString_ << "FMVoices::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); } - else - std::cerr << "FMVoices: Undefined Control Number (" << number << ")!!" << std::endl; #if defined(_STK_DEBUG_) - std::cerr << "FMVoices: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "FMVoices::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Filter.cpp b/src/Filter.cpp index a27b56e..097fc68 100644 --- a/src/Filter.cpp +++ b/src/Filter.cpp @@ -14,7 +14,7 @@ a[0]*y[n] = b[0]*x[n] + ... + b[nb]*x[n-nb] - a[1]*y[n-1] - ... - a[na]*y[n-na] - If a[0] is not equal to 1, the filter coeffcients + If a[0] is not equal to 1, the filter coefficients are normalized by a[0]. The \e gain parameter is applied at the filter @@ -23,7 +23,7 @@ results in one extra multiply per computed sample, but allows easy control of the overall filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -33,212 +33,209 @@ Filter :: Filter() { // The default constructor should setup for pass-through. - gain = 1.0; - nB = 1; - nA = 1; - b = new MY_FLOAT[nB]; - b[0] = 1.0; - a = new MY_FLOAT[nA]; - a[0] = 1.0; + gain_ = 1.0; + b_.push_back( 1.0 ); + a_.push_back( 1.0 ); - inputs = new MY_FLOAT[nB]; - outputs = new MY_FLOAT[nA]; - this->clear(); + inputs_.push_back( 0.0 ); + outputs_.push_back( 0.0 ); } -Filter :: Filter(int nb, MY_FLOAT *bCoefficients, int na, MY_FLOAT *aCoefficients) +Filter :: Filter( std::vector &bCoefficients, std::vector &aCoefficients ) { - char message[256]; - // Check the arguments. - if ( nb < 1 || na < 1 ) { - sprintf(message, "Filter: nb (%d) and na (%d) must be >= 1!", nb, na); - handleError( message, StkError::FUNCTION_ARGUMENT ); + if ( bCoefficients.size() == 0 || aCoefficients.size() == 0 ) { + errorString_ << "Filter: a and b coefficient vectors must both have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); } if ( aCoefficients[0] == 0.0 ) { - sprintf(message, "Filter: a[0] coefficient cannot == 0!"); - handleError( message, StkError::FUNCTION_ARGUMENT ); + errorString_ << "Filter: a[0] coefficient cannot == 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - gain = 1.0; - nB = nb; - nA = na; - b = new MY_FLOAT[nB]; - a = new MY_FLOAT[nA]; + gain_ = 1.0; + b_ = bCoefficients; + a_ = aCoefficients; - inputs = new MY_FLOAT[nB]; - outputs = new MY_FLOAT[nA]; + inputs_ = std::vector ( b_.size() ); + outputs_ = std::vector ( a_.size() ); this->clear(); - - this->setCoefficients(nB, bCoefficients, nA, aCoefficients); } Filter :: ~Filter() { - delete [] b; - delete [] a; - delete [] inputs; - delete [] outputs; } void Filter :: clear(void) { - int i; - for (i=0; i &bCoefficients, std::vector &aCoefficients ) { - int i; - char message[256]; - // Check the arguments. - if ( nb < 1 || na < 1 ) { - sprintf(message, "Filter: nb (%d) and na (%d) must be >= 1!", nb, na); - handleError( message, StkError::FUNCTION_ARGUMENT ); + if ( bCoefficients.size() == 0 || aCoefficients.size() == 0 ) { + errorString_ << "Filter::setCoefficients: a and b coefficient vectors must both have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); } if ( aCoefficients[0] == 0.0 ) { - sprintf(message, "Filter: a[0] coefficient cannot == 0!"); - handleError( message, StkError::FUNCTION_ARGUMENT ); + errorString_ << "Filter::setCoefficients: a[0] coefficient cannot == 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - if (nb != nB) { - delete [] b; - delete [] inputs; - nB = nb; - b = new MY_FLOAT[nB]; - inputs = new MY_FLOAT[nB]; - for (i=0; i ( b_.size() ); + } + else { + for ( unsigned int i=0; i ( a_.size() ); + } + else { + for ( unsigned int i=0; iclear(); - // scale coefficients by a[0] if necessary - if (a[0] != 1.0) { - for (i=0; i &bCoefficients ) { - int i; - char message[256]; - - // Check the arguments. - if ( nb < 1 ) { - sprintf(message, "Filter: nb (%d) must be >= 1!", nb); - handleError( message, StkError::FUNCTION_ARGUMENT ); + // Check the argument. + if ( bCoefficients.size() == 0 ) { + errorString_ << "Filter::setNumerator: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - if (nb != nB) { - delete [] b; - delete [] inputs; - nB = nb; - b = new MY_FLOAT[nB]; - inputs = new MY_FLOAT[nB]; - for (i=0; i ( b_.size() ); + } + else { + for ( unsigned int i=0; iclear(); } -void Filter :: setDenominator(int na, MY_FLOAT *aCoefficients) +void Filter :: setDenominator( std::vector &aCoefficients ) { - int i; - char message[256]; - - // Check the arguments. - if ( na < 1 ) { - sprintf(message, "Filter: na (%d) must be >= 1!", na); - handleError( message, StkError::FUNCTION_ARGUMENT ); + // Check the argument. + if ( aCoefficients.size() == 0 ) { + errorString_ << "Filter::setDenominator: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); } if ( aCoefficients[0] == 0.0 ) { - sprintf(message, "Filter: a[0] coefficient cannot == 0!"); - handleError( message, StkError::FUNCTION_ARGUMENT ); + errorString_ << "Filter::setDenominator: a[0] coefficient cannot == 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - if (na != nA) { - delete [] a; - delete [] outputs; - nA = na; - a = new MY_FLOAT[nA]; - outputs = new MY_FLOAT[nA]; - for (i=0; i ( a_.size() ); + } + else { + for ( unsigned int i=0; iclear(); - // scale coefficients by a[0] if necessary - if (a[0] != 1.0) { - for (i=0; i0; i--) { - outputs[0] += b[i] * inputs[i]; - inputs[i] = inputs[i-1]; + outputs_[0] = 0.0; + inputs_[0] = gain_ * sample; + for (i=b_.size()-1; i>0; i--) { + outputs_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; } - outputs[0] += b[0] * inputs[0]; + outputs_[0] += b_[0] * inputs_[0]; - for (i=nA-1; i>0; i--) { - outputs[0] += -a[i] * outputs[i]; - outputs[i] = outputs[i-1]; + for (i=a_.size()-1; i>0; i--) { + outputs_[0] += -a_[i] * outputs_[i]; + outputs_[i] = outputs_[i-1]; } - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *Filter :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Filter :: tick(StkFloat *vector, unsigned int vectorSize) { for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; i>= 1; - jetDelay = new DelayL( 49.0, length ); - jetTable = new JetTabl(); - filter = new OnePole(); - dcBlock = new PoleZero(); - dcBlock->setBlockZero(); - noise = new Noise(); - adsr = new ADSR(); + length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + boreDelay_.setMaximumDelay( length_ ); + boreDelay_.setDelay( 100.0 ); + + length_ >>= 1; + jetDelay_.setMaximumDelay( length_ ); + jetDelay_.setDelay( 49.0 ); // Concatenate the STK rawwave path to the rawwave file - vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); - vibrato->setFrequency( 5.925 ); + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency( 5.925 ); this->clear(); - filter->setPole( 0.7 - ((MY_FLOAT) 0.1 * 22050.0 / Stk::sampleRate() ) ); - filter->setGain( -1.0 ); - adsr->setAllTimes( 0.005, 0.01, 0.8, 0.010); - endReflection = (MY_FLOAT) 0.5; - jetReflection = (MY_FLOAT) 0.5; - noiseGain = 0.15; // Breath pressure random component. - vibratoGain = (MY_FLOAT) 0.05; // Breath periodic vibrato component. - jetRatio = (MY_FLOAT) 0.32; + filter_.setPole( 0.7 - ((StkFloat) 0.1 * 22050.0 / Stk::sampleRate() ) ); + filter_.setGain( -1.0 ); + adsr_.setAllTimes( 0.005, 0.01, 0.8, 0.010); + endReflection_ = 0.5; + jetReflection_ = 0.5; + noiseGain_ = 0.15; // Breath pressure random component. + vibratoGain_ = 0.05; // Breath periodic vibrato component. + jetRatio_ = 0.32; - maxPressure = (MY_FLOAT) 0.0; - lastFrequency = 220.0; + maxPressure_ = 0.0; + lastFrequency_ = 220.0; } Flute :: ~Flute() { - delete jetDelay; - delete boreDelay; - delete jetTable; - delete filter; - delete dcBlock; - delete noise; - delete adsr; - delete vibrato; + delete vibrato_; } void Flute :: clear() { - jetDelay->clear(); - boreDelay->clear(); - filter->clear(); - dcBlock->clear(); + jetDelay_.clear(); + boreDelay_.clear(); + filter_.clear(); + dcBlock_.clear(); } -void Flute :: setFrequency(MY_FLOAT frequency) +void Flute :: setFrequency(StkFloat frequency) { - lastFrequency = frequency; + lastFrequency_ = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Flute: setFrequency parameter is less than or equal to zero!" << std::endl; - lastFrequency = 220.0; + errorString_ << "Flute::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); + lastFrequency_ = 220.0; } // We're overblowing here. - lastFrequency *= 0.66666; - // Delay = length - approximate filter delay. - MY_FLOAT delay = Stk::sampleRate() / lastFrequency - (MY_FLOAT) 2.0; - if (delay <= 0.0) delay = 0.3; - else if (delay > length) delay = length; + lastFrequency_ *= 0.66666; - boreDelay->setDelay(delay); - jetDelay->setDelay(delay * jetRatio); + // delay = length - approximate filter delay. + StkFloat delay = Stk::sampleRate() / lastFrequency_ - (StkFloat) 2.0; + if ( delay <= 0.0 ) delay = 0.3; + else if ( delay > length_ ) delay = length_; + + boreDelay_.setDelay(delay); + jetDelay_.setDelay(delay * jetRatio_); } -void Flute :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +void Flute :: startBlowing(StkFloat amplitude, StkFloat rate) { - adsr->setAttackRate(rate); - maxPressure = amplitude / (MY_FLOAT) 0.8; - adsr->keyOn(); + adsr_.setAttackRate( rate ); + maxPressure_ = amplitude / (StkFloat) 0.8; + adsr_.keyOn(); } -void Flute :: stopBlowing(MY_FLOAT rate) +void Flute :: stopBlowing(StkFloat rate) { - adsr->setReleaseRate(rate); - adsr->keyOff(); + adsr_.setReleaseRate( rate ); + adsr_.keyOff(); } -void Flute :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Flute :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); - startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02); - outputGain = amplitude + 0.001; + this->setFrequency( frequency ); + this->startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02 ); + outputGain_ = amplitude + 0.001; #if defined(_STK_DEBUG_) - std::cerr << "Flute: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Flute::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Flute :: noteOff(MY_FLOAT amplitude) +void Flute :: noteOff(StkFloat amplitude) { - this->stopBlowing(amplitude * 0.02); + this->stopBlowing( amplitude * 0.02 ); #if defined(_STK_DEBUG_) - std::cerr << "Flute: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Flute::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Flute :: setJetReflection(MY_FLOAT coefficient) +void Flute :: setJetReflection(StkFloat coefficient) { - jetReflection = coefficient; + jetReflection_ = coefficient; } -void Flute :: setEndReflection(MY_FLOAT coefficient) +void Flute :: setEndReflection(StkFloat coefficient) { - endReflection = coefficient; + endReflection_ = coefficient; } -void Flute :: setJetDelay(MY_FLOAT aRatio) +void Flute :: setJetDelay(StkFloat aRatio) { // Delay = length - approximate filter delay. - MY_FLOAT temp = Stk::sampleRate() / lastFrequency - (MY_FLOAT) 2.0; - jetRatio = aRatio; - jetDelay->setDelay(temp * aRatio); // Scaled by ratio. + StkFloat temp = Stk::sampleRate() / lastFrequency_ - (StkFloat) 2.0; + jetRatio_ = aRatio; + jetDelay_.setDelay(temp * aRatio); // Scaled by ratio. } -MY_FLOAT Flute :: tick() +StkFloat Flute :: tick() { - MY_FLOAT pressureDiff; - MY_FLOAT breathPressure; + StkFloat pressureDiff; + StkFloat breathPressure; // Calculate the breath pressure (envelope + noise + vibrato) - breathPressure = maxPressure * adsr->tick(); - breathPressure += breathPressure * noiseGain * noise->tick(); - breathPressure += breathPressure * vibratoGain * vibrato->tick(); + breathPressure = maxPressure_ * adsr_.tick(); + breathPressure += breathPressure * noiseGain_ * noise_.tick(); + breathPressure += breathPressure * vibratoGain_ * vibrato_->tick(); - MY_FLOAT temp = filter->tick( boreDelay->lastOut() ); - temp = dcBlock->tick(temp); // Block DC on reflection. + StkFloat temp = filter_.tick( boreDelay_.lastOut() ); + temp = dcBlock_.tick(temp); // Block DC on reflection. - pressureDiff = breathPressure - (jetReflection * temp); - pressureDiff = jetDelay->tick( pressureDiff ); - pressureDiff = jetTable->tick( pressureDiff ) + (endReflection * temp); - lastOutput = (MY_FLOAT) 0.3 * boreDelay->tick( pressureDiff ); + pressureDiff = breathPressure - (jetReflection_ * temp); + pressureDiff = jetDelay_.tick( pressureDiff ); + pressureDiff = jetTable_.tick( pressureDiff ) + (endReflection_ * temp); + lastOutput_ = (StkFloat) 0.3 * boreDelay_.tick( pressureDiff ); - lastOutput *= outputGain; - return lastOutput; + lastOutput_ *= outputGain_; + return lastOutput_; } -void Flute :: controlChange(int number, MY_FLOAT value) +StkFloat *Flute :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Flute :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Flute :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Flute: Control value less than zero!" << std::endl; + errorString_ << "Flute::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Flute: Control value greater than 128.0!" << std::endl; + errorString_ << "Flute::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_JetDelay_) // 2 - this->setJetDelay( (MY_FLOAT) (0.08 + (0.48 * norm)) ); + this->setJetDelay( (StkFloat) (0.08 + (0.48 * norm)) ); else if (number == __SK_NoiseLevel_) // 4 - noiseGain = ( norm * 0.4); + noiseGain_ = ( norm * 0.4); else if (number == __SK_ModFrequency_) // 11 - vibrato->setFrequency( norm * 12.0); + vibrato_->setFrequency( norm * 12.0); else if (number == __SK_ModWheel_) // 1 - vibratoGain = ( norm * 0.4 ); + vibratoGain_ = ( norm * 0.4 ); else if (number == __SK_AfterTouch_Cont_) // 128 - adsr->setTarget( norm ); - else - std::cerr << "Flute: Undefined Control Number (" << number << ")!!" << std::endl; + adsr_.setTarget( norm ); + else { + errorString_ << "Flute::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Flute: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Flute::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/FormSwep.cpp b/src/FormSwep.cpp index 831c2c6..6ffebe0 100644 --- a/src/FormSwep.cpp +++ b/src/FormSwep.cpp @@ -8,7 +8,7 @@ It provides methods for controlling the sweep rate and target frequency. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -16,17 +16,17 @@ FormSwep :: FormSwep() : BiQuad() { - frequency = (MY_FLOAT) 0.0; - radius = (MY_FLOAT) 0.0; - targetGain = (MY_FLOAT) 1.0; - targetFrequency = (MY_FLOAT) 0.0; - targetRadius = (MY_FLOAT) 0.0; - deltaGain = (MY_FLOAT) 0.0; - deltaFrequency = (MY_FLOAT) 0.0; - deltaRadius = (MY_FLOAT) 0.0; - sweepState = (MY_FLOAT) 0.0; - sweepRate = (MY_FLOAT) 0.002; - dirty = false; + frequency_ = (StkFloat) 0.0; + radius_ = (StkFloat) 0.0; + targetGain_ = (StkFloat) 1.0; + targetFrequency_ = (StkFloat) 0.0; + targetRadius_ = (StkFloat) 0.0; + deltaGain_ = (StkFloat) 0.0; + deltaFrequency_ = (StkFloat) 0.0; + deltaRadius_ = (StkFloat) 0.0; + sweepState_ = (StkFloat) 0.0; + sweepRate_ = (StkFloat) 0.002; + dirty_ = false; this->clear(); } @@ -34,85 +34,87 @@ FormSwep :: ~FormSwep() { } -void FormSwep :: setResonance(MY_FLOAT aFrequency, MY_FLOAT aRadius) +void FormSwep :: setResonance(StkFloat frequency, StkFloat radius) { - dirty = false; - radius = aRadius; - frequency = aFrequency; + dirty_ = false; + radius_ = radius; + frequency_ = frequency; - BiQuad::setResonance( frequency, radius, true ); + BiQuad::setResonance( frequency_, radius_, true ); } -void FormSwep :: setStates(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain) +void FormSwep :: setStates(StkFloat frequency, StkFloat radius, StkFloat gain) { - dirty = false; + dirty_ = false; - if ( frequency != aFrequency || radius != aRadius ) - BiQuad::setResonance( aFrequency, aRadius, true ); + if ( frequency_ != frequency || radius_ != radius ) + BiQuad::setResonance( frequency, radius, true ); - frequency = aFrequency; - radius = aRadius; - gain = aGain; - targetFrequency = aFrequency; - targetRadius = aRadius; - targetGain = aGain; + frequency_ = frequency; + radius_ = radius; + gain_ = gain; + targetFrequency_ = frequency; + targetRadius_ = radius; + targetGain_ = gain; } -void FormSwep :: setTargets(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain) +void FormSwep :: setTargets(StkFloat frequency, StkFloat radius, StkFloat gain) { - dirty = true; - startFrequency = frequency; - startRadius = radius; - startGain = gain; - targetFrequency = aFrequency; - targetRadius = aRadius; - targetGain = aGain; - deltaFrequency = aFrequency - frequency; - deltaRadius = aRadius - radius; - deltaGain = aGain - gain; - sweepState = (MY_FLOAT) 0.0; + dirty_ = true; + startFrequency_ = frequency_; + startRadius_ = radius_; + startGain_ = gain_; + targetFrequency_ = frequency; + targetRadius_ = radius; + targetGain_ = gain; + deltaFrequency_ = frequency - frequency_; + deltaRadius_ = radius - radius_; + deltaGain_ = gain - gain_; + sweepState_ = (StkFloat) 0.0; } -void FormSwep :: setSweepRate(MY_FLOAT aRate) +void FormSwep :: setSweepRate(StkFloat rate) { - sweepRate = aRate; - if ( sweepRate > 1.0 ) sweepRate = 1.0; - if ( sweepRate < 0.0 ) sweepRate = 0.0; + sweepRate_ = rate; + if ( sweepRate_ > 1.0 ) sweepRate_ = 1.0; + if ( sweepRate_ < 0.0 ) sweepRate_ = 0.0; } -void FormSwep :: setSweepTime(MY_FLOAT aTime) +void FormSwep :: setSweepTime(StkFloat time) { - sweepRate = 1.0 / ( aTime * Stk::sampleRate() ); - if ( sweepRate > 1.0 ) sweepRate = 1.0; - if ( sweepRate < 0.0 ) sweepRate = 0.0; + sweepRate_ = 1.0 / ( time * Stk::sampleRate() ); + if ( sweepRate_ > 1.0 ) sweepRate_ = 1.0; + if ( sweepRate_ < 0.0 ) sweepRate_ = 0.0; } -MY_FLOAT FormSwep :: tick(MY_FLOAT sample) +StkFloat FormSwep :: tick(StkFloat sample) { - if (dirty) { - sweepState += sweepRate; - if ( sweepState >= 1.0 ) { - sweepState = (MY_FLOAT) 1.0; - dirty = false; - radius = targetRadius; - frequency = targetFrequency; - gain = targetGain; + if (dirty_) { + sweepState_ += sweepRate_; + if ( sweepState_ >= 1.0 ) { + sweepState_ = (StkFloat) 1.0; + dirty_ = false; + radius_ = targetRadius_; + frequency_ = targetFrequency_; + gain_ = targetGain_; } else { - radius = startRadius + (deltaRadius * sweepState); - frequency = startFrequency + (deltaFrequency * sweepState); - gain = startGain + (deltaGain * sweepState); + radius_ = startRadius_ + (deltaRadius_ * sweepState_); + frequency_ = startFrequency_ + (deltaFrequency_ * sweepState_); + gain_ = startGain_ + (deltaGain_ * sweepState_); } - BiQuad::setResonance( frequency, radius, true ); + BiQuad::setResonance( frequency_, radius_, true ); } return BiQuad::tick( sample ); } -MY_FLOAT *FormSwep :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *FormSwep :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; isetRatio(0, 1.0 * 1.000); this->setRatio(1, 4.0 * 0.999); this->setRatio(2, 3.0 * 1.001); this->setRatio(3, 0.5 * 1.002); - gains[0] = __FM_gains[92]; - gains[1] = __FM_gains[76]; - gains[2] = __FM_gains[91]; - gains[3] = __FM_gains[68]; + gains_[0] = fmGains_[92]; + gains_[1] = fmGains_[76]; + gains_[2] = fmGains_[91]; + gains_[3] = fmGains_[68]; - adsr[0]->setAllTimes( 0.001, 0.001, 1.0, 0.01); - adsr[1]->setAllTimes( 0.001, 0.010, 1.0, 0.50); - adsr[2]->setAllTimes( 0.010, 0.005, 1.0, 0.20); - adsr[3]->setAllTimes( 0.030, 0.010, 0.2, 0.20); + adsr_[0]->setAllTimes( 0.001, 0.001, 1.0, 0.01); + adsr_[1]->setAllTimes( 0.001, 0.010, 1.0, 0.50); + adsr_[2]->setAllTimes( 0.010, 0.005, 1.0, 0.20); + adsr_[3]->setAllTimes( 0.030, 0.010, 0.2, 0.20); - twozero->setGain( 2.0 ); - vibrato->setFrequency( 5.5 ); - modDepth = 0.0; + twozero_.setGain( 2.0 ); + vibrato_->setFrequency( 5.5 ); + modDepth_ = 0.0; } HevyMetl :: ~HevyMetl() { } -void HevyMetl :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void HevyMetl :: noteOn(StkFloat frequency, StkFloat amplitude) { - gains[0] = amplitude * __FM_gains[92]; - gains[1] = amplitude * __FM_gains[76]; - gains[2] = amplitude * __FM_gains[91]; - gains[3] = amplitude * __FM_gains[68]; - this->setFrequency(frequency); + gains_[0] = amplitude * fmGains_[92]; + gains_[1] = amplitude * fmGains_[76]; + gains_[2] = amplitude * fmGains_[91]; + gains_[3] = amplitude * fmGains_[68]; + this->setFrequency( frequency ); this->keyOn(); #if defined(_STK_DEBUG_) - cerr << "HevyMetl: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + errorString_ << "HevyMetl::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT HevyMetl :: tick() +StkFloat HevyMetl :: tick() { - register MY_FLOAT temp; + register StkFloat temp; - temp = vibrato->tick() * modDepth * 0.2; - waves[0]->setFrequency(baseFrequency * (1.0 + temp) * ratios[0]); - waves[1]->setFrequency(baseFrequency * (1.0 + temp) * ratios[1]); - waves[2]->setFrequency(baseFrequency * (1.0 + temp) * ratios[2]); - waves[3]->setFrequency(baseFrequency * (1.0 + temp) * ratios[3]); + temp = vibrato_->tick() * modDepth_ * 0.2; + waves_[0]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[0]); + waves_[1]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[1]); + waves_[2]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[2]); + waves_[3]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[3]); - temp = gains[2] * adsr[2]->tick() * waves[2]->tick(); - waves[1]->addPhaseOffset(temp); + temp = gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + waves_[1]->addPhaseOffset( temp ); - waves[3]->addPhaseOffset(twozero->lastOut()); - temp = (1.0 - (control2 * 0.5)) * gains[3] * adsr[3]->tick() * waves[3]->tick(); - twozero->tick(temp); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = (1.0 - (control2_ * 0.5)) * gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); - temp += control2 * (MY_FLOAT) 0.5 * gains[1] * adsr[1]->tick() * waves[1]->tick(); - temp = temp * control1; + temp += control2_ * 0.5 * gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; - waves[0]->addPhaseOffset(temp); - temp = gains[0] * adsr[0]->tick() * waves[0]->tick(); + waves_[0]->addPhaseOffset( temp ); + temp = gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); - lastOutput = temp * 0.5; - return lastOutput; + lastOutput_ = temp * 0.5; + return lastOutput_; } + +StkFloat *HevyMetl :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& HevyMetl :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + diff --git a/src/Instrmnt.cpp b/src/Instrmnt.cpp index 5c9af3a..b4e377c 100644 --- a/src/Instrmnt.cpp +++ b/src/Instrmnt.cpp @@ -5,7 +5,7 @@ This class provides a common interface for all STK instruments. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -19,28 +19,29 @@ Instrmnt :: ~Instrmnt() { } -void Instrmnt :: setFrequency(MY_FLOAT frequency) +void Instrmnt :: setFrequency(StkFloat frequency) { - std::cerr << "Instrmnt: virtual setFrequency function call!" << std::endl; + errorString_ << "Instrmnt::setFrequency: virtual setFrequency function call!"; + handleError( StkError::WARNING ); } -MY_FLOAT Instrmnt :: lastOut() const +StkFloat Instrmnt :: lastOut() const { - return lastOutput; + return lastOutput_; } // Support for stereo output: -MY_FLOAT Instrmnt :: lastOutLeft(void) const +StkFloat Instrmnt :: lastOutLeft(void) const { - return 0.5 * lastOutput; + return 0.5 * lastOutput_; } -MY_FLOAT Instrmnt :: lastOutRight(void) const +StkFloat Instrmnt :: lastOutRight(void) const { - return 0.5 * lastOutput; + return 0.5 * lastOutput_; } -MY_FLOAT *Instrmnt :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Instrmnt :: tick(StkFloat *vector, unsigned int vectorSize) { for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; i -JCRev :: JCRev(MY_FLOAT T60) +JCRev :: JCRev(StkFloat T60) { // Delay lengths for 44100 Hz sample rate. int lengths[9] = {1777, 1847, 1993, 2137, 389, 127, 43, 211, 179}; @@ -33,89 +33,101 @@ JCRev :: JCRev(MY_FLOAT T60) } } - for (i=0; i<3; i++) - allpassDelays[i] = new Delay(lengths[i+4], lengths[i+4]); - - for (i=0; i<4; i++) { - combDelays[i] = new Delay(lengths[i], lengths[i]); - combCoefficient[i] = pow(10.0,(-3 * lengths[i] / (T60 * Stk::sampleRate()))); + for (i=0; i<3; i++) { + allpassDelays_[i].setMaximumDelay( lengths[i+4] ); + allpassDelays_[i].setDelay( lengths[i+4] ); } - outLeftDelay = new Delay(lengths[7], lengths[7]); - outRightDelay = new Delay(lengths[8], lengths[8]); - allpassCoefficient = 0.7; - effectMix = 0.3; + for ( i=0; i<4; i++ ) { + combDelays_[i].setMaximumDelay( lengths[i] ); + combDelays_[i].setDelay( lengths[i] ); + } + + this->setT60( T60 ); + outLeftDelay_.setMaximumDelay( lengths[7] ); + outLeftDelay_.setDelay( lengths[7] ); + outRightDelay_.setMaximumDelay( lengths[8] ); + outRightDelay_.setDelay( lengths[8] ); + allpassCoefficient_ = 0.7; + effectMix_ = 0.3; this->clear(); } JCRev :: ~JCRev() { - delete allpassDelays[0]; - delete allpassDelays[1]; - delete allpassDelays[2]; - delete combDelays[0]; - delete combDelays[1]; - delete combDelays[2]; - delete combDelays[3]; - delete outLeftDelay; - delete outRightDelay; } void JCRev :: clear() { - allpassDelays[0]->clear(); - allpassDelays[1]->clear(); - allpassDelays[2]->clear(); - combDelays[0]->clear(); - combDelays[1]->clear(); - combDelays[2]->clear(); - combDelays[3]->clear(); - outRightDelay->clear(); - outLeftDelay->clear(); - lastOutput[0] = 0.0; - lastOutput[1] = 0.0; + allpassDelays_[0].clear(); + allpassDelays_[1].clear(); + allpassDelays_[2].clear(); + combDelays_[0].clear(); + combDelays_[1].clear(); + combDelays_[2].clear(); + combDelays_[3].clear(); + outRightDelay_.clear(); + outLeftDelay_.clear(); + lastOutput_[0] = 0.0; + lastOutput_[1] = 0.0; } -MY_FLOAT JCRev :: tick(MY_FLOAT input) +void JCRev :: setT60( StkFloat T60 ) { - MY_FLOAT temp, temp0, temp1, temp2, temp3, temp4, temp5, temp6; - MY_FLOAT filtout; + for ( int i=0; i<4; i++ ) + combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate()))); +} - temp = allpassDelays[0]->lastOut(); - temp0 = allpassCoefficient * temp; +StkFloat JCRev :: tick(StkFloat input) +{ + StkFloat temp, temp0, temp1, temp2, temp3, temp4, temp5, temp6; + StkFloat filtout; + + temp = allpassDelays_[0].lastOut(); + temp0 = allpassCoefficient_ * temp; temp0 += input; - allpassDelays[0]->tick(temp0); - temp0 = -(allpassCoefficient * temp0) + temp; + allpassDelays_[0].tick(temp0); + temp0 = -(allpassCoefficient_ * temp0) + temp; - temp = allpassDelays[1]->lastOut(); - temp1 = allpassCoefficient * temp; + temp = allpassDelays_[1].lastOut(); + temp1 = allpassCoefficient_ * temp; temp1 += temp0; - allpassDelays[1]->tick(temp1); - temp1 = -(allpassCoefficient * temp1) + temp; + allpassDelays_[1].tick(temp1); + temp1 = -(allpassCoefficient_ * temp1) + temp; - temp = allpassDelays[2]->lastOut(); - temp2 = allpassCoefficient * temp; + temp = allpassDelays_[2].lastOut(); + temp2 = allpassCoefficient_ * temp; temp2 += temp1; - allpassDelays[2]->tick(temp2); - temp2 = -(allpassCoefficient * temp2) + temp; + allpassDelays_[2].tick(temp2); + temp2 = -(allpassCoefficient_ * temp2) + temp; - temp3 = temp2 + (combCoefficient[0] * combDelays[0]->lastOut()); - temp4 = temp2 + (combCoefficient[1] * combDelays[1]->lastOut()); - temp5 = temp2 + (combCoefficient[2] * combDelays[2]->lastOut()); - temp6 = temp2 + (combCoefficient[3] * combDelays[3]->lastOut()); + temp3 = temp2 + (combCoefficient_[0] * combDelays_[0].lastOut()); + temp4 = temp2 + (combCoefficient_[1] * combDelays_[1].lastOut()); + temp5 = temp2 + (combCoefficient_[2] * combDelays_[2].lastOut()); + temp6 = temp2 + (combCoefficient_[3] * combDelays_[3].lastOut()); - combDelays[0]->tick(temp3); - combDelays[1]->tick(temp4); - combDelays[2]->tick(temp5); - combDelays[3]->tick(temp6); + combDelays_[0].tick(temp3); + combDelays_[1].tick(temp4); + combDelays_[2].tick(temp5); + combDelays_[3].tick(temp6); filtout = temp3 + temp4 + temp5 + temp6; - lastOutput[0] = effectMix * (outLeftDelay->tick(filtout)); - lastOutput[1] = effectMix * (outRightDelay->tick(filtout)); - temp = (1.0 - effectMix) * input; - lastOutput[0] += temp; - lastOutput[1] += temp; + lastOutput_[0] = effectMix_ * (outLeftDelay_.tick(filtout)); + lastOutput_[1] = effectMix_ * (outRightDelay_.tick(filtout)); + temp = (1.0 - effectMix_) * input; + lastOutput_[0] += temp; + lastOutput_[1] += temp; - return (lastOutput[0] + lastOutput[1]) * 0.5; + return Effect::lastOut(); +} + +StkFloat *JCRev :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Effect::tick( vector, vectorSize ); +} + +StkFrames& JCRev :: tick( StkFrames& frames, unsigned int channel ) +{ + return Effect::tick( frames, channel ); } diff --git a/src/JetTabl.cpp b/src/JetTabl.cpp deleted file mode 100644 index c4b9b61..0000000 --- a/src/JetTabl.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/***************************************************/ -/*! \class JetTabl - \brief STK jet table class. - - This class implements a flue jet non-linear - function, computed by a polynomial calculation. - Contrary to the name, this is not a "table". - - Consult Fletcher and Rossing, Karjalainen, - Cook, and others for more information. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#include "JetTabl.h" - -JetTabl :: JetTabl() -{ - lastOutput = (MY_FLOAT) 0.0; -} - -JetTabl :: ~JetTabl() -{ -} - -MY_FLOAT JetTabl :: lastOut() const -{ - return lastOutput; -} - -MY_FLOAT JetTabl :: tick( MY_FLOAT input ) -{ - // Perform "table lookup" using a polynomial - // calculation (x^3 - x), which approximates - // the jet sigmoid behavior. - lastOutput = input * (input * input - (MY_FLOAT) 1.0); - - // Saturate at +/- 1.0. - if (lastOutput > 1.0) - lastOutput = (MY_FLOAT) 1.0; - if (lastOutput < -1.0) - lastOutput = (MY_FLOAT) -1.0; - return lastOutput; -} - -MY_FLOAT *JetTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize) -{ - for (unsigned int i=0; i 1.0) + lastOutput_ = (StkFloat) 1.0; + if (lastOutput_ < -1.0) + lastOutput_ = (StkFloat) -1.0; + return lastOutput_; +} + +StkFloat *JetTable :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Function::tick( vector, vectorSize ); +} + +StkFrames& JetTable :: tick( StkFrames& frames, unsigned int channel ) +{ + return Function::tick( frames, channel ); +} diff --git a/src/Makefile.in b/src/Makefile.in index 08bb7fd..da76a6e 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -6,15 +6,15 @@ RM = /bin/rm OBJECT_PATH = @object_path@ vpath %.o $(OBJECT_PATH) -OBJECTS = Stk.o Noise.o SubNoise.o Envelope.o ADSR.o \ +OBJECTS = Stk.o Generator.o Noise.o SubNoise.o \ + Envelope.o ADSR.o Asymp.o Modulate.o SingWave.o \ WvIn.o WaveLoop.o WvOut.o \ Filter.o OneZero.o OnePole.o PoleZero.o TwoZero.o TwoPole.o \ BiQuad.o FormSwep.o Delay.o DelayL.o DelayA.o \ - Reverb.o PRCRev.o JCRev.o NRev.o \ + Effect.o PRCRev.o JCRev.o NRev.o \ Chorus.o Echo.o PitShift.o \ - Table.o ReedTabl.o JetTabl.o BowTabl.o \ - Modulate.o SingWave.o Voicer.o \ - Vector3D.o Sphere.o \ + Function.o Table.o ReedTable.o JetTable.o BowTable.o \ + Voicer.o Vector3D.o Sphere.o \ \ Instrmnt.o Clarinet.o BlowHole.o Saxofony.o Flute.o Brass.o BlowBotl.o \ Bowed.o Plucked.o StifKarp.o Sitar.o PluckTwo.o Mandolin.o Mesh2D.o \ @@ -22,7 +22,7 @@ OBJECTS = Stk.o Noise.o SubNoise.o Envelope.o ADSR.o \ Sampler.o Moog.o Simple.o Drummer.o Shakers.o \ Modal.o ModalBar.o BandedWG.o Resonate.o VoicForm.o Phonemes.o Whistle.o \ \ - Messager.o SKINI.o + Messager.o Skini.o MidiFileIn.o INCLUDE = @include@ @@ -42,9 +42,8 @@ CFLAGS += @warn@ $(INCLUDE) REALTIME = @realtime@ ifeq ($(REALTIME),yes) - OBJECTS += RtMidi.o RtAudio.o RtWvOut.o RtWvIn.o RtDuplex.o TcpWvOut.o TcpWvIn.o Thread.o Socket.o + OBJECTS += RtMidi.o RtAudio.o RtWvOut.o RtWvIn.o RtDuplex.o TcpWvOut.o TcpWvIn.o Thread.o Mutex.o Socket.o DEFS += @audio_apis@ - DEFS += @midiator@ endif RAWWAVES = @rawwaves@ diff --git a/src/Mandolin.cpp b/src/Mandolin.cpp index e466d98..3369987 100644 --- a/src/Mandolin.cpp +++ b/src/Mandolin.cpp @@ -23,139 +23,155 @@ - String Detuning = 1 - Microphone Position = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Mandolin.h" #include "SKINI.msg" -Mandolin :: Mandolin(MY_FLOAT lowestFrequency) +Mandolin :: Mandolin(StkFloat lowestFrequency) : PluckTwo(lowestFrequency) { // 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 ); + 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; - waveDone = soundfile[mic]->isFinished(); + mic_ = 0; + dampTime_ = 0; + waveDone_ = soundfile_[mic_]->isFinished(); } Mandolin :: ~Mandolin() { for ( int i=0; i<12; i++ ) - delete soundfile[i]; + delete soundfile_[i]; } -void Mandolin :: pluck(MY_FLOAT amplitude) +void Mandolin :: pluck(StkFloat amplitude) { // This function gets interesting, because pluck // may be longer than string length, so we just // reset the soundfile and add in the pluck in // the tick method. - soundfile[mic]->reset(); - waveDone = false; - pluckAmplitude = amplitude; + soundfile_[mic_]->reset(); + waveDone_ = false; + pluckAmplitude_ = amplitude; if ( amplitude < 0.0 ) { - std::cerr << "Mandolin: pluck amplitude parameter less than zero!" << std::endl; - pluckAmplitude = 0.0; + errorString_ << "Mandolin::pluck: amplitude parameter less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + pluckAmplitude_ = 0.0; } else if ( amplitude > 1.0 ) { - std::cerr << "Mandolin: pluck amplitude parameter greater than 1.0!" << std::endl; - pluckAmplitude = 1.0; + errorString_ << "Mandolin::pluck: amplitude parameter greater than one ... setting to 1.0!"; + handleError( StkError::WARNING ); + pluckAmplitude_ = 1.0; } // Set the pick position, which puts zeroes at position * length. - combDelay->setDelay((MY_FLOAT) 0.5 * pluckPosition * lastLength); - dampTime = (long) lastLength; // See tick method below. + combDelay_.setDelay( 0.5 * pluckPosition_ * lastLength_ ); + dampTime_ = (long) lastLength_; // See tick method below. } -void Mandolin :: pluck(MY_FLOAT amplitude, MY_FLOAT position) +void Mandolin :: pluck(StkFloat amplitude, StkFloat position) { // Pluck position puts zeroes at position * length. - pluckPosition = position; + pluckPosition_ = position; if ( position < 0.0 ) { - std::cerr << "Mandolin: pluck position parameter less than zero!" << std::endl; - pluckPosition = 0.0; + std::cerr << "Mandolin::pluck: position parameter less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + pluckPosition_ = 0.0; } else if ( position > 1.0 ) { - std::cerr << "Mandolin: pluck position parameter greater than 1.0!" << std::endl; - pluckPosition = 1.0; + errorString_ << "Mandolin::pluck: amplitude parameter greater than one ... setting to 1.0!"; + handleError( StkError::WARNING ); + pluckPosition_ = 1.0; } - this->pluck(amplitude); + this->pluck( amplitude ); } -void Mandolin :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Mandolin :: noteOn(StkFloat frequency, StkFloat amplitude) { - this->setFrequency(frequency); - this->pluck(amplitude); + this->setFrequency( frequency ); + this->pluck( amplitude ); #if defined(_STK_DEBUG_) - std::cerr << "Mandolin: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Mandolin::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Mandolin :: setBodySize(MY_FLOAT size) +void Mandolin :: setBodySize(StkFloat size) { // Scale the commuted body response by its sample rate (22050). - MY_FLOAT rate = size * 22050.0 / Stk::sampleRate(); + StkFloat rate = size * 22050.0 / Stk::sampleRate(); for ( int i=0; i<12; i++ ) - soundfile[i]->setRate(rate); + soundfile_[i]->setRate( rate ); } -MY_FLOAT Mandolin :: tick() +StkFloat Mandolin :: tick() { - MY_FLOAT temp = 0.0; - if ( !waveDone ) { + StkFloat temp = 0.0; + if ( !waveDone_ ) { // Scale the pluck excitation with comb // filtering for the duration of the file. - temp = soundfile[mic]->tick() * pluckAmplitude; - temp = temp - combDelay->tick(temp); - waveDone = soundfile[mic]->isFinished(); + temp = soundfile_[mic_]->tick() * pluckAmplitude_; + temp = temp - combDelay_.tick(temp); + waveDone_ = soundfile_[mic_]->isFinished(); } // Damping hack to help avoid overflow on re-plucking. - if ( dampTime >=0 ) { - dampTime -= 1; + if ( dampTime_ >=0 ) { + dampTime_ -= 1; // Calculate 1st delay filtered reflection plus pluck excitation. - lastOutput = delayLine->tick( filter->tick( temp + (delayLine->lastOut() * (MY_FLOAT) 0.7) ) ); + lastOutput_ = delayLine_.tick( filter_.tick( temp + (delayLine_.lastOut() * 0.7) ) ); // Calculate 2nd delay just like the 1st. - lastOutput += delayLine2->tick( filter2->tick( temp + (delayLine2->lastOut() * (MY_FLOAT) 0.7) ) ); + lastOutput_ += delayLine2_.tick( filter2_.tick( temp + (delayLine2_.lastOut() * 0.7) ) ); } else { // No damping hack after 1 period. // Calculate 1st delay filtered reflection plus pluck excitation. - lastOutput = delayLine->tick( filter->tick( temp + (delayLine->lastOut() * loopGain) ) ); + lastOutput_ = delayLine_.tick( filter_.tick( temp + (delayLine_.lastOut() * loopGain_) ) ); // Calculate 2nd delay just like the 1st. - lastOutput += delayLine2->tick( filter2->tick( temp + (delayLine2->lastOut() * loopGain) ) ); + lastOutput_ += delayLine2_.tick( filter2_.tick( temp + (delayLine2_.lastOut() * loopGain_) ) ); } - lastOutput *= (MY_FLOAT) 0.3; - return lastOutput; + lastOutput_ *= 0.3; + return lastOutput_; } -void Mandolin :: controlChange(int number, MY_FLOAT value) +StkFloat *Mandolin :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Mandolin :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Mandolin :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Mandolin: Control value less than zero!" << std::endl; + errorString_ << "Mandolin::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Mandolin: Control value greater than 128.0!" << std::endl; + errorString_ << "Mandolin::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_BodySize_) // 2 @@ -163,15 +179,18 @@ void Mandolin :: controlChange(int number, MY_FLOAT value) else if (number == __SK_PickPosition_) // 4 this->setPluckPosition( norm ); else if (number == __SK_StringDamping_) // 11 - this->setBaseLoopGain((MY_FLOAT) 0.97 + (norm * (MY_FLOAT) 0.03)); + this->setBaseLoopGain( 0.97 + (norm * 0.03)); else if (number == __SK_StringDetune_) // 1 - this->setDetune((MY_FLOAT) 1.0 - (norm * (MY_FLOAT) 0.1)); + this->setDetune( 1.0 - (norm * 0.1) ); else if (number == __SK_AfterTouch_Cont_) // 128 - mic = (int) (norm * 11.0); - else - std::cerr << "Mandolin: Undefined Control Number (" << number << ")!!" << std::endl; + mic_ = (int) (norm * 11.0); + else { + errorString_ << "Mandolin::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Mandolin: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Mandolin::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Mesh2D.cpp b/src/Mesh2D.cpp index bbd056d..4de75ee 100644 --- a/src/Mesh2D.cpp +++ b/src/Mesh2D.cpp @@ -26,41 +26,34 @@ #include "Mesh2D.h" #include "SKINI.msg" -#include Mesh2D :: Mesh2D(short nX, short nY) { this->setNX(nX); this->setNY(nY); - MY_FLOAT pole = 0.05; + StkFloat pole = 0.05; short i; for (i=0; isetGain(0.99); + filterY_[i].setPole( pole ); + filterY_[i].setGain( 0.99 ); } for (i=0; isetGain(0.99); + filterX_[i].setPole( pole ); + filterX_[i].setGain( 0.99 ); } this->clearMesh(); - counter=0; - xInput = 0; - yInput = 0; + counter_=0; + xInput_ = 0; + yInput_ = 0; } Mesh2D :: ~Mesh2D() { - short i; - for (i=0; iclearMesh(); short i; - for (i=0; iclear(); + for (i=0; iclear(); + for (i=0; i NXMAX ) { - std::cerr << "Mesh2D::setNX(" << lenX << "): Maximum length is " << NXMAX << "!" << std::endl; - NX = NXMAX; + errorString_ << "Mesh2D::setNX(" << lenX << "): Maximum length is " << NXMAX << '!';; + handleError( StkError::WARNING ); + NX_ = NXMAX; } } void Mesh2D :: setNY(short lenY) { - NY = lenY; + NY_ = lenY; if ( lenY < 2 ) { - std::cerr << "Mesh2D::setNY(" << lenY << "): Minimum length is 2!" << std::endl; - NY = 2; + errorString_ << "Mesh2D::setNY(" << lenY << "): Minimum length is 2!"; + handleError( StkError::WARNING ); + NY_ = 2; } else if ( lenY > NYMAX ) { - std::cerr << "Mesh2D::setNY(" << lenY << "): Maximum length is " << NYMAX << "!" << std::endl; - NY = NYMAX; + errorString_ << "Mesh2D::setNY(" << lenY << "): Maximum length is " << NXMAX << '!';; + handleError( StkError::WARNING ); + NY_ = NYMAX; } } -void Mesh2D :: setDecay(MY_FLOAT decayFactor) +void Mesh2D :: setDecay(StkFloat decayFactor) { - MY_FLOAT gain = decayFactor; + StkFloat gain = decayFactor; if ( decayFactor < 0.0 ) { - std::cerr << "Mesh2D::setDecay decayFactor value is less than 0.0!" << std::endl; + errorString_ << "Mesh2D::setDecay: decayFactor value is less than 0.0!"; + handleError( StkError::WARNING ); gain = 0.0; } else if ( decayFactor > 1.0 ) { - std::cerr << "Mesh2D::setDecay decayFactor value is greater than 1.0!" << std::endl; + errorString_ << "Mesh2D::setDecay decayFactor value is greater than 1.0!"; + handleError( StkError::WARNING ); gain = 1.0; } int i; for (i=0; isetGain(gain); + filterY_[i].setGain( gain ); for (i=0; isetGain(gain); + filterX_[i].setGain( gain ); } -void Mesh2D :: setInputPosition(MY_FLOAT xFactor, MY_FLOAT yFactor) +void Mesh2D :: setInputPosition(StkFloat xFactor, StkFloat yFactor) { if ( xFactor < 0.0 ) { - std::cerr << "Mesh2D::setInputPosition xFactor value is less than 0.0!" << std::endl; - xInput = 0; + errorString_ << "Mesh2D::setInputPosition xFactor value is less than 0.0!"; + handleError( StkError::WARNING ); + xInput_ = 0; } else if ( xFactor > 1.0 ) { - std::cerr << "Mesh2D::setInputPosition xFactor value is greater than 1.0!" << std::endl; - xInput = NX - 1; + errorString_ << "Mesh2D::setInputPosition xFactor value is greater than 1.0!"; + handleError( StkError::WARNING ); + xInput_ = NX_ - 1; } else - xInput = (short) (xFactor * (NX - 1)); + xInput_ = (short) (xFactor * (NX_ - 1)); if ( yFactor < 0.0 ) { - std::cerr << "Mesh2D::setInputPosition yFactor value is less than 0.0!" << std::endl; - yInput = 0; + errorString_ << "Mesh2D::setInputPosition yFactor value is less than 0.0!"; + handleError( StkError::WARNING ); + yInput_ = 0; } else if ( yFactor > 1.0 ) { - std::cerr << "Mesh2D::setInputPosition yFactor value is greater than 1.0!" << std::endl; - yInput = NY - 1; + errorString_ << "Mesh2D::setInputPosition yFactor value is greater than 1.0!"; + handleError( StkError::WARNING ); + yInput_ = NY_ - 1; } else - yInput = (short) (yFactor * (NY - 1)); + yInput_ = (short) (yFactor * (NY_ - 1)); } -void Mesh2D :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Mesh2D :: noteOn(StkFloat frequency, StkFloat amplitude) { // Input at corner. - if ( counter & 1 ) { - vxp1[xInput][yInput] += amplitude; - vyp1[xInput][yInput] += amplitude; + if ( counter_ & 1 ) { + vxp1_[xInput_][yInput_] += amplitude; + vyp1_[xInput_][yInput_] += amplitude; } else { - vxp[xInput][yInput] += amplitude; - vyp[xInput][yInput] += amplitude; + vxp_[xInput_][yInput_] += amplitude; + vyp_[xInput_][yInput_] += amplitude; } #if defined(_STK_DEBUG_) - std::cerr << "Mesh2D: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Mesh2D::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Mesh2D :: noteOff(MY_FLOAT amplitude) +void Mesh2D :: noteOff(StkFloat amplitude) { #if defined(_STK_DEBUG_) - std::cerr << "Mesh2D: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Mesh2D::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Mesh2D :: tick(MY_FLOAT input) +StkFloat Mesh2D :: tick(StkFloat input) { - if ( counter & 1 ) { - vxp1[xInput][yInput] += input; - vyp1[xInput][yInput] += input; - lastOutput = tick1(); + if ( counter_ & 1 ) { + vxp1_[xInput_][yInput_] += input; + vyp1_[xInput_][yInput_] += input; + lastOutput_ = tick1(); } else { - vxp[xInput][yInput] += input; - vyp[xInput][yInput] += input; - lastOutput = tick0(); + vxp_[xInput_][yInput_] += input; + vyp_[xInput_][yInput_] += input; + lastOutput_ = tick0(); } - counter++; - return lastOutput; + counter_++; + return lastOutput_; } -MY_FLOAT Mesh2D :: tick() +StkFloat Mesh2D :: tick() { - lastOutput = ((counter & 1) ? this->tick1() : this->tick0()); - counter++; - return lastOutput; + lastOutput_ = ((counter_ & 1) ? this->tick1() : this->tick0()); + counter_++; + return lastOutput_; } -#define VSCALE ((MY_FLOAT) (0.5)) +const StkFloat VSCALE = 0.5; -MY_FLOAT Mesh2D :: tick0() +StkFloat Mesh2D :: tick0() { int x, y; - MY_FLOAT outsamp = 0; + StkFloat outsamp = 0; // Update junction velocities. - for (x=0; xtick(vxm[0][y]); - vxm1[NX-1][y] = vxp[NX-1][y]; + for (y=0; ytick(vym[x][0]); - vym1[x][NY-1] = vyp[x][NY-1]; + for (x=0; xtick(vxm1[0][y]); - vxm[NX-1][y] = vxp1[NX-1][y]; + for (y=0; ytick(vym1[x][0]); - vym[x][NY-1] = vyp1[x][NY-1]; + for (x=0; x 1.0 ) { norm = 1.0; - std::cerr << "Mesh2D: Control value greater than 128.0!" << std::endl; + errorString_ << "Mesh2D::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == 2) // 2 - setNX( (short) (norm * (NXMAX-2) + 2) ); + this->setNX( (short) (norm * (NXMAX-2) + 2) ); else if (number == 4) // 4 - setNY( (short) (norm * (NYMAX-2) + 2) ); + this->setNY( (short) (norm * (NYMAX-2) + 2) ); else if (number == 11) // 11 - setDecay( 0.9 + (norm * 0.1) ); + this->setDecay( 0.9 + (norm * 0.1) ); else if (number == __SK_ModWheel_) // 1 - setInputPosition(norm, norm); - else if (number == __SK_AfterTouch_Cont_) // 128 - ; - else - std::cerr << "Mesh2D: Undefined Control Number (" << number << ")!!" << std::endl; + this->setInputPosition( norm, norm ); + else { + errorString_ << "Mesh2D::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Mesh2D: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Mesh2D::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Messager.cpp b/src/Messager.cpp index 8ae2c29..6563c51 100644 --- a/src/Messager.cpp +++ b/src/Messager.cpp @@ -2,393 +2,419 @@ /*! \class Messager \brief STK input control message parser. - This class reads and parses control messages - from a variety of sources, such as a MIDI - port, scorefile, socket connection, or pipe. - MIDI messages are retrieved using the RtMidi - class. All other input sources (scorefile, - socket, or pipe) are assumed to provide SKINI - formatted messages. + This class reads and parses control messages from a variety of + sources, such as a scorefile, MIDI port, socket connection, or + stdin. MIDI messages are retrieved using the RtMidi class. All + other input sources (scorefile, socket, or stdin) are assumed to + provide SKINI formatted messages. This class can be compiled with + generic, non-realtime support, in which case only scorefile + reading is possible. - For each call to nextMessage(), the active - input sources are queried to see if a new - control message is available. + The various \e realtime message acquisition mechanisms (from MIDI, + socket, or stdin) take place asynchronously, filling the message + queue. A call to popMessage() will pop the next available control + message from the queue and return it via the referenced Message + structure. When a \e non-realtime scorefile is set, it is not + possible to start reading realtime input messages (from MIDI, + socket, or stdin). Likewise, it is not possible to read from a + scorefile when a realtime input mechanism is running. - This class is primarily for use in STK main() - event loops. + When MIDI input is started, input is also automatically read from + stdin. This allows for program termination via the terminal + window. An __SK_Exit_ message is pushed onto the stack whenever + an "exit" or "Exit" message is received from stdin or when all + socket connections close and no stdin thread is running. - One of the original goals in creating this - class was to simplify the message acquisition - process by removing all threads. If the - windoze select() function behaved just like - the unix one, that would have been possible. - Since it does not (it can't be used to poll - STDIN), I am using a thread to acquire - messages from STDIN, which sends these - messages via a socket connection to the - message socket server. Perhaps in the future, - it will be possible to simplify things. + This class is primarily for use in STK example programs but it is + generic enough to work in many other contexts. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Messager.h" -#include #include +#include "SKINI.msg" -int socket_port = 2001; +static const int STK_FILE = 0x1; +static const int STK_MIDI = 0x2; +static const int STK_STDIN = 0x4; +static const int STK_SOCKET = 0x8; -Messager :: Messager(int inputMask, int port) +Messager :: Messager() { - sources = inputMask; - rtDelta = RT_BUFFER_SIZE; - messageIndex = 0; - nMessages = 0; - skini = new SKINI(); - + data_.sources = 0; + data_.queueLimit = DEFAULT_QUEUE_LIMIT; #if defined(__STK_REALTIME__) - // If no input source is specified, we assume the input is coming - // from a SKINI scorefile. If any input source is specified, we - // will always check STDIN, even if STK_PIPE is not specified. This - // provides a means to exit cleanly when reading MIDI or in case a - // socket connection cannot be made after STK_SOCKET has been - // specified. The current means of polling STDIN is via a thread, - // which sends its data via a socket connection to the socket - // server. - if ( sources ) { - - if ( sources & STK_MIDI ) { - // Attempt to open a MIDI device, but don't throw an exception - // if other input sources are specified. - try { - midi = new RtMidi(); - } - catch (StkError &exception) { - if ( sources == STK_MIDI ) { - throw exception; - } - // Disable the MIDI input and keep going. - sources &= ~STK_MIDI; - } - } - - // 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 ) - std::cout << "\nType `Exit' to quit.\n" << std::endl; - - sources |= STK_SOCKET; - socket_port = port; - soket = new Socket(port); - if (inputMask & STK_SOCKET) - printf("\nSocket server listening for connection(s) on port %d ...\n\n", port); - - nSockets = 0; - maxfd = 0; - FD_ZERO(&mask); - int d = soket->socket(); - FD_SET(d, &mask); - if (d > maxfd) maxfd = d; - - // The fd array is used to hold the file descriptors for all - // connected sockets. This saves some time incrementing through - // file descriptors when using select(). - for (int i=0; i<16; i++) - fd[i] = 0; - - // Start the stdin input thread. - thread = new Thread(); - if ( !thread->start( (THREAD_FUNCTION)&stdinHandler, NULL ) ) { - sprintf(error, "Messager: Unable to start stdin input thread!"); - handleError( error, StkError::PROCESS_THREAD ); - } - } -#endif // __STK_REALTIME__ + data_.socket = 0; + data_.midi = 0; +#endif } Messager :: ~Messager() { - delete skini; + // Clear the queue in case any thread is waiting on its limit. +#if defined(__STK_REALTIME__) + data_.mutex.lock(); +#endif + while ( data_.queue.size() ) data_.queue.pop(); + data_.sources = 0; + +#if defined(__STK_REALTIME__) + data_.mutex.unlock(); + if ( data_.socket ) { + socketThread_.wait(); + delete data_.socket; + } + + if ( data_.midi ) delete data_.midi; +#endif +} + +bool Messager :: setScoreFile( const char* filename ) +{ + if ( data_.sources ) { + if ( data_.sources == STK_FILE ) { + errorString_ << "Messager::setScoreFile: already reading a scorefile!"; + handleError( StkError::WARNING ); + } + else { + errorString_ << "Messager::setScoreFile: already reading realtime control input ... cannot do scorefile input too!"; + handleError( StkError::WARNING ); + } + return false; + } + + if ( !data_.skini.setFile( filename ) ) return false; + data_.sources = STK_FILE; + return true; +} + +void Messager :: popMessage( Skini::Message& message ) +{ + if ( data_.sources == STK_FILE ) { // scorefile input + if ( !data_.skini.nextMessage( message ) ) + message.type = __SK_Exit_; + return; + } + + if ( data_.queue.size() == 0 ) { + // An empty (or invalid) message is indicated by a type = 0. + message.type = 0; + return; + } + + // Copy queued message to the message pointer structure and then "pop" it. +#if defined(__STK_REALTIME__) + data_.mutex.lock(); +#endif + message = data_.queue.front(); + data_.queue.pop(); +#if defined(__STK_REALTIME__) + data_.mutex.unlock(); +#endif +} + +void Messager :: pushMessage( Skini::Message& message ) +{ +#if defined(__STK_REALTIME__) + data_.mutex.lock(); +#endif + data_.queue.push( message ); +#if defined(__STK_REALTIME__) + data_.mutex.unlock(); +#endif +} #if defined(__STK_REALTIME__) - if ( sources & STK_MIDI ) - delete midi; - - if ( sources & STK_SOCKET ) { - delete soket; - delete thread; - } -#endif // __STK_REALTIME__ -} - -long Messager :: getType() const +bool Messager :: startStdInput() { - return type; -} - -MY_FLOAT Messager :: getByteTwo() const -{ - return byte2; -} - -MY_FLOAT Messager :: getByteThree() const -{ - return byte3; -} - -long Messager :: getChannel() const -{ - return channel; -} - -void Messager :: setRtDelta(long nSamples) -{ - if ( nSamples > 0 ) - rtDelta = nSamples; - else - std::cerr << "Messager: setRtDelta(" << nSamples << ") less than or equal to zero!" << std::endl; -} - -long Messager :: getDelta() const -{ - return delta; -} - -long Messager :: nextMessage() -{ - if (nMessages > 0 ) nMessages--; - type = 0; - - if ( !sources ) { - // No realtime flags ... assuming scorefile input. - memset(message[messageIndex], 0, MESSAGE_LENGTH); - if ( fgets(message[messageIndex], MESSAGE_LENGTH, stdin) == 0 ) { - delta = 0; - return -1; // end of file - } - nMessages++; - } -#if defined(__STK_REALTIME__) - else if (nMessages == 0) { - if ( midiMessage() ) return type; - if ( !socketMessage() ) return type; - } -#endif - - skini->parseThis(message[messageIndex++]); - if (messageIndex >= MAX_MESSAGES) messageIndex = 0; - type = skini->getType(); - if (type <= 0) { - // Don't tick for comments or improperly formatted messages. - nMessages--; - delta = 0; - type = 0; - return type; + if ( data_.sources == STK_FILE ) { + errorString_ << "Messager::startStdInput: already reading a scorefile ... cannot do realtime control input too!"; + handleError( StkError::WARNING ); + return false; } - channel = skini->getChannel(); - byte2 = skini->getByteTwo(); - byte3 = skini->getByteThree(); - - MY_FLOAT temp = skini->getDelta(); - if ( temp >= 0.0 ) - delta = (long) (temp * Stk::sampleRate()); - else - // Ignore negative delta times (absolute time). - delta = rtDelta; - - return type; -} - -#if defined(__STK_REALTIME__) -bool Messager :: midiMessage( void ) -{ - if (sources & STK_MIDI) { - if ( midi->nextMessage() > 0 ) { - // get MIDI message info - type = midi->getType(); - channel = midi->getChannel(); - byte2 = midi->getByteTwo(); - byte3 = midi->getByteThree(); - nMessages++; - delta = rtDelta; - return true; - } - } - return false; -} - -bool Messager :: socketMessage() -{ - register fd_set rmask; - static struct timeval timeout = {0, 0}; - - rmask = mask; - if ( select(maxfd+1, &rmask, (fd_set *)0, (fd_set *)0, &timeout) ) { - // A file descriptor is set. - - // Check if there's a new socket connection available. - if ( FD_ISSET(soket->socket(), &rmask) ) { - // Accept and service new connection. - int newfd = soket->accept(); - if ( newfd < 0 ) { - sprintf(error, "Messager: Couldn't accept connection request!"); - handleError(error, StkError::WARNING); - } - - // We assume the first connection will occur for the stdin - // thread socket. Since this connection is "hidden" from - // the user, only print connected messages for subsequent - // connections. - if (nSockets == 0) - pipefd = newfd; - else - std::cout << "New socket connection made.\n" << std::endl; - - // Set the socket to non-blocking mode. - Socket::setBlocking( newfd, false ); - - // Save the descriptor and update the masks. - fd[nSockets++] = newfd; - FD_SET(newfd, &mask); - if ( newfd > maxfd) maxfd = newfd; - FD_CLR(soket->socket(), &rmask); - } - - // Check client socket connections. - unsigned int client = 0; - while ( client < nSockets ) { - if ( !FD_ISSET(fd[client], &rmask) ) - client++; - else { - // This connection has data. - if ( !readSocket( fd[client] ) ) { - // The socket connection closed. - nSockets--; - if ( nSockets == 0 ) { - type = -1; - return false; - } - if ( nSockets == 1 && FD_ISSET(pipefd, &mask) ) { - // The "piping" socket is still running. - if (sources & STK_MIDI) { - 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. - type = -1; - return false; - } - } - if (client < nSockets) { - // Move descriptors down in the list. - for (unsigned int j=client; j - -#endif - -bool Messager :: readSocket(int fd) +THREAD_RETURN THREAD_TYPE stdinHandler(void *ptr) { - // This method will read all data available from a socket - // connection, filling the message buffer. This is necessary - // because the select() function triggers on socket activity, not on - // the presence of (buffered) data. So, whenever activity is - // indicated, we need to grab all available data. - char buffer[MESSAGE_LENGTH]; - int index = 0, m = 0, bufferSize = 0; - int nextMessage; + Messager::MessagerData *data = (Messager::MessagerData *) ptr; + Skini::Message message; - nextMessage = (messageIndex + nMessages) % MAX_MESSAGES; - memset(message[nextMessage], 0, MESSAGE_LENGTH); + std::string line; + while ( !std::getline( std::cin, line).eof() ) { + if ( line.empty() ) continue; + if ( line.compare(0, 4, "Exit") == 0 || line.compare(0, 4, "exit") == 0 ) + break; -#if ( defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__) ) - errno = 0; - while (bufferSize != -1 && errno != EAGAIN) { -#elif defined(__OS_WINDOWS__) - while (bufferSize != SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) { -#endif - while (index < bufferSize) { - message[nextMessage][m++] = buffer[index]; - if (buffer[index++] == '\n') { - m = 0; - nMessages++; - nextMessage = (messageIndex + nMessages) % MAX_MESSAGES; - memset(message[nextMessage], 0, MESSAGE_LENGTH); - } - } - index = 0; + data->mutex.lock(); + if ( data->skini.parseString( line, message ) ) + data->queue.push( message ); + data->mutex.unlock(); - // Receive a new socket buffer. - memset(buffer, 0, MESSAGE_LENGTH); - bufferSize = Socket::readBuffer(fd, buffer, MESSAGE_LENGTH, 0); - if (bufferSize == 0) { - FD_CLR(fd, &mask); - Socket::close( fd ); + while ( data->queue.size() >= data->queueLimit ) Stk::sleep( 50 ); + } + + // We assume here that if someone types an "exit" message in the + // terminal window, all processing should stop. + message.type = __SK_Exit_; + data->queue.push( message ); + data->sources &= ~STK_STDIN; + + return NULL; +} + +void midiHandler( double timeStamp, std::vector *bytes, void *ptr ) +{ + if ( bytes->size() < 2 ) return; + + // Parse the MIDI bytes ... only keep MIDI channel messages. + if ( bytes->at(0) > 239 ) return; + + Messager::MessagerData *data = (Messager::MessagerData *) ptr; + Skini::Message message; + + message.type = bytes->at(0) & 0xF0; + message.channel = bytes->at(0) & 0x0F; + message.time = 0.0; // realtime messages should have delta time = 0.0 + message.intValues[0] = bytes->at(1); + message.floatValues[0] = (StkFloat) message.intValues[0]; + if ( ( message.type != 0xC0 ) && ( message.type != 0xD0 ) ) { + if ( bytes->size() < 3 ) return; + message.intValues[1] = bytes->at(2); + message.floatValues[1] = (StkFloat) message.intValues[1]; + } + + while ( data->queue.size() >= data->queueLimit ) Stk::sleep( 50 ); + + data->mutex.lock(); + data->queue.push( message ); + data->mutex.unlock(); +} + +bool Messager :: startMidiInput( int port ) +{ + if ( data_.sources == STK_FILE ) { + errorString_ << "Messager::startMidiInput: already reading a scorefile ... cannot do realtime control input too!"; + handleError( StkError::WARNING ); + return false; + } + + if ( data_.sources & STK_MIDI ) { + errorString_ << "Messager::startMidiInput: MIDI input already started."; + handleError( StkError::WARNING ); + return false; + } + + // First start the stdin input thread if it isn't already running + // (to allow the user to exit). + if ( !( data_.sources & STK_STDIN ) ) { + if ( this->startStdInput() == false ) { + errorString_ << "Messager::startMidiInput: unable to start input from stdin."; + handleError( StkError::WARNING ); return false; } } + try { + data_.midi = new RtMidiIn(); + data_.midi->setCallback( &midiHandler, (void *) &data_ ); + if ( port == -1 ) data_.midi->openVirtualPort(); + else data_.midi->openPort( (unsigned int)port ); + } + catch ( RtError &error ) { + errorString_ << "Messager::startMidiInput: error creating RtMidiIn instance (" << error.getMessage() << ")."; + handleError( StkError::WARNING ); + return false; + } + + data_.sources |= STK_MIDI; return true; } -THREAD_RETURN THREAD_TYPE stdinHandler(void *) +bool Messager :: startSocketInput( int port ) { - char message[MESSAGE_LENGTH]; + if ( data_.sources == STK_FILE ) { + errorString_ << "Messager::startSocketInput: already reading a scorefile ... cannot do realtime control input too!"; + handleError( StkError::WARNING ); + return false; + } - Socket *s; + if ( data_.sources & STK_SOCKET ) { + errorString_ << "Messager::startSocketInput: socket input thread already started."; + handleError( StkError::WARNING ); + return false; + } + + // Create the socket server. try { - s = new Socket( socket_port, "localhost" ); + data_.socket = new Socket( port ); } - catch (StkError &) { - fprintf(stderr, "Messager: Couldn't create stdin input thread!\n"); - return NULL; + catch ( StkError& ) { + return false; } - for (;;) { - memset(message, 0, MESSAGE_LENGTH); - if ( fgets(message, MESSAGE_LENGTH, stdin) == 0 ) - break; + errorString_ << "Socket server listening for connection(s) on port " << port << "..."; + handleError( StkError::STATUS ); - // Check for an "Exit" message. - if ( !strncmp(message, "Exit", 4) || !strncmp(message, "exit", 4) ) - break; + // Initialize socket descriptor information. + FD_ZERO(&data_.mask); + int fd = data_.socket->id(); + FD_SET( fd, &data_.mask ); + data_.fd.push_back( fd ); - if ( s->writeBuffer( (void *)message, strlen(message), 0) < 0 ) { - fprintf(stderr, "Messager: stdin thread connection to socket server failed!\n"); - break; + // Start the socket thread. + if ( !socketThread_.start( (THREAD_FUNCTION)&socketHandler, &data_ ) ) { + errorString_ << "Messager::startSocketInput: unable to start socket input thread!"; + handleError( StkError::WARNING ); + return false; + } + + data_.sources |= STK_SOCKET; + return true; +} + +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOS__)) + #include + #include + #include + #include + #include + #include + #include +#endif + +THREAD_RETURN THREAD_TYPE socketHandler(void *ptr) +{ + Messager::MessagerData *data = (Messager::MessagerData *) ptr; + Skini::Message message; + std::vector& fd = data->fd; + + struct timeval timeout; + fd_set rmask; + int newfd; + unsigned int i; + const int bufferSize = 1024; + char buffer[bufferSize]; + int index = 0, bytesRead = 0; + std::string line; + std::vector fdclose; + + while ( data->sources & STK_SOCKET ) { + + // Use select function to periodically poll socket desriptors. + rmask = data->mask; + timeout.tv_sec = 0; timeout.tv_usec = 50000; // 50 milliseconds + if ( select( fd.back()+1, &rmask, (fd_set *)0, (fd_set *)0, &timeout ) <= 0 ) continue; + + // A file descriptor is set. Check if there's a new socket connection available. + if ( FD_ISSET( data->socket->id(), &rmask ) ) { + + // Accept and service new connection. + newfd = data->socket->accept(); + if ( newfd >= 0 ) { + std::cout << "New socket connection made.\n" << std::endl; + + // Set the socket to non-blocking mode. + Socket::setBlocking( newfd, false ); + + // Save the descriptor and update the masks. + fd.push_back( newfd ); + std::sort( fd.begin(), data->fd.end() ); + FD_SET( newfd, &data->mask ); + FD_CLR( data->socket->id(), &rmask ); + } + else + std::cerr << "Messager: Couldn't accept connection request!\n"; } + + // Check the other descriptors. + for ( i=0; imutex.lock(); + if ( line.compare(0, 4, "Exit") == 0 || line.compare(0, 4, "exit") == 0 ) { + // Ignore this line and assume the connection will be + // closed on a subsequent read call. + ; + } + else if ( data->skini.parseString( line, message ) ) + data->queue.push( message ); + data->mutex.unlock(); + line.erase(); + } + } + index = 0; + + bytesRead = Socket::readBuffer(fd[i], buffer, bufferSize, 0); + if (bytesRead == 0) { + // This socket connection closed. + FD_CLR( fd[i], &data->mask ); + Socket::close( fd[i] ); + fdclose.push_back( fd[i] ); + } + } + } + + // Now remove descriptors for closed connections. + for ( i=0; isources &= ~STK_SOCKET; + if ( data->sources & STK_MIDI ) + std::cout << "MIDI input still running ... type 'exit' to quit.\n" << std::endl; + else if ( !(data->sources & STK_STDIN) ) { + // No stdin thread running, so quit now. + message.type = __SK_Exit_; + data->queue.push( message ); + } + } + fdclose.clear(); + } + + // Wait until we're below the queue limit. + while ( data->queue.size() >= data->queueLimit ) Stk::sleep( 50 ); } - delete s; return NULL; } -#endif // __STK_REALTIME__ +#endif diff --git a/src/MidiFileIn.cpp b/src/MidiFileIn.cpp new file mode 100644 index 0000000..b1d5304 --- /dev/null +++ b/src/MidiFileIn.cpp @@ -0,0 +1,359 @@ +/**********************************************************************/ +/*! \class MidiFileIn + \brief A standard MIDI file reading/parsing class. + + This class can be used to read events from a standard MIDI file. + Event bytes are copied to a C++ vector and must be subsequently + interpreted by the user. The function getNextMidiEvent() skips + meta and sysex events, returning only MIDI channel messages. + Event delta-times are returned in the form of "ticks" and a + function is provided to determine the current "seconds per tick". + Tempo changes are internally tracked by the class and reflected in + the values returned by the function getTickSeconds(). + + by Gary P. Scavone, 2003. +*/ +/**********************************************************************/ + +#include "MidiFileIn.h" +#include + +MidiFileIn :: MidiFileIn( std::string fileName ) +{ + // Attempt to open the file. + file_.open( fileName.c_str(), std::ios::in ); + if ( !file_ ) { + errorString_ << "MidiFileIn: error opening or finding file (" << fileName << ")."; + handleError( StkError::FILE_NOT_FOUND ); + } + + // Parse header info. + char chunkType[4]; + char buffer[4]; + SINT32 *length; + if ( !file_.read( chunkType, 4 ) ) goto error; + if ( !file_.read( buffer, 4 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&buffer); +#endif + length = (SINT32 *) &buffer; + if ( strncmp( chunkType, "MThd", 4 ) || ( *length != 6 ) ) { + errorString_ << "MidiFileIn: file (" << fileName << ") does not appear to be a MIDI file!"; + handleError( StkError::FILE_UNKNOWN_FORMAT ); + } + + // Read the MIDI file format. + SINT16 *data; + if ( !file_.read( buffer, 2 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&buffer); +#endif + data = (SINT16 *) &buffer; + if ( *data < 0 || *data > 2 ) { + errorString_ << "MidiFileIn: the file (" << fileName << ") format is invalid!"; + handleError( StkError::FILE_ERROR ); + } + format_ = *data; + + // Read the number of tracks. + if ( !file_.read( buffer, 2 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&buffer); +#endif + if ( format_ == 0 && *data != 1 ) { + errorString_ << "MidiFileIn: invalid number of tracks (>1) for a file format = 0!"; + handleError( StkError::FILE_ERROR ); + } + nTracks_ = *data; + + // Read the beat division. + if ( !file_.read( buffer, 2 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&buffer); +#endif + division_ = (int) *data; + double tickrate; + usingTimeCode_ = false; + if ( *data & 0x8000 ) { + // Determine ticks per second from time-code formats. + tickrate = (double) -(*data & 0x7F00); + // If frames per second value is 29, it really should be 29.97. + if ( tickrate == 29.0 ) tickrate = 29.97; + tickrate *= (*data & 0x00FF); + usingTimeCode_ = true; + } + else { + tickrate = (double) (*data & 0x7FFF); // ticks per quarter note + } + + // Now locate the track offsets and lengths. If not using time + // code, we can initialize the "tick time" using a default tempo of + // 120 beats per minute. We will then check for tempo meta-events + // afterward. + for ( unsigned int i=0; i event; + unsigned long value, count; + + // We need to temporarily change the usingTimeCode_ value here so + // that the getNextEvent() function doesn't try to check the tempo + // map (which we're creating here). + usingTimeCode_ = true; + count = getNextEvent( &event, 0 ); + while ( event.size() ) { + if ( ( event.size() == 6 ) && ( event[0] == 0xff ) && + ( event[1] == 0x51 ) && ( event[2] == 0x03 ) ) { + tempoEvent.count = count; + value = ( event[3] << 16 ) + ( event[4] << 8 ) + event[5]; + tempoEvent.tickSeconds = (double) (0.000001 * value / tickrate); + if ( count > tempoEvents_.back().count ) + tempoEvents_.push_back( tempoEvent ); + else + tempoEvents_.back() = tempoEvent; + } + count += getNextEvent( &event, 0 ); + } + rewindTrack( 0 ); + for ( unsigned int i=0; i= nTracks_ ) { + errorString_ << "MidiFileIn::getNextEvent: invalid track argument (" << track << ")."; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + trackPointers_[track] = trackOffsets_[track]; + trackStatus_[track] = 0; + tickSeconds_[track] = tempoEvents_[0].tickSeconds; +} + +double MidiFileIn :: getTickSeconds( unsigned int track ) +{ + // Return the current tick value in seconds for the given track. + if ( track >= nTracks_ ) { + errorString_ << "MidiFileIn::getTickSeconds: invalid track argument (" << track << ")."; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + return tickSeconds_[track]; +} + +unsigned long MidiFileIn :: getNextEvent( std::vector *event, unsigned int track ) +{ + // Fill the user-provided vector with the next event in the + // specified track (default = 0) and return the event delta time in + // ticks. This function assumes that the stored track pointer is + // positioned at the start of a track event. If the track has + // reached its end, the event vector size will be zero. + // + // If we have a format 0 or 2 file and we're not using timecode, we + // should check every meta-event for tempo changes and make + // appropriate updates to the tickSeconds_ parameter if so. + // + // If we have a format 1 file and we're not using timecode, keep a + // running sum of ticks for each track and update the tickSeconds_ + // parameter as needed based on the stored tempo map. + + if ( track >= nTracks_ ) { + errorString_ << "MidiFileIn::getNextEvent: invalid track argument (" << track << ")."; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + event->clear(); + // Check for the end of the track. + if ( (trackPointers_[track] - trackOffsets_[track]) >= trackLengths_[track] ) + return 0; + + unsigned long ticks = 0, bytes = 0; + bool isTempoEvent = false; + + // Read the event delta time. + file_.seekg( trackPointers_[track], std::ios_base::beg ); + if ( !readVariableLength( &ticks ) ) goto error; + + // Parse the event stream to determine the event length. + unsigned char c; + if ( !file_.read( (char *)&c, 1 ) ) goto error; + switch ( c ) { + + case 0xFF: // A Meta-Event + unsigned long position; + trackStatus_[track] = 0; + event->push_back( c ); + if ( !file_.read( (char *)&c, 1 ) ) goto error; + event->push_back( c ); + if ( format_ != 1 && ( c == 0x51 ) ) isTempoEvent = true; + position = file_.tellg(); + if ( !readVariableLength( &bytes ) ) goto error; + bytes += ( (unsigned long)file_.tellg() - position ); + file_.seekg( position, std::ios_base::beg ); + break; + + case 0xF0 || 0xF7: // The start or continuation of a Sysex event + trackStatus_[track] = 0; + event->push_back( c ); + position = file_.tellg(); + if ( !readVariableLength( &bytes ) ) goto error; + bytes += ( (unsigned long)file_.tellg() - position ); + file_.seekg( position, std::ios_base::beg ); + break; + + default: // Should be a MIDI channel event + if ( c & 0x80 ) { // MIDI status byte + if ( c > 0xF0 ) goto error; + trackStatus_[track] = c; + event->push_back( c ); + c &= 0xF0; + if ( (c == 0xC0) || (c == 0xD0) ) bytes = 1; + else bytes = 2; + } + else if ( trackStatus_[track] & 0x80 ) { // Running status + event->push_back( trackStatus_[track] ); + event->push_back( c ); + c = trackStatus_[track] & 0xF0; + if ( (c != 0xC0) && (c != 0xD0) ) bytes = 1; + } + else goto error; + + } + + // Read the rest of the event into the event vector. + for ( unsigned long i=0; ipush_back( c ); + } + + if ( !usingTimeCode_ ) { + if ( isTempoEvent ) { + // Parse the tempo event and update tickSeconds_[track]. + double tickrate = (double) (division_ & 0x7FFF); + unsigned long value = ( event->at(3) << 16 ) + ( event->at(4) << 8 ) + event->at(5); + tickSeconds_[track] = (double) (0.000001 * value / tickrate); + } + + if ( format_ == 1 ) { + // Update track counter and check the tempo map. + trackCounters_[track] += ticks; + TempoChange tempoEvent = tempoEvents_[ trackTempoIndex_[track] ]; + if ( trackCounters_[track] >= tempoEvent.count ) { + trackTempoIndex_[track]++; + tickSeconds_[track] = tempoEvent.tickSeconds; + } + } + } + + // Save the current track pointer value. + trackPointers_[track] = file_.tellg(); + + return ticks; + + error: + errorString_ << "MidiFileIn::getNextEvent: file read error!"; + handleError( StkError::FILE_ERROR ); + return 0; +} + +unsigned long MidiFileIn :: getNextMidiEvent( std::vector *midiEvent, unsigned int track ) +{ + // Fill the user-provided vector with the next MIDI event in the + // specified track (default = 0) and return the event delta time in + // ticks. Meta-Events preceeding this event are skipped and ignored. + if ( track >= nTracks_ ) { + errorString_ << "MidiFileIn::getNextMidiEvent: invalid track argument (" << track << ")."; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long ticks = getNextEvent( midiEvent, track ); + while ( midiEvent->size() && ( midiEvent->at(0) >= 0xF0 ) ) { + //for ( unsigned int i=0; isize(); i++ ) + //std::cout << "event byte = " << i << ", value = " << (int)midiEvent->at(i) << std::endl; + ticks = getNextEvent( midiEvent, track ); + } + + //for ( unsigned int i=0; isize(); i++ ) + //std::cout << "event byte = " << i << ", value = " << (int)midiEvent->at(i) << std::endl; + + return ticks; +} + +bool MidiFileIn :: readVariableLength( unsigned long *value ) +{ + // It is assumed that this function is called with the file read + // pointer positioned at the start of a variable-length value. The + // function returns "true" if the value is successfully parsed and + // "false" otherwise. + *value = 0; + char c; + + if ( !file_.read( &c, 1 ) ) return false; + *value = (unsigned long) c; + if ( *value & 0x80 ) { + *value &= 0x7f; + do { + if ( !file_.read( &c, 1 ) ) return false; + *value = ( *value << 7 ) + ( c & 0x7f ); + } while ( c & 0x80 ); + } + + return true; +} diff --git a/src/Modal.cpp b/src/Modal.cpp index 657bcf0..2f4b4fc 100644 --- a/src/Modal.cpp +++ b/src/Modal.cpp @@ -7,216 +7,216 @@ (non-sweeping BiQuad filters), where N is set during instantiation. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Modal.h" -#include -Modal :: Modal(int modes) - : nModes(modes) +Modal :: Modal(unsigned int modes) + : nModes_(modes) { - if ( nModes <= 0 ) { - char msg[256]; - sprintf(msg, "Modal: Invalid number of modes (%d) argument to constructor!", modes); - handleError(msg, StkError::FUNCTION_ARGUMENT); + if ( nModes_ == 0 ) { + errorString_ << "Modal: 'modes' argument to constructor is zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); } // We don't make the excitation wave here yet, because we don't know // what it's going to be. - ratios = (MY_FLOAT *) new MY_FLOAT[nModes]; - radii = (MY_FLOAT *) new MY_FLOAT[nModes]; - filters = (BiQuad **) calloc( nModes, sizeof(BiQuad *) ); - for (int i=0; isetEqualGainZeroes(); + ratios_.resize( nModes_ ); + radii_.resize( nModes_ ); + filters_ = (BiQuad **) calloc( nModes_, sizeof(BiQuad *) ); + for (unsigned int i=0; isetEqualGainZeroes(); } - envelope = new Envelope; - onepole = new OnePole; - // Concatenate the STK rawwave path to the rawwave file - vibrato = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); + vibrato_ = new WaveLoop( Stk::rawwavePath() + "sinewave.raw", true ); // Set some default values. - vibrato->setFrequency( 6.0 ); - vibratoGain = 0.0; - directGain = 0.0; - masterGain = 1.0; - baseFrequency = 440.0; + vibrato_->setFrequency( 6.0 ); + vibratoGain_ = 0.0; + directGain_ = 0.0; + masterGain_ = 1.0; + baseFrequency_ = 440.0; this->clear(); - stickHardness = 0.5; - strikePosition = 0.561; + stickHardness_ = 0.5; + strikePosition_ = 0.561; } Modal :: ~Modal() { - delete envelope; - delete onepole; - delete vibrato; + delete vibrato_; - delete [] ratios; - delete [] radii; - for (int i=0; iclear(); - for (int i=0; iclear(); + onepole_.clear(); + for (unsigned int i=0; iclear(); } -void Modal :: setFrequency(MY_FLOAT frequency) +void Modal :: setFrequency(StkFloat frequency) { - baseFrequency = frequency; - for (int i=0; isetRatioAndRadius(i, ratios[i], radii[i]); + baseFrequency_ = frequency; + for (unsigned int i=0; isetRatioAndRadius( i, ratios_[i], radii_[i] ); } -void Modal :: setRatioAndRadius(int modeIndex, MY_FLOAT ratio, MY_FLOAT radius) +void Modal :: setRatioAndRadius(unsigned int modeIndex, StkFloat ratio, StkFloat radius) { - if ( modeIndex < 0 ) { - std::cerr << "Modal: setRatioAndRadius modeIndex parameter is less than zero!" << std::endl; - return; - } - else if ( modeIndex >= nModes ) { - std::cerr << "Modal: setRatioAndRadius modeIndex parameter is greater than the number of operators!" << std::endl; + if ( modeIndex >= nModes_ ) { + errorString_ << "Modal::setRatioAndRadius: modeIndex parameter is greater than number of modes!"; + handleError( StkError::WARNING ); return; } - MY_FLOAT nyquist = Stk::sampleRate() / 2.0; - MY_FLOAT temp; + StkFloat nyquist = Stk::sampleRate() / 2.0; + StkFloat temp; - if (ratio * baseFrequency < nyquist) { - ratios[modeIndex] = ratio; + if ( ratio * baseFrequency_ < nyquist ) { + ratios_[modeIndex] = ratio; } else { temp = ratio; - while (temp * baseFrequency > nyquist) temp *= (MY_FLOAT) 0.5; - ratios[modeIndex] = temp; + while (temp * baseFrequency_ > nyquist) temp *= 0.5; + ratios_[modeIndex] = temp; #if defined(_STK_DEBUG_) - std::cerr << "Modal : Aliasing would occur here ... correcting." << std::endl; + errorString_ << "Modal::setRatioAndRadius: aliasing would occur here ... correcting."; + handleError( StkError::DEBUG_WARNING ); #endif } - radii[modeIndex] = radius; + radii_[modeIndex] = radius; if (ratio < 0) temp = -ratio; else - temp = ratio*baseFrequency; + temp = ratio * baseFrequency_; - filters[modeIndex]->setResonance(temp, radius); + filters_[modeIndex]->setResonance(temp, radius); } -void Modal :: setMasterGain(MY_FLOAT aGain) +void Modal :: setMasterGain(StkFloat aGain) { - masterGain = aGain; + masterGain_ = aGain; } -void Modal :: setDirectGain(MY_FLOAT aGain) +void Modal :: setDirectGain(StkFloat aGain) { - directGain = aGain; + directGain_ = aGain; } -void Modal :: setModeGain(int modeIndex, MY_FLOAT gain) +void Modal :: setModeGain(unsigned int modeIndex, StkFloat gain) { - if ( modeIndex < 0 ) { - std::cerr << "Modal: setModeGain modeIndex parameter is less than zero!" << std::endl; - return; - } - else if ( modeIndex >= nModes ) { - std::cerr << "Modal: setModeGain modeIndex parameter is greater than the number of operators!" << std::endl; + if ( modeIndex >= nModes_ ) { + errorString_ << "Modal::setModeGain: modeIndex parameter is greater than number of modes!"; + handleError( StkError::WARNING ); return; } - filters[modeIndex]->setGain(gain); + filters_[modeIndex]->setGain(gain); } -void Modal :: strike(MY_FLOAT amplitude) +void Modal :: strike(StkFloat amplitude) { - MY_FLOAT gain = amplitude; + StkFloat gain = amplitude; if ( amplitude < 0.0 ) { - std::cerr << "Modal: strike amplitude is less than zero!" << std::endl; + errorString_ << "Modal::strike: amplitude is less than zero ... setting to zero!"; + handleError( StkError::WARNING ); gain = 0.0; } else if ( amplitude > 1.0 ) { - std::cerr << "Modal: strike amplitude is greater than 1.0!" << std::endl; + errorString_ << "Modal::strike: amplitude is greater than one ... setting to 1.0!"; + handleError( StkError::WARNING ); gain = 1.0; } - envelope->setRate(1.0); - envelope->setTarget(gain); - onepole->setPole(1.0 - gain); - envelope->tick(); - wave->reset(); + envelope_.setRate( 1.0 ); + envelope_.setTarget( gain ); + onepole_.setPole( 1.0 - gain ); + envelope_.tick(); + wave_->reset(); - MY_FLOAT temp; - for (int i=0; isetResonance(temp, radii[i]); + temp = ratios_[i] * baseFrequency_; + filters_[i]->setResonance(temp, radii_[i]); } } -void Modal :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Modal :: noteOn(StkFloat frequency, StkFloat amplitude) { this->strike(amplitude); this->setFrequency(frequency); #if defined(_STK_DEBUG_) - std::cerr << "Modal: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Modal::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Modal :: noteOff(MY_FLOAT amplitude) +void Modal :: noteOff(StkFloat amplitude) { // This calls damp, but inverts the meaning of amplitude (high // amplitude means fast damping). - this->damp(1.0 - (amplitude * 0.03)); + this->damp( 1.0 - (amplitude * 0.03) ); #if defined(_STK_DEBUG_) - std::cerr << "Modal: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Modal::NoteOff: amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Modal :: damp(MY_FLOAT amplitude) +void Modal :: damp(StkFloat amplitude) { - MY_FLOAT temp; - for (int i=0; isetResonance(temp, radii[i]*amplitude); + temp = ratios_[i] * baseFrequency_; + filters_[i]->setResonance(temp, radii_[i]*amplitude); } } -MY_FLOAT Modal :: tick() +StkFloat Modal :: tick() { - MY_FLOAT temp = masterGain * onepole->tick(wave->tick() * envelope->tick()); + StkFloat temp = masterGain_ * onepole_.tick( wave_->tick() * envelope_.tick() ); - MY_FLOAT temp2 = 0.0; - for (int i=0; itick(temp); + StkFloat temp2 = 0.0; + for (unsigned int i=0; itick(temp); - temp2 -= temp2 * directGain; - temp2 += directGain * temp; + temp2 -= temp2 * directGain_; + temp2 += directGain_ * temp; - if (vibratoGain != 0.0) { + if (vibratoGain_ != 0.0) { // Calculate AM and apply to master out - temp = 1.0 + (vibrato->tick() * vibratoGain); + temp = 1.0 + (vibrato_->tick() * vibratoGain_); temp2 = temp * temp2; } - lastOutput = temp2; - return lastOutput; + lastOutput_ = temp2; + return lastOutput_; +} + +StkFloat *Modal :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Modal :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/ModalBar.cpp b/src/ModalBar.cpp index cb570cc..e5825ee 100644 --- a/src/ModalBar.cpp +++ b/src/ModalBar.cpp @@ -9,9 +9,9 @@ Control Change Numbers: - Stick Hardness = 2 - Stick Position = 4 - - Vibrato Gain = 11 - - Vibrato Frequency = 7 - - Direct Stick Mix = 1 + - Vibrato Gain = 1 + - Vibrato Frequency = 11 + - Direct Stick Mix = 8 - Volume = 128 - Modal Presets = 16 - Marimba = 0 @@ -24,7 +24,7 @@ - Two Fixed = 7 - Clump = 8 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -36,56 +36,60 @@ ModalBar :: ModalBar() : Modal() { // 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() ); + wave_ = new WvIn( (Stk::rawwavePath() + "marmstk1.raw").c_str(), true ); + wave_->setRate( 0.5 * 22050.0 / Stk::sampleRate() ); // Set the resonances for preset 0 (marimba). - setPreset( 0 ); + this->setPreset( 0 ); } ModalBar :: ~ModalBar() { - delete wave; + delete wave_; } -void ModalBar :: setStickHardness(MY_FLOAT hardness) +void ModalBar :: setStickHardness(StkFloat hardness) { - stickHardness = hardness; + stickHardness_ = hardness; if ( hardness < 0.0 ) { - std::cerr << "ModalBar: setStickHardness parameter is less than zero!" << std::endl; - stickHardness = 0.0; + errorString_ << "ModalBar::setStickHardness: parameter is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + stickHardness_ = 0.0; } else if ( hardness > 1.0 ) { - std::cerr << "ModalBar: setStickHarness parameter is greater than 1.0!" << std::endl; - stickHardness = 1.0; + errorString_ << "ModalBar::setStickHarness: parameter is greater than one ... setting to 1.0!"; + handleError( StkError::WARNING ); + stickHardness_ = 1.0; } - wave->setRate( (0.25 * (MY_FLOAT) pow(4.0, stickHardness)) ); - masterGain = 0.1 + (1.8 * stickHardness); + wave_->setRate( (0.25 * pow(4.0, stickHardness_) ) ); + masterGain_ = 0.1 + (1.8 * stickHardness_); } -void ModalBar :: setStrikePosition(MY_FLOAT position) +void ModalBar :: setStrikePosition(StkFloat position) { - strikePosition = position; + strikePosition_ = position; if ( position < 0.0 ) { - std::cerr << "ModalBar: setStrikePositions parameter is less than zero!" << std::endl; - strikePosition = 0.0; + errorString_ << "ModalBar::setStrikePosition: parameter is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + strikePosition_ = 0.0; } else if ( position > 1.0 ) { - std::cerr << "ModalBar: setStrikePosition parameter is greater than 1.0!" << std::endl; - strikePosition = 1.0; + errorString_ << "ModalBar::setStrikePosition: parameter is greater than one ... setting to 1.0!"; + handleError( StkError::WARNING ); + strikePosition_ = 1.0; } // Hack only first three modes. - MY_FLOAT temp2 = position * PI; - MY_FLOAT temp = sin(temp2); + StkFloat temp2 = position * PI; + StkFloat temp = sin(temp2); this->setModeGain(0, 0.12 * temp); temp = sin(0.05 + (3.9 * temp2)); - this->setModeGain(1,(MY_FLOAT) -0.03 * temp); + this->setModeGain(1, -0.03 * temp); - temp = (MY_FLOAT) sin(-0.05 + (11 * temp2)); - this->setModeGain(2,(MY_FLOAT) 0.11 * temp); + temp = sin(-0.05 + (11 * temp2)); + this->setModeGain(2, 0.11 * temp); } void ModalBar :: setPreset(int preset) @@ -97,7 +101,7 @@ void ModalBar :: setPreset(int preset) // Third line: mode volumes // Fourth line: stickHardness, strikePosition, and direct stick // gain (mixed directly into the output - static MY_FLOAT presets[9][4][4] = { + static StkFloat presets[9][4][4] = { {{1.0, 3.99, 10.65, -2443}, // Marimba {0.9996, 0.9994, 0.9994, 0.999}, {0.04, 0.01, 0.01, 0.008}, @@ -137,31 +141,33 @@ void ModalBar :: setPreset(int preset) }; int temp = (preset % 9); - for (int i=0; isetRatioAndRadius(i, presets[temp][0][i], presets[temp][1][i]); this->setModeGain(i, presets[temp][2][i]); } this->setStickHardness(presets[temp][3][0]); this->setStrikePosition(presets[temp][3][1]); - directGain = presets[temp][3][2]; + directGain_ = presets[temp][3][2]; if (temp == 1) // vibraphone - vibratoGain = 0.2; + vibratoGain_ = 0.2; else - vibratoGain = 0.0; + vibratoGain_ = 0.0; } -void ModalBar :: controlChange(int number, MY_FLOAT value) +void ModalBar :: controlChange(int number, StkFloat value) { - MY_FLOAT norm = value * ONE_OVER_128; + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "ModalBar: Control value less than zero!" << std::endl; + errorString_ << "ModalBar::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "ModalBar: Control value greater than 128.0!" << std::endl; + errorString_ << "ModalBar::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_StickHardness_) // 2 @@ -170,18 +176,21 @@ void ModalBar :: controlChange(int number, MY_FLOAT value) this->setStrikePosition( norm ); else if (number == __SK_ProphesyRibbon_) // 16 this->setPreset((int) value); + else if (number == __SK_Balance_) // 8 + directGain_ = norm; else if (number == __SK_ModWheel_) // 1 - directGain = norm; - else if (number == 11) // 11 - vibratoGain = norm * 0.3; - else if (number == __SK_ModFrequency_) // 7 - vibrato->setFrequency( norm * 12.0 ); + vibratoGain_ = norm * 0.3; + else if (number == __SK_ModFrequency_) // 11 + vibrato_->setFrequency( norm * 12.0 ); else if (number == __SK_AfterTouch_Cont_) // 128 - envelope->setTarget( norm ); - else - std::cerr << "ModalBar: Undefined Control Number (" << number << ")!!" << std::endl; + envelope_.setTarget( norm ); + else { + errorString_ << "ModalBar::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "ModalBar: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "ModalBar::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Modulate.cpp b/src/Modulate.cpp index c70ee4e..b21e115 100644 --- a/src/Modulate.cpp +++ b/src/Modulate.cpp @@ -6,7 +6,7 @@ modulations to give a nice, natural human modulation function. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -15,62 +15,58 @@ Modulate :: Modulate() { // 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; + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency( 6.0 ); + vibratoGain_ = 0.04; - noise = new SubNoise(330); - randomGain = 0.05; + noise_.setRate( 330 ); + randomGain_ = 0.05; - filter = new OnePole( 0.999 ); - filter->setGain( randomGain ); + filter_.setPole( 0.999 ); + filter_.setGain( randomGain_ ); } Modulate :: ~Modulate() { - delete vibrato; - delete noise; - delete filter; + delete vibrato_; } void Modulate :: reset() { - lastOutput = (MY_FLOAT) 0.0; + lastOutput_ = (StkFloat) 0.0; } -void Modulate :: setVibratoRate(MY_FLOAT aRate) +void Modulate :: setVibratoRate(StkFloat rate) { - vibrato->setFrequency( aRate ); + vibrato_->setFrequency( rate ); } -void Modulate :: setVibratoGain(MY_FLOAT aGain) +void Modulate :: setVibratoGain(StkFloat gain) { - vibratoGain = aGain; + vibratoGain_ = gain; } -void Modulate :: setRandomGain(MY_FLOAT aGain) +void Modulate :: setRandomGain(StkFloat gain) { - randomGain = aGain; - filter->setGain( randomGain ); + randomGain_ = gain; + filter_.setGain( randomGain_ ); } -MY_FLOAT Modulate :: tick() +StkFloat Modulate :: tick() { // Compute periodic and random modulations. - lastOutput = vibratoGain * vibrato->tick(); - lastOutput += filter->tick( noise->tick() ); - return lastOutput; + lastOutput_ = vibratoGain_ * vibrato_->tick(); + lastOutput_ += filter_.tick( noise_.tick() ); + return lastOutput_; } -MY_FLOAT *Modulate :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Modulate :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; isetFrequency((MY_FLOAT) 6.122); + attacks_.push_back( new WvIn( (Stk::rawwavePath() + "mandpluk.raw").c_str(), true ) ); + loops_.push_back ( new WaveLoop( (Stk::rawwavePath() + "impuls20.raw").c_str(), true ) ); + loops_.push_back ( new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ) ); // vibrato + loops_[1]->setFrequency( 6.122 ); - filters[0] = new FormSwep(); - filters[0]->setTargets( 0.0, 0.7 ); + filters_[0].setTargets( 0.0, 0.7 ); + filters_[1].setTargets( 0.0, 0.7 ); - filters[1] = new FormSwep(); - filters[1]->setTargets( 0.0, 0.7 ); - - adsr->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.5,(MY_FLOAT) 0.6,(MY_FLOAT) 0.250); - filterQ = (MY_FLOAT) 0.85; - filterRate = (MY_FLOAT) 0.0001; - modDepth = (MY_FLOAT) 0.0; + adsr_.setAllTimes( 0.001, 1.5, 0.6, 0.250 ); + filterQ_ = 0.85; + filterRate_ = 0.0001; + modDepth_ = 0.0; } Moog :: ~Moog() { - delete attacks[0]; - delete loops[0]; - delete loops[1]; - delete filters[0]; - delete filters[1]; } -void Moog :: setFrequency(MY_FLOAT frequency) +void Moog :: setFrequency(StkFloat frequency) { - baseFrequency = frequency; + baseFrequency_ = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Moog: setFrequency parameter is less than or equal to zero!" << std::endl; - baseFrequency = 220.0; + errorString_ << "Moog::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); + baseFrequency_ = 220.0; } - MY_FLOAT rate = attacks[0]->getSize() * 0.01 * baseFrequency / sampleRate(); - attacks[0]->setRate( rate ); - loops[0]->setFrequency(baseFrequency); + StkFloat rate = attacks_[0]->getSize() * 0.01 * baseFrequency_ / Stk::sampleRate(); + attacks_[0]->setRate( rate ); + loops_[0]->setFrequency( baseFrequency_ ); } -void Moog :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Moog :: noteOn(StkFloat frequency, StkFloat amplitude) { - MY_FLOAT temp; + StkFloat temp; this->setFrequency( frequency ); this->keyOn(); - attackGain = amplitude * (MY_FLOAT) 0.5; - loopGain = amplitude; + attackGain_ = amplitude * 0.5; + loopGain_ = amplitude; - temp = filterQ + (MY_FLOAT) 0.05; - filters[0]->setStates( 2000.0, temp ); - filters[1]->setStates( 2000.0, temp ); + temp = filterQ_ + 0.05; + filters_[0].setStates( 2000.0, temp ); + filters_[1].setStates( 2000.0, temp ); - temp = filterQ + (MY_FLOAT) 0.099; - filters[0]->setTargets( frequency, temp ); - filters[1]->setTargets( frequency, temp ); + temp = filterQ_ + 0.099; + filters_[0].setTargets( frequency, temp ); + filters_[1].setTargets( frequency, temp ); - filters[0]->setSweepRate( filterRate * 22050.0 / Stk::sampleRate() ); - filters[1]->setSweepRate( filterRate * 22050.0 / Stk::sampleRate() ); + filters_[0].setSweepRate( filterRate_ * 22050.0 / Stk::sampleRate() ); + filters_[1].setSweepRate( filterRate_ * 22050.0 / Stk::sampleRate() ); #if defined(_STK_DEBUG_) - std::cerr << "Moog: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Moog::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Moog :: setModulationSpeed(MY_FLOAT mSpeed) +void Moog :: setModulationSpeed(StkFloat mSpeed) { - loops[1]->setFrequency(mSpeed); + loops_[1]->setFrequency( mSpeed ); } -void Moog :: setModulationDepth(MY_FLOAT mDepth) +void Moog :: setModulationDepth(StkFloat mDepth) { - modDepth = mDepth * (MY_FLOAT) 0.5; + modDepth_ = mDepth * 0.5; } -MY_FLOAT Moog :: tick() +StkFloat Moog :: tick() { - MY_FLOAT temp; + StkFloat temp; - if ( modDepth != 0.0 ) { - temp = loops[1]->tick() * modDepth; - loops[0]->setFrequency( baseFrequency * (1.0 + temp) ); + if ( modDepth_ != 0.0 ) { + temp = loops_[1]->tick() * modDepth_; + loops_[0]->setFrequency( baseFrequency_ * (1.0 + temp) ); } - - temp = Sampler::tick(); - temp = filters[0]->tick( temp ); - lastOutput = filters[1]->tick( temp ); - return lastOutput * 3.0; + + temp = attackGain_ * attacks_[0]->tick(); + temp += loopGain_ * loops_[0]->tick(); + temp = filter_.tick( temp ); + temp *= adsr_.tick(); + temp = filters_[0].tick( temp ); + lastOutput_ = filters_[1].tick( temp ); + return lastOutput_ * 3.0; } -void Moog :: controlChange(int number, MY_FLOAT value) +StkFloat *Moog :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Moog :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Moog :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Moog: Control value less than zero!" << std::endl; + errorString_ << "Moog::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Moog: Control value greater than 128.0!" << std::endl; + errorString_ << "Moog::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_FilterQ_) // 2 - filterQ = 0.80 + ( 0.1 * norm ); + filterQ_ = 0.80 + ( 0.1 * norm ); else if (number == __SK_FilterSweepRate_) // 4 - filterRate = norm * 0.0002; + filterRate_ = norm * 0.0002; else if (number == __SK_ModFrequency_) // 11 this->setModulationSpeed( norm * 12.0 ); else if (number == __SK_ModWheel_) // 1 this->setModulationDepth( norm ); else if (number == __SK_AfterTouch_Cont_) // 128 - adsr->setTarget( norm ); - else - std::cerr << "Moog: Undefined Control Number (" << number << ")!!" << std::endl; + adsr_.setTarget( norm ); + else { + errorString_ << "Moog::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Moog: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Moog::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } - - - diff --git a/src/Mutex.cpp b/src/Mutex.cpp new file mode 100644 index 0000000..0ea57cc --- /dev/null +++ b/src/Mutex.cpp @@ -0,0 +1,100 @@ +/***************************************************/ +/*! \class Mutex + \brief STK mutex class. + + This class provides a uniform interface for + cross-platform mutex use. On Linux and IRIX + systems, the pthread library is used. Under + Windows, critical sections are used. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#include "Mutex.h" + +Mutex :: Mutex() +{ + +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + + pthread_mutex_init(&mutex_, NULL); + pthread_cond_init(&condition_, NULL); + +#elif defined(__OS_WINDOWS__) + + InitializeCriticalSection(&mutex_); + condition_ = CreateEvent(NULL, // no security + true, // manual-reset + false, // non-signaled initially + NULL); // unnamed + +#endif +} + +Mutex :: ~Mutex() +{ +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + + pthread_mutex_destroy(&mutex_); + pthread_cond_destroy(&condition_); + +#elif defined(__OS_WINDOWS__) + + DeleteCriticalSection(&mutex_); + CloseHandle( condition_ ); + +#endif +} + +void Mutex :: lock() +{ +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + + pthread_mutex_lock(&mutex_); + +#elif defined(__OS_WINDOWS__) + + EnterCriticalSection(&mutex_); + +#endif +} + +void Mutex :: unlock() +{ +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + + pthread_mutex_unlock(&mutex_); + +#elif defined(__OS_WINDOWS__) + + LeaveCriticalSection(&mutex_); + +#endif +} + +void Mutex :: wait() +{ +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + + pthread_cond_wait(&condition_, &mutex_); + +#elif defined(__OS_WINDOWS__) + + WaitForMultipleObjects(1, &condition_, false, INFINITE); + +#endif +} + +void Mutex :: signal() +{ +#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + + pthread_cond_signal(&condition_); + +#elif defined(__OS_WINDOWS__) + + SetEvent( condition_ ); + +#endif +} diff --git a/src/NRev.cpp b/src/NRev.cpp index 980c005..09e52ce 100644 --- a/src/NRev.cpp +++ b/src/NRev.cpp @@ -12,14 +12,14 @@ filters in parallel with corresponding right and left outputs. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "NRev.h" #include -NRev :: NRev(MY_FLOAT T60) +NRev :: NRev(StkFloat T60) { int lengths[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19}; double scaler = Stk::sampleRate() / 25641.0; @@ -33,77 +33,94 @@ NRev :: NRev(MY_FLOAT T60) } for (i=0; i<6; i++) { - combDelays[i] = new Delay( lengths[i], lengths[i]); - combCoefficient[i] = pow(10.0, (-3 * lengths[i] / (T60 * Stk::sampleRate()))); + combDelays_[i].setMaximumDelay( lengths[i] ); + combDelays_[i].setDelay( lengths[i] ); + combCoefficient_[i] = pow(10.0, (-3 * lengths[i] / (T60 * Stk::sampleRate()))); } - for (i=0; i<8; i++) - allpassDelays[i] = new Delay(lengths[i+6], lengths[i+6]); + for (i=0; i<8; i++) { + allpassDelays_[i].setMaximumDelay( lengths[i+6] ); + allpassDelays_[i].setDelay( lengths[i+6] ); + } - allpassCoefficient = 0.7; - effectMix = 0.3; + this->setT60( T60 ); + allpassCoefficient_ = 0.7; + effectMix_ = 0.3; this->clear(); } NRev :: ~NRev() { - int i; - for (i=0; i<6; i++) delete combDelays[i]; - for (i=0; i<8; i++) delete allpassDelays[i]; } void NRev :: clear() { int i; - for (i=0; i<6; i++) combDelays[i]->clear(); - for (i=0; i<8; i++) allpassDelays[i]->clear(); - lastOutput[0] = 0.0; - lastOutput[1] = 0.0; - lowpassState = 0.0; + for (i=0; i<6; i++) combDelays_[i].clear(); + for (i=0; i<8; i++) allpassDelays_[i].clear(); + lastOutput_[0] = 0.0; + lastOutput_[1] = 0.0; + lowpassState_ = 0.0; } -MY_FLOAT NRev :: tick(MY_FLOAT input) +void NRev :: setT60( StkFloat T60 ) { - MY_FLOAT temp, temp0, temp1, temp2, temp3; + for ( int i=0; i<6; i++ ) + combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate()))); +} + +StkFloat NRev :: tick(StkFloat input) +{ + StkFloat temp, temp0, temp1, temp2, temp3; int i; temp0 = 0.0; for (i=0; i<6; i++) { - temp = input + (combCoefficient[i] * combDelays[i]->lastOut()); - temp0 += combDelays[i]->tick(temp); + temp = input + (combCoefficient_[i] * combDelays_[i].lastOut()); + temp0 += combDelays_[i].tick(temp); } for (i=0; i<3; i++) { - temp = allpassDelays[i]->lastOut(); - temp1 = allpassCoefficient * temp; + temp = allpassDelays_[i].lastOut(); + temp1 = allpassCoefficient_ * temp; temp1 += temp0; - allpassDelays[i]->tick(temp1); - temp0 = -(allpassCoefficient * temp1) + temp; + allpassDelays_[i].tick(temp1); + temp0 = -(allpassCoefficient_ * temp1) + temp; } // One-pole lowpass filter. - lowpassState = 0.7*lowpassState + 0.3*temp0; - temp = allpassDelays[3]->lastOut(); - temp1 = allpassCoefficient * temp; - temp1 += lowpassState; - allpassDelays[3]->tick(temp1); - temp1 = -(allpassCoefficient * temp1) + temp; + lowpassState_ = 0.7*lowpassState_ + 0.3*temp0; + temp = allpassDelays_[3].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += lowpassState_; + allpassDelays_[3].tick(temp1); + temp1 = -(allpassCoefficient_ * temp1) + temp; - temp = allpassDelays[4]->lastOut(); - temp2 = allpassCoefficient * temp; + temp = allpassDelays_[4].lastOut(); + temp2 = allpassCoefficient_ * temp; temp2 += temp1; - allpassDelays[4]->tick(temp2); - lastOutput[0] = effectMix*(-(allpassCoefficient * temp2) + temp); + allpassDelays_[4].tick(temp2); + lastOutput_[0] = effectMix_*(-(allpassCoefficient_ * temp2) + temp); - temp = allpassDelays[5]->lastOut(); - temp3 = allpassCoefficient * temp; + temp = allpassDelays_[5].lastOut(); + temp3 = allpassCoefficient_ * temp; temp3 += temp1; - allpassDelays[5]->tick(temp3); - lastOutput[1] = effectMix*(-(allpassCoefficient * temp3) + temp); + allpassDelays_[5].tick(temp3); + lastOutput_[1] = effectMix_*(-(allpassCoefficient_ * temp3) + temp); - temp = (1.0 - effectMix) * input; - lastOutput[0] += temp; - lastOutput[1] += temp; + temp = (1.0 - effectMix_) * input; + lastOutput_[0] += temp; + lastOutput_[1] += temp; - return (lastOutput[0] + lastOutput[1]) * 0.5; + return Effect::lastOut(); } + +StkFloat *NRev :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Effect::tick( vector, vectorSize ); +} + +StkFrames& NRev :: tick( StkFrames& frames, unsigned int channel ) +{ + return Effect::tick( frames, channel ); +} diff --git a/src/Noise.cpp b/src/Noise.cpp index 197eeea..ed31026 100644 --- a/src/Noise.cpp +++ b/src/Noise.cpp @@ -6,7 +6,7 @@ C rand() function. The quality of the rand() function varies from one OS to another. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -14,18 +14,18 @@ #include #include -Noise :: Noise() : Stk() +Noise :: Noise() : Generator() { // Seed the random number generator with system time. this->setSeed( 0 ); - lastOutput = (MY_FLOAT) 0.0; + lastOutput_ = (StkFloat) 0.0; } -Noise :: Noise( unsigned int seed ) : Stk() +Noise :: Noise( unsigned int seed ) : Generator() { // Seed the random number generator this->setSeed( seed ); - lastOutput = (MY_FLOAT) 0.0; + lastOutput_ = (StkFloat) 0.0; } Noise :: ~Noise() @@ -40,23 +40,20 @@ void Noise :: setSeed( unsigned int seed ) srand( seed ); } -MY_FLOAT Noise :: tick() +StkFloat Noise :: tick() { - lastOutput = (MY_FLOAT) (2.0 * rand() / (RAND_MAX + 1.0) ); - lastOutput -= 1.0; - return lastOutput; + lastOutput_ = (StkFloat) (2.0 * rand() / (RAND_MAX + 1.0) ); + lastOutput_ -= 1.0; + return lastOutput_; } -MY_FLOAT *Noise :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Noise :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i b(1, 0.1); + std::vector a(2, 1.0); + a[1] = -0.9; + Filter::setCoefficients( b, a ); } -OnePole :: OnePole(MY_FLOAT thePole) : Filter() +OnePole :: OnePole(StkFloat thePole) : Filter() { - MY_FLOAT B; - MY_FLOAT A[2] = {1.0, -0.9}; + std::vector b(1); + std::vector a(2, 1.0); + a[1] = -thePole; // Normalize coefficients for peak unity gain. if (thePole > 0.0) - B = (MY_FLOAT) (1.0 - thePole); + b[0] = (StkFloat) (1.0 - thePole); else - B = (MY_FLOAT) (1.0 + thePole); + b[0] = (StkFloat) (1.0 + thePole); - A[1] = -thePole; - Filter::setCoefficients( 1, &B, 2, A ); + Filter::setCoefficients( b, a ); } OnePole :: ~OnePole() @@ -45,55 +46,57 @@ void OnePole :: clear(void) Filter::clear(); } -void OnePole :: setB0(MY_FLOAT b0) +void OnePole :: setB0(StkFloat b0) { - b[0] = b0; + b_[0] = b0; } -void OnePole :: setA1(MY_FLOAT a1) +void OnePole :: setA1(StkFloat a1) { - a[1] = a1; + a_[1] = a1; } -void OnePole :: setPole(MY_FLOAT thePole) +void OnePole :: setPole(StkFloat thePole) { // Normalize coefficients for peak unity gain. if (thePole > 0.0) - b[0] = (MY_FLOAT) (1.0 - thePole); + b_[0] = (StkFloat) (1.0 - thePole); else - b[0] = (MY_FLOAT) (1.0 + thePole); + b_[0] = (StkFloat) (1.0 + thePole); - a[1] = -thePole; + a_[1] = -thePole; } -void OnePole :: setGain(MY_FLOAT theGain) +void OnePole :: setGain(StkFloat gain) { - Filter::setGain(theGain); + Filter::setGain(gain); } -MY_FLOAT OnePole :: getGain(void) const +StkFloat OnePole :: getGain(void) const { return Filter::getGain(); } -MY_FLOAT OnePole :: lastOut(void) const +StkFloat OnePole :: lastOut(void) const { return Filter::lastOut(); } -MY_FLOAT OnePole :: tick(MY_FLOAT sample) +StkFloat OnePole :: tick(StkFloat sample) { - inputs[0] = gain * sample; - outputs[0] = b[0] * inputs[0] - a[1] * outputs[1]; - outputs[1] = outputs[0]; + inputs_[0] = gain_ * sample; + outputs_[0] = b_[0] * inputs_[0] - a_[1] * outputs_[1]; + outputs_[1] = outputs_[0]; - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *OnePole :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *OnePole :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i b(2, 0.5); + std::vector a(1, 1.0); + Filter::setCoefficients( b, a ); } -OneZero :: OneZero(MY_FLOAT theZero) : Filter() +OneZero :: OneZero(StkFloat theZero) : Filter() { - MY_FLOAT B[2]; - MY_FLOAT A = 1.0; + std::vector b(2); + std::vector a(1, 1.0); // Normalize coefficients for unity gain. if (theZero > 0.0) - B[0] = 1.0 / ((MY_FLOAT) 1.0 + theZero); + b[0] = 1.0 / ((StkFloat) 1.0 + theZero); else - B[0] = 1.0 / ((MY_FLOAT) 1.0 - theZero); + b[0] = 1.0 / ((StkFloat) 1.0 - theZero); - B[1] = -theZero * B[0]; - Filter::setCoefficients( 2, B, 1, &A ); + b[1] = -theZero * b[0]; + Filter::setCoefficients( b, a ); } OneZero :: ~OneZero(void) @@ -45,55 +45,57 @@ void OneZero :: clear(void) Filter::clear(); } -void OneZero :: setB0(MY_FLOAT b0) +void OneZero :: setB0(StkFloat b0) { - b[0] = b0; + b_[0] = b0; } -void OneZero :: setB1(MY_FLOAT b1) +void OneZero :: setB1(StkFloat b1) { - b[1] = b1; + b_[1] = b1; } -void OneZero :: setZero(MY_FLOAT theZero) +void OneZero :: setZero(StkFloat theZero) { // Normalize coefficients for unity gain. if (theZero > 0.0) - b[0] = 1.0 / ((MY_FLOAT) 1.0 + theZero); + b_[0] = 1.0 / ((StkFloat) 1.0 + theZero); else - b[0] = 1.0 / ((MY_FLOAT) 1.0 - theZero); + b_[0] = 1.0 / ((StkFloat) 1.0 - theZero); - b[1] = -theZero * b[0]; + b_[1] = -theZero * b_[0]; } -void OneZero :: setGain(MY_FLOAT theGain) +void OneZero :: setGain(StkFloat gain) { - Filter::setGain(theGain); + Filter::setGain(gain); } -MY_FLOAT OneZero :: getGain(void) const +StkFloat OneZero :: getGain(void) const { return Filter::getGain(); } -MY_FLOAT OneZero :: lastOut(void) const +StkFloat OneZero :: lastOut(void) const { return Filter::lastOut(); } -MY_FLOAT OneZero :: tick(MY_FLOAT sample) +StkFloat OneZero :: tick(StkFloat sample) { - inputs[0] = gain * sample; - outputs[0] = b[1] * inputs[1] + b[0] * inputs[0]; - inputs[1] = inputs[0]; + inputs_[0] = gain_ * sample; + outputs_[0] = b_[1] * inputs_[1] + b_[0] * inputs_[0]; + inputs_[1] = inputs_[0]; - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *OneZero :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *OneZero :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i -PRCRev :: PRCRev(MY_FLOAT T60) +PRCRev :: PRCRev(StkFloat T60) { // Delay lengths for 44100 Hz sample rate. int lengths[4]= {353, 1097, 1777, 2137}; @@ -35,59 +35,74 @@ 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.0,(-3 * lengths[i+2] / (T60 * Stk::sampleRate()))); + allpassDelays_[i].setMaximumDelay( lengths[i] ); + allpassDelays_[i].setDelay( lengths[i] ); + + combDelays_[i].setMaximumDelay( lengths[i+2] ); + combDelays_[i].setDelay( lengths[i+2] ); } - allpassCoefficient = 0.7; - effectMix = 0.5; + this->setT60( T60 ); + allpassCoefficient_ = 0.7; + effectMix_ = 0.5; this->clear(); } PRCRev :: ~PRCRev() { - delete allpassDelays[0]; - delete allpassDelays[1]; - delete combDelays[0]; - delete combDelays[1]; } void PRCRev :: clear() { - allpassDelays[0]->clear(); - allpassDelays[1]->clear(); - combDelays[0]->clear(); - combDelays[1]->clear(); - lastOutput[0] = 0.0; - lastOutput[1] = 0.0; + allpassDelays_[0].clear(); + allpassDelays_[1].clear(); + combDelays_[0].clear(); + combDelays_[1].clear(); + lastOutput_[0] = 0.0; + lastOutput_[1] = 0.0; } -MY_FLOAT PRCRev :: tick(MY_FLOAT input) +void PRCRev :: setT60( StkFloat T60 ) { - MY_FLOAT temp, temp0, temp1, temp2, temp3; + combCoefficient_[0] = pow(10.0, (-3.0 * combDelays_[0].getDelay() / (T60 * Stk::sampleRate()))); + combCoefficient_[1] = pow(10.0, (-3.0 * combDelays_[1].getDelay() / (T60 * Stk::sampleRate()))); +} - temp = allpassDelays[0]->lastOut(); - temp0 = allpassCoefficient * temp; +StkFloat PRCRev :: tick(StkFloat input) +{ + StkFloat temp, temp0, temp1, temp2, temp3; + + temp = allpassDelays_[0].lastOut(); + temp0 = allpassCoefficient_ * temp; temp0 += input; - allpassDelays[0]->tick(temp0); - temp0 = -(allpassCoefficient * temp0) + temp; + allpassDelays_[0].tick(temp0); + temp0 = -(allpassCoefficient_ * temp0) + temp; - temp = allpassDelays[1]->lastOut(); - temp1 = allpassCoefficient * temp; + temp = allpassDelays_[1].lastOut(); + temp1 = allpassCoefficient_ * temp; temp1 += temp0; - allpassDelays[1]->tick(temp1); - temp1 = -(allpassCoefficient * temp1) + temp; + allpassDelays_[1].tick(temp1); + temp1 = -(allpassCoefficient_ * temp1) + temp; - temp2 = temp1 + (combCoefficient[0] * combDelays[0]->lastOut()); - temp3 = temp1 + (combCoefficient[1] * combDelays[1]->lastOut()); + temp2 = temp1 + (combCoefficient_[0] * combDelays_[0].lastOut()); + temp3 = temp1 + (combCoefficient_[1] * combDelays_[1].lastOut()); - lastOutput[0] = effectMix * (combDelays[0]->tick(temp2)); - lastOutput[1] = effectMix * (combDelays[1]->tick(temp3)); - temp = (MY_FLOAT) (1.0 - effectMix) * input; - lastOutput[0] += temp; - lastOutput[1] += temp; + lastOutput_[0] = effectMix_ * (combDelays_[0].tick(temp2)); + lastOutput_[1] = effectMix_ * (combDelays_[1].tick(temp3)); + temp = (1.0 - effectMix_) * input; + lastOutput_[0] += temp; + lastOutput_[1] += temp; - return (lastOutput[0] + lastOutput[1]) * (MY_FLOAT) 0.5; + return Effect::lastOut(); } + +StkFloat *PRCRev :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Effect::tick( vector, vectorSize ); +} + +StkFrames& PRCRev :: tick( StkFrames& frames, unsigned int channel ) +{ + return Effect::tick( frames, channel ); +} diff --git a/src/PercFlut.cpp b/src/PercFlut.cpp index 4612d2f..943e976 100644 --- a/src/PercFlut.cpp +++ b/src/PercFlut.cpp @@ -22,7 +22,7 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -32,74 +32,85 @@ PercFlut :: PercFlut() : FM() { // 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 ); + for ( unsigned 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); this->setRatio(2, 2.99 * 1.005); this->setRatio(3, 6.00 * 0.997); - gains[0] = __FM_gains[99]; - gains[1] = __FM_gains[71]; - gains[2] = __FM_gains[93]; - gains[3] = __FM_gains[85]; + gains_[0] = fmGains_[99]; + gains_[1] = fmGains_[71]; + gains_[2] = fmGains_[93]; + gains_[3] = fmGains_[85]; - adsr[0]->setAllTimes( 0.05, 0.05, __FM_susLevels[14], 0.05); - adsr[1]->setAllTimes( 0.02, 0.50, __FM_susLevels[13], 0.5); - adsr[2]->setAllTimes( 0.02, 0.30, __FM_susLevels[11], 0.05); - adsr[3]->setAllTimes( 0.02, 0.05, __FM_susLevels[13], 0.01); + adsr_[0]->setAllTimes( 0.05, 0.05, fmSusLevels_[14], 0.05); + adsr_[1]->setAllTimes( 0.02, 0.50, fmSusLevels_[13], 0.5); + adsr_[2]->setAllTimes( 0.02, 0.30, fmSusLevels_[11], 0.05); + adsr_[3]->setAllTimes( 0.02, 0.05, fmSusLevels_[13], 0.01); - twozero->setGain( 0.0 ); - modDepth = 0.005; + twozero_.setGain( 0.0 ); + modDepth_ = 0.005; } PercFlut :: ~PercFlut() { } -void PercFlut :: setFrequency(MY_FLOAT frequency) +void PercFlut :: setFrequency(StkFloat frequency) { - baseFrequency = frequency; + baseFrequency_ = frequency; } -void PercFlut :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void PercFlut :: noteOn(StkFloat frequency, StkFloat amplitude) { - gains[0] = amplitude * __FM_gains[99] * 0.5; - gains[1] = amplitude * __FM_gains[71] * 0.5; - gains[2] = amplitude * __FM_gains[93] * 0.5; - gains[3] = amplitude * __FM_gains[85] * 0.5; - this->setFrequency(frequency); + gains_[0] = amplitude * fmGains_[99] * 0.5; + gains_[1] = amplitude * fmGains_[71] * 0.5; + gains_[2] = amplitude * fmGains_[93] * 0.5; + gains_[3] = amplitude * fmGains_[85] * 0.5; + this->setFrequency( frequency ); this->keyOn(); #if defined(_STK_DEBUG_) - cerr << "PercFlut: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + errorString_ << "PercFlut::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT PercFlut :: tick() +StkFloat PercFlut :: tick() { - register MY_FLOAT temp; + register StkFloat temp; - temp = vibrato->tick() * modDepth * (MY_FLOAT) 0.2; - waves[0]->setFrequency(baseFrequency * ((MY_FLOAT) 1.0 + temp) * ratios[0]); - waves[1]->setFrequency(baseFrequency * ((MY_FLOAT) 1.0 + temp) * ratios[1]); - waves[2]->setFrequency(baseFrequency * ((MY_FLOAT) 1.0 + temp) * ratios[2]); - waves[3]->setFrequency(baseFrequency * ((MY_FLOAT) 1.0 + temp) * ratios[3]); + temp = vibrato_->tick() * modDepth_ * 0.2; + waves_[0]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[0]); + waves_[1]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[1]); + waves_[2]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[2]); + waves_[3]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[3]); - waves[3]->addPhaseOffset(twozero->lastOut()); - temp = gains[3] * adsr[3]->tick() * waves[3]->tick(); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); - twozero->tick(temp); - waves[2]->addPhaseOffset(temp); - temp = (1.0 - (control2 * 0.5)) * gains[2] * adsr[2]->tick() * waves[2]->tick(); + twozero_.tick(temp); + waves_[2]->addPhaseOffset( temp ); + temp = (1.0 - (control2_ * 0.5)) * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); - temp += control2 * 0.5 * gains[1] * adsr[1]->tick() * waves[1]->tick(); - temp = temp * control1; + temp += control2_ * 0.5 * gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; - waves[0]->addPhaseOffset(temp); - temp = gains[0] * adsr[0]->tick() * waves[0]->tick(); + waves_[0]->addPhaseOffset(temp); + temp = gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); - lastOutput = temp * (MY_FLOAT) 0.5; - return lastOutput; + lastOutput_ = temp * 0.5; + return lastOutput_; +} + +StkFloat *PercFlut :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& PercFlut :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/Phonemes.cpp b/src/Phonemes.cpp index 45f0c18..78d2f40 100644 --- a/src/Phonemes.cpp +++ b/src/Phonemes.cpp @@ -6,7 +6,7 @@ set of 32 static phoneme formant parameters and provide access to those values. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -24,7 +24,7 @@ const char Phonemes :: phonemeNames[32][4] = "vvv", "zzz", "thz", "zhh" }; -const MY_FLOAT Phonemes :: phonemeGains[32][2] = +const StkFloat Phonemes :: phonemeGains[32][2] = {{1.0, 0.0}, // eee {1.0, 0.0}, // ihh {1.0, 0.0}, // ehh @@ -66,7 +66,7 @@ const MY_FLOAT Phonemes :: phonemeGains[32][2] = {1.0, 1.0} // zhh }; -const MY_FLOAT Phonemes :: phonemeParameters[32][4][3] = +const StkFloat Phonemes :: phonemeParameters[32][4][3] = {{ { 273, 0.996, 10}, // eee (beet) {2086, 0.945, -16}, {2754, 0.979, -12}, @@ -215,64 +215,79 @@ Phonemes :: ~Phonemes(void) const char *Phonemes :: name( unsigned int index ) { if ( index > 31 ) { - std::cerr << "Phonemes: name index is greater than 31!" << std::endl; + std::ostringstream error; + error << "Phonemes::name: index is greater than 31!"; + handleError( error.str(), StkError::WARNING ); return 0; } return phonemeNames[index]; } -MY_FLOAT Phonemes :: voiceGain( unsigned int index ) +StkFloat Phonemes :: voiceGain( unsigned int index ) { if ( index > 31 ) { - std::cerr << "Phonemes: voiceGain index is greater than 31!" << std::endl; + std::ostringstream error; + error << "Phonemes::voiceGain: index is greater than 31!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } return phonemeGains[index][0]; } -MY_FLOAT Phonemes :: noiseGain( unsigned int index ) +StkFloat Phonemes :: noiseGain( unsigned int index ) { if ( index > 31 ) { - std::cerr << "Phonemes: noiseGain index is greater than 31!" << std::endl; + std::ostringstream error; + error << "Phonemes::noiseGain: index is greater than 31!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } return phonemeGains[index][1]; } -MY_FLOAT Phonemes :: formantFrequency( unsigned int index, unsigned int partial ) +StkFloat Phonemes :: formantFrequency( unsigned int index, unsigned int partial ) { + std::ostringstream error; if ( index > 31 ) { - std::cerr << "Phonemes: formantFrequency index is greater than 31!" << std::endl; + error << "Phonemes::formantFrequency: index is greater than 31!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } if ( partial > 3 ) { - std::cerr << "Phonemes: formantFrequency partial is greater than 3!" << std::endl; + error << "Phonemes::formantFrequency: partial is greater than 3!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } return phonemeParameters[index][partial][0]; } -MY_FLOAT Phonemes :: formantRadius( unsigned int index, unsigned int partial ) +StkFloat Phonemes :: formantRadius( unsigned int index, unsigned int partial ) { + std::ostringstream error; if ( index > 31 ) { - std::cerr << "Phonemes: formantRadius index is greater than 31!" << std::endl; + error << "Phonemes::formantRadius: index is greater than 31!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } if ( partial > 3 ) { - std::cerr << "Phonemes: formantRadius partial is greater than 3!" << std::endl; + error << "Phonemes::formantRadius: partial is greater than 3!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } return phonemeParameters[index][partial][1]; } -MY_FLOAT Phonemes :: formantGain( unsigned int index, unsigned int partial ) +StkFloat Phonemes :: formantGain( unsigned int index, unsigned int partial ) { + std::ostringstream error; if ( index > 31 ) { - std::cerr << "Phonemes: formantGain index is greater than 31!" << std::endl; + error << "Phonemes::formantGain: index is greater than 31!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } if ( partial > 3 ) { - std::cerr << "Phonemes: formantGain partial is greater than 3!" << std::endl; + error << "Phonemes::formantGain: partial is greater than 3!"; + handleError( error.str(), StkError::WARNING ); return 0.0; } return phonemeParameters[index][partial][2]; diff --git a/src/PitShift.cpp b/src/PitShift.cpp index 44ba244..16614fc 100644 --- a/src/PitShift.cpp +++ b/src/PitShift.cpp @@ -5,93 +5,93 @@ This class implements a simple pitch shifter using delay lines. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "PitShift.h" -#include #include +const int maxDelay = 5024; + PitShift :: PitShift() { - delay[0] = 12; - delay[1] = 512; - delayLine[0] = new DelayL(delay[0], (long) 1024); - delayLine[1] = new DelayL(delay[1], (long) 1024); - effectMix = (MY_FLOAT) 0.5; - rate = 1.0; + delayLength = maxDelay - 24; + halfLength = delayLength / 2; + delay_[0] = 12; + delay_[1] = maxDelay / 2; + + delayLine_[0].setMaximumDelay( maxDelay ); + delayLine_[0].setDelay( delay_[0] ); + delayLine_[1].setMaximumDelay( maxDelay ); + delayLine_[1].setDelay( delay_[1] ); + effectMix_ = 0.5; + rate_ = 1.0; } PitShift :: ~PitShift() { - delete delayLine[0]; - delete delayLine[1]; } void PitShift :: clear() { - delayLine[0]->clear(); - delayLine[1]->clear(); - lastOutput = 0.0; + delayLine_[0].clear(); + delayLine_[1].clear(); + lastOutput_[0] = 0.0; + lastOutput_[1] = 0.0; } -void PitShift :: setEffectMix(MY_FLOAT mix) +void PitShift :: setShift(StkFloat shift) { - effectMix = mix; - if ( mix < 0.0 ) { - std::cerr << "PitShift: setEffectMix parameter is less than zero!" << std::endl; - effectMix = 0.0; + if (shift < 1.0) { + rate_ = 1.0 - shift; } - else if ( mix > 1.0 ) { - std::cerr << "PitShift: setEffectMix parameter is greater than 1.0!" << std::endl; - effectMix = 1.0; - } -} - -void PitShift :: setShift(MY_FLOAT shift) -{ - if (shift < 1.0) { - rate = 1.0 - shift; - } - else if (shift > 1.0) { - rate = 1.0 - shift; + else if (shift > 1.0) { + rate_ = 1.0 - shift; } else { - rate = 0.0; - delay[0] = 512; + rate_ = 0.0; + delay_[0] = halfLength+12; } } -MY_FLOAT PitShift :: lastOut() const +StkFloat PitShift :: tick(StkFloat input) { - return lastOutput; + // Calculate the two delay length values, keeping them within the + // range 12 to maxDelay-12. + delay_[0] += rate_; + while (delay_[0] > maxDelay-12) delay_[0] -= delayLength; + while (delay_[0] < 12) delay_[0] += delayLength; + + delay_[1] = delay_[0] + halfLength; + while (delay_[1] > maxDelay-12) delay_[1] -= delayLength; + while (delay_[1] < 12) delay_[1] += delayLength; + + // Set the new delay line lengths. + delayLine_[0].setDelay((long)delay_[0]); + delayLine_[1].setDelay((long)delay_[1]); + + // Calculate a triangular envelope. + env_[1] = fabs( (delay_[0] - halfLength + 12) * (1.0 / (halfLength+12) ) ); + env_[0] = 1.0 - env_[1]; + + // Delay input and apply envelope. + lastOutput_[0] = env_[0] * delayLine_[0].tick(input); + lastOutput_[0] += env_[1] * delayLine_[1].tick(input); + + // Compute effect mix and output. + lastOutput_[0] *= effectMix_; + lastOutput_[0] += (1.0 - effectMix_) * input; + lastOutput_[1] = lastOutput_[0]; + return lastOutput_[0]; } -MY_FLOAT PitShift :: tick(MY_FLOAT input) +StkFloat *PitShift :: tick(StkFloat *vector, unsigned int vectorSize) { - delay[0] = delay[0] + rate; - while (delay[0] > 1012) delay[0] -= 1000; - while (delay[0] < 12) delay[0] += 1000; - delay[1] = delay[0] + 500; - while (delay[1] > 1012) delay[1] -= 1000; - while (delay[1] < 12) delay[1] += 1000; - delayLine[0]->setDelay((long)delay[0]); - delayLine[1]->setDelay((long)delay[1]); - env[1] = fabs(delay[0] - 512) * 0.002; - env[0] = 1.0 - env[1]; - lastOutput = env[0] * delayLine[0]->tick(input); - lastOutput += env[1] * delayLine[1]->tick(input); - lastOutput *= effectMix; - lastOutput += (1.0 - effectMix) * input; - return lastOutput; + return Effect::tick( vector, vectorSize ); } -MY_FLOAT *PitShift :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFrames& PitShift :: tick( StkFrames& frames, unsigned int channel ) { - for (unsigned int i=0; iclear(); - delayLine2->clear(); - combDelay->clear(); - filter->clear(); - filter2->clear(); + delayLine_.clear(); + delayLine2_.clear(); + combDelay_.clear(); + filter_.clear(); + filter2_.clear(); } -void PluckTwo :: setFrequency(MY_FLOAT frequency) +void PluckTwo :: setFrequency(StkFloat frequency) { - lastFrequency = frequency; - if ( lastFrequency <= 0.0 ) { - std::cerr << "PluckTwo: setFrequency parameter less than or equal to zero!" << std::endl; - lastFrequency = 220.0; + lastFrequency_ = frequency; + if ( lastFrequency_ <= 0.0 ) { + errorString_ << "Clarinet::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); + lastFrequency_ = 220.0; } // Delay = length - approximate filter delay. - lastLength = ( Stk::sampleRate() / lastFrequency); - MY_FLOAT delay = (lastLength / detuning) - (MY_FLOAT) 0.5; + lastLength_ = Stk::sampleRate() / lastFrequency_; + StkFloat delay = (lastLength_ / detuning_) - 0.5; if ( delay <= 0.0 ) delay = 0.3; - else if ( delay > length ) delay = length; - delayLine->setDelay( delay ); + else if ( delay > length_ ) delay = length_; + delayLine_.setDelay( delay ); - delay = (lastLength * detuning) - (MY_FLOAT) 0.5; + delay = (lastLength_ * detuning_) - 0.5; if ( delay <= 0.0 ) delay = 0.3; - else if ( delay > length ) delay = length; - delayLine2->setDelay( delay ); + else if ( delay > length_ ) delay = length_; + delayLine2_.setDelay( delay ); - loopGain = baseLoopGain + (frequency * (MY_FLOAT) 0.000005); - if ( loopGain > 1.0 ) loopGain = (MY_FLOAT) 0.99999; + loopGain_ = baseLoopGain_ + (frequency * 0.000005); + if ( loopGain_ > 1.0 ) loopGain_ = 0.99999; } -void PluckTwo :: setDetune(MY_FLOAT detune) +void PluckTwo :: setDetune(StkFloat detune) { - detuning = detune; - if ( detuning <= 0.0 ) { - std::cerr << "PluckTwo: setDetune parameter less than or equal to zero!" << std::endl; - detuning = 0.1; + detuning_ = detune; + if ( detuning_ <= 0.0 ) { + errorString_ << "Clarinet::setDeturn: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); + detuning_ = 0.1; } - delayLine->setDelay(( lastLength / detuning) - (MY_FLOAT) 0.5); - delayLine2->setDelay( (lastLength * detuning) - (MY_FLOAT) 0.5); + delayLine_.setDelay(( lastLength_ / detuning_) - 0.5); + delayLine2_.setDelay( (lastLength_ * detuning_) - 0.5); } -void PluckTwo :: setFreqAndDetune(MY_FLOAT frequency, MY_FLOAT detune) +void PluckTwo :: setFreqAndDetune(StkFloat frequency, StkFloat detune) { - detuning = detune; - this->setFrequency(frequency); + detuning_ = detune; + this->setFrequency( frequency ); } -void PluckTwo :: setPluckPosition(MY_FLOAT position) +void PluckTwo :: setPluckPosition(StkFloat position) { - pluckPosition = position; + pluckPosition_ = position; if ( position < 0.0 ) { - std::cerr << "PluckTwo: setPluckPosition parameter is less than zero!" << std::endl; - pluckPosition = 0.0; + errorString_ << "PluckTwo::setPluckPosition: parameter is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + pluckPosition_ = 0.0; } else if ( position > 1.0 ) { - std::cerr << "PluckTwo: setPluckPosition parameter is greater than 1.0!" << std::endl; - pluckPosition = 1.0; + errorString_ << "PluckTwo::setPluckPosition: parameter is greater than one ... setting to 1.0!"; + handleError( StkError::WARNING ); + pluckPosition_ = 1.0; } } -void PluckTwo :: setBaseLoopGain(MY_FLOAT aGain) +void PluckTwo :: setBaseLoopGain(StkFloat aGain) { - baseLoopGain = aGain; - loopGain = baseLoopGain + (lastFrequency * (MY_FLOAT) 0.000005); - if ( loopGain > 0.99999 ) loopGain = (MY_FLOAT) 0.99999; + baseLoopGain_ = aGain; + loopGain_ = baseLoopGain_ + (lastFrequency_ * 0.000005); + if ( loopGain_ > 0.99999 ) loopGain_ = 0.99999; } -void PluckTwo :: noteOff(MY_FLOAT amplitude) +void PluckTwo :: noteOff(StkFloat amplitude) { - loopGain = ((MY_FLOAT) 1.0 - amplitude) * (MY_FLOAT) 0.5; + loopGain_ = (1.0 - amplitude) * 0.5; #if defined(_STK_DEBUG_) - std::cerr << "PluckTwo: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "PluckTwo::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Plucked.cpp b/src/Plucked.cpp index 649e6d3..23ab2fb 100644 --- a/src/Plucked.cpp +++ b/src/Plucked.cpp @@ -13,105 +13,119 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Plucked.h" -Plucked :: Plucked(MY_FLOAT lowestFrequency) +Plucked :: Plucked(StkFloat lowestFrequency) { - length = (long) (Stk::sampleRate() / lowestFrequency + 1); - loopGain = (MY_FLOAT) 0.999; - delayLine = new DelayA( (MY_FLOAT)(length / 2.0), length ); - loopFilter = new OneZero; - pickFilter = new OnePole; - noise = new Noise; + length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + loopGain_ = 0.999; + delayLine_.setMaximumDelay( length_ ); + delayLine_.setDelay( 0.5 * length_ ); this->clear(); } Plucked :: ~Plucked() { - delete delayLine; - delete loopFilter; - delete pickFilter; - delete noise; } void Plucked :: clear() { - delayLine->clear(); - loopFilter->clear(); - pickFilter->clear(); + delayLine_.clear(); + loopFilter_.clear(); + pickFilter_.clear(); } -void Plucked :: setFrequency(MY_FLOAT frequency) +void Plucked :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Plucked: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Plucked::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } // Delay = length - approximate filter delay. - MY_FLOAT delay = (Stk::sampleRate() / freakency) - (MY_FLOAT) 0.5; - if (delay <= 0.0) delay = 0.3; - else if (delay > length) delay = length; - delayLine->setDelay(delay); - loopGain = 0.995 + (freakency * 0.000005); - if ( loopGain >= 1.0 ) loopGain = (MY_FLOAT) 0.99999; + StkFloat delay = (Stk::sampleRate() / freakency) - 0.5; + if ( delay <= 0.0 ) + delay = 0.3; + else if ( delay > length_ ) + delay = length_; + delayLine_.setDelay( delay ); + + loopGain_ = 0.995 + (freakency * 0.000005); + if ( loopGain_ >= 1.0 ) loopGain_ = 0.99999; } -void Plucked :: pluck(MY_FLOAT amplitude) +void Plucked :: pluck(StkFloat amplitude) { - MY_FLOAT gain = amplitude; + StkFloat gain = amplitude; if ( gain > 1.0 ) { - std::cerr << "Plucked: pluck amplitude greater than 1.0!" << std::endl; + errorString_ << "Plucked::pluck: amplitude is greater than 1.0 ... setting to 1.0!"; + handleError( StkError::WARNING ); gain = 1.0; } else if ( gain < 0.0 ) { - std::cerr << "Plucked: pluck amplitude less than zero!" << std::endl; + errorString_ << "Plucked::pluck: amplitude is < 0.0 ... setting to 0.0!"; + handleError( StkError::WARNING ); gain = 0.0; } - pickFilter->setPole((MY_FLOAT) 0.999 - (gain * (MY_FLOAT) 0.15)); - pickFilter->setGain(gain * (MY_FLOAT) 0.5); - for (long i=0; itick( 0.6 * delayLine->lastOut() + pickFilter->tick( noise->tick() ) ); + delayLine_.tick( 0.6 * delayLine_.lastOut() + pickFilter_.tick( noise_.tick() ) ); } -void Plucked :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Plucked :: noteOn(StkFloat frequency, StkFloat amplitude) { - this->setFrequency(frequency); - this->pluck(amplitude); + this->setFrequency( frequency ); + this->pluck( amplitude ); #if defined(_STK_DEBUG_) - std::cerr << "Plucked: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Plucked::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Plucked :: noteOff(MY_FLOAT amplitude) +void Plucked :: noteOff(StkFloat amplitude) { - loopGain = (MY_FLOAT) 1.0 - amplitude; - if ( loopGain < 0.0 ) { - std::cerr << "Plucked: noteOff amplitude greater than 1.0!" << std::endl; - loopGain = 0.0; + loopGain_ = 1.0 - amplitude; + if ( loopGain_ < 0.0 ) { + errorString_ << "Plucked::noteOff: amplitude is greater than 1.0 ... setting to 1.0!"; + handleError( StkError::WARNING ); + loopGain_ = 0.0; } - else if ( loopGain > 1.0 ) { - std::cerr << "Plucked: noteOff amplitude less than or zero!" << std::endl; - loopGain = (MY_FLOAT) 0.99999; + else if ( loopGain_ > 1.0 ) { + errorString_ << "Plucked::noteOff: amplitude is < 0.0 ... setting to 0.0!"; + handleError( StkError::WARNING ); + loopGain_ = (StkFloat) 0.99999; } #if defined(_STK_DEBUG_) - std::cerr << "Plucked: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Plucked::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Plucked :: tick() +StkFloat Plucked :: tick() { // Here's the whole inner loop of the instrument!! - lastOutput = delayLine->tick( loopFilter->tick( delayLine->lastOut() * loopGain ) ); - lastOutput *= (MY_FLOAT) 3.0; - return lastOutput; + lastOutput_ = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) ); + lastOutput_ *= 3.0; + return lastOutput_; +} + +StkFloat *Plucked :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Plucked :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/PoleZero.cpp b/src/PoleZero.cpp index 5410b5f..0b6fbad 100644 --- a/src/PoleZero.cpp +++ b/src/PoleZero.cpp @@ -8,7 +8,7 @@ filter with a given coefficient. Another method is provided to create a DC blocking filter. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -17,9 +17,11 @@ PoleZero :: PoleZero() : Filter() { // Default setting for pass-through. - MY_FLOAT B[2] = {1.0, 0.0}; - MY_FLOAT A[2] = {1.0, 0.0}; - Filter::setCoefficients( 2, B, 2, A ); + std::vector b(2, 0.0); + std::vector a(2, 0.0); + b[0] = 1.0; + a[0] = 1.0; + Filter::setCoefficients( b, a ); } PoleZero :: ~PoleZero() @@ -31,67 +33,68 @@ void PoleZero :: clear(void) Filter::clear(); } -void PoleZero :: setB0(MY_FLOAT b0) +void PoleZero :: setB0(StkFloat b0) { - b[0] = b0; + b_[0] = b0; } -void PoleZero :: setB1(MY_FLOAT b1) +void PoleZero :: setB1(StkFloat b1) { - b[1] = b1; + b_[1] = b1; } -void PoleZero :: setA1(MY_FLOAT a1) +void PoleZero :: setA1(StkFloat a1) { - a[1] = a1; + a_[1] = a1; } -void PoleZero :: setAllpass(MY_FLOAT coefficient) +void PoleZero :: setAllpass(StkFloat coefficient) { - b[0] = coefficient; - b[1] = 1.0; - a[0] = 1.0; // just in case - a[1] = coefficient; + b_[0] = coefficient; + b_[1] = 1.0; + a_[0] = 1.0; // just in case + a_[1] = coefficient; } -void PoleZero :: setBlockZero(MY_FLOAT thePole) +void PoleZero :: setBlockZero(StkFloat thePole) { - b[0] = 1.0; - b[1] = -1.0; - a[0] = 1.0; // just in case - a[1] = -thePole; + b_[0] = 1.0; + b_[1] = -1.0; + a_[0] = 1.0; // just in case + a_[1] = -thePole; } -void PoleZero :: setGain(MY_FLOAT theGain) +void PoleZero :: setGain(StkFloat gain) { - Filter::setGain(theGain); + Filter::setGain(gain); } -MY_FLOAT PoleZero :: getGain(void) const +StkFloat PoleZero :: getGain(void) const { return Filter::getGain(); } -MY_FLOAT PoleZero :: lastOut(void) const +StkFloat PoleZero :: lastOut(void) const { return Filter::lastOut(); } -MY_FLOAT PoleZero :: tick(MY_FLOAT sample) +StkFloat PoleZero :: tick(StkFloat sample) { - inputs[0] = gain * sample; - outputs[0] = b[0] * inputs[0] + b[1] * inputs[1] - a[1] * outputs[1]; - inputs[1] = inputs[0]; - outputs[1] = outputs[0]; + inputs_[0] = gain_ * sample; + outputs_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] - a_[1] * outputs_[1]; + inputs_[1] = inputs_[0]; + outputs_[1] = outputs_[0]; - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *PoleZero :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *PoleZero :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i 1, the reed has slammed shut and the // reflection function value saturates at 1.0. - if (lastOutput > 1.0) lastOutput = (MY_FLOAT) 1.0; + if (lastOutput_ > 1.0) lastOutput_ = (StkFloat) 1.0; // This is nearly impossible in a physical system, but // a reflection function value of -1.0 corresponds to // an open end (and no discontinuity in bore profile). - if (lastOutput < -1.0) lastOutput = (MY_FLOAT) -1.0; - return lastOutput; + if (lastOutput_ < -1.0) lastOutput_ = (StkFloat) -1.0; + return lastOutput_; } -MY_FLOAT *ReedTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *ReedTable :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; isetResonance( poleFrequency, poleRadius, TRUE ); - zeroFrequency = 0.0; - zeroRadius = 0.0; + filter_.setResonance( poleFrequency_, poleRadius_, true ); + zeroFrequency_ = 0.0; + zeroRadius_ = 0.0; } Resonate :: ~Resonate() { - delete adsr; - delete filter; - delete noise; } void Resonate :: keyOn() { - adsr->keyOn(); + adsr_.keyOn(); } void Resonate :: keyOff() { - adsr->keyOff(); + adsr_.keyOff(); } -void Resonate :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Resonate :: noteOn(StkFloat frequency, StkFloat amplitude) { - adsr->setTarget( amplitude ); + adsr_.setTarget( amplitude ); this->keyOn(); - this->setResonance(frequency, poleRadius); + this->setResonance( frequency, poleRadius_ ); #if defined(_STK_DEBUG_) - std::cerr << "Resonate: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Resonate::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Resonate :: noteOff(MY_FLOAT amplitude) +void Resonate :: noteOff(StkFloat amplitude) { this->keyOff(); #if defined(_STK_DEBUG_) - std::cerr << "Resonate: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Resonate::NoteOff: amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Resonate :: setResonance(MY_FLOAT frequency, MY_FLOAT radius) +void Resonate :: setResonance( StkFloat frequency, StkFloat radius ) { - poleFrequency = frequency; + poleFrequency_ = frequency; if ( frequency < 0.0 ) { - std::cerr << "Resonate: setResonance frequency parameter is less than zero!" << std::endl; - poleFrequency = 0.0; + errorString_ << "Resonate::setResonance: frequency parameter is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + poleFrequency_ = 0.0; } - poleRadius = radius; + poleRadius_ = radius; if ( radius < 0.0 ) { - std::cerr << "Resonate: setResonance radius parameter is less than 0.0!" << std::endl; - poleRadius = 0.0; + std::cerr << "Resonate::setResonance: radius parameter is less than 0.0 ... setting to 0.0!"; + handleError( StkError::WARNING ); + poleRadius_ = 0.0; } else if ( radius >= 1.0 ) { - std::cerr << "Resonate: setResonance radius parameter is greater than or equal to 1.0, which is unstable!" << std::endl; - poleRadius = 0.9999; + errorString_ << "Resonate::setResonance: radius parameter is greater than or equal to 1.0, which is unstable ... correcting!"; + handleError( StkError::WARNING ); + poleRadius_ = 0.9999; } - filter->setResonance( poleFrequency, poleRadius, TRUE ); + filter_.setResonance( poleFrequency_, poleRadius_, true ); } -void Resonate :: setNotch(MY_FLOAT frequency, MY_FLOAT radius) +void Resonate :: setNotch(StkFloat frequency, StkFloat radius) { - zeroFrequency = frequency; + zeroFrequency_ = frequency; if ( frequency < 0.0 ) { - std::cerr << "Resonate: setNotch frequency parameter is less than zero!" << std::endl; - zeroFrequency = 0.0; + errorString_ << "Resonate::setNotch: frequency parameter is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + zeroFrequency_ = 0.0; } - zeroRadius = radius; + zeroRadius_ = radius; if ( radius < 0.0 ) { - std::cerr << "Resonate: setNotch radius parameter is less than 0.0!" << std::endl; - zeroRadius = 0.0; + errorString_ << "Resonate::setNotch: radius parameter is less than 0.0 ... setting to 0.0!"; + handleError( StkError::WARNING ); + zeroRadius_ = 0.0; } - filter->setNotch( zeroFrequency, zeroRadius ); + filter_.setNotch( zeroFrequency_, zeroRadius_ ); } void Resonate :: setEqualGainZeroes() { - filter->setEqualGainZeroes(); + filter_.setEqualGainZeroes(); } -MY_FLOAT Resonate :: tick() +StkFloat Resonate :: tick() { - lastOutput = filter->tick(noise->tick()); - lastOutput *= adsr->tick(); - return lastOutput; + lastOutput_ = filter_.tick( noise_.tick() ); + lastOutput_ *= adsr_.tick(); + return lastOutput_; } -void Resonate :: controlChange(int number, MY_FLOAT value) +StkFloat *Resonate :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Resonate :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Resonate :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Resonate: Control value less than zero!" << std::endl; + errorString_ << "Resonate::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Resonate: Control value greater than 128.0!" << std::endl; + errorString_ << "Resonate::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == 2) // 2 - setResonance( norm * Stk::sampleRate() * 0.5, poleRadius ); + setResonance( norm * Stk::sampleRate() * 0.5, poleRadius_ ); else if (number == 4) // 4 - setResonance( poleFrequency, norm*0.9999 ); + setResonance( poleFrequency_, norm*0.9999 ); else if (number == 11) // 11 - this->setNotch( norm * Stk::sampleRate() * 0.5, zeroRadius ); + this->setNotch( norm * Stk::sampleRate() * 0.5, zeroRadius_ ); else if (number == 1) - this->setNotch( zeroFrequency, norm ); + this->setNotch( zeroFrequency_, norm ); else if (number == __SK_AfterTouch_Cont_) // 128 - adsr->setTarget( norm ); - else - std::cerr << "Resonate: Undefined Control Number (" << number << ")!!" << std::endl; + adsr_.setTarget( norm ); + else { + errorString_ << "Resonate::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Resonate: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Resonate::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Reverb.cpp b/src/Reverb.cpp deleted file mode 100644 index 6389cd4..0000000 --- a/src/Reverb.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/***************************************************/ -/*! \class Reverb - \brief STK abstract reverberator parent class. - - This class provides common functionality for - STK reverberator subclasses. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#include "Reverb.h" -#include - -Reverb :: Reverb() -{ -} - -Reverb :: ~Reverb() -{ -} - -void Reverb :: setEffectMix(MY_FLOAT mix) -{ - effectMix = mix; -} - -MY_FLOAT Reverb :: lastOut() const -{ - return (lastOutput[0] + lastOutput[1]) * 0.5; -} - -MY_FLOAT Reverb :: lastOutLeft() const -{ - return lastOutput[0]; -} - -MY_FLOAT Reverb :: lastOutRight() const -{ - return lastOutput[1]; -} - -MY_FLOAT *Reverb :: tick(MY_FLOAT *vector, unsigned int vectorSize) -{ - for (unsigned int i=0; isetRatio(0, 1.0); this->setRatio(1, 0.5); this->setRatio(2, 1.0); this->setRatio(3, 15.0); - gains[0] = __FM_gains[99]; - gains[1] = __FM_gains[90]; - gains[2] = __FM_gains[99]; - gains[3] = __FM_gains[67]; + gains_[0] = fmGains_[99]; + gains_[1] = fmGains_[90]; + gains_[2] = fmGains_[99]; + gains_[3] = fmGains_[67]; - adsr[0]->setAllTimes( 0.001, 1.50, 0.0, 0.04); - adsr[1]->setAllTimes( 0.001, 1.50, 0.0, 0.04); - adsr[2]->setAllTimes( 0.001, 1.00, 0.0, 0.04); - adsr[3]->setAllTimes( 0.001, 0.25, 0.0, 0.04); + adsr_[0]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[1]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[2]->setAllTimes( 0.001, 1.00, 0.0, 0.04); + adsr_[3]->setAllTimes( 0.001, 0.25, 0.0, 0.04); - twozero->setGain((MY_FLOAT) 1.0); + twozero_.setGain( 1.0 ); } Rhodey :: ~Rhodey() { } -void Rhodey :: setFrequency(MY_FLOAT frequency) +void Rhodey :: setFrequency(StkFloat frequency) { - baseFrequency = frequency * (MY_FLOAT) 2.0; + baseFrequency_ = frequency * 2.0; - for (int i=0; isetFrequency( baseFrequency * ratios[i] ); + for (unsigned int i=0; isetFrequency( baseFrequency_ * ratios_[i] ); } -void Rhodey :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Rhodey :: noteOn(StkFloat frequency, StkFloat amplitude) { - gains[0] = amplitude * __FM_gains[99]; - gains[1] = amplitude * __FM_gains[90]; - gains[2] = amplitude * __FM_gains[99]; - gains[3] = amplitude * __FM_gains[67]; - this->setFrequency(frequency); + gains_[0] = amplitude * fmGains_[99]; + gains_[1] = amplitude * fmGains_[90]; + gains_[2] = amplitude * fmGains_[99]; + gains_[3] = amplitude * fmGains_[67]; + this->setFrequency( frequency ); this->keyOn(); #if defined(_STK_DEBUG_) - cerr << "Rhodey: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + errorString_ << "Rhodey::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Rhodey :: tick() +StkFloat Rhodey :: tick() { - MY_FLOAT temp, temp2; + StkFloat temp, temp2; - temp = gains[1] * adsr[1]->tick() * waves[1]->tick(); - temp = temp * control1; + temp = gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; - waves[0]->addPhaseOffset(temp); - waves[3]->addPhaseOffset(twozero->lastOut()); - temp = gains[3] * adsr[3]->tick() * waves[3]->tick(); - twozero->tick(temp); + waves_[0]->addPhaseOffset( temp ); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); - waves[2]->addPhaseOffset(temp); - temp = ( 1.0 - (control2 * 0.5)) * gains[0] * adsr[0]->tick() * waves[0]->tick(); - temp += control2 * 0.5 * gains[2] * adsr[2]->tick() * waves[2]->tick(); + waves_[2]->addPhaseOffset( temp ); + temp = ( 1.0 - (control2_ * 0.5)) * gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += control2_ * 0.5 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); // Calculate amplitude modulation and apply it to output. - temp2 = vibrato->tick() * modDepth; + temp2 = vibrato_->tick() * modDepth_; temp = temp * (1.0 + temp2); - lastOutput = temp * 0.5; - return lastOutput; + lastOutput_ = temp * 0.5; + return lastOutput_; +} + +StkFloat *Rhodey :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Rhodey :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/RtAudio.cpp b/src/RtAudio.cpp index 767908d..a1c7ec5 100644 --- a/src/RtAudio.cpp +++ b/src/RtAudio.cpp @@ -9,7 +9,7 @@ RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/ - RtAudio: a realtime audio i/o C++ class + RtAudio: realtime audio i/o C++ classes Copyright (c) 2001-2004 Gary P. Scavone Permission is hereby granted, free of charge, to any person @@ -37,10 +37,11 @@ */ /************************************************************************/ -// RtAudio: Version 3.0.1, 22 March 2004 +// RtAudio: Version 3.0.2, pre-release for STK 4.2.0 #include "RtAudio.h" #include +#include // Static variable definitions. const unsigned int RtApi::MAX_SAMPLE_RATES = 14; @@ -92,6 +93,26 @@ RtAudio :: RtAudio( int outputDevice, int outputChannels, } } +RtAudio :: RtAudio( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int *numberOfBuffers, RtAudioApi api ) +{ + initialize( api ); + + try { + rtapi_->openStream( outputDevice, outputChannels, + inputDevice, inputChannels, + format, sampleRate, + bufferSize, numberOfBuffers ); + } + catch (RtError &exception) { + // Deallocate the RtApi instance. + delete rtapi_; + throw exception; + } +} + RtAudio :: ~RtAudio() { delete rtapi_; @@ -107,6 +128,16 @@ void RtAudio :: openStream( int outputDevice, int outputChannels, bufferSize, numberOfBuffers ); } +void RtAudio :: openStream( int outputDevice, int outputChannels, + int inputDevice, int inputChannels, + RtAudioFormat format, int sampleRate, + int *bufferSize, int *numberOfBuffers ) +{ + rtapi_->openStream( outputDevice, outputChannels, inputDevice, + inputChannels, format, sampleRate, + bufferSize, *numberOfBuffers ); +} + void RtAudio::initialize( RtAudioApi api ) { rtapi_ = 0; @@ -216,6 +247,7 @@ void RtAudio::initialize( RtAudioApi api ) RtApi :: RtApi() { stream_.mode = UNINITIALIZED; + stream_.state = STREAM_STOPPED; stream_.apiHandle = 0; MUTEX_INITIALIZE(&stream_.mutex); } @@ -225,6 +257,17 @@ 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 ) +{ + this->openStream( outputDevice, outputChannels, inputDevice, + inputChannels, format, sampleRate, + bufferSize, *numberOfBuffers ); + *numberOfBuffers = stream_.nBuffers; +} + void RtApi :: openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, @@ -259,6 +302,7 @@ void RtApi :: openStream( int outputDevice, int outputChannels, } } + std::string errorMessages; clearStreamInfo(); bool result = FAILURE; int device, defaultDevice = 0; @@ -291,6 +335,9 @@ void RtApi :: openStream( int outputDevice, int outputChannels, result = probeDeviceOpen(device, mode, channels, sampleRate, format, bufferSize, numberOfBuffers); if ( result == SUCCESS ) break; + errorMessages.append( " " ); + errorMessages.append( message_ ); + errorMessages.append( "\n" ); if ( outputDevice > 0 ) break; clearStreamInfo(); } @@ -323,6 +370,9 @@ void RtApi :: openStream( int outputDevice, int outputChannels, result = probeDeviceOpen(device, mode, channels, sampleRate, format, bufferSize, numberOfBuffers); if (result == SUCCESS) break; + errorMessages.append( " " ); + errorMessages.append( message_ ); + errorMessages.append( "\n" ); if ( outputDevice > 0 ) break; } } @@ -336,9 +386,11 @@ void RtApi :: openStream( int outputDevice, int outputChannels, clearStreamInfo(); if ( ( outputDevice == 0 && outputChannels > 0 ) || ( inputDevice == 0 && inputChannels > 0 ) ) - sprintf(message_,"RtApi: no devices found for given stream parameters."); + sprintf(message_,"RtApi: no devices found for given stream parameters: \n%s", + errorMessages.c_str()); else - sprintf(message_,"RtApi: unable to open specified device(s) with given stream parameters."); + sprintf(message_,"RtApi: unable to open specified device(s) with given stream parameters: \n%s", + errorMessages.c_str()); error(RtError::INVALID_PARAMETER); return; @@ -349,6 +401,11 @@ int RtApi :: getDeviceCount(void) return devices_.size(); } +RtApi::StreamState RtApi :: getStreamState( void ) const +{ + return stream_.state; +} + RtAudioDeviceInfo RtApi :: getDeviceInfo( int device ) { if (device > (int) devices_.size() || device < 1) { @@ -1130,6 +1187,49 @@ bool RtApiOss :: probeDeviceOpen(int device, StreamMode mode, int channels, else stream_.mode = mode; + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) { + if (mode == INPUT) { // convert device to user buffer + stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; + stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; + stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; + stream_.convertInfo[mode].outFormat = stream_.userFormat; + } + else { // convert user to device buffer + stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; + stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; + stream_.convertInfo[mode].inFormat = stream_.userFormat; + stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; + } + + if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; + else + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; + + // Set up the interleave/deinterleave offsets. + if ( mode == INPUT && stream_.deInterleave[1] ) { + for (int k=0; kindex[mode] = iStream; // Allocate necessary internal buffers. @@ -2171,6 +2271,49 @@ bool RtApiCore :: probeDeviceOpen( int device, StreamMode mode, int channels, stream_.state = STREAM_STOPPED; stream_.callbackInfo.object = (void *) this; + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) { + if (mode == INPUT) { // convert device to user buffer + stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; + stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; + stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; + stream_.convertInfo[mode].outFormat = stream_.userFormat; + } + else { // convert user to device buffer + stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; + stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; + stream_.convertInfo[mode].inFormat = stream_.userFormat; + stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; + } + + if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; + else + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; + + // Set up the interleave/deinterleave offsets. + if ( mode == INPUT && stream_.deInterleave[1] ) { + for (int k=0; kdeviceBuffer; - convertStreamBuffer(OUTPUT); + convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] ); if ( stream_.doByteSwap[0] ) byteSwapBuffer(stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[0], @@ -2453,7 +2596,7 @@ void RtApiCore :: callbackEvent( AudioDeviceID deviceId, void *inData, void *out byteSwapBuffer(stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[1], stream_.deviceFormat[1]); - convertStreamBuffer(INPUT); + convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] ); } else { @@ -2526,9 +2669,10 @@ void RtApiCore :: cancelStreamCallback() // // .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, +// or through an interface program such as qjackctl. Many of the +// parameters normally set for a stream are fixed by the JACK server +// and can be specified when the JACK server is started. In +// particular, // // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4 // @@ -2645,7 +2789,7 @@ void RtApiJack :: probeDeviceInfo(RtApiDevice *info) if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) { jack_client_close(client); sprintf(message_, "RtApiJack: error determining jack input/output channels!"); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return; } @@ -2671,7 +2815,7 @@ void RtApiJack :: probeDeviceInfo(RtApiDevice *info) if (info->nativeFormats == 0) { jack_client_close(client); sprintf(message_, "RtApiJack: error determining jack server data format!"); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return; } @@ -2700,6 +2844,14 @@ void jackShutdown(void *infoPointer) JackHandle *handle = (JackHandle *) info->apiInfo; handle->clientOpen = false; RtApiJack *object = (RtApiJack *) info->object; + + // Check current stream state. If stopped, then we'll assume this + // was called as a result of a call to RtApiJack::stopStream (the + // deactivation of a client handle causes this function to be called). + // If not, we'll assume the Jack server is shutting down or some + // other problem occurred and we should close the stream. + if ( object->getStreamState() == RtApi::STREAM_STOPPED ) return; + try { object->closeStream(); } @@ -2708,7 +2860,7 @@ void jackShutdown(void *infoPointer) return; } - fprintf(stderr, "\nRtApiJack: the Jack server is shutting down ... stream stopped and closed!!!\n\n"); + fprintf(stderr, "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!!\n\n"); } int jackXrun( void * ) @@ -2877,6 +3029,49 @@ bool RtApiJack :: probeDeviceOpen(int device, StreamMode mode, int channels, jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); } + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) { + if (mode == INPUT) { // convert device to user buffer + stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; + stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; + stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; + stream_.convertInfo[mode].outFormat = stream_.userFormat; + } + else { // convert user to device buffer + stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; + stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; + stream_.convertInfo[mode].inFormat = stream_.userFormat; + stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; + } + + if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; + else + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; + + // Set up the interleave/deinterleave offsets. + if ( mode == INPUT && stream_.deInterleave[1] ) { + for (int k=0; kports[0][i], @@ -3120,7 +3315,7 @@ void RtApiJack :: callbackEvent( unsigned long nframes ) (jack_nframes_t) nframes); memcpy(&stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes ); } - convertStreamBuffer(INPUT); + convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] ); } else { // single channel only jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][0], @@ -3178,6 +3373,17 @@ void RtApiJack :: cancelStreamCallback() #include #include +// A structure to hold various information related to the ALSA API +// implementation. +struct AlsaHandle { + snd_pcm_t *handles[2]; + bool synchronized; + char *tempBuffer; + + AlsaHandle() + :synchronized(false), tempBuffer(0) {} +}; + extern "C" void *alsaCallbackHandler(void * ptr); RtApiAlsa :: RtApiAlsa() @@ -3319,7 +3525,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); goto capture_probe; } @@ -3330,7 +3536,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware minimum channel probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); goto capture_probe; } info->minOutputChannels = value; @@ -3340,7 +3546,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware maximum channel probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); goto capture_probe; } info->maxOutputChannels = value; @@ -3391,7 +3597,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); if (info->maxOutputChannels > 0) goto probe_parameters; else @@ -3404,7 +3610,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) 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); + error(RtError::DEBUG_WARNING); if (info->maxOutputChannels > 0) goto probe_parameters; else @@ -3417,7 +3623,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) 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); + error(RtError::DEBUG_WARNING); if (info->maxOutputChannels > 0) goto probe_parameters; else @@ -3453,7 +3659,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) if (err < 0) { sprintf(message_, "RtApiAlsa: pcm (%s) won't reopen during probe: %s.", info->name.c_str(), snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return; } @@ -3463,7 +3669,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware reopen probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return; } @@ -3509,7 +3715,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info) snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: pcm device (%s) data format not supported by RtAudio.", info->name.c_str()); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return; } @@ -3544,7 +3750,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, if (err < 0) { sprintf(message_,"RtApiAlsa: pcm device (%s) won't open: %s.", name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3556,7 +3762,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error getting parameter handle (%s): %s.", name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3576,14 +3782,14 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, else { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: device (%s) access not supported by RtAudio.", name); - error(RtError::WARNING); + error(RtError::DEBUG_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); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3649,7 +3855,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, // 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); + error(RtError::DEBUG_WARNING); return FAILURE; set_format: @@ -3658,7 +3864,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting format (%s): %s.", name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3672,7 +3878,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error getting format endian-ness (%s): %s.", name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } } @@ -3683,7 +3889,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting sample rate (%d) on device (%s): %s.", sampleRate, name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3697,7 +3903,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: channels (%d) not supported by device (%s).", channels, name); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3705,7 +3911,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, if (err < 0 ) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error getting min channels count on device (%s).", name); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } device_channels = value; @@ -3718,7 +3924,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting channels (%d) on device (%s): %s.", device_channels, name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3727,54 +3933,26 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, 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); + err = snd_pcm_hw_params_set_periods_near(handle, hw_params, &periods, &dir); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting periods (%s): %s.", name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_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); + snd_pcm_uframes_t period_size = *bufferSize; + err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, &dir); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting period size (%s): %s.", name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } + *bufferSize = period_size; // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! @@ -3793,7 +3971,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error installing hardware configuration (%s): %s.", name, snd_strerror(err)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3802,23 +3980,40 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, snd_pcm_hw_params_dump(hw_params, out); #endif - // Allocate the stream handle if necessary and then save. - snd_pcm_t **handles; + // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns. + snd_pcm_sw_params_t *sw_params = NULL; + snd_pcm_sw_params_alloca( &sw_params ); + snd_pcm_sw_params_current( handle, sw_params ); + snd_pcm_sw_params_set_start_threshold( handle, sw_params, *bufferSize ); + snd_pcm_sw_params_set_stop_threshold( handle, sw_params, 0x7fffffff ); + snd_pcm_sw_params_set_silence_threshold( handle, sw_params, 0 ); + snd_pcm_sw_params_set_silence_size( handle, sw_params, INT_MAX ); + 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::DEBUG_WARNING); + return FAILURE; + } + +#if defined(__RTAUDIO_DEBUG__) + fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n"); + snd_pcm_sw_params_dump(sw_params, out); +#endif + + // Allocate the ApiHandle if necessary and then save. + AlsaHandle *apiInfo = 0; 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; + apiInfo = (AlsaHandle *) new AlsaHandle; + stream_.apiHandle = (void *) apiInfo; + apiInfo->handles[0] = 0; + apiInfo->handles[1] = 0; } else { - handles = (snd_pcm_t **) stream_.apiHandle; + apiInfo = (AlsaHandle *) stream_.apiHandle; } - handles[mode] = handle; + apiInfo->handles[mode] = handle; // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; @@ -3840,8 +4035,10 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); + if (apiInfo->tempBuffer) free(apiInfo->tempBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); - if (stream_.userBuffer == NULL) { + apiInfo->tempBuffer = (char *) calloc(buffer_bytes, 1); + if ( stream_.userBuffer == NULL || apiInfo->tempBuffer == NULL ) { sprintf(message_, "RtApiAlsa: error allocating user buffer memory (%s).", devices_[device].name.c_str()); goto error; @@ -3876,23 +4073,77 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, stream_.device[mode] = device; stream_.state = STREAM_STOPPED; - if ( stream_.mode == OUTPUT && mode == INPUT ) + if ( stream_.mode == OUTPUT && mode == INPUT ) { // We had already set up an output stream. stream_.mode = DUPLEX; + // Link the streams if possible. + apiInfo->synchronized = false; + if (snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0) + apiInfo->synchronized = true; + else { + sprintf(message_, "RtApiAlsa: unable to synchronize input and output streams (%s).", + devices_[device].name.c_str()); + error(RtError::DEBUG_WARNING); + } + } else stream_.mode = mode; stream_.nBuffers = periods; stream_.sampleRate = sampleRate; + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) { + if (mode == INPUT) { // convert device to user buffer + stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; + stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; + stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; + stream_.convertInfo[mode].outFormat = stream_.userFormat; + } + else { // convert user to device buffer + stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; + stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; + stream_.convertInfo[mode].inFormat = stream_.userFormat; + stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; + } + + if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; + else + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; + + // Set up the interleave/deinterleave offsets. + if ( mode == INPUT && stream_.deInterleave[1] ) { + for (int k=0; khandles[0]) + snd_pcm_close(apiInfo->handles[0]); + if (apiInfo->handles[1]) + snd_pcm_close(apiInfo->handles[1]); + if ( apiInfo->tempBuffer ) free(apiInfo->tempBuffer); + delete apiInfo; stream_.apiHandle = 0; } @@ -3901,7 +4152,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, stream_.userBuffer = 0; } - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -3916,12 +4167,12 @@ void RtApiAlsa :: closeStream() return; } - snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; if (stream_.state == STREAM_RUNNING) { if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) - snd_pcm_drop(handle[0]); + snd_pcm_drop(apiInfo->handles[0]); if (stream_.mode == INPUT || stream_.mode == DUPLEX) - snd_pcm_drop(handle[1]); + snd_pcm_drop(apiInfo->handles[1]); stream_.state = STREAM_STOPPED; } @@ -3930,11 +4181,12 @@ void RtApiAlsa :: closeStream() 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 (apiInfo) { + if (apiInfo->handles[0]) snd_pcm_close(apiInfo->handles[0]); + if (apiInfo->handles[1]) snd_pcm_close(apiInfo->handles[1]); + free(apiInfo->tempBuffer); + delete apiInfo; + stream_.apiHandle = 0; } if (stream_.userBuffer) { @@ -3961,7 +4213,8 @@ void RtApiAlsa :: startStream() int err; snd_pcm_state_t state; - snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; + snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { state = snd_pcm_state(handle[0]); if (state != SND_PCM_STATE_PREPARED) { @@ -3975,7 +4228,7 @@ void RtApiAlsa :: startStream() } } - if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) { state = snd_pcm_state(handle[1]); if (state != SND_PCM_STATE_PREPARED) { err = snd_pcm_prepare(handle[1]); @@ -4003,7 +4256,8 @@ void RtApiAlsa :: stopStream() MUTEX_LOCK(&stream_.mutex); int err; - snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; + snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { err = snd_pcm_drain(handle[0]); if (err < 0) { @@ -4014,7 +4268,7 @@ void RtApiAlsa :: stopStream() } } - if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) { err = snd_pcm_drain(handle[1]); if (err < 0) { sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", @@ -4038,7 +4292,8 @@ void RtApiAlsa :: abortStream() MUTEX_LOCK(&stream_.mutex); int err; - snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; + snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { err = snd_pcm_drop(handle[0]); if (err < 0) { @@ -4049,7 +4304,7 @@ void RtApiAlsa :: abortStream() } } - if (stream_.mode == INPUT || stream_.mode == DUPLEX) { + if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) { err = snd_pcm_drop(handle[1]); if (err < 0) { sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", @@ -4070,7 +4325,8 @@ int RtApiAlsa :: streamWillBlock() MUTEX_LOCK(&stream_.mutex); int err = 0, frames = 0; - snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle; + AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; + snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { err = snd_pcm_avail_update(handle[0]); if (err < 0) { @@ -4124,69 +4380,19 @@ void RtApiAlsa :: tickStream() int err; char *buffer; int channels; + AlsaHandle *apiInfo; snd_pcm_t **handle; RtAudioFormat format; - handle = (snd_pcm_t **) stream_.apiHandle; - if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + apiInfo = (AlsaHandle *) stream_.apiHandle; + handle = (snd_pcm_t **) apiInfo->handles; - // 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; itempBuffer, stream_.userBuffer, bufferBytes ); } if (stream_.mode == INPUT || stream_.mode == DUPLEX) { @@ -4251,7 +4457,75 @@ void RtApiAlsa :: tickStream() // Do buffer conversion if necessary. if (stream_.doConvertBuffer[1]) - convertStreamBuffer(INPUT); + convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] ); + } + + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { + + // Setup parameters and do buffer conversion if necessary. + if (stream_.doConvertBuffer[0]) { + buffer = stream_.deviceBuffer; + if ( stream_.mode == DUPLEX ) + convertBuffer( buffer, apiInfo->tempBuffer, stream_.convertInfo[0] ); + else + convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] ); + channels = stream_.nDeviceChannels[0]; + format = stream_.deviceFormat[0]; + } + else { + if ( stream_.mode == DUPLEX ) + buffer = apiInfo->tempBuffer; + 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; ibufferInfos = 0; // Create a manual-reset event. - handle->condition = CreateEvent(NULL, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - NULL); // unnamed + handle->condition = CreateEvent( NULL, // no security + TRUE, // manual-reset + FALSE, // non-signaled initially + NULL ); // unnamed stream_.apiHandle = (void *) handle; } @@ -4907,6 +5181,49 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels, asioCallbackInfo = &stream_.callbackInfo; stream_.callbackInfo.object = (void *) this; + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) { + if (mode == INPUT) { // convert device to user buffer + stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; + stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; + stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; + stream_.convertInfo[mode].outFormat = stream_.userFormat; + } + else { // convert user to device buffer + stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; + stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; + stream_.convertInfo[mode].inFormat = stream_.userFormat; + stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; + } + + if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; + else + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; + + // Set up the interleave/deinterleave offsets. + if ( mode == INPUT && stream_.deInterleave[1] ) { + for (int k=0; kusingCallback ) SetEvent( handle->condition ); + // The following call was suggested by Malte Clasen. While the API + // documentation indicates it should not be required, some device + // drivers apparently do not function correctly without it. + ASIOOutputReady(); + MUTEX_UNLOCK(&stream_.mutex); } @@ -5364,7 +5686,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info) if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.", getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return; } @@ -5377,7 +5699,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info) if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.", info->name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); goto playback_probe; } @@ -5388,7 +5710,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info) input->Release(); sprintf(message_, "RtApiDs: Could not get capture capabilities (%s): %s.", info->name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); goto playback_probe; } @@ -5449,7 +5771,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info) if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.", getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return; } @@ -5463,7 +5785,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info) if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.", info->name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); goto check_parameters; } @@ -5473,7 +5795,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info) output->Release(); sprintf(message_, "RtApiDs: Could not get playback capabilities (%s): %s.", info->name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); goto check_parameters; } @@ -5485,17 +5807,31 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info) // if it exists. if ( info->sampleRates.size() == 0 ) { info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate ); - info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate ); + if ( out_caps.dwMaxSecondarySampleRate > out_caps.dwMinSecondarySampleRate ) + info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate ); } else { - // Check input rates against output rate range. - for ( 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 ); + // Check input rates against output rate range. If there's an + // inconsistency (such as a duplex-capable device which reports a + // single output rate of 48000 Hz), we'll go with the output + // rate(s) since the DirectSoundCapture API is stupid and broken. + // Note that the probed sample rate values are NOT used when + // opening the device. Thanks to Tue Andersen for reporting this. + if ( info->sampleRates.back() < (int) out_caps.dwMinSecondarySampleRate ) { + info->sampleRates.clear(); + info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate ); + if ( out_caps.dwMaxSecondarySampleRate > out_caps.dwMinSecondarySampleRate ) + info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate ); } - while ( info->sampleRates.size() > 0 && - ((unsigned int) info->sampleRates[0] < out_caps.dwMinSecondarySampleRate) ) { - info->sampleRates.erase( info->sampleRates.begin() ); + else { + for ( 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.size() > 0 && + ((unsigned int) info->sampleRates[0] < out_caps.dwMinSecondarySampleRate) ) { + info->sampleRates.erase( info->sampleRates.begin() ); + } } } @@ -5642,7 +5978,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, object->Release(); sprintf(message_, "RtApiDs: Unable to set cooperative level (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5659,7 +5995,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, object->Release(); sprintf(message_, "RtApiDs: Unable to access primary buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5669,7 +6005,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, object->Release(); sprintf(message_, "RtApiDs: Unable to set primary buffer format (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5695,7 +6031,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, object->Release(); sprintf(message_, "RtApiDs: Unable to create secondary DS buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } } @@ -5713,7 +6049,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, buffer->Release(); sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5727,7 +6063,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, buffer->Release(); sprintf(message_, "RtApiDs: Unable to unlock buffer(%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5738,8 +6074,11 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, if ( mode == INPUT ) { - if ( devices_[device].maxInputChannels < channels ) + if ( devices_[device].maxInputChannels < channels ) { + sprintf(message_, "RtAudioDS: device (%s) does not support %d channels.", devices_[device].name.c_str(), channels); + error(RtError::DEBUG_WARNING); return FAILURE; + } // Enumerate through input devices to find the id (if it exists). result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); @@ -5765,7 +6104,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5784,7 +6123,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, object->Release(); sprintf(message_, "RtApiDs: Unable to create capture buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5795,7 +6134,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, buffer->Release(); sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5809,7 +6148,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, buffer->Release(); sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -5907,6 +6246,49 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, stream_.nBuffers = nBuffers; stream_.sampleRate = sampleRate; + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) { + if (mode == INPUT) { // convert device to user buffer + stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; + stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; + stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; + stream_.convertInfo[mode].outFormat = stream_.userFormat; + } + else { // convert user to device buffer + stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; + stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; + stream_.convertInfo[mode].inFormat = stream_.userFormat; + stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; + } + + if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; + else + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; + + // Set up the interleave/deinterleave offsets. + if ( mode == INPUT && stream_.deInterleave[1] ) { + for (int k=0; kname.c_str(), alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); } else { info->maxOutputChannels = value.i; @@ -6916,7 +7308,7 @@ void RtApiAl :: probeDeviceInfo(RtApiDevice *info) if (result < 0) { sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.", info->name.c_str(), alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); } else { info->sampleRates.clear(); @@ -6939,7 +7331,7 @@ void RtApiAl :: probeDeviceInfo(RtApiDevice *info) if (result < 0) { sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.", info->name.c_str(), alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); } else { info->maxInputChannels = value.i; @@ -6950,7 +7342,7 @@ void RtApiAl :: probeDeviceInfo(RtApiDevice *info) if (result < 0) { sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.", info->name.c_str(), alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); } else { // In the case of the default device, these values will @@ -7007,7 +7399,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, if ( !al_config ) { sprintf(message_,"RtApiAl: can't get AL config: %s.", alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -7017,7 +7409,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, alFreeConfig(al_config); sprintf(message_,"RtApiAl: can't set %d channels in AL config: %s.", channels, alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -7039,7 +7431,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, alFreeConfig(al_config); sprintf(message_,"RtApiAl: can't set buffer size (%ld) in AL config: %s.", buffer_size, alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } *bufferSize = buffer_size / nBuffers; @@ -7078,7 +7470,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting sample format in AL config: %s.", alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -7094,7 +7486,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.", devices_[device].name.c_str(), alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -7104,7 +7496,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, alFreeConfig(al_config); sprintf(message_,"RtApiAl: error opening output port: %s.", alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -7119,7 +7511,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, 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); + error(RtError::DEBUG_WARNING); return FAILURE; } } @@ -7135,7 +7527,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.", devices_[device].name.c_str(), alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -7145,7 +7537,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, alFreeConfig(al_config); sprintf(message_,"RtApiAl: error opening input port: %s.", alGetErrorString(oserror())); - error(RtError::WARNING); + error(RtError::DEBUG_WARNING); return FAILURE; } @@ -7160,7 +7552,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, 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); + error(RtError::DEBUG_WARNING); return FAILURE; } } @@ -7246,6 +7638,49 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, stream_.bufferSize = *bufferSize; stream_.sampleRate = sampleRate; + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) { + if (mode == INPUT) { // convert device to user buffer + stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; + stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; + stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; + stream_.convertInfo[mode].outFormat = stream_.userFormat; + } + else { // convert user to device buffer + stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; + stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; + stream_.convertInfo[mode].inFormat = stream_.userFormat; + stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; + } + + if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; + else + stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; + + // Set up the interleave/deinterleave offsets. + if ( mode == INPUT && stream_.deInterleave[1] ) { + for (int k=0; k offset_in(channels); - std::vector offset_out(channels); - if (mode == INPUT && stream_.deInterleave[1]) { - for (int k=0; k> 16) & 0x0000ffff); + for (j=0; j> 16) & 0x0000ffff); } - in += jump_in; - out += jump_out; + in += info.inJump; + out += info.outJump; } } - else if (format_in == RTAUDIO_SINT32) { - Int32 *in = (Int32 *)input; + else if (info.inFormat == RTAUDIO_SINT32) { + Int32 *in = (Int32 *)inBuffer; for (int i=0; i> 16) & 0x0000ffff); + for (j=0; j> 16) & 0x0000ffff); } - in += jump_in; - out += jump_out; + in += info.inJump; + out += info.outJump; } } - else if (format_in == RTAUDIO_FLOAT32) { - Float32 *in = (Float32 *)input; + else if (info.inFormat == RTAUDIO_FLOAT32) { + Float32 *in = (Float32 *)inBuffer; for (int i=0; i> 8) & 0x00ff); + for (j=0; j> 8) & 0x00ff); } - in += jump_in; - out += jump_out; + in += info.inJump; + out += info.outJump; } } - else if (format_in == RTAUDIO_SINT24) { - Int32 *in = (Int32 *)input; + else if (info.inFormat == RTAUDIO_SINT24) { + Int32 *in = (Int32 *)inBuffer; for (int i=0; i> 24) & 0x000000ff); + for (j=0; j> 24) & 0x000000ff); } - in += jump_in; - out += jump_out; + in += info.inJump; + out += info.outJump; } } - else if (format_in == RTAUDIO_SINT32) { - Int32 *in = (Int32 *)input; + else if (info.inFormat == RTAUDIO_SINT32) { + Int32 *in = (Int32 *)inBuffer; for (int i=0; i> 24) & 0x000000ff); + for (j=0; j> 24) & 0x000000ff); } - in += jump_in; - out += jump_out; + in += info.inJump; + out += info.outJump; } } - else if (format_in == RTAUDIO_FLOAT32) { - Float32 *in = (Float32 *)input; + else if (info.inFormat == RTAUDIO_FLOAT32) { + Float32 *in = (Float32 *)inBuffer; for (int i=0; iopenStream(device, channels_, device, channels_, format, (int)sampleRate, &bufferSize_, nBuffers); - data_ = (MY_FLOAT *) audio_->getStreamBuffer(); + data_ = (StkFloat *) audio_->getStreamBuffer(); } catch (RtError &error) { handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } - lastOutput_ = (MY_FLOAT *) new MY_FLOAT[channels_]; + lastOutput_ = (StkFloat *) new StkFloat[channels_]; for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; itickStream(); + } + catch (RtError &error) { + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); + } + } + + if ( channels_ > 1 && frames.interleaved() == false ) { + k = i; + for (j=0; j= (long) bufferSize_) + counter_ = 0; + } + + return frames; +} diff --git a/src/RtMidi.cpp b/src/RtMidi.cpp index b2369db..6c0f9b2 100644 --- a/src/RtMidi.cpp +++ b/src/RtMidi.cpp @@ -1,1149 +1,1981 @@ -/***************************************************/ +/**********************************************************************/ /*! \class RtMidi - \brief STK realtime MIDI class. + \brief An abstract base class for realtime MIDI input/output. - At the moment, this object only handles MIDI - input, though MIDI output code can go here - when someone decides they need it (and writes - it). + This class implements some common functionality for the realtime + MIDI input/output subclasses RtMidiIn and RtMidiOut. - This object opens a MIDI input device and - parses MIDI messages into a MIDI buffer. Time - stamp info is converted to a delta-time - value. MIDI data is stored as MY_FLOAT to - conform with SKINI. System exclusive messages - are currently ignored. + RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ - An optional argument to the constructor can be - used to specify a device or card. When no - argument is given, a default device is opened. - If a device argument fails, a list of available - devices is printed to allow selection by the user. + RtMidi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2004 Gary P. Scavone - This code is based in part on work of Perry - Cook (SGI), Paul Leonard (Linux), the - RoseGarden team (Linux), and Bill Putnam - (Windows). + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/***************************************************/ +/**********************************************************************/ + +// RtMidi: Version 1.0.2, 21 September 2004 #include "RtMidi.h" -#include -#include -#include +#include -#define MIDI_BUFFER_SIZE 256 -int writeIndex; +//*********************************************************************// +// Common RtMidi Definitions +//*********************************************************************// + +RtMidi :: RtMidi() + : apiData_( 0 ), connected_( false ) +{ +} + +void RtMidi :: error( RtError::Type type ) +{ + if (type == RtError::WARNING) { + std::cerr << '\n' << errorString_ << "\n\n"; + } + else if (type == RtError::DEBUG_WARNING) { +#if defined(__RTMIDI_DEBUG__) + std::cerr << '\n' << errorString_ << "\n\n"; +#endif + } + else { + std::cerr << '\n' << errorString_ << "\n\n"; + throw RtError( errorString_, type ); + } +} + +//*********************************************************************// +// Common RtMidiIn Definitions +//*********************************************************************// + +RtMidiIn :: RtMidiIn() : RtMidi() +{ + this->initialize(); +} + +void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) +{ + if ( inputData_.usingCallback ) { + errorString_ = "RtMidiIn::setCallback: a callback function is already set!"; + error( RtError::WARNING ); + return; + } + + if ( !callback ) { + errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; + error( RtError::WARNING ); + return; + } + + inputData_.userCallback = (void *) callback; + inputData_.userData = userData; + inputData_.usingCallback = true; +} + +void RtMidiIn :: cancelCallback() +{ + if ( !inputData_.usingCallback ) { + errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; + error( RtError::WARNING ); + return; + } + + inputData_.userCallback = 0; + inputData_.userData = 0; + inputData_.usingCallback = false; +} + +void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize ) +{ + inputData_.queueLimit = queueSize; +} + +void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) +{ + inputData_.ignoreFlags = 0; + if ( midiSysex ) inputData_.ignoreFlags = 0x01; + if ( midiTime ) inputData_.ignoreFlags |= 0x02; + if ( midiSense ) inputData_.ignoreFlags |= 0x04; +} + +double RtMidiIn :: getMessage( std::vector *message ) +{ + message->clear(); + + if ( inputData_.usingCallback ) { + errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; + error( RtError::WARNING ); + return 0.0; + } + + if ( inputData_.queue.size() == 0 ) return 0.0; + + // Copy queued message to the vector pointer argument and then "pop" it. + std::vector *bytes = &(inputData_.queue.front().bytes); + message->assign( bytes->begin(), bytes->end() ); + double deltaTime = inputData_.queue.front().timeStamp; + inputData_.queue.pop(); + + return deltaTime; +} + +//*********************************************************************// +// Common RtMidiOut Definitions +//*********************************************************************// + +RtMidiOut :: RtMidiOut() : RtMidi() +{ + this->initialize(); +} + + +//*********************************************************************// +// API: Macintosh OS-X +//*********************************************************************// + +// API information found at: +// - http://developer. apple .com/audio/pdf/coreaudio.pdf #if defined(__MACOSX_CORE__) +// The CoreMIDI API is based on the use of a callback function for +// MIDI input. We convert the system specific time stamps to delta +// time values. + +// OS-X CoreMIDI header files. #include #include -MIDIClientRef client; -MIDIPortRef port; +// A structure to hold variables related to the CoreMIDI API +// implementation. +struct CoreMidiData { + MIDIClientRef client; + MIDIPortRef port; + MIDIEndpointRef endpoint; + MIDIEndpointRef destinationId; + unsigned long long lastTime; +}; -typedef unsigned char byte; - -/* MIDI System Messages */ -#define MD_SYSTEM_MSG ((byte)0xF0) -#define MD_PITCH_BEND ((byte)0xE0) -#define MessageType(MSG) (byte)((MSG) & ((byte)0xF0)) - -typedef struct { - byte data[3]; - float delta_time; -} MIDIMESSAGE; - -MIDIMESSAGE *midiBuffer; -UInt64 lastTime = 0; +//*********************************************************************// +// API: OS-X +// Class Definitions: RtMidiIn +//*********************************************************************// void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) { - MIDIMESSAGE newMessage; - UInt64 deltaTime; + RtMidiIn::RtMidiInData *data = static_cast (procRef); + CoreMidiData *apiData = static_cast (data->apiData); + bool continueSysex = false; + unsigned char status; + unsigned short nBytes, iByte, size; + unsigned long long time; + RtMidiIn::MidiMessage message; const MIDIPacket *packet = &list->packet[0]; - for (unsigned int i=0; inumPackets; ++i){ + for ( unsigned int i=0; inumPackets; ++i ) { - // Block unwanted messages. - if ( packet->length > 3 ) continue; - if (MessageType(packet->data[0]) == MD_SYSTEM_MSG) continue; + // My interpretation of the CoreMIDI documentation: all message + // types, except sysex, are complete within a packet and there may + // be several of them in a single packet. Sysex messages can be + // broken across multiple packets but are bundled alone within a + // packet. I'm assuming that sysex messages, if segmented, must + // be complete within the same MIDIPacketList. - memcpy( newMessage.data, packet->data, packet->length ); - deltaTime = packet->timeStamp - lastTime; - deltaTime = AudioConvertHostTimeToNanos( deltaTime ); - newMessage.delta_time = deltaTime * 0.000000001; + nBytes = packet->length; + if ( nBytes == 0 ) continue; - // Put newMessage in the circular buffer - midiBuffer[writeIndex] = newMessage; - writeIndex++; + // Calculate time stamp. + message.timeStamp = 0.0; + if ( data->firstMessage ) + data->firstMessage = false; + else { + time = packet->timeStamp; + time -= apiData->lastTime; + time = AudioConvertHostTimeToNanos( time ); + message.timeStamp = time * 0.000000001; + } + apiData->lastTime = packet->timeStamp; - if( writeIndex >= MIDI_BUFFER_SIZE ) - writeIndex = 0; + iByte = 0; + if ( continueSysex ) { + // We have a continuing, segmented sysex message. + if ( !(data->ignoreFlags & 0x01) ) { + // If we're not ignoring sysex messages, copy the entire packet. + for ( unsigned int j=0; jdata[j] ); + } + if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; + if ( !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->usingCallback && message.bytes.size() > 0 ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + } + else { + while ( iByte < nBytes ) { + size = 0; + // We are expecting that the next byte in the packet is a status byte. + status = packet->data[iByte]; + if ( !(status & 0x80) ) break; + // Determine the number of bytes in the MIDI message. + if ( status < 0xC0 ) size = 3; + else if ( status < 0xE0 ) size = 2; + else if ( status < 0xF0 ) size = 3; + else if ( status == 0xF0 ) { + // A MIDI sysex + if ( data->ignoreFlags & 0x01 ) { + size = 0; + iByte = nBytes; + } + else size = nBytes - iByte; + if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; + } + else if ( status < 0xF3 ) { + if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { + // A MIDI time code message and we're ignoring it. + size = 0; + iByte += 3; + } + else size = 3; + } + else if ( status == 0xF3 ) size = 2; + else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { + // A MIDI active sensing message and we're ignoring it. + size = 0; + iByte += 1; + } + else size = 1; + // Copy the MIDI data to our vector. + if ( size ) { + message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); + if ( !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + iByte += size; + } + } + } packet = MIDIPacketNext(packet); } } -RtMidi :: RtMidi(int device) +void RtMidiIn :: initialize( void ) { - char msg[256]; + // Set up our client. + MIDIClientRef client; + OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client ); + if ( result != noErr ) { + errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object."; + error( RtError::DRIVER_ERROR ); + } - int nSrc = MIDIGetNumberOfSources(); + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData; + data->client = client; + data->endpoint = 0; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; +} + +void RtMidiIn :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nSrc = MIDIGetNumberOfSources(); if (nSrc < 1) { - sprintf(msg, "RtMidi: No OS X MIDI input devices available."); - handleError(msg, StkError::MIDI_SYSTEM); + errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); } - OSStatus result = MIDIClientCreate( CFSTR("Stk MIDI Input Client"), NULL, NULL, &client ); + std::ostringstream ost; + if ( portNumber >= nSrc ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + MIDIPortRef port; + CoreMidiData *data = static_cast (apiData_); + OSStatus result = MIDIInputPortCreate( data->client, CFSTR("RtMidi MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port ); if ( result != noErr ) { - sprintf(msg, "RtMidi: OSX error creating MIDI client object."); - handleError(msg, StkError::MIDI_SYSTEM); + MIDIClientDispose( data->client ); + errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; + error( RtError::DRIVER_ERROR ); } - result = MIDIInputPortCreate( client, CFSTR("Stk MIDI Input Port"), midiInputCallback, (void *)this, &port ); + // Get the desired input source identifier. + MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); + if ( endpoint == NULL ) { + MIDIPortDispose( port ); + MIDIClientDispose( data->client ); + errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference."; + error( RtError::DRIVER_ERROR ); + } + + // Make the connection. + result = MIDIPortConnectSource( port, endpoint, NULL ); if ( result != noErr ) { - MIDIClientDispose( client ); - sprintf(msg, "RtMidi: OSX error creating MIDI input port."); - handleError(msg, StkError::MIDI_SYSTEM); + MIDIPortDispose( port ); + MIDIClientDispose( data->client ); + errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port."; + error( RtError::DRIVER_ERROR ); } - // Set up the circular buffer for the Midi input messages. - midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; - readIndex = 0; - writeIndex = 0; + // Save our api-specific port information. + data->port = port; - // If specified device is bogus, print a list of available devices. - int iSrc = device; - MIDIEndpointRef src; - if ( device < 0 || device >= nSrc ) { - CFStringRef nameRef; - char name[128]; - printf("\n"); - for (int i=0; i= nSrc ) { - printf("\nType a MIDI interface number from above: "); - fgets(choice, 16, stdin); - iSrc = atoi(choice); - } - printf("\n"); - } - - // Open the source. - src = MIDIGetSource(iSrc); - MIDIPortConnectSource(port, src, NULL ); - - lastTime = AudioGetCurrentHostTime(); + connected_ = true; } -RtMidi :: ~RtMidi() +void RtMidiIn :: openVirtualPort() { - MIDIClientDispose( client ); - MIDIPortDispose( port ); - delete [] midiBuffer; + CoreMidiData *data = static_cast (apiData_); + + // Create a virtual MIDI input destination. + MIDIEndpointRef endpoint; + OSStatus result = MIDIDestinationCreate( data->client, CFSTR("RtMidi Input"), midiInputCallback, (void *)&inputData_, &endpoint ); + if ( result != noErr ) { + errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + data->endpoint = endpoint; } -int RtMidi::nextMessage() +void RtMidiIn :: closePort( void ) { - MIDIMESSAGE lastEvent; - - if ( readIndex == writeIndex ) return 0; - - lastEvent = midiBuffer[readIndex]; - - readIndex++; - if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; - - messageType = (int) (lastEvent.data[0] & 0xf0); - channel = (int) (lastEvent.data[0] & 0x0f); - byteTwo = (float) lastEvent.data[1]; - if (messageType == (int) MD_PITCH_BEND) - byteTwo = (float) lastEvent.data[2] + (byteTwo / 128.0); - else - byteThree = (float) lastEvent.data[2]; - deltaTime = (float) lastEvent.delta_time; - - return messageType; + if ( connected_ ) { + CoreMidiData *data = static_cast (apiData_); + MIDIPortDispose( data->port ); + connected_ = false; + } } -#elif defined(__OS_IRIX__) - -#include -#include -#include -#include - -MDport inport; -MDevent *midiBuffer; -pthread_t midi_input_thread; - -void *midiInputThread(void *) +RtMidiIn :: ~RtMidiIn() { - MDevent newMessage; - int status; + // Close a connection if it exists. + closePort(); - while (1) { - mdReceive(inport, &newMessage, 1); - status = (newMessage.msg[0] & MD_STATUSMASK); + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + MIDIClientDispose( data->client ); + if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); + delete data; +} - // Ignore all system messages - if (status != 0xf0) { - midiBuffer[writeIndex] = newMessage; - writeIndex++; +unsigned int RtMidiIn :: getPortCount() +{ + return MIDIGetNumberOfSources(); +} - if( writeIndex >= MIDI_BUFFER_SIZE ) - writeIndex = 0; +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + std::ostringstream ost; + char name[128]; + + if ( portNumber >= MIDIGetNumberOfSources() ) { + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + portRef = MIDIGetSource( portNumber ); + + MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); + CFStringGetCString( nameRef, name, sizeof(name), 0); + CFRelease( nameRef ); + std::string stringName = name; + return stringName; +} + +//*********************************************************************// +// API: OS-X +// Class Definitions: RtMidiOut +//*********************************************************************// + +unsigned int RtMidiOut :: getPortCount() +{ + return MIDIGetNumberOfDestinations(); +} + +std::string RtMidiOut :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + std::ostringstream ost; + char name[128]; + + if ( portNumber >= MIDIGetNumberOfDestinations() ) { + ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + portRef = MIDIGetDestination( portNumber ); + + MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); + CFStringGetCString( nameRef, name, sizeof(name), 0); + CFRelease( nameRef ); + std::string stringName = name; + return stringName; +} + +void RtMidiOut :: initialize( void ) +{ + // Set up our client. + MIDIClientRef client; + OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData; + data->client = client; + data->endpoint = 0; + apiData_ = (void *) data; +} + +void RtMidiOut :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nDest = MIDIGetNumberOfDestinations(); + if (nDest < 1) { + errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nDest ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + MIDIPortRef port; + CoreMidiData *data = static_cast (apiData_); + OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port ); + if ( result != noErr ) { + MIDIClientDispose( data->client ); + errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; + error( RtError::DRIVER_ERROR ); + } + + // Get the desired output port identifier. + MIDIEndpointRef destination = MIDIGetDestination( portNumber ); + if ( destination == NULL ) { + MIDIPortDispose( port ); + MIDIClientDispose( data->client ); + errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + data->port = port; + data->destinationId = destination; + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + CoreMidiData *data = static_cast (apiData_); + MIDIPortDispose( data->port ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort() +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!"; + error( RtError::WARNING ); + return; + } + + // Create a virtual MIDI output source. + MIDIEndpointRef endpoint; + OSStatus result = MIDISourceCreate( data->client, CFSTR("RtMidi Output"), &endpoint ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source."; + error( RtError::DRIVER_ERROR ); + } + + // Save our api-specific connection information. + data->endpoint = endpoint; +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + MIDIClientDispose( data->client ); + if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + unsigned int nBytes = message->size(); + // Pad the buffer for extra (unknown) structure data. + Byte buffer[nBytes+32]; + MIDIPacketList *pktlist = (MIDIPacketList *) buffer; + MIDIPacket *curPacket = MIDIPacketListInit( pktlist ); + + MIDITimeStamp timeStamp = 0; + curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) ); + + CoreMidiData *data = static_cast (apiData_); + + // Send to any destinations that may have connected to us. + OSStatus result; + if ( data->endpoint ) { + result = MIDIReceived( data->endpoint, pktlist ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations."; + error( RtError::WARNING ); + } + } + + // And send to an explicit destination port if we're connected. + if ( connected_ ) { + result = MIDISend( data->port, data->destinationId, pktlist ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; + error( RtError::WARNING ); } } } -RtMidi :: RtMidi(int device) -{ - int nports, card; - char msg[256]; - char *device_name = 0; - - nports = mdInit(); - if (nports < 1) { - sprintf(msg, "RtMidi: No Irix MIDI device available."); - handleError(msg, StkError::MIDI_SYSTEM); - } - - if (device == 0) { - // Open default MIDI interface. - inport = mdOpenInPort(NULL); - if (inport == NULL) { - sprintf(msg, "RtMidi: Cannot open default Irix MIDI device."); - handleError(msg, StkError::MIDI_SYSTEM); - } - } - else { - card = device; - if ( (card < 0) || (card >= nports) ) { - printf("\n"); - for (card=0; card= nports ) { - printf("\nType a MIDI interface number from above: "); - fgets(choice, 16, stdin); - card = atoi(choice); - } - printf("\n"); - } - inport = mdOpenInPort(mdGetName(card)); - if (inport == NULL) { - sprintf(msg, "RtMidi: Cannot open Irix MIDI interface %d!", card); - handleError(msg, StkError::MIDI_SYSTEM); - } - } - - mdSetStampMode(inport, MD_NOSTAMP); - - // Set up the circular buffer for the Midi input messages. - midiBuffer = new MDevent[MIDI_BUFFER_SIZE]; - readIndex = 0; - writeIndex = 0; - - if ( pthread_create(&midi_input_thread, NULL, midiInputThread, NULL) ) { - sprintf(msg, "RtMidi: unable to create MIDI input thread."); - handleError(msg, StkError::PROCESS_THREAD); - } -} - -RtMidi :: ~RtMidi() -{ - pthread_cancel(midi_input_thread); - pthread_join(midi_input_thread, NULL); - mdClosePort(inport); - delete [] midiBuffer; -} - -int RtMidi :: nextMessage() -{ - int status; - int byte1; - int byte2; - MDevent lastEvent; - static unsigned long long lastTimeStamp = 0; - - if ( readIndex == writeIndex ) return 0; - - lastEvent = midiBuffer[readIndex]; - - readIndex++; - if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; - - status = (lastEvent.msg[0] & MD_STATUSMASK); - byte1 = lastEvent.msg[1]; - byte2 = lastEvent.msg[2]; - channel = (lastEvent.msg[0] & MD_CHANNELMASK); - - if ((status == MD_PROGRAMCHANGE) || - (status == MD_CHANNELPRESSURE)) - { - messageType = status; - byteTwo = (float) byte1; - deltaTime = (MY_FLOAT) ((lastEvent.stamp - lastTimeStamp) * 0.000000001); - lastTimeStamp = lastEvent.stamp; - } - else if ((status == MD_NOTEON) || (status == MD_NOTEOFF) || - (status == MD_CONTROLCHANGE) || (status == MD_POLYKEYPRESSURE)) - { - messageType = status; - byteTwo = (float) byte1; - byteThree = (float) byte2; - deltaTime = (MY_FLOAT) ((lastEvent.stamp - lastTimeStamp) * 0.000000001); - lastTimeStamp = lastEvent.stamp; - } - else if (status == MD_PITCHBENDCHANGE) - { - messageType = status; - byteTwo = (float) byte1 * ONE_OVER_128; - byteTwo += (float) byte2; - deltaTime = (MY_FLOAT) ((lastEvent.stamp - lastTimeStamp) * 0.000000001); - lastTimeStamp = lastEvent.stamp; - } - else - { - messageType = -1; - } - - return messageType; -} +#endif // __MACOSX_CORE__ -#elif ( defined(__LINUX_OSS__) || defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) +//*********************************************************************// +// API: LINUX ALSA SEQUENCER +//*********************************************************************// + +// API information found at: +// - http://www.alsa-project.org/documentation.php#Library + +#if defined(__LINUX_ALSASEQ__) + +// The ALSA Sequencer API is based on the use of a callback function for +// MIDI input. We convert the system specific time stamps to delta +// time values. #include #include -#if defined(__MIDIATOR__) - -#include -#include -#include -#include -int midi_in; - -#elif ( defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) - +// ALSA header file. #include -snd_rawmidi_t *midi_in = 0; -#elif defined(__LINUX_OSS__) +// A structure to hold variables related to the ALSA API +// implementation. +struct AlsaMidiData { + snd_seq_t *seq; + int vport; + snd_seq_port_subscribe_t *subscription; + snd_midi_event_t *coder; + unsigned int bufferSize; + unsigned char *buffer; + pthread_t thread; + unsigned long long lastTime; +}; -#include -#include -#include -#include -#include -int midi_in; +#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) -#endif +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: RtMidiIn +//*********************************************************************// -typedef unsigned char byte; - -/* MIDI System Messages */ -#define MD_SYSTEM_MSG ((byte)0xF0) -#define MD_PGM_CHANGE ((byte)0xC0) -#define MD_CHN_PRESSURE ((byte)0xD0) -#define MD_PITCH_BEND ((byte)0xE0) -#define MessageType(MSG) (byte)((MSG) & ((byte)0xF0)) - -typedef struct { - byte data[3]; - float delta_time; -} MIDIMESSAGE; - -MIDIMESSAGE *midiBuffer; - -pthread_t midi_input_thread; - -void *midiInputThread(void *) +extern "C" void *alsaMidiHandler( void *ptr ) { - int numArgs = 2, argsLeft = 0; - double lastTime = 0.0, newTime = 0.0; - byte newByte; - MIDIMESSAGE newMessage; - int n; + RtMidiIn::RtMidiInData *data = static_cast (ptr); + AlsaMidiData *apiData = static_cast (data->apiData); + + long nBytes; + unsigned long long time, lastTime; + unsigned char lastStatus = 0; + RtMidiIn::MidiMessage message; + + snd_seq_event_t *ev; struct timeval tv; + int result; + apiData->bufferSize = 32; + result = snd_midi_event_new( 0, &apiData->coder ); + if ( result < 0 ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n"; + return 0; + } + unsigned char *buffer = (unsigned char *) malloc(apiData->bufferSize); + if ( buffer == NULL ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n"; + return 0; + } + snd_midi_event_init( apiData->coder ); - (void)gettimeofday(&tv, (struct timezone *)NULL); - lastTime = (double) (tv.tv_sec + (tv.tv_usec * 0.000001)); + while ( data->doInput ) { - for (;;) { + if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { + // No data pending ... sleep a bit. + usleep( 1000 ); + continue; + } -#if ( defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) + // If here, there should be data. + result = snd_seq_event_input( apiData->seq, &ev ); + if ( result == -ENOSPC ) { + std::cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n"; + continue; + } + else if ( result <= 0 ) { + std::cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n"; + continue; + } - 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 - // compatability in ALSA, we need to ignore such values. - - n = read(midi_in, &newByte, 1); + // This is a bit weird, but we now have to decode an ALSA MIDI + // event (back) into MIDI bytes. We'll ignore non-MIDI types. + message.bytes.clear(); + switch (ev->type) { + case SND_SEQ_EVENT_PORT_SUBSCRIBED: +#if defined(__RTMIDI_DEBUG__) + std::cout << "RtMidiIn::alsaMidiHandler: port connection made!\n"; #endif + break; - while (n > 0) { - if (newByte & 0x80) { // status byte - if (MessageType(newByte) == MD_SYSTEM_MSG) { - n--; - continue; - } - else if (MessageType(newByte) == MD_PGM_CHANGE || - MessageType(newByte) == MD_CHN_PRESSURE) { - numArgs = 1; - } - else { - numArgs = 2; - } - newMessage.data[0] = newByte; - newMessage.data[1] = 0; - newMessage.data[2] = 0; - argsLeft = numArgs; - } - else { // data byte - if ( argsLeft == numArgs ) - newMessage.data[1] = newByte; - else { - newMessage.data[2] = newByte; - } - argsLeft--; - - if ( !argsLeft ) { // MIDI message complete - // setup for running status mode (another event of the - // same type without status byte) - if (MessageType(newMessage.data[0]) == (int) MD_PGM_CHANGE || - MessageType(newMessage.data[0]) == (int) MD_CHN_PRESSURE) { - argsLeft = 1; - } - else { - argsLeft = 2; - } + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; + data->doInput = false; + break; - // determine the delta time since the last event - (void)gettimeofday(&tv, (struct timezone *)NULL); - newTime = (double) ((double)tv.tv_sec + (((double)tv.tv_usec) * 0.000001)); - newMessage.delta_time = (float) (newTime - lastTime); - lastTime = newTime; + case SND_SEQ_EVENT_QFRAME: // MIDI time code + if ( data->ignoreFlags & 0x02 ) break; - // Put newMessage in the circular buffer - midiBuffer[writeIndex] = newMessage; - writeIndex++; + case SND_SEQ_EVENT_SENSING: // Active sensing + if ( data->ignoreFlags & 0x04 ) break; - if( writeIndex >= MIDI_BUFFER_SIZE ) - writeIndex = 0; + case SND_SEQ_EVENT_SYSEX: + if ( (data->ignoreFlags & 0x01) ) break; + if ( ev->data.ext.len > apiData->bufferSize ) { + apiData->bufferSize = ev->data.ext.len; + free(buffer); + buffer = (unsigned char *) malloc(apiData->bufferSize); + if ( buffer == NULL ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n"; + break; } } - n--; + + default: + nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); + if ( nBytes <= 0 ) { +#if defined(__RTMIDI_DEBUG__) + std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; +#endif + break; + } + message.bytes.assign( buffer, &buffer[nBytes] ); + + // Save last status byte in case of running status. + if ( message.bytes[0] & 0x80 ) lastStatus = message.bytes[0]; + else if ( lastStatus ) message.bytes.insert( message.bytes.begin(), lastStatus ); + // I found the ALSA sequencer documentation to be very inadequate, + // especially regarding timestamps. So, I ignore the event + // timestamp and use system time to determine ours. + message.timeStamp = 0.0; + (void)gettimeofday(&tv, (struct timezone *)NULL); + time = (tv.tv_sec * 1000000) + tv.tv_usec; + lastTime = time; + time -= apiData->lastTime; + apiData->lastTime = lastTime; + if ( data->firstMessage == true ) + data->firstMessage = false; + else { + message.timeStamp = time * 0.000001; + } + } + + snd_seq_free_event(ev); + if ( message.bytes.size() == 0 ) continue; + + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; } } + + snd_midi_event_free( apiData->coder ); + apiData->coder = 0; return 0; } - -#if defined(__MIDIATOR__) - -/* - Hopefully, this special support for the MIDIator serial - port MIDI device is temporary and it will eventually be - incorporated into the OSS and ALSA APIs. - - This code is based almost entirely on David Topper's - driver code, which is available from: - - ftp://presto.music.virginia.edu/pub/midiator - - See the README-Linux STK document for details on how to - get the MIDIator setup correctly for use under linux. -*/ - -#include -#include - -#define BAUD_RATE B19200 -#define MAX_DEVICES 3 -#define MIDI_NAME "/dev/ttyS" - -void initializeMidiator(); - -RtMidi :: RtMidi(int device) +void RtMidiIn :: initialize( void ) { - char msg[256]; - char name[16]; - char deviceNames[MAX_DEVICES][16]; - midi_in = 0; + // Set up the ALSA sequencer client. + snd_seq_t *seq; + int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_INPUT, 0); + if ( result < 0 ) { + errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; + error( RtError::DRIVER_ERROR ); + } - int i, nDevices = 0; - for ( i=0; i= 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 serial port (%s) for MIDI input!", - deviceNames[deveyes]); - handleError(msg, StkError::MIDI_SYSTEM); - } - - printf("\nInitializing MIDIator MS-124w ... "); - initializeMidiator(); - printf("ready on serial port %s.\n", deviceNames[deveyes]); - - // 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); - } + // Save our api-specific connection information. + AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; + data->seq = seq; + data->vport = -1; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; } -#elif ( defined(__LINUX_ALSA__) || defined(__LINUX_JACK__) ) - -#define MAX_DEVICES 8 - -RtMidi :: RtMidi(int device) +// This function is used to count or get the pinfo structure for a given port number. +unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) { - midi_in = 0; - char msg[256]; - int result, card, deveyes, nDevices; - char name[32]; - char deviceNames[MAX_DEVICES][32]; - snd_ctl_t *handle; - snd_ctl_card_info_t *info; - snd_ctl_card_info_alloca(&info); + snd_seq_client_info_t *cinfo; + int client; + int count = 0; + snd_seq_client_info_alloca( &cinfo ); - // Count cards and devices - card = -1; - nDevices = 0; - snd_card_next(&card); - while ( card >= 0 ) { - sprintf(name, "hw:%d", card); - result = snd_ctl_open(&handle, name, 0); - if (result < 0) { - sprintf(msg, "RtMidi: ALSA control open (%i): %s.", card, snd_strerror(result)); - handleError(msg, StkError::WARNING); - goto next_card; + snd_seq_client_info_set_client( cinfo, -1 ); + while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { + client = snd_seq_client_info_get_client( cinfo ); + if ( client == 0 ) continue; + // Reset query info + snd_seq_port_info_set_client( pinfo, client ); + snd_seq_port_info_set_port( pinfo, -1 ); + while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { + if ( !PORT_TYPE( pinfo, type ) ) continue; + if ( count == portNumber ) return 1; + count++; } - result = snd_ctl_card_info(handle, info); - if (result < 0) { - sprintf(msg, "RtMidi: ALSA control hardware info (%i): %s.", card, snd_strerror(result)); - handleError(msg, StkError::WARNING); - goto next_card; - } - deveyes = -1; - while (1) { - result = snd_ctl_rawmidi_next_device(handle, &deveyes); - if (result < 0) { - sprintf(msg, "RtMidi: ALSA control next rawmidi device (%i): %s.", card, snd_strerror(result)); - handleError(msg, StkError::WARNING); - break; - } - if (deveyes < 0) - break; - sprintf( deviceNames[nDevices++], "hw:%d,%d", card, deveyes ); - if ( nDevices > MAX_DEVICES ) break; - } - if ( nDevices > MAX_DEVICES ) break; - next_card: - snd_ctl_close(handle); - snd_card_next(&card); - } + } - if (nDevices == 0) { - sprintf(msg, "RtMidi: no ALSA MIDI cards reported available."); - handleError(msg, StkError::MIDI_SYSTEM); - } - - // Check device argument and print list if necessary. - if ( device >= 0 && device < nDevices ) - deveyes = device; - else if ( nDevices == 1 ) - deveyes = 0; - else { - // Invalid device argument ... print list. - printf("\n"); - for ( int i=0; i= nDevices ) { - printf("\nType a MIDI device number from above: "); - fgets(choice, 16, stdin); - deveyes = atoi(choice); - } - printf("\n"); - } - - result = snd_rawmidi_open(&midi_in, NULL, deviceNames[deveyes], 0); - if (result) { - sprintf(msg, "RtMidi: Error opening ALSA raw MIDI device: %s.", 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; - - 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); - } + // If a negative portNumber was used, return the port count. + if ( portNumber < 0 ) return count; + return 0; } -#elif defined(__LINUX_OSS__) // normal OSS setup - -#define MAX_DEVICES 8 -#define MIDI_NAME "/dev/midi" - -RtMidi :: RtMidi(int device) +void RtMidiIn :: openPort( unsigned int portNumber ) { - 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() -{ - pthread_cancel(midi_input_thread); - delete [] midiBuffer; - -#if defined(__MIDIATOR__) - tcdrain(midi_in); - if (midi_in != 0) close(midi_in); -#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 -} - -int RtMidi::nextMessage() -{ - MIDIMESSAGE lastEvent; - - if ( readIndex == writeIndex ) return 0; - - lastEvent = midiBuffer[readIndex]; - - readIndex++; - if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; - - messageType = (int) (lastEvent.data[0] & 0xf0); - channel = (int) (lastEvent.data[0] & 0x0f); - byteTwo = (float) lastEvent.data[1]; - if (messageType == (int) MD_PITCH_BEND) - byteTwo = (float) lastEvent.data[2] + (byteTwo / 128.0); - else - byteThree = (float) lastEvent.data[2]; - deltaTime = (float) lastEvent.delta_time; - - return messageType; -} - -#if defined(__MIDIATOR__) - -void initializeMidiator() -{ - struct termios info; // serial port configuration info - int status; // serial port status - struct timeval tv; // to do a little time delay - - // Get the current serial port attributes, so we can change - // the ones we care about. - if (tcgetattr(midi_in, &info) < 0) { - fprintf(stderr, "RtMidi: ioctl to get tty info failed (MIDIator support)!"); + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); return; } - bzero(&info, sizeof(info)); - info.c_cflag = BAUD_RATE | CRTSCTS | CS8 | CLOCAL | CREAD; - info.c_iflag &= ~IGNCR; - info.c_oflag &= ~IGNCR; - - // set input mode (non-canonical, no echo,...) - info.c_lflag = 0; - - info.c_cc[VTIME] = 1; // inter-character timer unused - info.c_cc[VMIN] = 1; // blocking read until 5 chars received - - tcflush(midi_in, TCIFLUSH); + unsigned int nSrc = this->getPortCount(); + if (nSrc < 1) { + errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } - // Set the attributes - tcsetattr(midi_in, TCSANOW, &info); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + std::ostringstream ost; + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } - // Startup sequence, as per ron@MIDI_DEV's instructions - // Many thanks to Ron for supporting Linux - // Step 1 - // Power down and deassert DTR and RTS - ioctl(midi_in, TIOCMGET, &status); - status &= ~TIOCM_DTR; - status &= ~TIOCM_RTS; - ioctl(midi_in, TIOCMSET, status); - // Wait 600 ms to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 600000; - select(0, NULL, NULL, NULL, &tv); + snd_seq_addr_t sender, receiver; + sender.client = snd_seq_port_info_get_client( pinfo ); + sender.port = snd_seq_port_info_get_port( pinfo ); + receiver.client = snd_seq_client_id( data->seq ); + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Input", + SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + if ( data->vport < 0 ) { + errorString_ = "RtMidiIn::openPort: ALSA error creating input port."; + error( RtError::DRIVER_ERROR ); + } + } - // Step 2 - // Power up and assert break - ioctl(midi_in, TIOCMGET, &status); - status |= TCSBRK; - ioctl(midi_in, TIOCMSET, status); - // Wait 300 ms to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 300000; - select(0, NULL, NULL, NULL, &tv); + receiver.port = data->vport; - // Step 3 - // Set input mode - ioctl(midi_in, TIOCMGET, &status); - status &= ~TIOCM_DTR; - status |= TIOCM_RTS; - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); + // Make subscription + snd_seq_port_subscribe_malloc( &data->subscription ); + snd_seq_port_subscribe_set_sender(data->subscription, &sender); + snd_seq_port_subscribe_set_dest(data->subscription, &receiver); + snd_seq_port_subscribe_set_time_update(data->subscription, 1); + snd_seq_port_subscribe_set_time_real(data->subscription, 1); + if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { + errorString_ = "RtMidiIn::openPort: ALSA error making port connection."; + error( RtError::DRIVER_ERROR ); + } - // Step 4 - // Set input mode - ioctl(midi_in, TIOCMGET, &status); - status |= TIOCM_DTR; - status &= TIOCM_RTS; - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); + if ( inputData_.doInput == false ) { + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setschedpolicy(&attr, SCHED_RR); - // Step 5 - // Set output mode - // Bitval = RTS, clock = DTR + inputData_.doInput = true; + int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); + pthread_attr_destroy(&attr); + if (err) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + inputData_.doInput = false; + errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; + error( RtError::THREAD_ERROR ); + } + } - // 1 - ioctl(midi_in, TIOCMGET, &status); - status &= ~TIOCM_DTR; /* 0 */ - status |= TIOCM_RTS; /* 1 */ - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); - // - ioctl(midi_in, TIOCMGET, &status); - status |= TIOCM_DTR; // 1 rising edge clock - status |= TIOCM_RTS; // 1 - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); - - // 1 - ioctl(midi_in, TIOCMGET, &status); - status &= ~TIOCM_DTR; // 0 - status |= TIOCM_RTS; // 1 - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); - // - ioctl(midi_in, TIOCMGET, &status); - status |= TIOCM_DTR; // 1 rising edge clock - status |= TIOCM_RTS; // 1 - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); - - // 0 - ioctl(midi_in, TIOCMGET, &status); - status &= ~TIOCM_DTR; // 0 - status &= ~TIOCM_RTS; // 0 - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); - // - ioctl(midi_in, TIOCMGET, &status); - status |= TIOCM_DTR; // 1 rising edge clock - status &= ~TIOCM_RTS; // 0 - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); - - // Step 6 ... necessary ? - // Set RTS=0,DTR=1 ... but they already are from previous ^ - // - ioctl(midi_in, TIOCMGET, &status); - status |= TIOCM_DTR; // 1 rising edge clock - status &= ~TIOCM_RTS; // 0 - ioctl(midi_in, TIOCMSET, status); - // Wait 40 us to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 40; - select(0, NULL, NULL, NULL, &tv); - - // Step 7 - // Deassert break - ioctl(midi_in, TIOCMGET, &status); - status &= ~TCSBRK; - ioctl(midi_in, TIOCMSET, status); - // Wait 100 ms to make sure everything is stable - tv.tv_sec = 0; - tv.tv_usec = 100000; - select(0, NULL, NULL, NULL, &tv); - // End Midiator startup sequence -- midi_dev_type = MIDIATOR + connected_ = true; } -#endif // MIDIator -#elif defined(__OS_WINDOWS__) +void RtMidiIn :: openVirtualPort() +{ + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Input", + SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + if ( data->vport < 0 ) { + errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port."; + error( RtError::DRIVER_ERROR ); + } + } + + if ( inputData_.doInput == false ) { + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + + inputData_.doInput = true; + int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); + pthread_attr_destroy(&attr); + if (err) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + inputData_.doInput = false; + errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; + error( RtError::THREAD_ERROR ); + } + } +} + +void RtMidiIn :: closePort( void ) +{ + if ( connected_ ) { + AlsaMidiData *data = static_cast (apiData_); + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + connected_ = false; + } +} + +RtMidiIn :: ~RtMidiIn() +{ + // Close a connection if it exists. + closePort(); + + // Shutdown the input thread. + AlsaMidiData *data = static_cast (apiData_); + if ( inputData_.doInput ) { + inputData_.doInput = false; + pthread_join( data->thread, NULL ); + } + + // Cleanup. + if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); + snd_seq_close( data->seq ); + delete data; +} + +unsigned int RtMidiIn :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); +} + +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { + std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); + return stringName; + } + + // If we get here, we didn't find a match. + errorString_ = "RtMidiIn::getPortName: error looking for port name!"; + error( RtError::INVALID_PARAMETER ); + return 0; +} + +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: RtMidiOut +//*********************************************************************// + +unsigned int RtMidiOut :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); +} + +std::string RtMidiOut :: getPortName( unsigned int portNumber ) +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { + std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); + return stringName; + } + + // If we get here, we didn't find a match. + errorString_ = "RtMidiOut::getPortName: error looking for port name!"; + error( RtError::INVALID_PARAMETER ); + return 0; +} + +void RtMidiOut :: initialize( void ) +{ + // Set up the ALSA sequencer client. + snd_seq_t *seq; + int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0); + if ( result < 0 ) { + errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object."; + error( RtError::DRIVER_ERROR ); + } + + // Set client name. + snd_seq_set_client_name(seq, "RtMidi Output Client"); + + // Save our api-specific connection information. + AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; + data->seq = seq; + data->vport = -1; + data->bufferSize = 32; + data->coder = 0; + data->buffer = 0; + result = snd_midi_event_new( data->bufferSize, &data->coder ); + if ( result < 0 ) { + delete data; + errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n"; + error( RtError::DRIVER_ERROR ); + } + data->buffer = (unsigned char *) malloc( data->bufferSize ); + if ( data->buffer == NULL ) { + delete data; + errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; + error( RtError::MEMORY_ERROR ); + } + snd_midi_event_init( data->coder ); + apiData_ = (void *) data; +} + +void RtMidiOut :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nSrc = this->getPortCount(); + if (nSrc < 1) { + errorString_ = "RtMidiOut::openPort: no MIDI output sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + std::ostringstream ost; + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + snd_seq_addr_t sender, receiver; + receiver.client = snd_seq_port_info_get_client( pinfo ); + receiver.port = snd_seq_port_info_get_port( pinfo ); + sender.client = snd_seq_client_id( data->seq ); + + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + if ( data->vport < 0 ) { + errorString_ = "RtMidiOut::openPort: ALSA error creating output port."; + error( RtError::DRIVER_ERROR ); + } + } + + sender.port = data->vport; + + // Make subscription + snd_seq_port_subscribe_malloc( &data->subscription ); + snd_seq_port_subscribe_set_sender(data->subscription, &sender); + snd_seq_port_subscribe_set_dest(data->subscription, &receiver); + snd_seq_port_subscribe_set_time_update(data->subscription, 1); + snd_seq_port_subscribe_set_time_real(data->subscription, 1); + if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { + errorString_ = "RtMidiOut::openPort: ALSA error making port connection."; + error( RtError::DRIVER_ERROR ); + } + + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + AlsaMidiData *data = static_cast (apiData_); + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort() +{ + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + + if ( data->vport < 0 ) { + errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port."; + error( RtError::DRIVER_ERROR ); + } + } +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); + if ( data->coder ) snd_midi_event_free( data->coder ); + if ( data->buffer ) free( data->buffer ); + snd_seq_close( data->seq ); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + int result; + AlsaMidiData *data = static_cast (apiData_); + unsigned int nBytes = message->size(); + if ( nBytes > data->bufferSize ) { + data->bufferSize = nBytes; + result = snd_midi_event_resize_buffer ( data->coder, nBytes); + if ( result != 0 ) { + errorString_ = "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer."; + error( RtError::DRIVER_ERROR ); + } + free (data->buffer); + data->buffer = (unsigned char *) malloc( data->bufferSize ); + if ( data->buffer == NULL ) { + errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; + error( RtError::MEMORY_ERROR ); + } + } + + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, data->vport); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_direct(&ev); + for ( unsigned int i=0; ibuffer[i] = message->at(i); + result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); + if ( result < (int)nBytes ) { + errorString_ = "RtMidiOut::sendMessage: event parsing error!"; + error( RtError::WARNING ); + return; + } + + // Send the event. + result = snd_seq_event_output(data->seq, &ev); + if ( result < 0 ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; + error( RtError::WARNING ); + } + snd_seq_drain_output(data->seq); +} + +#endif // __LINUX_ALSA__ + + +//*********************************************************************// +// API: IRIX MD +//*********************************************************************// + +// API information gleamed from: +// http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro + +// If the Makefile doesn't work, try the following: +// CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd +// CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd +// CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd +// CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd + +#if defined(__IRIX_MD__) + +#include +#include +#include + +// Irix MIDI header file. +#include + +// A structure to hold variables related to the IRIX API +// implementation. +struct IrixMidiData { + MDport port; + pthread_t thread; +}; + +//*********************************************************************// +// API: IRIX +// Class Definitions: RtMidiIn +//*********************************************************************// + +extern "C" void *irixMidiHandler( void *ptr ) +{ + RtMidiIn::RtMidiInData *data = static_cast (ptr); + IrixMidiData *apiData = static_cast (data->apiData); + + bool continueSysex = false; + unsigned char status; + unsigned short size; + MDevent event; + int fd = mdGetFd( apiData->port ); + if ( fd < 0 ) { + data->doInput = false; + std::cerr << "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n"; + return 0; + } + + fd_set mask, rmask; + FD_ZERO( &mask ); + FD_SET( fd, &mask ); + struct timeval timeout = {0, 0}; + RtMidiIn::MidiMessage message; + int result; + + while ( data->doInput ) { + + rmask = mask; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if ( select( fd+1, &rmask, NULL, NULL, &timeout ) <= 0 ) { + // No data pending ... sleep a bit. + usleep( 1000 ); + continue; + } + + // If here, there should be data. + result = mdReceive( apiData->port, &event, 1); + if ( result <= 0 ) { + std::cerr << "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n"; + continue; + } + + message.timeStamp = event.stamp * 0.000000001; + + size = 0; + status = event.msg[0]; + if ( !(status & 0x80) ) continue; + if ( status == 0xF0 ) { + // Sysex message ... can be segmented across multiple messages. + if ( !(data->ignoreFlags & 0x01) ) { + if ( continueSysex ) { + // We have a continuing, segmented sysex message. Append + // the new bytes to our existing message. + for ( int i=0; iusingCallback && message.bytes.size() > 0 ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + } + } + mdFree( NULL ); + continue; + } + else if ( status < 0xC0 ) size = 3; + else if ( status < 0xE0 ) size = 2; + else if ( status < 0xF0 ) size = 3; + else if ( status < 0xF3 ) { + if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) { + // A MIDI time code message and we're not ignoring it. + size = 3; + } + } + else if ( status == 0xF3 ) size = 2; + else if ( status == 0xFE ) { // MIDI active sensing + if ( !(data->ignoreFlags & 0x04) ) + size = 1; + } + else size = 1; + + // Copy the MIDI data to our vector. + if ( size ) { + message.bytes.assign( &event.msg[0], &event.msg[size] ); + // Invoke the user callback function or queue the message. + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + } + + return 0; +} + +void RtMidiIn :: initialize( void ) +{ + // Initialize the Irix MIDI system. At the moment, we will not + // worry about a return value of zero (ports) because there is a + // chance the user could plug something in after instantiation. + int nPorts = mdInit(); + + // Create our api-specific connection information. + IrixMidiData *data = (IrixMidiData *) new IrixMidiData; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; +} + +void RtMidiIn :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + int nPorts = mdInit(); + if (nPorts < 1) { + errorString_ = "RtMidiIn::openPort: no Irix MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + IrixMidiData *data = static_cast (apiData_); + data->port = mdOpenInPort( mdGetName(portNumber) ); + if ( data->port == NULL ) { + ost << "RtMidiIn::openPort: Irix error opening the port (" << portNumber << ")."; + errorString_ = ost.str(); + error( RtError::DRIVER_ERROR ); + } + mdSetStampMode(data->port, MD_DELTASTAMP); + + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + + inputData_.doInput = true; + int err = pthread_create(&data->thread, &attr, irixMidiHandler, &inputData_); + pthread_attr_destroy(&attr); + if (err) { + mdClosePort( data->port ); + inputData_.doInput = false; + errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; + error( RtError::THREAD_ERROR ); + } + + connected_ = true; +} + +void RtMidiIn :: openVirtualPort() +{ + // This function cannot be implemented for the Irix MIDI API. + errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!"; + error( RtError::WARNING ); +} + +void RtMidiIn :: closePort( void ) +{ + if ( connected_ ) { + IrixMidiData *data = static_cast (apiData_); + mdClosePort( data->port ); + connected_ = false; + + // Shutdown the input thread. + inputData_.doInput = false; + pthread_join( data->thread, NULL ); + } +} + +RtMidiIn :: ~RtMidiIn() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + IrixMidiData *data = static_cast (apiData_); + delete data; +} + +unsigned int RtMidiIn :: getPortCount() +{ + int nPorts = mdInit(); + if ( nPorts >= 0 ) return nPorts; + else return 0; +} + +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + int nPorts = mdInit(); + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + std::string stringName = std::string( mdGetName( portNumber ) ); + return stringName; +} + +//*********************************************************************// +// API: IRIX MD +// Class Definitions: RtMidiOut +//*********************************************************************// + +unsigned int RtMidiOut :: getPortCount() +{ + int nPorts = mdInit(); + if ( nPorts >= 0 ) return nPorts; + else return 0; +} + +std::string RtMidiOut :: getPortName( unsigned int portNumber ) +{ + int nPorts = mdInit(); + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + std::string stringName = std::string( mdGetName( portNumber ) ); + return stringName; +} + +void RtMidiOut :: initialize( void ) +{ + // Initialize the Irix MIDI system. At the moment, we will not + // worry about a return value of zero (ports) because there is a + // chance the user could plug something in after instantiation. + int nPorts = mdInit(); + + // Create our api-specific connection information. + IrixMidiData *data = (IrixMidiData *) new IrixMidiData; + apiData_ = (void *) data; +} + +void RtMidiOut :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + int nPorts = mdInit(); + if (nPorts < 1) { + errorString_ = "RtMidiOut::openPort: no Irix MIDI output sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nPorts ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + IrixMidiData *data = static_cast (apiData_); + data->port = mdOpenOutPort( mdGetName(portNumber) ); + if ( data->port == NULL ) { + ost << "RtMidiOut::openPort: Irix error opening the port (" << portNumber << ")."; + errorString_ = ost.str(); + error( RtError::DRIVER_ERROR ); + } + mdSetStampMode(data->port, MD_NOSTAMP); + + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + IrixMidiData *data = static_cast (apiData_); + mdClosePort( data->port ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort() +{ + // This function cannot be implemented for the Irix MIDI API. + errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!"; + error( RtError::WARNING ); +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + IrixMidiData *data = static_cast (apiData_); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + int result; + MDevent event; + IrixMidiData *data = static_cast (apiData_); + char *buffer = 0; + + unsigned int nBytes = message->size(); + if ( nBytes == 0 ) return; + event.stamp = 0; + if ( message->at(0) == 0xF0 ) { + if ( nBytes < 3 ) return; // check for bogus sysex + event.msg[0] = 0xF0; + event.msglen = nBytes; + buffer = (char *) malloc( nBytes ); + for ( int i=0; iat(i); + event.sysexmsg = buffer; + } + else { + for ( int i=0; iat(i); + } + + // Send the event. + result = mdSend( data->port, &event, 1 ); + if ( buffer ) free( buffer ); + if ( result < 1 ) { + errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!"; + error( RtError::WARNING ); + return; + } +} + +#endif // __IRIX_MD__ + +//*********************************************************************// +// API: Windows Multimedia Library (MM) +//*********************************************************************// + +// API information deciphered from: +// - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp + +#if defined(__WINDOWS_MM__) + +// The Windows MM API is based on the use of a callback function for +// MIDI input. We convert the system specific time stamps to delta +// time values. + +// Windows MM MIDI header files. #include #include -static void CALLBACK midiInputCallback( HMIDIOUT hmin, UINT inputStatus, - DWORD instancePtr, DWORD midiMessage, DWORD timestamp); +// A structure to hold variables related to the CoreMIDI API +// implementation. +struct WinMidiData { + HMIDIIN inHandle; // Handle to Midi Input Device + HMIDIOUT outHandle; // Handle to Midi Output Device + DWORD lastTime; + RtMidiIn::MidiMessage message; +}; -#define MIDI_NOTEON 0x90 -#define MIDI_NOTEOFF 0x80 -#define MIDI_POLYKEYPRESSURE 0xA0 -#define MIDI_CHANNELPRESSURE 0xD0 -#define MIDI_PROGRAMCHANGE 0xC0 -#define MIDI_CONTROLCHANGE 0xB0 -#define MIDI_PITCHBEND 0xE0 +//*********************************************************************// +// API: Windows MM +// Class Definitions: RtMidiIn +//*********************************************************************// -typedef struct { - DWORD data; - DWORD time; -} MIDIMESSAGE; - -MIDIMESSAGE *midiBuffer; - -static void CALLBACK midiInputCallback( HMIDIOUT hmin, UINT inputStatus, - DWORD instancePtr, DWORD midiMessage, DWORD timestamp) +static void CALLBACK midiInputCallback( HMIDIOUT hmin, + UINT inputStatus, + DWORD instancePtr, + DWORD midiMessage, + DWORD timestamp ) { - MIDIMESSAGE newMessage; - - switch (inputStatus) { + if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA ) return; - case MIM_DATA: + //RtMidiIn::RtMidiInData *data = static_cast (instancePtr); + RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; + WinMidiData *apiData = static_cast (data->apiData); - // Ignore Active Sensing messages - if ((midiMessage & 0xff) == 0xfe || (midiMessage & 0xff) == 0xf8) { - break; + // Calculate time stamp. + apiData->message.timeStamp = 0.0; + if ( data->firstMessage == true ) data->firstMessage = false; + else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; + apiData->lastTime = timestamp; + + if ( inputStatus == MIM_DATA ) { // Channel or system message + + // Make sure the first byte is a status byte. + unsigned char status = (unsigned char) (midiMessage & 0x000000FF); + if ( !(status & 0x80) ) return; + + // Determine the number of bytes in the MIDI message. + unsigned short nBytes = 1; + if ( status < 0xC0 ) nBytes = 3; + else if ( status < 0xE0 ) nBytes = 2; + else if ( status < 0xF0 ) nBytes = 3; + else if ( status < 0xF3 ) { + // A MIDI time code message and we're ignoring it. + if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return; + nBytes = 3; + } + else if ( status == 0xF3 ) nBytes = 2; + else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { + // A MIDI active sensing message and we're ignoring it. + return; } - newMessage.data = midiMessage; - newMessage.time = timestamp; - // Put newMessage in the circular buffer - midiBuffer[writeIndex] = newMessage; - writeIndex++; + // Copy bytes to our MIDI message. + unsigned char *ptr = (unsigned char *) &midiMessage; + for ( int i=0; imessage.bytes.push_back( *ptr++ ); + } + else { // Sysex message + MIDIHDR *sysex = ( MIDIHDR *) midiMessage; + for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) + apiData->message.bytes.push_back( sysex->lpData[i] ); + if ( apiData->message.bytes.back() != 0xF7 ) return; + } - if( writeIndex >= MIDI_BUFFER_SIZE ) - writeIndex = 0; - break; + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( data->queueLimit > data->queue.size() ) + data->queue.push( apiData->message ); + else + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + } - default: - break; + // Clear the vector for the next input message. Note that doing + // this here allows our code to work for sysex messages which are + // segmented across multiple buffers. + apiData->message.bytes.clear(); +} + +void RtMidiIn :: initialize( void ) +{ + // We'll issue a warning here if no devices are available but not + // throw an error since the user can plugin something later. + unsigned int nDevices = midiInGetNumDevs(); + if ( nDevices == 0 ) { + errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available."; + error( RtError::WARNING ); + } + + // Save our api-specific connection information. + WinMidiData *data = (WinMidiData *) new WinMidiData; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; + data->message.bytes.clear(); // needs to be empty for first input message +} + +void RtMidiIn :: openPort( unsigned int portNumber ) +{ + if ( connected_ ) { + errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; + error( RtError::WARNING ); + return; + } + + unsigned int nDevices = midiInGetNumDevs(); + if (nDevices == 0) { + errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nDevices ) { + ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + WinMidiData *data = static_cast (apiData_); + MMRESULT result = midiInOpen( &data->inHandle, + portNumber, + (DWORD)&midiInputCallback, + (DWORD)&inputData_, + CALLBACK_FUNCTION ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port."; + error( RtError::DRIVER_ERROR ); + } + + result = midiInStart( data->inHandle ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port."; + error( RtError::DRIVER_ERROR ); + } + + connected_ = true; +} + +void RtMidiIn :: openVirtualPort() +{ + // This function cannot be implemented for the Windows MM MIDI API. + errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtError::WARNING ); +} + +void RtMidiIn :: closePort( void ) +{ + if ( connected_ ) { + WinMidiData *data = static_cast (apiData_); + midiInReset( data->inHandle ); + midiInStop( data->inHandle ); + midiInClose( data->inHandle ); + connected_ = false; } } -HMIDIIN hMidiIn ; // Handle to Midi Input Device - -RtMidi :: RtMidi(int device) +RtMidiIn :: ~RtMidiIn() { - MMRESULT result; - UINT uDeviceID; - int deveyes; + // Close a connection if it exists. + closePort(); + + // Cleanup. + WinMidiData *data = static_cast (apiData_); + delete data; +} + +unsigned int RtMidiIn :: getPortCount() +{ + return midiInGetNumDevs(); +} + +std::string RtMidiIn :: getPortName( unsigned int portNumber ) +{ + unsigned int nDevices = midiInGetNumDevs(); + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + MIDIINCAPS deviceCaps; - UINT i; - char msg[256]; + MMRESULT result = midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); - uDeviceID = midiInGetNumDevs(); - if (uDeviceID < 1) { - sprintf(msg, "RtMidi: No windoze MIDI device available."); - handleError(msg, StkError::MIDI_SYSTEM); + std::string stringName = std::string( deviceCaps.szPname ); + return stringName; +} + +//*********************************************************************// +// API: Windows MM +// Class Definitions: RtMidiOut +//*********************************************************************// + +unsigned int RtMidiOut :: getPortCount() +{ + return midiOutGetNumDevs(); +} + +std::string RtMidiOut :: getPortName( unsigned int portNumber ) +{ + unsigned int nDevices = midiOutGetNumDevs(); + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); } - // Our normal scheme is to use the default device if no argument - // is supplied to RtMidi() or if the argument = 0. However, - // there is no way to specify a default MIDI device under windoze. - // So, I'm going to print the list if device is not a valid identifier. - if ( device >= 0 && device < (int)uDeviceID ) { - // try to open device specified as argument - result = midiInOpen(&hMidiIn, device, - (DWORD)&midiInputCallback, - (DWORD)NULL, - CALLBACK_FUNCTION); - if (result == MMSYSERR_NOERROR) - goto have_good_device; + MIDIOUTCAPS deviceCaps; + MMRESULT result = midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); + + std::string stringName = std::string( deviceCaps.szPname ); + return stringName; +} + +void RtMidiOut :: initialize( void ) +{ + // We'll issue a warning here if no devices are available but not + // throw an error since the user can plug something in later. + unsigned int nDevices = midiOutGetNumDevs(); + if ( nDevices == 0 ) { + errorString_ = "RtMidiOut::initialize: no MIDI output devices currently available."; + error( RtError::WARNING ); } - printf("\nMIDI input interfaces available: %i\n", uDeviceID); - for (i=0; i 1) { - char choice[16]; - while ( deveyes < 0 || deveyes >= (int)uDeviceID ) { - printf("\nType a MIDI device number from above: "); - fgets(choice, 16, stdin); - deveyes = atoi(choice); + unsigned int nDevices = midiOutGetNumDevs(); + if (nDevices < 1) { + errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; + error( RtError::NO_DEVICES_FOUND ); + } + + std::ostringstream ost; + if ( portNumber >= nDevices ) { + ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtError::INVALID_PARAMETER ); + } + + WinMidiData *data = static_cast (apiData_); + MMRESULT result = midiOutOpen( &data->outHandle, + portNumber, + (DWORD)NULL, + (DWORD)NULL, + CALLBACK_NULL ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port."; + error( RtError::DRIVER_ERROR ); + } + + connected_ = true; +} + +void RtMidiOut :: closePort( void ) +{ + if ( connected_ ) { + WinMidiData *data = static_cast (apiData_); + midiOutReset( data->outHandle ); + midiOutClose( data->outHandle ); + connected_ = false; + } +} + +void RtMidiOut :: openVirtualPort() +{ + // This function cannot be implemented for the Windows MM MIDI API. + errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtError::WARNING ); +} + +RtMidiOut :: ~RtMidiOut() +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + WinMidiData *data = static_cast (apiData_); + delete data; +} + +void RtMidiOut :: sendMessage( std::vector *message ) +{ + unsigned int nBytes = message->size(); + if ( nBytes == 0 ) { + errorString_ = "RtMidiOut::sendMessage: message argument is empty!"; + error( RtError::WARNING ); + return; + } + + MMRESULT result; + WinMidiData *data = static_cast (apiData_); + if ( message->at(0) == 0xF0 ) { // Sysex message + + // Allocate buffer for sysex data. + char *buffer = (char *) malloc( nBytes ); + if ( buffer == NULL ) { + errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!"; + error( RtError::MEMORY_ERROR ); + } + + // Copy data to buffer. + for ( unsigned int i=0; iat(i); + + // Create and prepare MIDIHDR structure. + MIDIHDR sysex; + sysex.lpData = (LPSTR) buffer; + sysex.dwBufferLength = nBytes; + sysex.dwFlags = 0; + result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + errorString_ = "RtMidiOut::sendMessage: error preparing sysex header."; + error( RtError::DRIVER_ERROR ); + } + + // Send the message. + result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + errorString_ = "RtMidiOut::sendMessage: error sending sysex message."; + error( RtError::DRIVER_ERROR ); + } + + // Unprepare the buffer and MIDIHDR. + while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); + free( buffer ); + + } + else { // Channel or system message. + + // Make sure the message size isn't too big. + if ( nBytes > 3 ) { + errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!"; + error( RtError::WARNING ); + return; + } + + // Pack MIDI bytes into double word. + DWORD packet; + unsigned char *ptr = (unsigned char *) &packet; + for ( unsigned int i=0; iat(i); + ptr++; + } + + // Send the message immediately. + result = midiOutShortMsg( data->outHandle, packet ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message."; + error( RtError::DRIVER_ERROR ); } } - else deveyes = 0; - - // Open the port and return any errors - result = midiInOpen(&hMidiIn, (UINT)deveyes, - (DWORD)&midiInputCallback, - (DWORD)NULL, - CALLBACK_FUNCTION); - if (result != MMSYSERR_NOERROR) { - sprintf(msg, "RtMidi: Cannot open Windoze MIDI interface %d.", deveyes); - handleError(msg, StkError::MIDI_SYSTEM); - } - - have_good_device: // the current device is what we will use - - // Set up the circular buffer for the Midi Input Messages - midiBuffer = new MIDIMESSAGE[MIDI_BUFFER_SIZE]; - readIndex = 0; - writeIndex = 0; - - midiInStart( hMidiIn ); -} - -RtMidi :: ~RtMidi() -{ - midiInReset( hMidiIn ); - midiInStop( hMidiIn ); - midiInClose( hMidiIn ); - delete [] midiBuffer; -} - -int RtMidi :: nextMessage() -{ - int status; - int byte1; - int byte2; - MIDIMESSAGE lastEvent; - static DWORD lastTime = 0; - static DWORD newTime = 0; - - if ( readIndex == writeIndex ) return 0; - - lastEvent = midiBuffer[readIndex]; - - readIndex++; - if ( readIndex >= MIDI_BUFFER_SIZE ) readIndex = 0; - - status = (int) (lastEvent.data & 0xff); - byte1 = (int) (lastEvent.data & 0xff00) >> 8; - byte2 = (int) (lastEvent.data & 0xff0000) >> 16; - channel = (int) (status & 0x0f); - status &= 0xf0; // Clear lower byte of status - newTime = lastEvent.time; - deltaTime = (float) (newTime - lastTime) * 0.001; - lastTime = newTime; - - if ((status == MIDI_PROGRAMCHANGE) || - (status == MIDI_CHANNELPRESSURE)) - { - messageType = status; - byteTwo = (float) byte1; - } - else if ((status == MIDI_NOTEON) || (status == MIDI_NOTEOFF) || - (status == MIDI_CONTROLCHANGE) || (status == MIDI_POLYKEYPRESSURE)) - { - messageType = status; - byteTwo = (float) byte1; - byteThree = (float) byte2; - } - else if (status == MIDI_PITCHBEND) - { - messageType = status; - byteTwo = (float) (byte1 * ONE_OVER_128); - byteTwo += (float) byte2; - } - else - { - messageType = -1; - } - - return messageType; -} - -#endif - -void RtMidi :: printMessage() const -{ - printf("type = %d, channel = %d, byte2 = %f, byte3 = %f\n", - this->getType(), this->getChannel(), this->getByteTwo(), - this->getByteThree()); -} - -int RtMidi :: getType() const -{ - return messageType; -} - -int RtMidi :: getChannel() const -{ - return channel; -} - -MY_FLOAT RtMidi :: getByteTwo() const -{ - return byteTwo; -} - -MY_FLOAT RtMidi :: getByteThree() const -{ - return byteThree; -} - -MY_FLOAT RtMidi :: getDeltaTime() const -{ - return deltaTime; } +#endif // __WINDOWS_MM__ diff --git a/src/RtWvIn.cpp b/src/RtWvIn.cpp index 4eb81d1..658f0d2 100644 --- a/src/RtWvIn.cpp +++ b/src/RtWvIn.cpp @@ -1,6 +1,6 @@ /***************************************************/ /*! \class RtWvIn - \brief STK realtime audio input class. + \brief STK realtime audio (blocking) input class. This class provides a simplified interface to RtAudio for realtime audio input. It is a @@ -15,17 +15,17 @@ For single-channel data, these methods return equivalent values. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "RtWvIn.h" -RtWvIn :: RtWvIn(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers ) +RtWvIn :: RtWvIn(int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers ) { - channels = nChannels; + channels_ = nChannels; int size = bufferFrames; - RtAudioFormat format = ( sizeof(MY_FLOAT) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; audio_ = 0; try { @@ -37,17 +37,17 @@ RtWvIn :: RtWvIn(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrame // Now open a stream and get the buffer pointer. try { - audio_->openStream(0, 0, device, channels, format, + audio_->openStream(0, 0, device, channels_, format, (int)sampleRate, &size, nBuffers); - data = (MY_FLOAT *) audio_->getStreamBuffer(); + data_ = (StkFloat *) 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(); delete audio_; - data = 0; // RtAudio deletes the buffer itself. + data_ = 0; // RtAudio deletes the buffer itself. } void RtWvIn :: start() @@ -76,34 +76,36 @@ void RtWvIn :: stop() } } -MY_FLOAT RtWvIn :: lastOut(void) const +StkFloat RtWvIn :: lastOut(void) const { return WvIn::lastOut(); } -MY_FLOAT RtWvIn :: tick(void) +StkFloat RtWvIn :: tick(void) { - tickFrame(); + this->tickFrame(); return lastOut(); } -MY_FLOAT *RtWvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *RtWvIn :: tick(StkFloat *vector, unsigned int vectorSize) { - for ( unsigned int i=0; istart(); if (counter_ == 0) { try { @@ -114,18 +116,23 @@ const MY_FLOAT *RtWvIn :: tickFrame(void) } } - long temp = counter_ * channels; - for (unsigned int i=0; i= (long) bufferSize) + if (counter_ >= (long) bufferSize_) counter_ = 0; - return lastOutput; + return lastOutputs_; } -MY_FLOAT *RtWvIn :: tickFrame(MY_FLOAT *frameVector, unsigned int frames) +StkFloat *RtWvIn :: tickFrame(StkFloat *frameVector, unsigned int frames) { return WvIn::tickFrame( frameVector, frames ); } + +StkFrames& RtWvIn :: tickFrame( StkFrames& frames ) +{ + return WvIn::tickFrame( frames ); +} diff --git a/src/RtWvOut.cpp b/src/RtWvOut.cpp index 945f6a2..6dbd3c8 100644 --- a/src/RtWvOut.cpp +++ b/src/RtWvOut.cpp @@ -1,6 +1,6 @@ /***************************************************/ /*! \class RtWvOut - \brief STK realtime audio output class. + \brief STK realtime audio (blocking) output class. This class provides a simplified interface to RtAudio for realtime audio output. It is a @@ -14,19 +14,19 @@ takes a pointer to multi-channel sample frame data. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "RtWvOut.h" #include -RtWvOut :: RtWvOut( unsigned int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers ) +RtWvOut :: RtWvOut( unsigned int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers ) { // We'll let RtAudio deal with channel and srate limitations. - channels = nChannels; + channels_ = nChannels; bufferSize_ = bufferFrames; - RtAudioFormat format = ( sizeof(MY_FLOAT) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; audio_ = 0; try { @@ -36,11 +36,11 @@ RtWvOut :: RtWvOut( unsigned int nChannels, MY_FLOAT sampleRate, int device, int handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } - // Now open a stream and set the callback function. + // Now open a stream and get the buffer pointer. try { - audio_->openStream(device, (int)channels, 0, 0, format, + audio_->openStream(device, (int)channels_, 0, 0, format, (int)sampleRate, &bufferSize_, nBuffers); - data = (MY_FLOAT *) audio_->getStreamBuffer(); + dataPtr_ = (StkFloat *) audio_->getStreamBuffer(); } catch (RtError &error) { handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); @@ -54,7 +54,6 @@ RtWvOut :: ~RtWvOut() if ( !stopped_ ) audio_->stopStream(); delete audio_; - data = 0; } void RtWvOut :: start() @@ -75,62 +74,155 @@ void RtWvOut :: stop() unsigned long RtWvOut :: getFrames( void ) const { - return totalCount; + return totalCount_; } -MY_FLOAT RtWvOut :: getTime( void ) const +StkFloat RtWvOut :: getTime( void ) const { - return (MY_FLOAT) totalCount / Stk::sampleRate(); + return (StkFloat) totalCount_ / Stk::sampleRate(); } -void RtWvOut :: tick(const MY_FLOAT sample) +void RtWvOut :: tick( const StkFloat sample ) { if ( stopped_ ) start(); - for ( unsigned int j=0; jclipTest( input ); + for ( unsigned int j=0; j= (unsigned int )bufferSize_ ) { + if ( counter_ >= (unsigned int )bufferSize_ ) { try { audio_->tickStream(); } catch (RtError &error) { handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } - counter = 0; + counter_ = 0; } } -void RtWvOut :: tick(const MY_FLOAT *vector, unsigned int vectorSize) +void RtWvOut :: tick( const StkFloat *vector, unsigned int vectorSize ) { for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( stopped_ ) + start(); + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; iclipTest( sample ); + dataPtr_[counter_*channels_+j] = sample; } - counter++; + counter_++; + totalCount_++; - if ( counter >= (unsigned int)bufferSize_ ) { + if ( counter_ >= (unsigned int)bufferSize_ ) { try { audio_->tickStream(); } catch (RtError &error) { handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); } - counter = 0; + counter_ = 0; } } } +void RtWvOut :: tickFrame( const StkFrames& frames ) +{ + if ( channels_ != frames.channels() ) { + errorString_ << "RtWvOut::tickFrame(): incompatible channel value in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( stopped_ ) + start(); + + unsigned int j; + StkFloat sample; + if ( channels_ == 1 || frames.interleaved() ) { + unsigned long iFrames = 0, iData = counter_; + for ( unsigned int i=0; iclipTest( sample ); + dataPtr_[iData++] = sample; + } + counter_++; + totalCount_++; + + if ( counter_ >= (unsigned int)bufferSize_ ) { + try { + audio_->tickStream(); + } + catch (RtError &error) { + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); + } + counter_ = 0; + } + } + } + else { + unsigned int hop = frames.frames(); + unsigned long iData = counter_; + for ( unsigned int i=0; iclipTest( sample ); + dataPtr_[iData++] = sample; + } + counter_++; + totalCount_++; + + if ( counter_ >= (unsigned int)bufferSize_ ) { + try { + audio_->tickStream(); + } + catch (RtError &error) { + handleError( error.getMessageString(), StkError::AUDIO_SYSTEM ); + } + counter_ = 0; + } + } + } +} diff --git a/src/SKINI.cpp b/src/SKINI.cpp deleted file mode 100644 index d1727ad..0000000 --- a/src/SKINI.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/***************************************************/ -/*! \class SKINI - \brief STK SKINI parsing class - - This class parses SKINI formatted text - messages. It can be used to parse individual - messages or it can be passed an entire file. - The file specification is Perry's and his - alone, but it's all text so it shouldn't be to - hard to figure out. - - SKINI (Synthesis toolKit Instrument Network - Interface) is like MIDI, but allows for - floating-point control changes, note numbers, - etc. The following example causes a sharp - middle C to be played with a velocity of 111.132: - - noteOn 60.01 111.13 - - See also SKINI.txt. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. -*/ -/***************************************************/ - -#include "SKINI.h" -#include -#include - -// Constructor for use when parsing SKINI strings (coming over socket -// for example. Use parseThis() method with string pointer. -SKINI :: SKINI() -{ -} - -// Constructor for reading SKINI files ... use nextMessage() method. -SKINI :: SKINI(char *fileName) -{ - char msg[256]; - - myFile = fopen(fileName,"r"); - if ((int) myFile < 0) { - sprintf(msg, "SKINI: Could not open or find file (%s).", fileName); - handleError(msg, StkError::FILE_NOT_FOUND); - } - - this->nextMessage(); -} - -SKINI :: ~SKINI() -{ -} - -/***************** SOME HANDY ROUTINES *******************/ - -#include "SKINI.tbl" - -#define __SK_MAX_FIELDS_ 5 -#define __SK_MAX_SIZE_ 32 - -short ignore(char aChar) -{ - short ignoreIt = 0; - if (aChar == 0) ignoreIt = 1; // Null String Termination - if (aChar == '\n') ignoreIt = 1; // Carraige Return??? - if (aChar == '/') ignoreIt = 2; // Comment Line - return ignoreIt; -} - -short delimit(char aChar) -{ - if (aChar == ' ' || // Space - aChar == ',' || // Or Comma - aChar == '\t') // Or Tab - return 1; - else - return 0; -} - -short nextChar(char* aString) -{ - int i; - - for (i=0;i<__SK_MAX_SIZE_;i++) { - if ( aString[i] != ' ' && // Space - aString[i] != ',' && // Or Comma - aString[i] != '\t' ) // Or Tab - return i; - } - return 1024; -} - -int subStrings(char *aString, - char someStrings[__SK_MAX_FIELDS_][__SK_MAX_SIZE_], - int somePointrs[__SK_MAX_FIELDS_], - char *remainderString) -{ - int notDone,howMany,point,temp; - notDone = 1; - howMany = 0; - point = 0; - temp = nextChar(aString); - if (temp >= __SK_MAX_SIZE_) { - notDone = 0; - printf("Confusion here: Ignoring this line\n"); - printf("%s\n",aString); - return howMany; - } - point = temp; - somePointrs[howMany] = point; - temp = 0; - while (notDone) { - if (aString[point] == '\n') { - notDone = 0; - } - else { - someStrings[howMany][temp++] = aString[point++]; - if (temp >= __SK_MAX_SIZE_) { - howMany = 0; - return howMany; - } - if (delimit(aString[point]) || aString[point] == '\n') { - someStrings[howMany][temp] = 0; - howMany += 1; - if (howMany < __SK_MAX_FIELDS_) { - temp = nextChar(&aString[point]); - point += temp; - somePointrs[howMany-1] = point; - temp = 0; - } - else { - temp = 0; - somePointrs[howMany-1] = point; - while(aString[point] != '\n') - remainderString[temp++] = aString[point++]; - remainderString[temp] = aString[point]; - } - } - } - } - // printf("Got: %i Strings:\n",howMany); - // for (temp=0;temp 0) - which = 0; - aField = 0; - strcpy(msgTypeString,someStrings[aField]); - while ((which < __SK_MaxMsgTypes_) && - (strcmp(msgTypeString, - skini_msgs[which].messageString))) { - which += 1; - } - if (which >= __SK_MaxMsgTypes_) { - messageType = 0; - printf("Couldn't parse this message field: =%s\n %s\n", - msgTypeString,aString); - return messageType; - } - else { - messageType = skini_msgs[which].type; - // printf("Message Token = %s type = %i\n", msgTypeString,messageType); - } - aField += 1; - - if (someStrings[aField][0] == '=') { - deltaTime = (MY_FLOAT) atof(&someStrings[aField][1]); - deltaTime = -deltaTime; - } - else { - deltaTime = (MY_FLOAT) atof(someStrings[aField]); - } - // printf("DeltaTime = %f\n",deltaTime); - aField += 1; - - channel = atoi(someStrings[aField]); - // printf("Channel = %i\n",channel); - aField += 1; - - if (skini_msgs[which].data2 != NOPE) { - if (skini_msgs[which].data2 == SK_INT) { - byteTwoInt = atoi(someStrings[aField]); - byteTwo = (MY_FLOAT) byteTwoInt; - } - else if (skini_msgs[which].data2 == SK_DBL) { - byteTwo = (MY_FLOAT) atof(someStrings[aField]); - byteTwoInt = (long) byteTwo; - } - else if (skini_msgs[which].data2 == SK_STR) { - temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */ - temp2 = 0; - while (aString[temp] != '\n') { - remainderString[temp2++] = aString[temp++]; - } - remainderString[temp2] = 0; - } - else { - byteTwoInt = skini_msgs[which].data2; - byteTwo = (MY_FLOAT) byteTwoInt; - aField -= 1; - } - - aField += 1; - if (skini_msgs[which].data3 != NOPE) { - if (skini_msgs[which].data3 == SK_INT) { - byteThreeInt = atoi(someStrings[aField]); - byteThree = (MY_FLOAT) byteThreeInt; - } - else if (skini_msgs[which].data3 == SK_DBL) { - byteThree = (MY_FLOAT) atof(someStrings[aField]); - byteThreeInt = (long) byteThree; - } - else if (skini_msgs[which].data3 == SK_STR) { - temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */ - temp2 = 0; - while (aString[temp] != '\n') { - remainderString[temp2++] = aString[temp++]; - } - remainderString[temp2] = 0; - } - else { - byteThreeInt = skini_msgs[which].data3; - byteThree = (MY_FLOAT) byteThreeInt; - } - } - else { - byteThreeInt = byteTwoInt; - byteThree = byteTwo; - } - } - } - return messageType; -} - -long SKINI :: nextMessage() -{ - int notDone; - char inputString[1024]; - - notDone = 1; - while (notDone) { - notDone = 0; - if (!fgets(inputString,1024,myFile)) { - printf("// End of Score. Thanks for using SKINI!!\n"); - messageType = -1; - return messageType; - } - else if (parseThis(inputString) == 0) { - notDone = 1; - } - } - return messageType; -} - -long SKINI :: getType() const -{ - return messageType; -} - -long SKINI :: getChannel() const -{ - return channel; -} - -MY_FLOAT SKINI :: getDelta() const -{ - return deltaTime; -} - -MY_FLOAT SKINI :: getByteTwo() const -{ - return byteTwo; -} - -long SKINI :: getByteTwoInt() const -{ - return byteTwoInt; -} - -MY_FLOAT SKINI :: getByteThree() const -{ - return byteThree; -} - -long SKINI :: getByteThreeInt() const -{ - return byteThreeInt; -} - -const char* SKINI :: getRemainderString() -{ - return remainderString; -} - -const char* SKINI :: getMessageTypeString() -{ - return msgTypeString; -} - -const char* SKINI :: whatsThisType(long type) -{ - int i = 0; - whatString[0] = 0; - for ( i=0; i<__SK_MaxMsgTypes_; i++ ) { - if ( type == skini_msgs[i].type ) { - strcat(whatString, skini_msgs[i].messageString); - strcat(whatString, ","); - } - } - return whatString; -} - -const char* SKINI :: whatsThisController(long contNum) -{ - int i = 0; - whatString[0] = 0; - for ( i=0; i<__SK_MaxMsgTypes_; i++) { - if ( skini_msgs[i].type == __SK_ControlChange_ - && contNum == skini_msgs[i].data2) { - strcat(whatString, skini_msgs[i].messageString); - strcat(whatString, ","); - } - } - return whatString; -} - - diff --git a/src/Sampler.cpp b/src/Sampler.cpp index aaf6bdf..4ce7d2a 100644 --- a/src/Sampler.cpp +++ b/src/Sampler.cpp @@ -2,10 +2,10 @@ /*! \class Sampler \brief STK sampling synthesis abstract base class. - This instrument contains up to 5 attack waves, - 5 looped waves, and an ADSR envelope. + This instrument provides an ADSR envelope, a one-pole filter, and + structures for an arbitrary number of attack and loop waves. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -15,45 +15,40 @@ Sampler :: Sampler() { // We don't make the waves here yet, because // we don't know what they will be. - adsr = new ADSR; - baseFrequency = 440.0; - filter = new OnePole; - attackGain = 0.25; - loopGain = 0.25; - whichOne = 0; + baseFrequency_ = 440.0; + attackGain_ = 0.25; + loopGain_ = 0.25; } Sampler :: ~Sampler() { - delete adsr; - delete filter; + unsigned int i; + for ( i=0; ikeyOn(); - attacks[0]->reset(); + // Reset all attack waves. + for ( unsigned int i=0; ireset(); + + // Start the envelope. + adsr_.keyOn(); + } void Sampler :: keyOff() { - adsr->keyOff(); + adsr_.keyOff(); } -void Sampler :: noteOff(MY_FLOAT amplitude) +void Sampler :: noteOff(StkFloat amplitude) { this->keyOff(); #if defined(_STK_DEBUG_) - cerr << "Sampler: NoteOff amplitude = " << amplitude << endl; + errorString_ << "Sampler::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } - -MY_FLOAT Sampler :: tick() -{ - lastOutput = attackGain * attacks[whichOne]->tick(); - lastOutput += loopGain * loops[whichOne]->tick(); - lastOutput = filter->tick(lastOutput); - lastOutput *= adsr->tick(); - return lastOutput; -} diff --git a/src/Saxofony.cpp b/src/Saxofony.cpp index d930385..8943f08 100644 --- a/src/Saxofony.cpp +++ b/src/Saxofony.cpp @@ -31,170 +31,179 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Saxofony.h" #include "SKINI.msg" -Saxofony :: Saxofony(MY_FLOAT lowestFrequency) +Saxofony :: Saxofony(StkFloat lowestFrequency) { - length = (long) (Stk::sampleRate() / lowestFrequency + 1); + length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); // Initialize blowing position to 0.2 of length / 2. - position = 0.2; - delays[0] = (DelayL *) new DelayL( (1.0-position) * (length >> 1), length ); - delays[1] = (DelayL *) new DelayL( position * (length >> 1), length ); + position_ = 0.2; + delays_[0].setMaximumDelay( length_ ); + delays_[0].setDelay( (1.0-position_) * (length_ >> 1) ); + delays_[1].setMaximumDelay( length_ ); + delays_[1].setDelay( (1.0-position_) * (length_ >> 1) ); - reedTable = new ReedTabl; - reedTable->setOffset((MY_FLOAT) 0.7); - reedTable->setSlope((MY_FLOAT) 0.3); - filter = new OneZero; - envelope = new Envelope; - noise = new Noise; + reedTable_.setOffset( 0.7 ); + reedTable_.setSlope( 0.3 ); // 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); + vibrato_ = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + vibrato_->setFrequency((StkFloat) 5.735); - outputGain = (MY_FLOAT) 0.3; - noiseGain = (MY_FLOAT) 0.2; - vibratoGain = (MY_FLOAT) 0.1; + outputGain_ = 0.3; + noiseGain_ = 0.2; + vibratoGain_ = 0.1; } Saxofony :: ~Saxofony() { - delete delays[0]; - delete delays[1]; - delete reedTable; - delete filter; - delete envelope; - delete noise; - delete vibrato; + delete vibrato_; } void Saxofony :: clear() { - delays[0]->clear(); - delays[1]->clear(); - filter->tick((MY_FLOAT) 0.0); + delays_[0].clear(); + delays_[1].clear(); + filter_.clear(); } -void Saxofony :: setFrequency(MY_FLOAT frequency) +void Saxofony :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Saxofony: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Saxofony::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } - MY_FLOAT delay = (Stk::sampleRate() / freakency) - (MY_FLOAT) 3.0; + StkFloat delay = (Stk::sampleRate() / freakency) - (StkFloat) 3.0; if (delay <= 0.0) delay = 0.3; - else if (delay > length) delay = length; + else if (delay > length_) delay = length_; - delays[0]->setDelay((1.0-position) * delay); - delays[1]->setDelay(position * delay); + delays_[0].setDelay( (1.0-position_) * delay ); + delays_[1].setDelay( position_ * delay ); } -void Saxofony :: setBlowPosition(MY_FLOAT aPosition) +void Saxofony :: setBlowPosition(StkFloat position) { - if (position == aPosition) return; + if ( position_ == position ) return; - if (aPosition < 0.0) position = 0.0; - else if (aPosition > 1.0) position = 1.0; - else position = aPosition; + if ( position < 0.0 ) position_ = 0.0; + else if ( position > 1.0 ) position_ = 1.0; + else position_ = position; - MY_FLOAT total_delay = delays[0]->getDelay(); - total_delay += delays[1]->getDelay(); + StkFloat totalDelay = delays_[0].getDelay(); + totalDelay += delays_[1].getDelay(); - delays[0]->setDelay((1.0-position) * total_delay); - delays[1]->setDelay(position * total_delay); + delays_[0].setDelay( (1.0-position_) * totalDelay ); + delays_[1].setDelay( position_ * totalDelay ); } -void Saxofony :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +void Saxofony :: startBlowing(StkFloat amplitude, StkFloat rate) { - envelope->setRate(rate); - envelope->setTarget(amplitude); + envelope_.setRate( rate ); + envelope_.setTarget( amplitude ); } -void Saxofony :: stopBlowing(MY_FLOAT rate) +void Saxofony :: stopBlowing(StkFloat rate) { - envelope->setRate(rate); - envelope->setTarget((MY_FLOAT) 0.0); + envelope_.setRate( rate ); + envelope_.setTarget( 0.0 ); } -void Saxofony :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Saxofony :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); - startBlowing((MY_FLOAT) 0.55 + (amplitude * 0.30), amplitude * 0.005); - outputGain = amplitude + 0.001; + this->setFrequency( frequency ); + this->startBlowing( 0.55 + (amplitude * 0.30), amplitude * 0.005 ); + outputGain_ = amplitude + 0.001; #if defined(_STK_DEBUG_) - std::cerr << "Saxofony: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Saxofony::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Saxofony :: noteOff(MY_FLOAT amplitude) +void Saxofony :: noteOff(StkFloat amplitude) { - this->stopBlowing(amplitude * 0.01); + this->stopBlowing( amplitude * 0.01 ); #if defined(_STK_DEBUG_) - std::cerr << "Saxofony: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Saxofony::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Saxofony :: tick() +StkFloat Saxofony :: tick() { - MY_FLOAT pressureDiff; - MY_FLOAT breathPressure; - MY_FLOAT temp; + StkFloat pressureDiff; + StkFloat breathPressure; + StkFloat temp; // Calculate the breath pressure (envelope + noise + vibrato) - breathPressure = envelope->tick(); - breathPressure += breathPressure * noiseGain * noise->tick(); - breathPressure += breathPressure * vibratoGain * vibrato->tick(); + breathPressure = envelope_.tick(); + breathPressure += breathPressure * noiseGain_ * noise_.tick(); + breathPressure += breathPressure * vibratoGain_ * vibrato_->tick(); - temp = -0.95 * filter->tick( delays[0]->lastOut() ); - lastOutput = temp - delays[1]->lastOut(); - pressureDiff = breathPressure - lastOutput; - delays[1]->tick(temp); - delays[0]->tick(breathPressure - (pressureDiff * reedTable->tick(pressureDiff)) - temp); + temp = -0.95 * filter_.tick( delays_[0].lastOut() ); + lastOutput_ = temp - delays_[1].lastOut(); + pressureDiff = breathPressure - lastOutput_; + delays_[1].tick( temp ); + delays_[0].tick( breathPressure - (pressureDiff * reedTable_.tick(pressureDiff)) - temp ); - lastOutput *= outputGain; - return lastOutput; + lastOutput_ *= outputGain_; + return lastOutput_; } -void Saxofony :: controlChange(int number, MY_FLOAT value) +StkFloat *Saxofony :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Saxofony :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Saxofony :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Saxofony: Control value less than zero!" << std::endl; + errorString_ << "Saxofony::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Saxofony: Control value greater than 128.0!" << std::endl; + errorString_ << "Saxofony::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_ReedStiffness_) // 2 - reedTable->setSlope( 0.1 + (0.4 * norm) ); + reedTable_.setSlope( 0.1 + (0.4 * norm) ); else if (number == __SK_NoiseLevel_) // 4 - noiseGain = ( norm * 0.4 ); + noiseGain_ = ( norm * 0.4 ); else if (number == 29) // 29 - vibrato->setFrequency( norm * 12.0 ); + vibrato_->setFrequency( norm * 12.0 ); else if (number == __SK_ModWheel_) // 1 - vibratoGain = ( norm * 0.5 ); + vibratoGain_ = ( norm * 0.5 ); else if (number == __SK_AfterTouch_Cont_) // 128 - envelope->setValue( norm ); + envelope_.setValue( norm ); else if (number == 11) // 11 this->setBlowPosition( norm ); else if (number == 26) // reed table offset - reedTable->setOffset(0.4 + ( norm * 0.6)); - else - std::cerr << "Saxofony: Undefined Control Number (" << number << ")!!" << std::endl; + reedTable_.setOffset(0.4 + ( norm * 0.6)); + else { + errorString_ << "Saxofony::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Saxofony: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Saxofony::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif - } diff --git a/src/Shakers.cpp b/src/Shakers.cpp index 256b1e0..195c5f2 100644 --- a/src/Shakers.cpp +++ b/src/Shakers.cpp @@ -48,13 +48,11 @@ - Little Rocks = 21 - Tuned Bamboo Chimes = 22 - by Perry R. Cook, 1996 - 1999. + by Perry R. Cook, 1996 - 2004. */ /***************************************************/ #include "Stk.h" -#include -#include #include int my_random(int max) // Return Random Int Between 0 and max @@ -63,216 +61,216 @@ int my_random(int max) // Return Random Int Between 0 and max return temp; } -MY_FLOAT float_random(MY_FLOAT max) // Return random float between 0.0 and max +StkFloat float_random(StkFloat max) // Return random float between 0.0 and max { - MY_FLOAT temp = (MY_FLOAT) (max * rand() / (RAND_MAX + 1.0) ); - return temp; + StkFloat temp = (StkFloat) (max * rand() / (RAND_MAX + 1.0) ); + return temp; } -MY_FLOAT noise_tick() // Return random MY_FLOAT float between -1.0 and 1.0 +StkFloat noise_tick() // Return random StkFloat float between -1.0 and 1.0 { - MY_FLOAT temp = (MY_FLOAT) (2.0 * rand() / (RAND_MAX + 1.0) ); + StkFloat temp = (StkFloat) (2.0 * rand() / (RAND_MAX + 1.0) ); temp -= 1.0; return temp; } // Maraca -#define MARA_SOUND_DECAY 0.95 -#define MARA_SYSTEM_DECAY 0.999 -#define MARA_GAIN 20.0 -#define MARA_NUM_BEANS 25 -#define MARA_CENTER_FREQ 3200.0 -#define MARA_RESON 0.96 +const StkFloat MARA_SOUND_DECAY = 0.95; +const StkFloat MARA_SYSTEM_DECAY = 0.999; +const StkFloat MARA_GAIN = 20.0; +const StkFloat MARA_NUM_BEANS = 25; +const StkFloat MARA_CENTER_FREQ = 3200.0; +const StkFloat MARA_RESON = 0.96; // Sekere -#define SEKE_SOUND_DECAY 0.96 -#define SEKE_SYSTEM_DECAY 0.999 -#define SEKE_GAIN 20.0 -#define SEKE_NUM_BEANS 64 -#define SEKE_CENTER_FREQ 5500.0 -#define SEKE_RESON 0.6 +const StkFloat SEKE_SOUND_DECAY = 0.96; +const StkFloat SEKE_SYSTEM_DECAY = 0.999; +const StkFloat SEKE_GAIN = 20.0; +const StkFloat SEKE_NUM_BEANS = 64; +const StkFloat SEKE_CENTER_FREQ = 5500.0; +const StkFloat SEKE_RESON = 0.6; // Sandpaper -#define SANDPAPR_SOUND_DECAY 0.999 -#define SANDPAPR_SYSTEM_DECAY 0.999 -#define SANDPAPR_GAIN 0.5 -#define SANDPAPR_NUM_GRAINS 128 -#define SANDPAPR_CENTER_FREQ 4500.0 -#define SANDPAPR_RESON 0.6 +const StkFloat SANDPAPR_SOUND_DECAY = 0.999; +const StkFloat SANDPAPR_SYSTEM_DECAY = 0.999; +const StkFloat SANDPAPR_GAIN = 0.5; +const StkFloat SANDPAPR_NUM_GRAINS = 128; +const StkFloat SANDPAPR_CENTER_FREQ = 4500.0; +const StkFloat SANDPAPR_RESON = 0.6; // Cabasa -#define CABA_SOUND_DECAY 0.96 -#define CABA_SYSTEM_DECAY 0.997 -#define CABA_GAIN 40.0 -#define CABA_NUM_BEADS 512 -#define CABA_CENTER_FREQ 3000.0 -#define CABA_RESON 0.7 +const StkFloat CABA_SOUND_DECAY = 0.96; +const StkFloat CABA_SYSTEM_DECAY = 0.997; +const StkFloat CABA_GAIN = 40.0; +const StkFloat CABA_NUM_BEADS = 512; +const StkFloat CABA_CENTER_FREQ = 3000.0; +const StkFloat CABA_RESON = 0.7; // Bamboo Wind Chimes -#define BAMB_SOUND_DECAY 0.95 -#define BAMB_SYSTEM_DECAY 0.9999 -#define BAMB_GAIN 2.0 -#define BAMB_NUM_TUBES 1.25 -#define BAMB_CENTER_FREQ0 2800.0 -#define BAMB_CENTER_FREQ1 0.8 * 2800.0 -#define BAMB_CENTER_FREQ2 1.2 * 2800.0 -#define BAMB_RESON 0.995 +const StkFloat BAMB_SOUND_DECAY = 0.95; +const StkFloat BAMB_SYSTEM_DECAY = 0.9999; +const StkFloat BAMB_GAIN = 2.0; +const StkFloat BAMB_NUM_TUBES = 1.25; +const StkFloat BAMB_CENTER_FREQ0 = 2800.0; +const StkFloat BAMB_CENTER_FREQ1 = 0.8 * 2800.0; +const StkFloat BAMB_CENTER_FREQ2 = 1.2 * 2800.0; +const StkFloat BAMB_RESON = 0.995; // Tuned Bamboo Wind Chimes (Anklung) -#define TBAMB_SOUND_DECAY 0.95 -#define TBAMB_SYSTEM_DECAY 0.9999 -#define TBAMB_GAIN 1.0 -#define TBAMB_NUM_TUBES 1.25 -#define TBAMB_CENTER_FREQ0 1046.6 -#define TBAMB_CENTER_FREQ1 1174.8 -#define TBAMB_CENTER_FREQ2 1397.0 -#define TBAMB_CENTER_FREQ3 1568.0 -#define TBAMB_CENTER_FREQ4 1760.0 -#define TBAMB_CENTER_FREQ5 2093.3 -#define TBAMB_CENTER_FREQ6 2350.0 -#define TBAMB_RESON 0.996 +const StkFloat TBAMB_SOUND_DECAY = 0.95; +const StkFloat TBAMB_SYSTEM_DECAY = 0.9999; +const StkFloat TBAMB_GAIN = 1.0; +const StkFloat TBAMB_NUM_TUBES = 1.25; +const StkFloat TBAMB_CENTER_FREQ0 = 1046.6; +const StkFloat TBAMB_CENTER_FREQ1 = 1174.8; +const StkFloat TBAMB_CENTER_FREQ2 = 1397.0; +const StkFloat TBAMB_CENTER_FREQ3 = 1568.0; +const StkFloat TBAMB_CENTER_FREQ4 = 1760.0; +const StkFloat TBAMB_CENTER_FREQ5 = 2093.3; +const StkFloat TBAMB_CENTER_FREQ6 = 2350.0; +const StkFloat TBAMB_RESON = 0.996; // Water Drops -#define WUTR_SOUND_DECAY 0.95 -#define WUTR_SYSTEM_DECAY 0.996 -#define WUTR_GAIN 1.0 -#define WUTR_NUM_SOURCES 10 -#define WUTR_CENTER_FREQ0 450.0 -#define WUTR_CENTER_FREQ1 600.0 -#define WUTR_CENTER_FREQ2 750.0 -#define WUTR_RESON 0.9985 -#define WUTR_FREQ_SWEEP 1.0001 +const StkFloat WUTR_SOUND_DECAY = 0.95; +const StkFloat WUTR_SYSTEM_DECAY = 0.996; +const StkFloat WUTR_GAIN = 1.0; +const StkFloat WUTR_NUM_SOURCES = 10; +const StkFloat WUTR_CENTER_FREQ0 = 450.0; +const StkFloat WUTR_CENTER_FREQ1 = 600.0; +const StkFloat WUTR_CENTER_FREQ2 = 750.0; +const StkFloat WUTR_RESON = 0.9985; +const StkFloat WUTR_FREQ_SWEEP = 1.0001; // Tambourine -#define TAMB_SOUND_DECAY 0.95 -#define TAMB_SYSTEM_DECAY 0.9985 -#define TAMB_GAIN 5.0 -#define TAMB_NUM_TIMBRELS 32 -#define TAMB_SHELL_FREQ 2300 -#define TAMB_SHELL_GAIN 0.1 -#define TAMB_SHELL_RESON 0.96 -#define TAMB_CYMB_FREQ1 5600 -#define TAMB_CYMB_FREQ2 8100 -#define TAMB_CYMB_RESON 0.99 +const StkFloat TAMB_SOUND_DECAY = 0.95; +const StkFloat TAMB_SYSTEM_DECAY = 0.9985; +const StkFloat TAMB_GAIN = 5.0; +const StkFloat TAMB_NUM_TIMBRELS = 32; +const StkFloat TAMB_SHELL_FREQ = 2300; +const StkFloat TAMB_SHELL_GAIN = 0.1; +const StkFloat TAMB_SHELL_RESON = 0.96; +const StkFloat TAMB_CYMB_FREQ1 = 5600; +const StkFloat TAMB_CYMB_FREQ2 = 8100; +const StkFloat TAMB_CYMB_RESON = 0.99; // Sleighbells -#define SLEI_SOUND_DECAY 0.97 -#define SLEI_SYSTEM_DECAY 0.9994 -#define SLEI_GAIN 1.0 -#define SLEI_NUM_BELLS 32 -#define SLEI_CYMB_FREQ0 2500 -#define SLEI_CYMB_FREQ1 5300 -#define SLEI_CYMB_FREQ2 6500 -#define SLEI_CYMB_FREQ3 8300 -#define SLEI_CYMB_FREQ4 9800 -#define SLEI_CYMB_RESON 0.99 +const StkFloat SLEI_SOUND_DECAY = 0.97; +const StkFloat SLEI_SYSTEM_DECAY = 0.9994; +const StkFloat SLEI_GAIN = 1.0; +const StkFloat SLEI_NUM_BELLS = 32; +const StkFloat SLEI_CYMB_FREQ0 = 2500; +const StkFloat SLEI_CYMB_FREQ1 = 5300; +const StkFloat SLEI_CYMB_FREQ2 = 6500; +const StkFloat SLEI_CYMB_FREQ3 = 8300; +const StkFloat SLEI_CYMB_FREQ4 = 9800; +const StkFloat SLEI_CYMB_RESON = 0.99; // Guiro -#define GUIR_SOUND_DECAY 0.95 -#define GUIR_GAIN 10.0 -#define GUIR_NUM_PARTS 128 -#define GUIR_GOURD_FREQ 2500.0 -#define GUIR_GOURD_RESON 0.97 -#define GUIR_GOURD_FREQ2 4000.0 -#define GUIR_GOURD_RESON2 0.97 +const StkFloat GUIR_SOUND_DECAY = 0.95; +const StkFloat GUIR_GAIN = 10.0; +const StkFloat GUIR_NUM_PARTS = 128; +const StkFloat GUIR_GOURD_FREQ = 2500.0; +const StkFloat GUIR_GOURD_RESON = 0.97; +const StkFloat GUIR_GOURD_FREQ2 = 4000.0; +const StkFloat GUIR_GOURD_RESON2 = 0.97; // Wrench -#define WRENCH_SOUND_DECAY 0.95 -#define WRENCH_GAIN 5 -#define WRENCH_NUM_PARTS 128 -#define WRENCH_FREQ 3200.0 -#define WRENCH_RESON 0.99 -#define WRENCH_FREQ2 8000.0 -#define WRENCH_RESON2 0.992 +const StkFloat WRENCH_SOUND_DECAY = 0.95; +const StkFloat WRENCH_GAIN = 5; +const StkFloat WRENCH_NUM_PARTS = 128; +const StkFloat WRENCH_FREQ = 3200.0; +const StkFloat WRENCH_RESON = 0.99; +const StkFloat WRENCH_FREQ2 = 8000.0; +const StkFloat WRENCH_RESON2 = 0.992; // Cokecan -#define COKECAN_SOUND_DECAY 0.97 -#define COKECAN_SYSTEM_DECAY 0.999 -#define COKECAN_GAIN 0.8 -#define COKECAN_NUM_PARTS 48 -#define COKECAN_HELMFREQ 370 -#define COKECAN_HELM_RES 0.99 -#define COKECAN_METLFREQ0 1025 -#define COKECAN_METLFREQ1 1424 -#define COKECAN_METLFREQ2 2149 -#define COKECAN_METLFREQ3 3596 -#define COKECAN_METL_RES 0.992 +const StkFloat COKECAN_SOUND_DECAY = 0.97; +const StkFloat COKECAN_SYSTEM_DECAY = 0.999; +const StkFloat COKECAN_GAIN = 0.8; +const StkFloat COKECAN_NUM_PARTS = 48; +const StkFloat COKECAN_HELMFREQ = 370; +const StkFloat COKECAN_HELM_RES = 0.99; +const StkFloat COKECAN_METLFREQ0 = 1025; +const StkFloat COKECAN_METLFREQ1 = 1424; +const StkFloat COKECAN_METLFREQ2 = 2149; +const StkFloat COKECAN_METLFREQ3 = 3596; +const StkFloat COKECAN_METL_RES = 0.992; // PhOLIES (Physically-Oriented Library of Imitated Environmental // Sounds), Perry Cook, 1997-8 // Stix1 -#define STIX1_SOUND_DECAY 0.96 -#define STIX1_SYSTEM_DECAY 0.998 -#define STIX1_GAIN 30.0 -#define STIX1_NUM_BEANS 2 -#define STIX1_CENTER_FREQ 5500.0 -#define STIX1_RESON 0.6 +const StkFloat STIX1_SOUND_DECAY = 0.96; +const StkFloat STIX1_SYSTEM_DECAY = 0.998; +const StkFloat STIX1_GAIN = 30.0; +const StkFloat STIX1_NUM_BEANS = 2; +const StkFloat STIX1_CENTER_FREQ = 5500.0; +const StkFloat STIX1_RESON = 0.6; // Crunch1 -#define CRUNCH1_SOUND_DECAY 0.95 -#define CRUNCH1_SYSTEM_DECAY 0.99806 -#define CRUNCH1_GAIN 20.0 -#define CRUNCH1_NUM_BEADS 7 -#define CRUNCH1_CENTER_FREQ 800.0 -#define CRUNCH1_RESON 0.95 +const StkFloat CRUNCH1_SOUND_DECAY = 0.95; +const StkFloat CRUNCH1_SYSTEM_DECAY = 0.99806; +const StkFloat CRUNCH1_GAIN = 20.0; +const StkFloat CRUNCH1_NUM_BEADS = 7; +const StkFloat CRUNCH1_CENTER_FREQ = 800.0; +const StkFloat CRUNCH1_RESON = 0.95; // Nextmug -#define NEXTMUG_SOUND_DECAY 0.97 -#define NEXTMUG_SYSTEM_DECAY 0.9995 -#define NEXTMUG_GAIN 0.8 -#define NEXTMUG_NUM_PARTS 3 -#define NEXTMUG_FREQ0 2123 -#define NEXTMUG_FREQ1 4518 -#define NEXTMUG_FREQ2 8856 -#define NEXTMUG_FREQ3 10753 -#define NEXTMUG_RES 0.997 +const StkFloat NEXTMUG_SOUND_DECAY = 0.97; +const StkFloat NEXTMUG_SYSTEM_DECAY = 0.9995; +const StkFloat NEXTMUG_GAIN = 0.8; +const StkFloat NEXTMUG_NUM_PARTS = 3; +const StkFloat NEXTMUG_FREQ0 = 2123; +const StkFloat NEXTMUG_FREQ1 = 4518; +const StkFloat NEXTMUG_FREQ2 = 8856; +const StkFloat NEXTMUG_FREQ3 = 10753; +const StkFloat NEXTMUG_RES = 0.997; -#define PENNY_FREQ0 11000 -#define PENNY_FREQ1 5200 -#define PENNY_FREQ2 3835 -#define PENNY_RES 0.999 +const StkFloat PENNY_FREQ0 = 11000; +const StkFloat PENNY_FREQ1 = 5200; +const StkFloat PENNY_FREQ2 = 3835; +const StkFloat PENNY_RES = 0.999; -#define NICKEL_FREQ0 5583 -#define NICKEL_FREQ1 9255 -#define NICKEL_FREQ2 9805 -#define NICKEL_RES 0.9992 +const StkFloat NICKEL_FREQ0 = 5583; +const StkFloat NICKEL_FREQ1 = 9255; +const StkFloat NICKEL_FREQ2 = 9805; +const StkFloat NICKEL_RES = 0.9992; -#define DIME_FREQ0 4450 -#define DIME_FREQ1 4974 -#define DIME_FREQ2 9945 -#define DIME_RES 0.9993 +const StkFloat DIME_FREQ0 = 4450; +const StkFloat DIME_FREQ1 = 4974; +const StkFloat DIME_FREQ2 = 9945; +const StkFloat DIME_RES = 0.9993; -#define QUARTER_FREQ0 1708 -#define QUARTER_FREQ1 8863 -#define QUARTER_FREQ2 9045 -#define QUARTER_RES 0.9995 +const StkFloat QUARTER_FREQ0 = 1708; +const StkFloat QUARTER_FREQ1 = 8863; +const StkFloat QUARTER_FREQ2 = 9045; +const StkFloat QUARTER_RES = 0.9995; -#define FRANC_FREQ0 5583 -#define FRANC_FREQ1 11010 -#define FRANC_FREQ2 1917 -#define FRANC_RES 0.9995 +const StkFloat FRANC_FREQ0 = 5583; +const StkFloat FRANC_FREQ1 = 11010; +const StkFloat FRANC_FREQ2 = 1917; +const StkFloat FRANC_RES = 0.9995; -#define PESO_FREQ0 7250 -#define PESO_FREQ1 8150 -#define PESO_FREQ2 10060 -#define PESO_RES 0.9996 +const StkFloat PESO_FREQ0 = 7250; +const StkFloat PESO_FREQ1 = 8150; +const StkFloat PESO_FREQ2 = 10060; +const StkFloat PESO_RES = 0.9996; // Big Gravel -#define BIGROCKS_SOUND_DECAY 0.98 -#define BIGROCKS_SYSTEM_DECAY 0.9965 -#define BIGROCKS_GAIN 20.0 -#define BIGROCKS_NUM_PARTS 23 -#define BIGROCKS_FREQ 6460 -#define BIGROCKS_RES 0.932 +const StkFloat BIGROCKS_SOUND_DECAY = 0.98; +const StkFloat BIGROCKS_SYSTEM_DECAY = 0.9965; +const StkFloat BIGROCKS_GAIN = 20.0; +const StkFloat BIGROCKS_NUM_PARTS = 23; +const StkFloat BIGROCKS_FREQ = 6460; +const StkFloat BIGROCKS_RES = 0.932; // Little Gravel -#define LITLROCKS_SOUND_DECAY 0.98 -#define LITLROCKS_SYSTEM_DECAY 0.99586 -#define LITLROCKS_GAIN 20.0 -#define LITLROCKS_NUM_PARTS 1600 -#define LITLROCKS_FREQ 9000 -#define LITLROCKS_RES 0.843 +const StkFloat LITLROCKS_SOUND_DECAY = 0.98; +const StkFloat LITLROCKS_SYSTEM_DECAY = 0.99586; +const StkFloat LITLROCKS_GAIN = 20.0; +const StkFloat LITLROCKS_NUM_PARTS = 1600; +const StkFloat LITLROCKS_FREQ = 9000; +const StkFloat LITLROCKS_RES = 0.843; // Finally ... the class code! @@ -283,47 +281,46 @@ Shakers :: Shakers() { int i; - instType = 0; - shakeEnergy = 0.0; - nFreqs = 0; - sndLevel = 0.0; + instType_ = 0; + shakeEnergy_ = 0.0; + nFreqs_ = 0; + sndLevel_ = 0.0; for ( i=0; isetupNum(instType); + this->setupNum(instType_); } Shakers :: ~Shakers() { } -#define MAX_SHAKE 2000.0 +const StkFloat MAX_SHAKE = 2000.0; char instrs[NUM_INSTR][10] = { "Maraca", "Cabasa", "Sekere", "Guiro", @@ -344,30 +341,34 @@ int Shakers :: setupName(char* instr) } #if defined(_STK_DEBUG_) - std::cerr << "Shakers: Setting instrument to " << instrs[which] << std::endl; + errorString_ << "Shakers::setupName: setting instrument to " << instrs[which] << '.'; + handleError( StkError::DEBUG_WARNING ); #endif return this->setupNum(which); } -void Shakers :: setFinalZs(MY_FLOAT z0, MY_FLOAT z1, MY_FLOAT z2) { - finalZCoeffs[0] = z0; - finalZCoeffs[1] = z1; - finalZCoeffs[2] = z2; +void Shakers :: setFinalZs(StkFloat z0, StkFloat z1, StkFloat z2) +{ + finalZCoeffs_[0] = z0; + finalZCoeffs_[1] = z1; + finalZCoeffs_[2] = z2; } -void Shakers :: setDecays(MY_FLOAT sndDecay, MY_FLOAT sysDecay) { - soundDecay = sndDecay; - systemDecay = sysDecay; +void Shakers :: setDecays(StkFloat sndDecay_, StkFloat sysDecay_) +{ + soundDecay_ = sndDecay_; + systemDecay_ = sysDecay_; } -int Shakers :: setFreqAndReson(int which, MY_FLOAT freq, MY_FLOAT reson) { +int Shakers :: setFreqAndReson(int which, StkFloat freq, StkFloat reson) +{ if (which < MAX_FREQS) { - resons[which] = reson; - center_freqs[which] = freq; - t_center_freqs[which] = freq; - coeffs[which][1] = reson * reson; - coeffs[which][0] = -reson * 2.0 * cos(freq * TWO_PI / Stk::sampleRate()); + resons_[which] = reson; + center_freqs_[which] = freq; + t_center_freqs_[which] = freq; + coeffs_[which][1] = reson * reson; + coeffs_[which][0] = -reson * 2.0 * cos(freq * TWO_PI / Stk::sampleRate()); return 1; } else return 0; @@ -376,78 +377,78 @@ int Shakers :: setFreqAndReson(int which, MY_FLOAT freq, MY_FLOAT reson) { int Shakers :: setupNum(int inst) { int i, rv = 0; - MY_FLOAT temp; + StkFloat temp; if (inst == 1) { // Cabasa rv = inst; - nObjects = CABA_NUM_BEADS; - defObjs[inst] = CABA_NUM_BEADS; + nObjects_ = CABA_NUM_BEADS; + defObjs_[inst] = CABA_NUM_BEADS; setDecays(CABA_SOUND_DECAY, CABA_SYSTEM_DECAY); - defDecays[inst] = CABA_SYSTEM_DECAY; - decayScale[inst] = 0.97; - nFreqs = 1; - baseGain = CABA_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0] = temp; - freqalloc[0] = 0; + defDecays_[inst] = CABA_SYSTEM_DECAY; + decayScale_[inst] = 0.97; + nFreqs_ = 1; + baseGain_ = CABA_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0] = temp; + freqalloc_[0] = 0; setFreqAndReson(0,CABA_CENTER_FREQ,CABA_RESON); setFinalZs(1.0,-1.0,0.0); } else if (inst == 2) { // Sekere rv = inst; - nObjects = SEKE_NUM_BEANS; - defObjs[inst] = SEKE_NUM_BEANS; + nObjects_ = SEKE_NUM_BEANS; + defObjs_[inst] = SEKE_NUM_BEANS; this->setDecays(SEKE_SOUND_DECAY,SEKE_SYSTEM_DECAY); - defDecays[inst] = SEKE_SYSTEM_DECAY; - decayScale[inst] = 0.94; - nFreqs = 1; - baseGain = SEKE_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0] = temp; - freqalloc[0] = 0; + defDecays_[inst] = SEKE_SYSTEM_DECAY; + decayScale_[inst] = 0.94; + nFreqs_ = 1; + baseGain_ = SEKE_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0] = temp; + freqalloc_[0] = 0; this->setFreqAndReson(0,SEKE_CENTER_FREQ,SEKE_RESON); this->setFinalZs(1.0, 0.0, -1.0); } else if (inst == 3) { // Guiro rv = inst; - nObjects = GUIR_NUM_PARTS; - defObjs[inst] = GUIR_NUM_PARTS; + nObjects_ = GUIR_NUM_PARTS; + defObjs_[inst] = GUIR_NUM_PARTS; setDecays(GUIR_SOUND_DECAY,1.0); - defDecays[inst] = 0.9999; - decayScale[inst] = 1.0; - nFreqs = 2; - baseGain = GUIR_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - gains[1]=temp; - freqalloc[0] = 0; - freqalloc[1] = 0; - freq_rand[0] = 0.0; - freq_rand[1] = 0.0; + defDecays_[inst] = 0.9999; + decayScale_[inst] = 1.0; + nFreqs_ = 2; + baseGain_ = GUIR_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + gains_[1]=temp; + freqalloc_[0] = 0; + freqalloc_[1] = 0; + freq_rand_[0] = 0.0; + freq_rand_[1] = 0.0; setFreqAndReson(0,GUIR_GOURD_FREQ,GUIR_GOURD_RESON); setFreqAndReson(1,GUIR_GOURD_FREQ2,GUIR_GOURD_RESON2); - ratchet = 0; - ratchetPos = 10; + ratchet_ = 0; + ratchetPos_ = 10; } else if (inst == 4) { // Water Drops rv = inst; - nObjects = WUTR_NUM_SOURCES; - defObjs[inst] = WUTR_NUM_SOURCES; + nObjects_ = WUTR_NUM_SOURCES; + defObjs_[inst] = WUTR_NUM_SOURCES; setDecays(WUTR_SOUND_DECAY,WUTR_SYSTEM_DECAY); - defDecays[inst] = WUTR_SYSTEM_DECAY; - decayScale[inst] = 0.8; - nFreqs = 3; - baseGain = WUTR_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - gains[1]=temp; - gains[2]=temp; - freqalloc[0] = 1; - freqalloc[1] = 1; - freqalloc[2] = 1; - freq_rand[0] = 0.2; - freq_rand[1] = 0.2; - freq_rand[2] = 0.2; + defDecays_[inst] = WUTR_SYSTEM_DECAY; + decayScale_[inst] = 0.8; + nFreqs_ = 3; + baseGain_ = WUTR_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + gains_[1]=temp; + gains_[2]=temp; + freqalloc_[0] = 1; + freqalloc_[1] = 1; + freqalloc_[2] = 1; + freq_rand_[0] = 0.2; + freq_rand_[1] = 0.2; + freq_rand_[2] = 0.2; setFreqAndReson(0,WUTR_CENTER_FREQ0,WUTR_RESON); setFreqAndReson(1,WUTR_CENTER_FREQ0,WUTR_RESON); setFreqAndReson(2,WUTR_CENTER_FREQ0,WUTR_RESON); @@ -455,23 +456,23 @@ int Shakers :: setupNum(int inst) } else if (inst == 5) { // Bamboo rv = inst; - nObjects = BAMB_NUM_TUBES; - defObjs[inst] = BAMB_NUM_TUBES; + nObjects_ = BAMB_NUM_TUBES; + defObjs_[inst] = BAMB_NUM_TUBES; setDecays(BAMB_SOUND_DECAY, BAMB_SYSTEM_DECAY); - defDecays[inst] = BAMB_SYSTEM_DECAY; - decayScale[inst] = 0.7; - nFreqs = 3; - baseGain = BAMB_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - gains[1]=temp; - gains[2]=temp; - freqalloc[0] = 1; - freqalloc[1] = 1; - freqalloc[2] = 1; - freq_rand[0] = 0.2; - freq_rand[1] = 0.2; - freq_rand[2] = 0.2; + defDecays_[inst] = BAMB_SYSTEM_DECAY; + decayScale_[inst] = 0.7; + nFreqs_ = 3; + baseGain_ = BAMB_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + gains_[1]=temp; + gains_[2]=temp; + freqalloc_[0] = 1; + freqalloc_[1] = 1; + freqalloc_[2] = 1; + freq_rand_[0] = 0.2; + freq_rand_[1] = 0.2; + freq_rand_[2] = 0.2; setFreqAndReson(0,BAMB_CENTER_FREQ0,BAMB_RESON); setFreqAndReson(1,BAMB_CENTER_FREQ1,BAMB_RESON); setFreqAndReson(2,BAMB_CENTER_FREQ2,BAMB_RESON); @@ -479,23 +480,23 @@ int Shakers :: setupNum(int inst) } else if (inst == 6) { // Tambourine rv = inst; - nObjects = TAMB_NUM_TIMBRELS; - defObjs[inst] = TAMB_NUM_TIMBRELS; + nObjects_ = TAMB_NUM_TIMBRELS; + defObjs_[inst] = TAMB_NUM_TIMBRELS; setDecays(TAMB_SOUND_DECAY,TAMB_SYSTEM_DECAY); - defDecays[inst] = TAMB_SYSTEM_DECAY; - decayScale[inst] = 0.95; - nFreqs = 3; - baseGain = TAMB_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp*TAMB_SHELL_GAIN; - gains[1]=temp*0.8; - gains[2]=temp; - freqalloc[0] = 0; - freqalloc[1] = 1; - freqalloc[2] = 1; - freq_rand[0] = 0.0; - freq_rand[1] = 0.05; - freq_rand[2] = 0.05; + defDecays_[inst] = TAMB_SYSTEM_DECAY; + decayScale_[inst] = 0.95; + nFreqs_ = 3; + baseGain_ = TAMB_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp*TAMB_SHELL_GAIN; + gains_[1]=temp*0.8; + gains_[2]=temp; + freqalloc_[0] = 0; + freqalloc_[1] = 1; + freqalloc_[2] = 1; + freq_rand_[0] = 0.0; + freq_rand_[1] = 0.05; + freq_rand_[2] = 0.05; setFreqAndReson(0,TAMB_SHELL_FREQ,TAMB_SHELL_RESON); setFreqAndReson(1,TAMB_CYMB_FREQ1,TAMB_CYMB_RESON); setFreqAndReson(2,TAMB_CYMB_FREQ2,TAMB_CYMB_RESON); @@ -503,22 +504,22 @@ int Shakers :: setupNum(int inst) } else if (inst == 7) { // Sleighbell rv = inst; - nObjects = SLEI_NUM_BELLS; - defObjs[inst] = SLEI_NUM_BELLS; + nObjects_ = SLEI_NUM_BELLS; + defObjs_[inst] = SLEI_NUM_BELLS; setDecays(SLEI_SOUND_DECAY,SLEI_SYSTEM_DECAY); - defDecays[inst] = SLEI_SYSTEM_DECAY; - decayScale[inst] = 0.9; - nFreqs = 5; - baseGain = SLEI_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - gains[1]=temp; - gains[2]=temp; - gains[3]=temp*0.5; - gains[4]=temp*0.3; - for (i=0;isetDecays(SANDPAPR_SOUND_DECAY,SANDPAPR_SYSTEM_DECAY); - defDecays[inst] = SANDPAPR_SYSTEM_DECAY; - decayScale[inst] = 0.97; - nFreqs = 1; - baseGain = SANDPAPR_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0] = temp; - freqalloc[0] = 0; + defDecays_[inst] = SANDPAPR_SYSTEM_DECAY; + decayScale_[inst] = 0.97; + nFreqs_ = 1; + baseGain_ = SANDPAPR_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0] = temp; + freqalloc_[0] = 0; this->setFreqAndReson(0,SANDPAPR_CENTER_FREQ,SANDPAPR_RESON); this->setFinalZs(1.0, 0.0, -1.0); } else if (inst == 12) { // Cokecan rv = inst; - nObjects = COKECAN_NUM_PARTS; - defObjs[inst] = COKECAN_NUM_PARTS; + nObjects_ = COKECAN_NUM_PARTS; + defObjs_[inst] = COKECAN_NUM_PARTS; setDecays(COKECAN_SOUND_DECAY,COKECAN_SYSTEM_DECAY); - defDecays[inst] = COKECAN_SYSTEM_DECAY; - decayScale[inst] = 0.95; - nFreqs = 5; - baseGain = COKECAN_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - gains[1]=temp*1.8; - gains[2]=temp*1.8; - gains[3]=temp*1.8; - gains[4]=temp*1.8; - freqalloc[0] = 0; - freqalloc[1] = 0; - freqalloc[2] = 0; - freqalloc[3] = 0; - freqalloc[4] = 0; + defDecays_[inst] = COKECAN_SYSTEM_DECAY; + decayScale_[inst] = 0.95; + nFreqs_ = 5; + baseGain_ = COKECAN_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + gains_[1]=temp*1.8; + gains_[2]=temp*1.8; + gains_[3]=temp*1.8; + gains_[4]=temp*1.8; + freqalloc_[0] = 0; + freqalloc_[1] = 0; + freqalloc_[2] = 0; + freqalloc_[3] = 0; + freqalloc_[4] = 0; setFreqAndReson(0,COKECAN_HELMFREQ,COKECAN_HELM_RES); setFreqAndReson(1,COKECAN_METLFREQ0,COKECAN_METL_RES); setFreqAndReson(2,COKECAN_METLFREQ1,COKECAN_METL_RES); @@ -623,24 +624,24 @@ int Shakers :: setupNum(int inst) } else if (inst>12 && inst<20) { // Nextmug rv = inst; - nObjects = NEXTMUG_NUM_PARTS; - defObjs[inst] = NEXTMUG_NUM_PARTS; + nObjects_ = NEXTMUG_NUM_PARTS; + defObjs_[inst] = NEXTMUG_NUM_PARTS; setDecays(NEXTMUG_SOUND_DECAY,NEXTMUG_SYSTEM_DECAY); - defDecays[inst] = NEXTMUG_SYSTEM_DECAY; - decayScale[inst] = 0.95; - nFreqs = 4; - baseGain = NEXTMUG_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - gains[1]=temp*0.8; - gains[2]=temp*0.6; - gains[3]=temp*0.4; - freqalloc[0] = 0; - freqalloc[1] = 0; - freqalloc[2] = 0; - freqalloc[3] = 0; - freqalloc[4] = 0; - freqalloc[5] = 0; + defDecays_[inst] = NEXTMUG_SYSTEM_DECAY; + decayScale_[inst] = 0.95; + nFreqs_ = 4; + baseGain_ = NEXTMUG_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + gains_[1]=temp*0.8; + gains_[2]=temp*0.6; + gains_[3]=temp*0.4; + freqalloc_[0] = 0; + freqalloc_[1] = 0; + freqalloc_[2] = 0; + freqalloc_[3] = 0; + freqalloc_[4] = 0; + freqalloc_[5] = 0; setFreqAndReson(0,NEXTMUG_FREQ0,NEXTMUG_RES); setFreqAndReson(1,NEXTMUG_FREQ1,NEXTMUG_RES); setFreqAndReson(2,NEXTMUG_FREQ2,NEXTMUG_RES); @@ -648,123 +649,123 @@ int Shakers :: setupNum(int inst) setFinalZs(1.0,0.0,-1.0); if (inst == 14) { // Mug + Penny - nFreqs = 7; - gains[4] = temp; - gains[5] = temp*0.8; - gains[6] = temp*0.5; + nFreqs_ = 7; + gains_[4] = temp; + gains_[5] = temp*0.8; + gains_[6] = temp*0.5; setFreqAndReson(4,PENNY_FREQ0,PENNY_RES); setFreqAndReson(5,PENNY_FREQ1,PENNY_RES); setFreqAndReson(6,PENNY_FREQ2,PENNY_RES); } else if (inst == 15) { // Mug + Nickel - nFreqs = 6; - gains[4] = temp; - gains[5] = temp*0.8; - gains[6] = temp*0.5; + nFreqs_ = 6; + gains_[4] = temp; + gains_[5] = temp*0.8; + gains_[6] = temp*0.5; setFreqAndReson(4,NICKEL_FREQ0,NICKEL_RES); setFreqAndReson(5,NICKEL_FREQ1,NICKEL_RES); setFreqAndReson(6,NICKEL_FREQ2,NICKEL_RES); } else if (inst == 16) { // Mug + Dime - nFreqs = 6; - gains[4] = temp; - gains[5] = temp*0.8; - gains[6] = temp*0.5; + nFreqs_ = 6; + gains_[4] = temp; + gains_[5] = temp*0.8; + gains_[6] = temp*0.5; setFreqAndReson(4,DIME_FREQ0,DIME_RES); setFreqAndReson(5,DIME_FREQ1,DIME_RES); setFreqAndReson(6,DIME_FREQ2,DIME_RES); } else if (inst == 17) { // Mug + Quarter - nFreqs = 6; - gains[4] = temp*1.3; - gains[5] = temp*1.0; - gains[6] = temp*0.8; + nFreqs_ = 6; + gains_[4] = temp*1.3; + gains_[5] = temp*1.0; + gains_[6] = temp*0.8; setFreqAndReson(4,QUARTER_FREQ0,QUARTER_RES); setFreqAndReson(5,QUARTER_FREQ1,QUARTER_RES); setFreqAndReson(6,QUARTER_FREQ2,QUARTER_RES); } else if (inst == 18) { // Mug + Franc - nFreqs = 6; - gains[4] = temp*0.7; - gains[5] = temp*0.4; - gains[6] = temp*0.3; + nFreqs_ = 6; + gains_[4] = temp*0.7; + gains_[5] = temp*0.4; + gains_[6] = temp*0.3; setFreqAndReson(4,FRANC_FREQ0,FRANC_RES); setFreqAndReson(5,FRANC_FREQ1,FRANC_RES); setFreqAndReson(6,FRANC_FREQ2,FRANC_RES); } else if (inst == 19) { // Mug + Peso - nFreqs = 6; - gains[4] = temp; - gains[5] = temp*1.2; - gains[6] = temp*0.7; + nFreqs_ = 6; + gains_[4] = temp; + gains_[5] = temp*1.2; + gains_[6] = temp*0.7; setFreqAndReson(4,PESO_FREQ0,PESO_RES); setFreqAndReson(5,PESO_FREQ1,PESO_RES); setFreqAndReson(6,PESO_FREQ2,PESO_RES); } } else if (inst == 20) { // Big Rocks - nFreqs = 1; + nFreqs_ = 1; rv = inst; - nObjects = BIGROCKS_NUM_PARTS; - defObjs[inst] = BIGROCKS_NUM_PARTS; + nObjects_ = BIGROCKS_NUM_PARTS; + defObjs_[inst] = BIGROCKS_NUM_PARTS; setDecays(BIGROCKS_SOUND_DECAY,BIGROCKS_SYSTEM_DECAY); - defDecays[inst] = BIGROCKS_SYSTEM_DECAY; - decayScale[inst] = 0.95; - baseGain = BIGROCKS_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - freqalloc[0] = 1; - freq_rand[0] = 0.11; + defDecays_[inst] = BIGROCKS_SYSTEM_DECAY; + decayScale_[inst] = 0.95; + baseGain_ = BIGROCKS_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + freqalloc_[0] = 1; + freq_rand_[0] = 0.11; setFreqAndReson(0,BIGROCKS_FREQ,BIGROCKS_RES); setFinalZs(1.0,0.0,-1.0); } else if (inst == 21) { // Little Rocks - nFreqs = 1; + nFreqs_ = 1; rv = inst; - nObjects = LITLROCKS_NUM_PARTS; - defObjs[inst] = LITLROCKS_NUM_PARTS; + nObjects_ = LITLROCKS_NUM_PARTS; + defObjs_[inst] = LITLROCKS_NUM_PARTS; setDecays(LITLROCKS_SOUND_DECAY,LITLROCKS_SYSTEM_DECAY); - defDecays[inst] = LITLROCKS_SYSTEM_DECAY; - decayScale[inst] = 0.95; - baseGain = LITLROCKS_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - freqalloc[0] = 1; - freq_rand[0] = 0.18; + defDecays_[inst] = LITLROCKS_SYSTEM_DECAY; + decayScale_[inst] = 0.95; + baseGain_ = LITLROCKS_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + freqalloc_[0] = 1; + freq_rand_[0] = 0.18; setFreqAndReson(0,LITLROCKS_FREQ,LITLROCKS_RES); setFinalZs(1.0,0.0,-1.0); } else if (inst == 22) { // Tuned Bamboo rv = inst; - nObjects = TBAMB_NUM_TUBES; - defObjs[inst] = TBAMB_NUM_TUBES; + nObjects_ = TBAMB_NUM_TUBES; + defObjs_[inst] = TBAMB_NUM_TUBES; setDecays(TBAMB_SOUND_DECAY, TBAMB_SYSTEM_DECAY); - defDecays[inst] = TBAMB_SYSTEM_DECAY; - decayScale[inst] = 0.7; - nFreqs = 7; - baseGain = TBAMB_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - gains[1]=temp; - gains[2]=temp; - gains[3]=temp; - gains[4]=temp; - gains[5]=temp; - gains[6]=temp; - freqalloc[0] = 0; - freqalloc[1] = 0; - freqalloc[2] = 0; - freqalloc[3] = 0; - freqalloc[4] = 0; - freqalloc[5] = 0; - freqalloc[6] = 0; - freq_rand[0] = 0.0; - freq_rand[1] = 0.0; - freq_rand[2] = 0.0; - freq_rand[3] = 0.0; - freq_rand[4] = 0.0; - freq_rand[5] = 0.0; - freq_rand[6] = 0.0; + defDecays_[inst] = TBAMB_SYSTEM_DECAY; + decayScale_[inst] = 0.7; + nFreqs_ = 7; + baseGain_ = TBAMB_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + gains_[1]=temp; + gains_[2]=temp; + gains_[3]=temp; + gains_[4]=temp; + gains_[5]=temp; + gains_[6]=temp; + freqalloc_[0] = 0; + freqalloc_[1] = 0; + freqalloc_[2] = 0; + freqalloc_[3] = 0; + freqalloc_[4] = 0; + freqalloc_[5] = 0; + freqalloc_[6] = 0; + freq_rand_[0] = 0.0; + freq_rand_[1] = 0.0; + freq_rand_[2] = 0.0; + freq_rand_[3] = 0.0; + freq_rand_[4] = 0.0; + freq_rand_[5] = 0.0; + freq_rand_[6] = 0.0; setFreqAndReson(0,TBAMB_CENTER_FREQ0,TBAMB_RESON); setFreqAndReson(1,TBAMB_CENTER_FREQ1,TBAMB_RESON); setFreqAndReson(2,TBAMB_CENTER_FREQ2,TBAMB_RESON); @@ -776,346 +777,361 @@ int Shakers :: setupNum(int inst) } else { // Maraca (inst == 0) or default rv = 0; - nObjects = MARA_NUM_BEANS; - defObjs[0] = MARA_NUM_BEANS; + nObjects_ = MARA_NUM_BEANS; + defObjs_[0] = MARA_NUM_BEANS; setDecays(MARA_SOUND_DECAY,MARA_SYSTEM_DECAY); - defDecays[0] = MARA_SYSTEM_DECAY; - decayScale[inst] = 0.9; - nFreqs = 1; - baseGain = MARA_GAIN; - temp = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - gains[0]=temp; - freqalloc[0] = 0; + defDecays_[0] = MARA_SYSTEM_DECAY; + decayScale_[inst] = 0.9; + nFreqs_ = 1; + baseGain_ = MARA_GAIN; + temp = log(nObjects_) * baseGain_ / (StkFloat) nObjects_; + gains_[0]=temp; + freqalloc_[0] = 0; setFreqAndReson(0,MARA_CENTER_FREQ,MARA_RESON); setFinalZs(1.0,-1.0,0.0); } return rv; } -void Shakers :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Shakers :: noteOn(StkFloat frequency, StkFloat amplitude) { // Yep ... pretty kludgey, but it works! 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 (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_) - std::cerr << "Shakers: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Shakers::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Shakers :: noteOff(MY_FLOAT amplitude) +void Shakers :: noteOff(StkFloat amplitude) { - shakeEnergy = 0.0; - if (instType==10 || instType==3) ratchetPos = 0; + shakeEnergy_ = 0.0; + if (instType_==10 || instType_==3) ratchetPos_ = 0; } -#define MIN_ENERGY 0.3 +const StkFloat MIN_ENERGY = 0.3; -MY_FLOAT Shakers :: tick() +StkFloat Shakers :: tick() { - MY_FLOAT data; - MY_FLOAT temp_rand; + StkFloat data; + StkFloat temp_rand; int i; - if (instType == 4) { - if (shakeEnergy > MIN_ENERGY) { - lastOutput = wuter_tick(); - lastOutput *= 0.0001; + if (instType_ == 4) { + if (shakeEnergy_ > MIN_ENERGY) { + lastOutput_ = wuter_tick(); + lastOutput_ *= 0.0001; } else { - lastOutput = 0.0; + lastOutput_ = 0.0; } } - else if (instType == 22) { - lastOutput = tbamb_tick(); + else if (instType_ == 22) { + lastOutput_ = tbamb_tick(); } - else if (instType == 10 || instType == 3) { - if (ratchetPos > 0) { - ratchet -= (ratchetDelta + (0.002*totalEnergy)); - if (ratchet < 0.0) { - ratchet = 1.0; - ratchetPos -= 1; + else if (instType_ == 10 || instType_ == 3) { + if (ratchetPos_ > 0) { + ratchet_ -= (ratchetDelta_ + (0.002*totalEnergy_)); + if (ratchet_ < 0.0) { + ratchet_ = 1.0; + ratchetPos_ -= 1; } - totalEnergy = ratchet; - lastOutput = ratchet_tick(); - lastOutput *= 0.0001; + totalEnergy_ = ratchet_; + lastOutput_ = ratchet_tick(); + lastOutput_ *= 0.0001; } - else lastOutput = 0.0; + else lastOutput_ = 0.0; } - else { - // MY_FLOAT generic_tick() { - if (shakeEnergy > MIN_ENERGY) { - shakeEnergy *= systemDecay; // Exponential system decay - if (float_random(1024.0) < nObjects) { - sndLevel += shakeEnergy; - for (i=0;i MIN_ENERGY) { + shakeEnergy_ *= systemDecay_; // Exponential system decay + if (float_random(1024.0) < nObjects_) { + sndLevel_ += shakeEnergy_; + for (i=0;i 10000.0) data = 10000.0; if (data < -10000.0) data = -10000.0; - lastOutput = data * 0.0001; + lastOutput_ = data * 0.0001; } - else lastOutput = 0.0; + else lastOutput_ = 0.0; } - return lastOutput; + return lastOutput_; } -void Shakers :: controlChange(int number, MY_FLOAT value) +StkFloat *Shakers :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Shakers :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Shakers :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Shakers: Control value less than zero!" << std::endl; + errorString_ << "Shakers::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Shakers: Control value greater than 128.0!" << std::endl; + errorString_ << "Shakers::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } - MY_FLOAT temp; + StkFloat temp; int i; if (number == __SK_Breath_) { // 2 ... energy - shakeEnergy += norm * MAX_SHAKE * 0.1; - if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE; - if (instType==10 || instType==3) { - ratchetPos = (int) fabs(value - lastRatchetPos); - ratchetDelta = 0.0002 * ratchetPos; - lastRatchetPos = (int) value; + shakeEnergy_ += norm * MAX_SHAKE * 0.1; + if (shakeEnergy_ > MAX_SHAKE) shakeEnergy_ = MAX_SHAKE; + if (instType_==10 || instType_==3) { + ratchetPos_ = (int) fabs(value - lastRatchetPos_); + ratchetDelta_ = 0.0002 * ratchetPos_; + lastRatchetPos_ = (int) value; } } else if (number == __SK_ModFrequency_) { // 4 ... decay - if (instType != 3 && instType != 10) { - systemDecay = defDecays[instType] + ((value - 64.0) * - decayScale[instType] * - (1.0 - defDecays[instType]) / 64.0 ); - gains[0] = log(nObjects) * baseGain / (MY_FLOAT) nObjects; - for (i=1;i MAX_SHAKE) shakeEnergy = MAX_SHAKE; - if (instType==10 || instType==3) { - ratchetPos = (int) fabs(value - lastRatchetPos); - ratchetDelta = 0.0002 * ratchetPos; - lastRatchetPos = (int) value; + shakeEnergy_ += norm * MAX_SHAKE * 0.1; + if (shakeEnergy_ > MAX_SHAKE) shakeEnergy_ = MAX_SHAKE; + if (instType_==10 || instType_==3) { + ratchetPos_ = (int) fabs(value - lastRatchetPos_); + ratchetDelta_ = 0.0002 * ratchetPos_; + lastRatchetPos_ = (int) value; } } else if (number == __SK_ShakerInst_) { // 1071 - instType = (int) (value + 0.5); // Just to be safe - this->setupNum(instType); + instType_ = (int) (value + 0.5); // Just to be safe + this->setupNum(instType_); } - else - std::cerr << "Shakers: Undefined Control Number (" << number << ")!!" << std::endl; + else { + errorString_ << "Shakers::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Shakers: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Shakers::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } // KLUDGE-O-MATIC-O-RAMA -MY_FLOAT Shakers :: wuter_tick() { - MY_FLOAT data; +StkFloat Shakers :: wuter_tick() { + StkFloat data; int j; - shakeEnergy *= systemDecay; // Exponential system decay - if (my_random(32767) < nObjects) { - sndLevel = shakeEnergy; + shakeEnergy_ *= systemDecay_; // Exponential system decay + if (my_random(32767) < nObjects_) { + sndLevel_ = shakeEnergy_; j = my_random(3); if (j == 0) { - center_freqs[0] = WUTR_CENTER_FREQ1 * (0.75 + (0.25 * noise_tick())); - gains[0] = fabs(noise_tick()); + center_freqs_[0] = WUTR_CENTER_FREQ1 * (0.75 + (0.25 * noise_tick())); + gains_[0] = fabs(noise_tick()); } else if (j == 1) { - center_freqs[1] = WUTR_CENTER_FREQ1 * (1.0 + (0.25 * noise_tick())); - gains[1] = fabs(noise_tick()); + center_freqs_[1] = WUTR_CENTER_FREQ1 * (1.0 + (0.25 * noise_tick())); + gains_[1] = fabs(noise_tick()); } else { - center_freqs[2] = WUTR_CENTER_FREQ1 * (1.25 + (0.25 * noise_tick())); - gains[2] = fabs(noise_tick()); + center_freqs_[2] = WUTR_CENTER_FREQ1 * (1.25 + (0.25 * noise_tick())); + gains_[2] = fabs(noise_tick()); } } - gains[0] *= resons[0]; - if (gains[0] > 0.001) { - center_freqs[0] *= WUTR_FREQ_SWEEP; - coeffs[0][0] = -resons[0] * 2.0 * - cos(center_freqs[0] * TWO_PI / Stk::sampleRate()); + gains_[0] *= resons_[0]; + if (gains_[0] > 0.001) { + center_freqs_[0] *= WUTR_FREQ_SWEEP; + coeffs_[0][0] = -resons_[0] * 2.0 * + cos(center_freqs_[0] * TWO_PI / Stk::sampleRate()); } - gains[1] *= resons[1]; - if (gains[1] > 0.001) { - center_freqs[1] *= WUTR_FREQ_SWEEP; - coeffs[1][0] = -resons[1] * 2.0 * - cos(center_freqs[1] * TWO_PI / Stk::sampleRate()); + gains_[1] *= resons_[1]; + if (gains_[1] > 0.001) { + center_freqs_[1] *= WUTR_FREQ_SWEEP; + coeffs_[1][0] = -resons_[1] * 2.0 * + cos(center_freqs_[1] * TWO_PI / Stk::sampleRate()); } - gains[2] *= resons[2]; - if (gains[2] > 0.001) { - center_freqs[2] *= WUTR_FREQ_SWEEP; - coeffs[2][0] = -resons[2] * 2.0 * - cos(center_freqs[2] * TWO_PI / Stk::sampleRate()); + gains_[2] *= resons_[2]; + if (gains_[2] > 0.001) { + center_freqs_[2] *= WUTR_FREQ_SWEEP; + coeffs_[2][0] = -resons_[2] * 2.0 * + cos(center_freqs_[2] * TWO_PI / Stk::sampleRate()); } - sndLevel *= soundDecay; // Each (all) event(s) - // decay(s) exponentially - inputs[0] = sndLevel; - inputs[0] *= noise_tick(); // Actual Sound is Random - inputs[1] = inputs[0] * gains[1]; - inputs[2] = inputs[0] * gains[2]; - inputs[0] *= gains[0]; - inputs[0] -= outputs[0][0]*coeffs[0][0]; - inputs[0] -= outputs[0][1]*coeffs[0][1]; - outputs[0][1] = outputs[0][0]; - outputs[0][0] = inputs[0]; - data = gains[0]*outputs[0][0]; - inputs[1] -= outputs[1][0]*coeffs[1][0]; - inputs[1] -= outputs[1][1]*coeffs[1][1]; - outputs[1][1] = outputs[1][0]; - outputs[1][0] = inputs[1]; - data += gains[1]*outputs[1][0]; - inputs[2] -= outputs[2][0]*coeffs[2][0]; - inputs[2] -= outputs[2][1]*coeffs[2][1]; - outputs[2][1] = outputs[2][0]; - outputs[2][0] = inputs[2]; - data += gains[2]*outputs[2][0]; + sndLevel_ *= soundDecay_; // Each (all) event(s) + // decay(s) exponentially + inputs_[0] = sndLevel_; + inputs_[0] *= noise_tick(); // Actual Sound is Random + inputs_[1] = inputs_[0] * gains_[1]; + inputs_[2] = inputs_[0] * gains_[2]; + inputs_[0] *= gains_[0]; + inputs_[0] -= outputs_[0][0]*coeffs_[0][0]; + inputs_[0] -= outputs_[0][1]*coeffs_[0][1]; + outputs_[0][1] = outputs_[0][0]; + outputs_[0][0] = inputs_[0]; + data = gains_[0]*outputs_[0][0]; + inputs_[1] -= outputs_[1][0]*coeffs_[1][0]; + inputs_[1] -= outputs_[1][1]*coeffs_[1][1]; + outputs_[1][1] = outputs_[1][0]; + outputs_[1][0] = inputs_[1]; + data += gains_[1]*outputs_[1][0]; + inputs_[2] -= outputs_[2][0]*coeffs_[2][0]; + inputs_[2] -= outputs_[2][1]*coeffs_[2][1]; + outputs_[2][1] = outputs_[2][0]; + outputs_[2][0] = inputs_[2]; + data += gains_[2]*outputs_[2][0]; - finalZ[2] = finalZ[1]; - finalZ[1] = finalZ[0]; - finalZ[0] = data * 4; + finalZ_[2] = finalZ_[1]; + finalZ_[1] = finalZ_[0]; + finalZ_[0] = data * 4; - data = finalZ[2] - finalZ[0]; + data = finalZ_[2] - finalZ_[0]; return data; } -MY_FLOAT Shakers :: ratchet_tick() { - MY_FLOAT data; - if (my_random(1024) < nObjects) { - sndLevel += 512 * ratchet * totalEnergy; +StkFloat Shakers :: ratchet_tick() { + StkFloat data; + if (my_random(1024) < nObjects_) { + sndLevel_ += 512 * ratchet_ * totalEnergy_; } - inputs[0] = sndLevel; - inputs[0] *= noise_tick() * ratchet; - sndLevel *= soundDecay; + inputs_[0] = sndLevel_; + inputs_[0] *= noise_tick() * ratchet_; + sndLevel_ *= soundDecay_; - inputs[1] = inputs[0]; - inputs[0] -= outputs[0][0]*coeffs[0][0]; - inputs[0] -= outputs[0][1]*coeffs[0][1]; - outputs[0][1] = outputs[0][0]; - outputs[0][0] = inputs[0]; - inputs[1] -= outputs[1][0]*coeffs[1][0]; - inputs[1] -= outputs[1][1]*coeffs[1][1]; - outputs[1][1] = outputs[1][0]; - outputs[1][0] = inputs[1]; + inputs_[1] = inputs_[0]; + inputs_[0] -= outputs_[0][0]*coeffs_[0][0]; + inputs_[0] -= outputs_[0][1]*coeffs_[0][1]; + outputs_[0][1] = outputs_[0][0]; + outputs_[0][0] = inputs_[0]; + inputs_[1] -= outputs_[1][0]*coeffs_[1][0]; + inputs_[1] -= outputs_[1][1]*coeffs_[1][1]; + outputs_[1][1] = outputs_[1][0]; + outputs_[1][0] = inputs_[1]; - finalZ[2] = finalZ[1]; - finalZ[1] = finalZ[0]; - finalZ[0] = gains[0]*outputs[0][1] + gains[1]*outputs[1][1]; - data = finalZ[0] - finalZ[2]; + finalZ_[2] = finalZ_[1]; + finalZ_[1] = finalZ_[0]; + finalZ_[0] = gains_[0]*outputs_[0][1] + gains_[1]*outputs_[1][1]; + data = finalZ_[0] - finalZ_[2]; return data; } -MY_FLOAT Shakers :: tbamb_tick() { - MY_FLOAT data, temp; +StkFloat Shakers :: tbamb_tick() { + StkFloat data, temp; static int which = 0; int i; - if (shakeEnergy > MIN_ENERGY) { - shakeEnergy *= systemDecay; // Exponential system decay - if (float_random(1024.0) < nObjects) { - sndLevel += shakeEnergy; + if (shakeEnergy_ > MIN_ENERGY) { + shakeEnergy_ *= systemDecay_; // Exponential system decay + if (float_random(1024.0) < nObjects_) { + sndLevel_ += shakeEnergy_; which = my_random(7); } - temp = sndLevel * noise_tick(); // Actual Sound is Random - for (i=0;i 10000.0) data = 10000.0; if (data < -10000.0) data = -10000.0; data = data * 0.0001; diff --git a/src/Simple.cpp b/src/Simple.cpp index d6345ac..f226da3 100644 --- a/src/Simple.cpp +++ b/src/Simple.cpp @@ -13,7 +13,7 @@ - Envelope Rate = 11 - Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -22,101 +22,110 @@ Simple :: Simple() { - adsr = new ADSR; - baseFrequency = (MY_FLOAT) 440.0; - // Concatenate the STK rawwave path to the rawwave file - loop = new WaveLoop( (Stk::rawwavePath() + "impuls10.raw").c_str(), TRUE ); + loop_ = new WaveLoop( (Stk::rawwavePath() + "impuls10.raw").c_str(), true ); - filter = new OnePole(0.5); - noise = new Noise; - biquad = new BiQuad(); - - setFrequency(baseFrequency); - loopGain = 0.5; + filter_.setPole( 0.5 ); + baseFrequency_ = 440.0; + setFrequency( baseFrequency_ ); + loopGain_ = 0.5; } Simple :: ~Simple() { - delete adsr; - delete loop; - delete filter; - delete biquad; + delete loop_; } void Simple :: keyOn() { - adsr->keyOn(); + adsr_.keyOn(); } void Simple :: keyOff() { - adsr->keyOff(); + adsr_.keyOff(); } -void Simple :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Simple :: noteOn(StkFloat frequency, StkFloat amplitude) { - keyOn(); - setFrequency(frequency); - filter->setGain(amplitude); + this->keyOn(); + this->setFrequency( frequency ); + filter_.setGain( amplitude ); #if defined(_STK_DEBUG_) - std::cerr << "Simple: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Simple::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Simple :: noteOff(MY_FLOAT amplitude) +void Simple :: noteOff(StkFloat amplitude) { - keyOff(); + this->keyOff(); #if defined(_STK_DEBUG_) - std::cerr << "Simple: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Simple::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Simple :: setFrequency(MY_FLOAT frequency) +void Simple :: setFrequency(StkFloat frequency) { - biquad->setResonance( frequency, 0.98, true ); - loop->setFrequency(frequency); + biquad_.setResonance( frequency, 0.98, true ); + loop_->setFrequency( frequency ); } -MY_FLOAT Simple :: tick() +StkFloat Simple :: tick() { - lastOutput = loopGain * loop->tick(); - biquad->tick( noise->tick() ); - lastOutput += (1.0 - loopGain) * biquad->lastOut(); - lastOutput = filter->tick( lastOutput ); - lastOutput *= adsr->tick(); - return lastOutput; + lastOutput_ = loopGain_ * loop_->tick(); + biquad_.tick( noise_.tick() ); + lastOutput_ += (1.0 - loopGain_) * biquad_.lastOut(); + lastOutput_ = filter_.tick( lastOutput_ ); + lastOutput_ *= adsr_.tick(); + return lastOutput_; } -void Simple :: controlChange(int number, MY_FLOAT value) +StkFloat *Simple :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Simple :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Simple :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Clarinet: Control value less than zero!" << std::endl; + errorString_ << "Simple::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Clarinet: Control value greater than 128.0!" << std::endl; + errorString_ << "Simple::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_Breath_) // 2 - filter->setPole( 0.99 * (1.0 - (norm * 2.0)) ); + filter_.setPole( 0.99 * (1.0 - (norm * 2.0)) ); else if (number == __SK_NoiseLevel_) // 4 - loopGain = norm; + loopGain_ = norm; else if (number == __SK_ModFrequency_) { // 11 norm /= 0.2 * Stk::sampleRate(); - adsr->setAttackRate( norm ); - adsr->setDecayRate( norm ); - adsr->setReleaseRate( norm ); + adsr_.setAttackRate( norm ); + adsr_.setDecayRate( norm ); + adsr_.setReleaseRate( norm ); } else if (number == __SK_AfterTouch_Cont_) // 128 - adsr->setTarget( norm ); - else - std::cerr << "Simple: Undefined Control Number (" << number << ")!!" << std::endl; + adsr_.setTarget( norm ); + else { + errorString_ << "Simple::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Simple: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Simple::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/SingWave.cpp b/src/SingWave.cpp index b45fc36..8f382f1 100644 --- a/src/SingWave.cpp +++ b/src/SingWave.cpp @@ -9,120 +9,121 @@ from pitch shifting. It will be used as an excitation source for other instruments. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "SingWave.h" -SingWave :: SingWave(const char *fileName, bool raw) +SingWave :: SingWave( std::string fileName, bool raw) { // An exception could be thrown here. - wave = new WaveLoop( fileName, raw ); + wave_ = new WaveLoop( fileName, raw ); - rate = 1.0; - sweepRate = 0.001; - modulator = new Modulate(); - modulator->setVibratoRate( 6.0 ); - modulator->setVibratoGain( 0.04 ); - modulator->setRandomGain( 0.005 ); - envelope = new Envelope; - pitchEnvelope = new Envelope; - setFrequency( 75.0 ); - pitchEnvelope->setRate( 1.0 ); + rate_ = 1.0; + sweepRate_ = 0.001; + modulator_ = new Modulate(); + modulator_->setVibratoRate( 6.0 ); + modulator_->setVibratoGain( 0.04 ); + modulator_->setRandomGain( 0.005 ); + this->setFrequency( 75.0 ); + pitchEnvelope_.setRate( 1.0 ); this->tick(); this->tick(); - pitchEnvelope->setRate( sweepRate * rate ); + pitchEnvelope_.setRate( sweepRate_ * rate_ ); } SingWave :: ~SingWave() { - delete wave; - delete modulator; - delete envelope; - delete pitchEnvelope; + delete wave_; + delete modulator_; } void SingWave :: reset() { - wave->reset(); - lastOutput = 0.0; + wave_->reset(); + lastOutput_ = 0.0; } void SingWave :: normalize() { - wave->normalize(); + wave_->normalize(); } -void SingWave :: normalize(MY_FLOAT newPeak) +void SingWave :: normalize(StkFloat peak) { - wave->normalize( newPeak ); + wave_->normalize( peak ); } -void SingWave :: setFrequency(MY_FLOAT frequency) +void SingWave :: setFrequency(StkFloat frequency) { - MY_FLOAT temp = rate; - rate = wave->getSize() * frequency / Stk::sampleRate(); - temp -= rate; + StkFloat temp = rate_; + rate_ = wave_->getSize() * frequency / Stk::sampleRate(); + temp -= rate_; if ( temp < 0) temp = -temp; - pitchEnvelope->setTarget( rate ); - pitchEnvelope->setRate( sweepRate * temp ); + pitchEnvelope_.setTarget( rate_ ); + pitchEnvelope_.setRate( sweepRate_ * temp ); } -void SingWave :: setVibratoRate(MY_FLOAT aRate) +void SingWave :: setVibratoRate(StkFloat rate) { - modulator->setVibratoRate( aRate ); + modulator_->setVibratoRate( rate ); } -void SingWave :: setVibratoGain(MY_FLOAT gain) +void SingWave :: setVibratoGain(StkFloat gain) { - modulator->setVibratoGain(gain); + modulator_->setVibratoGain(gain); } -void SingWave :: setRandomGain(MY_FLOAT gain) +void SingWave :: setRandomGain(StkFloat gain) { - modulator->setRandomGain(gain); + modulator_->setRandomGain(gain); } -void SingWave :: setSweepRate(MY_FLOAT aRate) +void SingWave :: setSweepRate(StkFloat rate) { - sweepRate = aRate; + sweepRate_ = rate; } -void SingWave :: setGainRate(MY_FLOAT aRate) +void SingWave :: setGainRate(StkFloat rate) { - envelope->setRate(aRate); + envelope_.setRate(rate); } -void SingWave :: setGainTarget(MY_FLOAT target) +void SingWave :: setGainTarget(StkFloat target) { - envelope->setTarget(target); + envelope_.setTarget(target); } void SingWave :: noteOn() { - envelope->keyOn(); + envelope_.keyOn(); } void SingWave :: noteOff() { - envelope->keyOff(); + envelope_.keyOff(); } -MY_FLOAT SingWave :: tick() +StkFloat SingWave :: tick() { // Set the wave rate. - MY_FLOAT newRate = pitchEnvelope->tick(); - newRate += newRate * modulator->tick(); - wave->setRate( newRate ); + StkFloat newRate = pitchEnvelope_.tick(); + newRate += newRate * modulator_->tick(); + wave_->setRate( newRate ); - lastOutput = wave->tick(); - lastOutput *= envelope->tick(); + lastOutput_ = wave_->tick(); + lastOutput_ *= envelope_.tick(); - return lastOutput; + return lastOutput_; } -MY_FLOAT SingWave :: lastOut() +StkFloat *SingWave :: tick(StkFloat *vector, unsigned int vectorSize) { - return lastOutput; + return Generator::tick( vector, vectorSize ); +} + +StkFrames& SingWave :: tick( StkFrames& frames, unsigned int channel ) +{ + return Generator::tick( frames, channel ); } diff --git a/src/Sitar.cpp b/src/Sitar.cpp index 605a3cc..b808b31 100644 --- a/src/Sitar.cpp +++ b/src/Sitar.cpp @@ -13,105 +13,113 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Sitar.h" #include -Sitar :: Sitar(MY_FLOAT lowestFrequency) +Sitar :: Sitar(StkFloat lowestFrequency) { - length = (long) (Stk::sampleRate() / lowestFrequency + 1); - loopGain = (MY_FLOAT) 0.999; - delayLine = new DelayA( (MY_FLOAT)(length / 2.0), length ); - delay = length / 2.0; - targetDelay = delay; + unsigned long length = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + delayLine_.setMaximumDelay( length ); + delay_ = 0.5 * length; + delayLine_.setDelay( delay_ ); + targetDelay_ = delay_; - loopFilter = new OneZero; - loopFilter->setZero(0.01); + loopFilter_.setZero(0.01); + loopGain_ = 0.999; - envelope = new ADSR(); - envelope->setAllTimes(0.001, 0.04, 0.0, 0.5); - - noise = new Noise; + envelope_.setAllTimes(0.001, 0.04, 0.0, 0.5); this->clear(); } Sitar :: ~Sitar() { - delete delayLine; - delete loopFilter; - delete noise; - delete envelope; } void Sitar :: clear() { - delayLine->clear(); - loopFilter->clear(); + delayLine_.clear(); + loopFilter_.clear(); } -void Sitar :: setFrequency(MY_FLOAT frequency) +void Sitar :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "Sitar: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Sitar::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } - targetDelay = (Stk::sampleRate() / freakency); - delay = targetDelay * (1.0 + (0.05 * noise->tick())); - delayLine->setDelay(delay); - loopGain = 0.995 + (freakency * 0.0000005); - if (loopGain > 0.9995) loopGain = 0.9995; + targetDelay_ = (Stk::sampleRate() / freakency); + delay_ = targetDelay_ * (1.0 + (0.05 * noise_.tick())); + delayLine_.setDelay( delay_ ); + loopGain_ = 0.995 + (freakency * 0.0000005); + if ( loopGain_ > 0.9995 ) loopGain_ = 0.9995; } -void Sitar :: pluck(MY_FLOAT amplitude) +void Sitar :: pluck(StkFloat amplitude) { - envelope->keyOn(); + envelope_.keyOn(); } -void Sitar :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Sitar :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); - pluck(amplitude); - amGain = 0.1 * amplitude; + this->setFrequency( frequency ); + this->pluck( amplitude ); + amGain_ = 0.1 * amplitude; #if defined(_STK_DEBUG_) - std::cerr << "Sitar: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Sitar::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void Sitar :: noteOff(MY_FLOAT amplitude) +void Sitar :: noteOff(StkFloat amplitude) { - loopGain = (MY_FLOAT) 1.0 - amplitude; - if ( loopGain < 0.0 ) { - std::cerr << "Plucked: noteOff amplitude greater than 1.0!" << std::endl; - loopGain = 0.0; + loopGain_ = (StkFloat) 1.0 - amplitude; + if ( loopGain_ < 0.0 ) { + errorString_ << "Sitar::noteOff: amplitude is greater than 1.0 ... setting to 1.0!"; + handleError( StkError::WARNING ); + loopGain_ = 0.0; } - else if ( loopGain > 1.0 ) { - std::cerr << "Plucked: noteOff amplitude less than or zero!" << std::endl; - loopGain = (MY_FLOAT) 0.99999; + else if ( loopGain_ > 1.0 ) { + errorString_ << "Sitar::noteOff: amplitude is < 0.0 ... setting to 0.0!"; + handleError( StkError::WARNING ); + loopGain_ = 0.99999; } #if defined(_STK_DEBUG_) - std::cerr << "Plucked: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Sitar::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Sitar :: tick() +StkFloat Sitar :: tick() { - if ( fabs(targetDelay - delay) > 0.001 ) { - if (targetDelay < delay) - delay *= 0.99999; + if ( fabs(targetDelay_ - delay_) > 0.001 ) { + if ( targetDelay_ < delay_ ) + delay_ *= 0.99999; else - delay *= 1.00001; - delayLine->setDelay(delay); + delay_ *= 1.00001; + delayLine_.setDelay( delay_ ); } - lastOutput = delayLine->tick( loopFilter->tick( delayLine->lastOut() * loopGain ) + - (amGain * envelope->tick() * noise->tick())); + lastOutput_ = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) + + (amGain_ * envelope_.tick() * noise_.tick())); - return lastOutput; + return lastOutput_; +} + +StkFloat *Sitar :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Sitar :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/Skini.cpp b/src/Skini.cpp new file mode 100644 index 0000000..4234d48 --- /dev/null +++ b/src/Skini.cpp @@ -0,0 +1,212 @@ +/***************************************************/ +/*! \class Skini + \brief STK SKINI parsing class + + This class parses SKINI formatted text + messages. It can be used to parse individual + messages or it can be passed an entire file. + The SKINI specification is Perry's and his + alone, but it's all text so it shouldn't be too + hard to figure out. + + SKINI (Synthesis toolKit Instrument Network + Interface) is like MIDI, but allows for + floating-point control changes, note numbers, + etc. The following example causes a sharp + middle C to be played with a velocity of 111.132: + + noteOn 60.01 111.132 + + See also SKINI.txt. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. +*/ +/***************************************************/ + +#include "Skini.h" +#include "SKINI.tbl" + +Skini :: Skini() +{ +} + +Skini :: ~Skini() +{ +} + +bool Skini :: setFile( std::string fileName ) +{ + if ( file_.is_open() ) { + errorString_ << "Skini::setFile: already reaading a file!"; + handleError( StkError::WARNING ); + return false; + } + + file_.open( fileName.c_str() ); + if ( !file_ ) { + errorString_ << "Skini::setFile: unable to open file (" << fileName << ")"; + handleError( StkError::WARNING ); + return false; + } + + return true; +} + +long Skini :: nextMessage( Message& message ) +{ + if ( !file_.is_open() ) return 0; + + std::string line; + bool done = false; + while ( !done ) { + + // Read a line from the file and skip over invalid messages. + if ( std::getline( file_, line ).eof() ) { + errorString_ << "// End of Score. Thanks for using SKINI!!"; + handleError( StkError::STATUS ); + file_.close(); + message.type = 0; + done = true; + } + else if ( parseString( line, message ) > 0 ) done = true; + } + + return message.type; +} + +void Skini :: tokenize( const std::string& str, + std::vector& tokens, + const std::string& delimiters ) +{ + // Skip delimiters at beginning. + std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); + // Find first "non-delimiter". + std::string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) { + // Found a token, add it to the vector. + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // Skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(delimiters, pos); + // Find next "non-delimiter" + pos = str.find_first_of(delimiters, lastPos); + } +} + +long Skini :: parseString( std::string& line, Message& message ) +{ + message.type = 0; + if ( line.empty() ) return message.type; + + // Check for comment lines. + std::string::size_type lastPos = line.find_first_not_of(" ,\t", 0); + std::string::size_type pos = line.find_first_of("/", lastPos); + if ( std::string::npos != pos ) { + errorString_ << "// Comment Line: " << line; + handleError( StkError::STATUS ); + return message.type; + } + + // Tokenize the string. + std::vector tokens; + this->tokenize( line, tokens, " ,\t"); + + // Valid SKINI messages must have at least three fields (type, time, + // and channel). + if ( tokens.size() < 3 ) return message.type; + + // Determine message type. + int iSkini = 0; + while ( iSkini < __SK_MaxMsgTypes_ ) { + if ( tokens[0] == skini_msgs[iSkini].messageString ) break; + iSkini++; + } + + if ( iSkini >= __SK_MaxMsgTypes_ ) { + errorString_ << "Skini::parseString: couldn't parse this line:\n " << line; + handleError( StkError::WARNING ); + return message.type; + } + + // Found the type. + message.type = skini_msgs[iSkini].type; + + // Parse time field. + if ( tokens[1][0] == '=' ) { + tokens[1].erase( tokens[1].begin() ); + if ( tokens[1].empty() ) { + errorString_ << "Skini::parseString: couldn't parse time field in line:\n " << line; + handleError( StkError::WARNING ); + return message.type = 0; + } + message.time = (StkFloat) -atof( tokens[1].c_str() ); + } + else + message.time = (StkFloat) atof( tokens[1].c_str() ); + + // Parse the channel field. + message.channel = atoi( tokens[2].c_str() ); + + // Parse the remaining fields (maximum of 2 more). + int iValue = 0; + long dataType = skini_msgs[iSkini].data2; + while ( dataType != NOPE ) { + + if ( tokens.size() <= (unsigned int) (iValue+3) ) { + errorString_ << "Skini::parseString: inconsistency between type table and parsed line:\n " << line; + handleError( StkError::WARNING ); + return message.type = 0; + } + + switch ( dataType ) { + + case SK_INT: + message.intValues[iValue] = atoi( tokens[iValue+3].c_str() ); + message.floatValues[iValue] = (StkFloat) message.intValues[iValue]; + break; + + case SK_DBL: + message.floatValues[iValue] = atof( tokens[iValue+3].c_str() ); + message.intValues[iValue] = (long) message.floatValues[iValue]; + break; + + case SK_STR: // Must be the last field. + message.remainder = tokens[iValue+3]; + return message.type; + } + + if ( ++iValue == 1 ) + dataType = skini_msgs[iSkini].data3; + else + dataType = NOPE; + } + + return message.type; +} + +std::string Skini :: whatsThisType(long type) +{ + std::string typeString; + + for ( int i=0; i<__SK_MaxMsgTypes_; i++ ) { + if ( type == skini_msgs[i].type ) { + typeString = skini_msgs[i].messageString; + break; + } + } + return typeString; +} + +std::string Skini :: whatsThisController( long number ) +{ + std::string controller; + + for ( int i=0; i<__SK_MaxMsgTypes_; i++) { + if ( skini_msgs[i].type == __SK_ControlChange_ && + number == skini_msgs[i].data2) { + controller = skini_msgs[i].messageString; + break; + } + } + return controller; +} diff --git a/src/Socket.cpp b/src/Socket.cpp index 369f453..863f85f 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -14,7 +14,7 @@ less than or equal to zero indicate a closed or lost connection or the occurence of an error. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -31,6 +31,7 @@ #include #include #include + #include #elif defined(__OS_WINDOWS__) @@ -40,9 +41,9 @@ Socket :: Socket( int port ) { - soket = -1; - server = true; - poort = port; + soket_ = -1; + server_ = true; + port_ = port; // Create a socket server. #if defined(__OS_WINDOWS__) // windoze-only stuff @@ -51,16 +52,23 @@ Socket :: Socket( int port ) WSAStartup(wVersionRequested, &wsaData); if (wsaData.wVersion != wVersionRequested) { - sprintf( msg, "Socket: Incompatible Windows socket library version!" ); - handleError( msg, StkError::PROCESS_SOCKET ); + errorString_ << "Socket: Incompatible Windows socket library version!"; + handleError( StkError::PROCESS_SOCKET ); } #endif // Create the server-side socket - soket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (soket < 0) { - sprintf(msg, "Socket: Couldn't create socket server!"); - handleError( msg, StkError::PROCESS_SOCKET ); + soket_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (soket_ < 0) { + errorString_ << "Socket: Couldn't create socket server!"; + handleError( StkError::PROCESS_SOCKET ); + } + + int flag = 1; + int result = setsockopt( soket_, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int) ); + if (result < 0) { + errorString_ << "Socket: Error setting socket options!"; + handleError( StkError::PROCESS_SOCKET ); } struct sockaddr_in mysocket; @@ -69,23 +77,23 @@ Socket :: Socket( int port ) mysocket.sin_port=htons( port ); // Bind socket to the appropriate port and interface (INADDR_ANY) - if (bind(soket, (struct sockaddr *)&mysocket, sizeof(mysocket)) < 0) { - sprintf(msg, "Socket: Couldn't bind socket!"); - handleError( msg, StkError::PROCESS_SOCKET ); + if ( bind(soket_, (struct sockaddr *)&mysocket, sizeof(mysocket)) < 0 ) { + errorString_ << "Socket: Couldn't bind socket!"; + handleError( StkError::PROCESS_SOCKET ); } // Listen for incoming connection(s) - if ( listen(soket, 1) < 0 ) { - sprintf(msg, "Socket: Couldn't start server listening!"); - handleError( msg, StkError::PROCESS_SOCKET ); + if ( listen(soket_, 1) < 0 ) { + errorString_ << "Socket: Couldn't start server listening!"; + handleError( StkError::PROCESS_SOCKET ); } } Socket :: Socket(int port, const char *hostname ) { - soket = -1; - server = false; - poort = port; + soket_ = -1; + server_ = false; + port_ = port; #if defined(__OS_WINDOWS__) // windoze-only stuff WSADATA wsaData; @@ -93,8 +101,8 @@ Socket :: Socket(int port, const char *hostname ) WSAStartup(wVersionRequested, &wsaData); if (wsaData.wVersion != wVersionRequested) { - sprintf( msg, "Socket: Incompatible Windows socket library version!" ); - handleError( msg, StkError::PROCESS_SOCKET ); + errorString_ << "Socket: Incompatible Windows socket library version!"; + handleError( StkError::PROCESS_SOCKET ); } #endif @@ -106,11 +114,11 @@ Socket :: ~Socket() { #if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) - ::close( soket ); + ::close( soket_ ); #elif defined(__OS_WINDOWS__) - ::closesocket( soket ); + ::closesocket( soket_ ); WSACleanup(); #endif @@ -119,22 +127,29 @@ Socket :: ~Socket() int Socket :: connect( int port, const char *hostname ) { // This method is for client connections only! - if ( server == true ) return -1; + if ( server_ == true ) return -1; // Close an existing connection if it exists. - if ( isValid( soket ) ) this->close(); + if ( isValid( soket_ ) ) this->close(); // Create the client-side socket - soket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (soket < 0) { - sprintf(msg, "Socket: Couldn't create socket client!"); - handleError( msg, StkError::PROCESS_SOCKET ); + soket_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (soket_ < 0) { + errorString_ << "Socket: Couldn't create socket client!"; + handleError( StkError::PROCESS_SOCKET ); + } + + int flag = 1; + int result = setsockopt( soket_, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int) ); + if (result < 0) { + errorString_ << "Socket: Error setting socket options!"; + handleError( StkError::PROCESS_SOCKET ); } struct hostent *hostp; if ( (hostp = gethostbyname(hostname)) == 0 ) { - sprintf(msg, "Socket: unknown host (%s)!", hostname); - handleError( msg, StkError::PROCESS_SOCKET_IPADDR ); + errorString_ << "Socket: unknown host (" << hostname << ")!"; + handleError( StkError::PROCESS_SOCKET_IPADDR ); } // Fill in the address structure @@ -144,29 +159,29 @@ int Socket :: connect( int port, const char *hostname ) server_address.sin_port = htons(port); // Connect to the server - if ( ::connect(soket, (struct sockaddr *)&server_address, + if ( ::connect(soket_, (struct sockaddr *)&server_address, sizeof(server_address) ) < 0) { - sprintf(msg, "Socket: Couldn't connect to socket server!"); - handleError( msg, StkError::PROCESS_SOCKET ); + errorString_ << "Socket: Couldn't connect to socket server!"; + handleError( StkError::PROCESS_SOCKET ); } - return soket; + return soket_; } -int Socket :: socket( void ) const +int Socket :: id( void ) const { - return soket; + return soket_; } int Socket :: port( void ) const { - return poort; + return port_; } int Socket :: accept( void ) { - if ( server ) - return ::accept( soket, NULL, NULL ); + if ( server_ ) + return ::accept( soket_, NULL, NULL ); else return -1; } @@ -196,9 +211,9 @@ void Socket :: setBlocking( int socket, bool enable ) void Socket :: close( void ) { - if ( !isValid( soket ) ) return; - this->close( soket ); - soket = -1; + if ( !isValid( soket_ ) ) return; + this->close( soket_ ); + soket_ = -1; } void Socket :: close( int socket ) @@ -218,8 +233,8 @@ void Socket :: close( int socket ) int Socket :: writeBuffer(const void *buffer, long bufferSize, int flags ) { - if ( !isValid( soket ) ) return -1; - return send( soket, (const char *)buffer, bufferSize, flags ); + if ( !isValid( soket_ ) ) return -1; + return send( soket_, (const char *)buffer, bufferSize, flags ); } int Socket :: writeBuffer(int socket, const void *buffer, long bufferSize, int flags ) @@ -230,8 +245,8 @@ int Socket :: writeBuffer(int socket, const void *buffer, long bufferSize, int f int Socket :: readBuffer(void *buffer, long bufferSize, int flags ) { - if ( !isValid( soket ) ) return -1; - return recv( soket, (char *)buffer, bufferSize, flags ); + if ( !isValid( soket_ ) ) return -1; + return recv( soket_, (char *)buffer, bufferSize, flags ); } int Socket :: readBuffer(int socket, void *buffer, long bufferSize, int flags ) diff --git a/src/Sphere.cpp b/src/Sphere.cpp index 43b3916..1d249e5 100644 --- a/src/Sphere.cpp +++ b/src/Sphere.cpp @@ -5,100 +5,95 @@ This class implements a spherical ball with radius, mass, position, and velocity parameters. - by Perry R. Cook, 1995 - 2002. + by Perry R. Cook, 1995 - 2004. */ /***************************************************/ #include "Sphere.h" -#include #include -Sphere::Sphere(double initRadius) +Sphere::Sphere(StkFloat radius) { - myRadius = initRadius; - myMass = 1.0; - myPosition = new Vector3D(0, 0, 0); - myVelocity = new Vector3D(0, 0, 0); + radius_ = radius; + mass_ = 1.0; }; Sphere::~Sphere() { - delete myPosition; - delete myVelocity; } -void Sphere::setPosition(double anX, double aY, double aZ) +void Sphere::setPosition(StkFloat x, StkFloat y, StkFloat z) { - myPosition->setXYZ(anX, aY, aZ); + position_.setXYZ(x, y, z); }; -void Sphere::setVelocity(double anX, double aY, double aZ) +void Sphere::setVelocity(StkFloat x, StkFloat y, StkFloat z) { - myVelocity->setXYZ(anX, aY, aZ); + velocity_.setXYZ(x, y, z); }; -void Sphere::setRadius(double aRadius) +void Sphere::setRadius(StkFloat radius) { - myRadius = aRadius; + radius_ = radius; }; -void Sphere::setMass(double aMass) +void Sphere::setMass(StkFloat mass) { - myMass = aMass; + mass_ = mass; }; Vector3D* Sphere::getPosition() { - return myPosition; + return &position_; }; -Vector3D* Sphere::getRelativePosition(Vector3D* aPosition) +Vector3D* Sphere::getRelativePosition(Vector3D* position) { - workingVector.setXYZ(aPosition->getX() - myPosition->getX(), - aPosition->getY() - myPosition->getY(), - aPosition->getZ() - myPosition->getZ()); - return &workingVector; + workingVector_.setXYZ(position->getX() - position_.getX(), + position->getY() - position_.getY(), + position->getZ() - position_.getZ()); + return &workingVector_; }; -double Sphere::getVelocity(Vector3D* aVelocity) +StkFloat Sphere::getVelocity(Vector3D* velocity) { - aVelocity->setXYZ(myVelocity->getX(), myVelocity->getY(), myVelocity->getZ()); - return myVelocity->getLength(); + velocity->setXYZ( velocity_.getX(), velocity_.getY(), velocity_.getZ() ); + return velocity_.getLength(); }; -double Sphere::isInside(Vector3D *aPosition) +StkFloat Sphere::isInside(Vector3D *position) { // Return directed distance from aPosition to spherical boundary ( < // 0 if inside). - double distance; + StkFloat distance; Vector3D *tempVector; - tempVector = this->getRelativePosition(aPosition); + tempVector = this->getRelativePosition( position ); distance = tempVector->getLength(); - return distance - myRadius; + return distance - radius_; }; -double Sphere::getRadius() +StkFloat Sphere::getRadius() { - return myRadius; + return radius_; }; -double Sphere::getMass() +StkFloat Sphere::getMass() { - return myMass; + return mass_; }; -void Sphere::addVelocity(double anX, double aY, double aZ) +void Sphere::addVelocity(StkFloat x, StkFloat y, StkFloat z) { - myVelocity->setX(myVelocity->getX() + anX); - myVelocity->setY(myVelocity->getY() + aY); - myVelocity->setZ(myVelocity->getZ() + aZ); + velocity_.setX(velocity_.getX() + x); + velocity_.setY(velocity_.getY() + y); + velocity_.setZ(velocity_.getZ() + z); } -void Sphere::tick(double timeIncrement) +void Sphere::tick(StkFloat timeIncrement) { - myPosition->setX(myPosition->getX() + (timeIncrement * myVelocity->getX())); - myPosition->setY(myPosition->getY() + (timeIncrement * myVelocity->getY())); - myPosition->setZ(myPosition->getZ() + (timeIncrement * myVelocity->getZ())); + position_.setX(position_.getX() + (timeIncrement * velocity_.getX())); + position_.setY(position_.getY() + (timeIncrement * velocity_.getY())); + position_.setZ(position_.getZ() + (timeIncrement * velocity_.getZ())); }; diff --git a/src/StifKarp.cpp b/src/StifKarp.cpp index e5c4718..c860b16 100644 --- a/src/StifKarp.cpp +++ b/src/StifKarp.cpp @@ -17,211 +17,223 @@ - String Sustain = 11 - String Stretch = 1 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "StifKarp.h" #include "SKINI.msg" -#include #include -StifKarp :: StifKarp(MY_FLOAT lowestFrequency) +StifKarp :: StifKarp(StkFloat lowestFrequency) { - length = (long) (Stk::sampleRate() / lowestFrequency + 1); - delayLine = new DelayA(0.5 * length, length); - combDelay = new DelayL( 0.2 * length, length); + length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + delayLine_.setMaximumDelay( length_ ); + delayLine_.setDelay( 0.5 * length_ ); + combDelay_.setMaximumDelay( length_ ); + combDelay_.setDelay( 0.2 * length_ ); - filter = new OneZero(); - noise = new Noise(); - biQuad[0] = new BiQuad(); - biQuad[1] = new BiQuad(); - biQuad[2] = new BiQuad(); - biQuad[3] = new BiQuad(); + pluckAmplitude_ = 0.3; + pickupPosition_ = 0.4; + lastFrequency_ = lowestFrequency * 2.0; + lastLength_ = length_ * 0.5; + stretching_ = 0.9999; + baseLoopGain_ = 0.995; + loopGain_ = 0.999; - pluckAmplitude = 0.3; - pickupPosition = (MY_FLOAT) 0.4; - lastFrequency = lowestFrequency * 2.0; - lastLength = length * 0.5; - stretching = 0.9999; - baseLoopGain = 0.995; - loopGain = 0.999; - - clear(); + this->clear(); } StifKarp :: ~StifKarp() { - delete delayLine; - delete combDelay; - delete filter; - delete noise; - delete biQuad[0]; - delete biQuad[1]; - delete biQuad[2]; - delete biQuad[3]; } void StifKarp :: clear() { - delayLine->clear(); - combDelay->clear(); - filter->clear(); + delayLine_.clear(); + combDelay_.clear(); + filter_.clear(); } -void StifKarp :: setFrequency(MY_FLOAT frequency) +void StifKarp :: setFrequency(StkFloat frequency) { - lastFrequency = frequency; + lastFrequency_ = frequency; if ( frequency <= 0.0 ) { - std::cerr << "StifKarp: setFrequency parameter is less than or equal to zero!" << std::endl; - lastFrequency = 220.0; + errorString_ << "StifKarp::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); + lastFrequency_ = 220.0; } - lastLength = Stk::sampleRate() / lastFrequency; - MY_FLOAT delay = lastLength - 0.5; - if (delay <= 0.0) delay = 0.3; - else if (delay > length) delay = length; - delayLine->setDelay( delay ); + lastLength_ = Stk::sampleRate() / lastFrequency_; + StkFloat delay = lastLength_ - 0.5; + if (delay <= 0.0) + delay = 0.3; + else if (delay > length_) + delay = length_; + delayLine_.setDelay( delay ); - loopGain = baseLoopGain + (frequency * (MY_FLOAT) 0.000005); - if (loopGain >= 1.0) loopGain = (MY_FLOAT) 0.99999; + loopGain_ = baseLoopGain_ + (frequency * 0.000005); + if (loopGain_ >= 1.0) loopGain_ = 0.99999; - setStretch(stretching); + setStretch(stretching_); - combDelay->setDelay((MY_FLOAT) 0.5 * pickupPosition * lastLength); + combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); } -void StifKarp :: setStretch(MY_FLOAT stretch) +void StifKarp :: setStretch(StkFloat stretch) { - stretching = stretch; - MY_FLOAT coefficient; - MY_FLOAT freq = lastFrequency * 2.0; - MY_FLOAT dFreq = ( (0.5 * Stk::sampleRate()) - freq ) * 0.25; - MY_FLOAT temp = 0.5 + (stretch * 0.5); + stretching_ = stretch; + StkFloat coefficient; + StkFloat freq = lastFrequency_ * 2.0; + StkFloat dFreq = ( (0.5 * Stk::sampleRate()) - freq ) * 0.25; + StkFloat temp = 0.5 + (stretch * 0.5); if (temp > 0.9999) temp = 0.9999; for (int i=0; i<4; i++) { coefficient = temp * temp; - biQuad[i]->setA2( coefficient ); - biQuad[i]->setB0( coefficient ); - biQuad[i]->setB2( 1.0 ); + biquad_[i].setA2( coefficient ); + biquad_[i].setB0( coefficient ); + biquad_[i].setB2( 1.0 ); coefficient = -2.0 * temp * cos(TWO_PI * freq / Stk::sampleRate()); - biQuad[i]->setA1( coefficient ); - biQuad[i]->setB1( coefficient ); + biquad_[i].setA1( coefficient ); + biquad_[i].setB1( coefficient ); freq += dFreq; } } -void StifKarp :: setPickupPosition(MY_FLOAT position) { - pickupPosition = position; +void StifKarp :: setPickupPosition(StkFloat position) { + pickupPosition_ = position; if ( position < 0.0 ) { - std::cerr << "StifKarp: setPickupPosition parameter is less than zero!" << std::endl; - pickupPosition = 0.0; + errorString_ << "StifKarp::setPickupPosition: parameter is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); + pickupPosition_ = 0.0; } else if ( position > 1.0 ) { - std::cerr << "StifKarp: setPickupPosition parameter is greater than 1.0!" << std::endl; - pickupPosition = 1.0; + errorString_ << "StifKarp::setPickupPosition: parameter is greater than 1.0 ... setting to 1.0!"; + handleError( StkError::WARNING ); + pickupPosition_ = 1.0; } // Set the pick position, which puts zeroes at position * length. - combDelay->setDelay(0.5 * pickupPosition * lastLength); + combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); } -void StifKarp :: setBaseLoopGain(MY_FLOAT aGain) +void StifKarp :: setBaseLoopGain(StkFloat aGain) { - baseLoopGain = aGain; - loopGain = baseLoopGain + (lastFrequency * 0.000005); - if ( loopGain > 0.99999 ) loopGain = (MY_FLOAT) 0.99999; + baseLoopGain_ = aGain; + loopGain_ = baseLoopGain_ + (lastFrequency_ * 0.000005); + if ( loopGain_ > 0.99999 ) loopGain_ = (StkFloat) 0.99999; } -void StifKarp :: pluck(MY_FLOAT amplitude) +void StifKarp :: pluck(StkFloat amplitude) { - MY_FLOAT gain = amplitude; + StkFloat gain = amplitude; if ( gain > 1.0 ) { - std::cerr << "StifKarp: pluck amplitude greater than 1.0!" << std::endl; + errorString_ << "StifKarp::pluck: amplitude is greater than 1.0 ... setting to 1.0!"; + handleError( StkError::WARNING ); gain = 1.0; } else if ( gain < 0.0 ) { - std::cerr << "StifKarp: pluck amplitude less than zero!" << std::endl; + errorString_ << "StifKarp::pluck: amplitude is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); gain = 0.0; } - pluckAmplitude = amplitude; - for (long i=0; itick((delayLine->lastOut() * 0.6) + 0.4 * noise->tick() * pluckAmplitude); - //delayLine->tick( combDelay->tick((delayLine->lastOut() * 0.6) + 0.4 * noise->tick() * pluckAmplitude)); + delayLine_.tick( (delayLine_.lastOut() * 0.6) + 0.4 * noise_.tick() * pluckAmplitude_ ); + //delayLine_.tick( combDelay_.tick((delayLine_.lastOut() * 0.6) + 0.4 * noise->tick() * pluckAmplitude_) ); } } -void StifKarp :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void StifKarp :: noteOn(StkFloat frequency, StkFloat amplitude) { - this->setFrequency(frequency); - this->pluck(amplitude); + this->setFrequency( frequency ); + this->pluck( amplitude ); #if defined(_STK_DEBUG_) - std::cerr << "StifKarp: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "StifKarp::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -void StifKarp :: noteOff(MY_FLOAT amplitude) +void StifKarp :: noteOff(StkFloat amplitude) { - MY_FLOAT gain = amplitude; + StkFloat gain = amplitude; if ( gain > 1.0 ) { - std::cerr << "StifKarp: noteOff amplitude greater than 1.0!" << std::endl; + errorString_ << "StifKarp::noteOff: amplitude is greater than 1.0 ... setting to 1.0!"; + handleError( StkError::WARNING ); gain = 1.0; } else if ( gain < 0.0 ) { - std::cerr << "StifKarp: noteOff amplitude less than zero!" << std::endl; + errorString_ << "StifKarp::noteOff: amplitude is < 0.0 ... setting to 0.0!"; + handleError( StkError::WARNING ); gain = 0.0; } - loopGain = (1.0 - gain) * 0.5; + loopGain_ = (1.0 - gain) * 0.5; #if defined(_STK_DEBUG_) - std::cerr << "StifPluck: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "StifKarp::NoteOff: amplitude = " << amplitude << "."; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT StifKarp :: tick() +StkFloat StifKarp :: tick() { - MY_FLOAT temp = delayLine->lastOut() * loopGain; + StkFloat temp = delayLine_.lastOut() * loopGain_; // Calculate allpass stretching. for (int i=0; i<4; i++) - temp = biQuad[i]->tick(temp); + temp = biquad_[i].tick(temp); // Moving average filter. - temp = filter->tick(temp); + temp = filter_.tick(temp); - lastOutput = delayLine->tick(temp); - lastOutput = lastOutput - combDelay->tick(lastOutput); - return lastOutput; + lastOutput_ = delayLine_.tick(temp); + lastOutput_ = lastOutput_ - combDelay_.tick( lastOutput_ ); + return lastOutput_; } -void StifKarp :: controlChange(int number, MY_FLOAT value) +StkFloat *StifKarp :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& StifKarp :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void StifKarp :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "StifKarp: Control value less than zero!" << std::endl; + errorString_ << "StifKarp::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "StifKarp: Control value greater than 128.0!" << std::endl; + errorString_ << "StifKarp::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_PickPosition_) // 4 - setPickupPosition( norm ); + this->setPickupPosition( norm ); else if (number == __SK_StringDamping_) // 11 - setBaseLoopGain( 0.97 + (norm * 0.03) ); + this->setBaseLoopGain( 0.97 + (norm * 0.03) ); else if (number == __SK_StringDetune_) // 1 - setStretch( 0.9 + (0.1 * (1.0 - norm)) ); - else - std::cerr << "StifKarp: Undefined Control Number (" << number << ")!!" << std::endl; + this->setStretch( 0.9 + (0.1 * (1.0 - norm)) ); + else { + errorString_ << "StifKarp::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "StifKarp: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "StifKarp::controlChange: number = " << number << ", value = " << value << "."; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Stk.cpp b/src/Stk.cpp index 139650b..8461210 100644 --- a/src/Stk.cpp +++ b/src/Stk.cpp @@ -8,21 +8,19 @@ provides error handling and byte-swapping functions. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Stk.h" -#include -#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 :: MY_FLOAT32 = 16; -const Stk::STK_FORMAT Stk :: MY_FLOAT64 = 32; +StkFloat Stk :: srate_ = (StkFloat) SRATE; +std::string Stk :: rawwavepath_ = RAWWAVE_PATH; +const Stk::StkFormat Stk :: STK_SINT8 = 0x1; +const Stk::StkFormat Stk :: STK_SINT16 = 0x2; +const Stk::StkFormat Stk :: STK_SINT32 = 0x8; +const Stk::StkFormat Stk :: STK_FLOAT32 = 0x10; +const Stk::StkFormat Stk :: STK_FLOAT64 = 0x20; Stk :: Stk(void) { @@ -32,30 +30,14 @@ Stk :: ~Stk(void) { } -MY_FLOAT Stk :: sampleRate(void) +void Stk :: setRawwavePath( std::string path ) { - return srate; -} - -void Stk :: setSampleRate(MY_FLOAT newRate) -{ - if (newRate > 0) - srate = newRate; -} - -std::string Stk :: rawwavePath(void) -{ - return rawwavepath; -} - -void Stk :: setRawwavePath(std::string newPath) -{ - if ( !newPath.empty() ) - rawwavepath = newPath; + if ( !path.empty() ) + rawwavepath_ = path; // Make sure the path includes a "/" - if ( rawwavepath[rawwavepath.length()-1] != '/' ) - rawwavepath += "/"; + if ( rawwavepath_[rawwavepath_.length()-1] != '/' ) + rawwavepath_ += "/"; } void Stk :: swap16(unsigned char *ptr) @@ -127,33 +109,74 @@ void Stk :: sleep(unsigned long milliseconds) #endif } -void Stk :: handleError( const char *message, StkError::TYPE type ) +void Stk :: handleError( StkError::Type type ) { - if (type == StkError::WARNING) - fprintf(stderr, "\n%s\n\n", message); + handleError( errorString_.str(), type ); + errorString_.str( std::string() ); // reset the ostringstream buffer +} + +void Stk :: handleError( const char *message, StkError::Type type ) +{ + std::string msg( message ); + handleError( msg, type ); +} + +void Stk :: handleError( std::string message, StkError::Type type ) +{ + if (type == StkError::WARNING || type == StkError::STATUS ) + std::cerr << '\n' << message << '\n' << std::endl; else if (type == StkError::DEBUG_WARNING) { #if defined(_STK_DEBUG_) - fprintf(stderr, "\n%s\n\n", message); + std::cerr << '\n' << message << '\n' << std::endl; #endif } else { // Print error message before throwing. - fprintf(stderr, "\n%s\n\n", message); + std::cerr << '\n' << message << '\n' << std::endl; throw StkError(message, type); } } -StkError :: StkError(const char *p, TYPE tipe) - : type(tipe) +StkFrames :: StkFrames( unsigned int nFrames, unsigned int nChannels, bool interleaved ) + : nFrames_( nFrames ), nChannels_( nChannels ), interleaved_( interleaved ) { - strncpy(message, p, 256); + if ( nChannels == 0 ) { + std::string message = "StkFrames::StkFrames: nChannels argument should be 1 or greater (even if nFrames = 0) ... correcting to one channel!"; + Stk::handleError( message, StkError::WARNING ); + nChannels_ = 1; + } + + size_ = nFrames_ * nChannels_; + if ( size_ > 0 ) data_.resize( size_, 0.0 ); } -StkError :: ~StkError(void) +StkFrames :: StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels, bool interleaved ) + : nFrames_( nFrames ), nChannels_( nChannels ), interleaved_( interleaved ) { + if ( nChannels == 0 ) { + std::string message = "StkFrames::StkFrames: nChannels argument should be 1 or greater (even if nFrames = 0) ... correcting to one channel!"; + Stk::handleError( message, StkError::WARNING ); + nChannels_ = 1; + } + + size_ = nFrames_ * nChannels_; + if ( size_ > 0 ) data_.resize( size_, value ); } -void StkError :: printMessage(void) +void StkFrames :: resize( unsigned int nFrames, unsigned int nChannels, StkFloat value ) { - printf("\n%s\n\n", message); + nFrames_ = nFrames; + nChannels_ = nChannels; + + if ( nChannels == 0 ) { + std::string message = "StkFrames::resize(): nChannels argument should be 1 or greater (even if nFrames = 0) ... correcting to one channel!"; + Stk::handleError( message, StkError::WARNING ); + nChannels_ = 1; + } + + size_t newSize = nFrames_ * nChannels_; + if ( size_ != newSize ) { + size_ = newSize; + data_.resize( size_, value ); + } } diff --git a/src/SubNoise.cpp b/src/SubNoise.cpp index 04b64c8..5400eb7 100644 --- a/src/SubNoise.cpp +++ b/src/SubNoise.cpp @@ -6,7 +6,7 @@ using the C rand() function. The quality of the rand() function varies from one OS to another. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -14,8 +14,8 @@ SubNoise :: SubNoise(int subRate) : Noise() { - rate = subRate; - counter = rate; + rate_ = subRate; + counter_ = rate_; } SubNoise :: ~SubNoise() { @@ -23,21 +23,31 @@ SubNoise :: ~SubNoise() int SubNoise :: subRate(void) const { - return rate; + return rate_; } void SubNoise :: setRate(int subRate) { if (subRate > 0) - rate = subRate; + rate_ = subRate; } -MY_FLOAT SubNoise :: tick() +StkFloat SubNoise :: tick() { - if ( ++counter > rate ) { + if ( ++counter_ > rate_ ) { Noise::tick(); - counter = 1; + counter_ = 1; } - return lastOutput; + return lastOutput_; +} + +StkFloat *SubNoise :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Generator::tick( vector, vectorSize ); +} + +StkFrames& SubNoise :: tick( StkFrames& frames, unsigned int channel ) +{ + return Generator::tick( frames, channel ); } diff --git a/src/Table.cpp b/src/Table.cpp index 16bffc0..6982cd3 100644 --- a/src/Table.cpp +++ b/src/Table.cpp @@ -10,97 +10,91 @@ An StkError will be thrown if the table file is not found. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ #include "Table.h" -#include +#include #include -#include -#include -Table :: Table(char *fileName) +Table :: Table( std::string fileName) { - char message[256]; - // Use the system call "stat" to determine the file length struct stat filestat; - if ( stat(fileName, &filestat) == -1 ) { - sprintf(message, "Table: Couldn't stat or find file (%s).", fileName); - handleError( message, StkError::FILE_NOT_FOUND ); + if ( stat( fileName.c_str(), &filestat ) == -1 ) { + errorString_ << "Table: Couldn't stat or find file (" << fileName << ")."; + handleError( StkError::FILE_NOT_FOUND ); } - length = (long) filestat.st_size / 8; // length in 8-byte samples + length_ = (long) filestat.st_size / 8; // length in 8-byte samples // Open the file and read samples into data[] FILE *fd; - fd = fopen(fileName,"rb"); + fd = fopen(fileName.c_str(),"rb"); if (!fd) { - sprintf(message, "Table: Couldn't open or find file (%s).", fileName); - handleError( message, StkError::FILE_NOT_FOUND ); + errorString_ << "Table::Table: unable to open or find file (" << fileName << ")"; + handleError( StkError::FILE_NOT_FOUND ); } - data = (MY_FLOAT *) new MY_FLOAT[length]; + data_.resize( length_, 0.0 ); - // Read samples into data[] + // Read samples into data long i = 0; double temp; - while ( fread(&temp, 8, 1, fd) ) { + while ( fread( &temp, 8, 1, fd ) ) { #ifdef __LITTLE_ENDIAN__ - swap64((unsigned char *)&temp); + swap64( (unsigned char *)&temp ); #endif - data[i++] = (MY_FLOAT) temp; + data_[i++] = (StkFloat) temp; } fclose(fd); - lastOutput = 0.0; + lastOutput_ = 0.0; } Table :: ~Table() { - delete [ ] data; } long Table :: getLength() const { - return length; + return length_; } -MY_FLOAT Table :: lastOut() const +StkFloat Table :: tick(StkFloat index) { - return lastOutput; -} - -MY_FLOAT Table :: tick(MY_FLOAT index) -{ - MY_FLOAT alpha; + StkFloat alpha; long temp; - if (index > length-1) { - std::cerr << "Table: Index (" << index << ") exceeds table length ... sticking at end!" << std::endl; - index = length-1; + if ( index > length_-1 ) { + errorString_ << "Table: Index (" << index << ") exceeds table length ... sticking at end!\n"; + handleError( StkError::WARNING ); + index = length_-1; } else if (index < 0.0) { - std::cerr << "Table: Index (" << index << ") is less than zero ... setting to zero!" << std::endl; + errorString_ << "Table: Index (" << index << ") is less than zero ... setting to zero!\n"; + handleError( StkError::WARNING ); index = 0.0; } // Index in range 0 to length-1 - temp = (long) index; // Integer part of index - alpha = index - (MY_FLOAT) temp; // Fractional part of index + temp = (long) index; // Integer part of index + alpha = index - (StkFloat) temp; // Fractional part of index if (alpha > 0.0) { // Do linear interpolation - lastOutput = data[temp]; - lastOutput += (alpha*(data[temp+1] - lastOutput)); + lastOutput_ = data_[temp]; + lastOutput_ += (alpha*(data_[temp+1] - lastOutput_)); } - else lastOutput = data[temp]; + else lastOutput_ = data_[temp]; - return lastOutput; + return lastOutput_; } -MY_FLOAT *Table :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Table :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; ifinished ) { - Thread::test(); ((TcpWvIn *) info->object)->receive(); } @@ -57,135 +56,137 @@ TcpWvIn :: TcpWvIn( int port ) TcpWvIn :: ~TcpWvIn() { // Close down the thread. - connected = false; - threadInfo.finished = true; - thread->wait( -1 ); - delete thread; + connected_ = false; + threadInfo_.finished = true; + delete thread_; - delete soket; + delete soket_; - if (buffer) - delete [] buffer; + if (buffer_) + delete [] buffer_; } void TcpWvIn :: init( int port ) { - buffer = 0; - bufferBytes = 0; - connected = false; + buffer_ = 0; + bufferBytes_ = 0; + connected_ = false; // Start socket server ... an error can be thrown from the Socket class. - soket = new Socket( port ); + soket_ = new Socket( port ); - thread = new Thread(); - threadInfo.finished = false; - threadInfo.object = (void *) this; + thread_ = new Thread(); + threadInfo_.finished = false; + threadInfo_.object = (void *) this; // Start the input thread. - if ( !thread->start( &inputThread, &threadInfo ) ) { - sprintf(msg, "TcpWvIn: Unable to start input thread!"); - handleError( msg, StkError::PROCESS_THREAD ); + if ( !thread_->start( &inputThread, &threadInfo_ ) ) { + errorString_ << "TcpWvIn::init: unable to start input thread!"; + handleError( StkError::PROCESS_THREAD ); } } -void TcpWvIn :: listen(unsigned int nChannels, Stk::STK_FORMAT format) +void TcpWvIn :: listen(unsigned int nChannels, Stk::StkFormat format) { - mutex.lock(); + mutex_.lock(); - if ( connected ) { - soket->close(fd); + if ( connected_ ) { + soket_->close(fd_); } - if (nChannels < 1) { - sprintf(msg, "TcpWvOut: the channel argument (%d) must be greater than zero.", nChannels); - handleError( msg, StkError::FUNCTION_ARGUMENT ); + if ( nChannels < 1 ) { + errorString_ << "TcpWvOut::listen: the channel argument (" << nChannels << ") must be greater than zero."; + handleError( StkError::FUNCTION_ARGUMENT ); } - unsigned int lastChannels = channels; - channels = nChannels; + unsigned int lastChannels = channels_; + channels_ = nChannels; - if ( format == STK_SINT16 ) dataSize = 2; - else if ( format == STK_SINT32 || format == MY_FLOAT32 ) dataSize = 4; - else if ( format == MY_FLOAT64 ) dataSize = 8; - else if ( format == STK_SINT8 ) dataSize = 1; + 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_SINT8 ) dataSize_ = 1; else { - sprintf( msg, "TcpWvIn: Unknown data type specified (%ld).", format ); - handleError(msg, StkError::FUNCTION_ARGUMENT); + errorString_ << "TcpWvIn::listen: unknown data type specified (" << format << ")."; + handleError( StkError::FUNCTION_ARGUMENT ); } - dataType = format; + dataType_ = format; - int lastBufferBytes = bufferBytes; - bufferBytes = CHUNK_SIZE * N_BUFFERS * channels * dataSize; + int lastBufferBytes = bufferBytes_; + bufferBytes_ = CHUNK_SIZE * N_BUFFERS * channels_ * dataSize_; // Allocate new memory if necessary. - if ( lastBufferBytes < bufferBytes ) { - if ( buffer) delete [] buffer; - buffer = (char *) new char[bufferBytes]; + if ( lastBufferBytes < bufferBytes_ ) { + if ( buffer_) delete [] buffer_; + buffer_ = (char *) new char[bufferBytes_]; } - if ( lastChannels < channels ) { - if ( data ) delete [] data; - data = (MY_FLOAT *) new MY_FLOAT[CHUNK_SIZE*channels]; - if ( lastOutput ) delete [] lastOutput; - lastOutput = (MY_FLOAT *) new MY_FLOAT[channels]; + if ( lastChannels < channels_ ) { + if ( data_ ) delete [] data_; + data_ = (StkFloat *) new StkFloat[CHUNK_SIZE*channels_]; + if ( lastOutputs_ ) delete [] lastOutputs_; + lastOutputs_ = (StkFloat *) new StkFloat[channels_]; } WvIn::reset(); - counter = 0; - writePoint = 0; - bytesFilled = 0; + counter_ = 0; + writePoint_ = 0; + bytesFilled_ = 0; // Accept a connection. - printf("Listening for connection on port %d ... ", soket->port()); - fd = soket->accept(); - if ( fd < 0) { - sprintf( msg, "TcpWvIn: Couldn't accept connection request!"); - handleError( msg, StkError::PROCESS_SOCKET ); + errorString_ << "TcpWvIn: listening for connection on port " << soket_->port() << " ... "; + handleError( StkError::STATUS ); + fd_ = soket_->accept(); + if ( fd_ < 0) { + errorString_ << "TcpWvIn: Could not accept connection request!"; + handleError( StkError::PROCESS_SOCKET ); } - printf(" connection made!\n\n"); + errorString_ << "TcpWvIn::listen: connection made!"; + handleError( StkError::STATUS ); // Start input thread. - connected = true; + connected_ = true; - mutex.unlock(); + mutex_.unlock(); } void TcpWvIn :: receive( void ) { - if ( !connected ) { + if ( !connected_ ) { Stk::sleep(100); return; } fd_set mask; FD_ZERO(&mask); - FD_SET(fd, &mask); + FD_SET(fd_, &mask); // The select function will block until data is available for reading. - select(fd+1, &mask, (fd_set *)0, (fd_set *)0, NULL); + select(fd_+1, &mask, (fd_set *)0, (fd_set *)0, NULL); - if (FD_ISSET(fd, &mask)) { - mutex.lock(); - long unfilled = bufferBytes - bytesFilled; + if (FD_ISSET(fd_, &mask)) { + mutex_.lock(); + long unfilled = bufferBytes_ - bytesFilled_; if ( unfilled > 0 ) { // There's room in our buffer for more data. - long endPoint = writePoint + unfilled; - if ( endPoint > bufferBytes ) unfilled -= endPoint - bufferBytes; - int i = Socket::readBuffer(fd, (void *)&buffer[writePoint], unfilled, 0); + long endPoint = writePoint_ + unfilled; + if ( endPoint > bufferBytes_ ) unfilled -= endPoint - bufferBytes_; + int i = Socket::readBuffer(fd_, (void *)&buffer_[writePoint_], unfilled, 0); if ( i <= 0 ) { - printf("The remote TcpWvIn socket connection has closed.\n\n"); - connected = false; - mutex.unlock(); + errorString_ << "TcpWvIn::receive: the remote TcpWvIn socket connection has closed."; + handleError( StkError::STATUS ); + connected_ = false; + mutex_.unlock(); return; } - bytesFilled += i; - writePoint += i; - if (writePoint == bufferBytes) - writePoint = 0; - mutex.unlock(); + bytesFilled_ += i; + writePoint_ += i; + if (writePoint_ == bufferBytes_) + writePoint_ = 0; + mutex_.unlock(); } else { // Sleep 10 milliseconds AFTER unlocking mutex. - mutex.unlock(); + mutex_.unlock(); Stk::sleep( 10 ); } } @@ -202,124 +203,134 @@ int TcpWvIn :: readData( void ) // adequate network bandwidth and speed). // Wait until data is ready. - long bytes = CHUNK_SIZE * channels * dataSize; - while ( connected && bytesFilled < bytes ) + long bytes = CHUNK_SIZE * channels_ * dataSize_; + while ( connected_ && bytesFilled_ < bytes ) Stk::sleep( 10 ); - if ( !connected && bytesFilled == 0 ) return 0; - bytes = ( bytesFilled < bytes ) ? bytesFilled : bytes; + if ( !connected_ && bytesFilled_ == 0 ) return 0; + bytes = ( bytesFilled_ < bytes ) ? bytesFilled_ : bytes; // Copy samples from buffer to data. - long samples = bytes / dataSize; - mutex.lock(); - if ( dataType == STK_SINT16 ) { - gain = 1.0 / 32767.0; - SINT16 *buf = (SINT16 *) (buffer+readPoint); + long samples = bytes / dataSize_; + mutex_.lock(); + if ( dataType_ == STK_SINT16 ) { + gain_ = 1.0 / 32767.0; + SINT16 *buf = (SINT16 *) (buffer_+readPoint_); for (int i=0; i 0 || counter > 0 ) + if ( bytesFilled_ > 0 || counter_ > 0 ) return true; else - return connected; + return connected_; } -const MY_FLOAT *TcpWvIn :: lastFrame(void) const +const StkFloat *TcpWvIn :: lastFrame(void) const { - return lastOutput; + return lastOutputs_; } -MY_FLOAT TcpWvIn :: lastOut(void) const +StkFloat TcpWvIn :: lastOut(void) const { return WvIn::lastOut(); } -MY_FLOAT TcpWvIn :: tick(void) +StkFloat TcpWvIn :: tick(void) { return WvIn::tick(); } -MY_FLOAT *TcpWvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *TcpWvIn :: tick(StkFloat *vector, unsigned int vectorSize) { return WvIn::tick( vector, vectorSize ); } -const MY_FLOAT *TcpWvIn :: tickFrame(void) +StkFrames& TcpWvIn :: tick( StkFrames& frames, unsigned int channel ) { - // If no connection and we've output all samples in the queue, return. - if ( !connected && bytesFilled == 0 && counter == 0 ) return lastOutput; - - if (counter == 0) - counter = readData(); - - long temp = (CHUNK_SIZE - counter) * channels; - for (unsigned int i=0; iisValid( soket->socket() ) ) + if ( soket_ && soket_->isValid( soket_->id() ) ) disconnect(); if (nChannels < 1) { - sprintf(msg, "TcpWvOut: the channel argument (%d) must be greater than zero.", nChannels); - handleError( msg, StkError::FUNCTION_ARGUMENT ); + errorString_ << "TcpWvOut::connect: the channel argument (" << nChannels << ") must be greater than zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - unsigned int lastChannels = channels; - channels = nChannels; + unsigned int lastChannels = channels_; + channels_ = nChannels; - if ( format == STK_SINT8 ) dataSize = 1; - else if ( format == STK_SINT16 ) dataSize = 2; - else if ( format == STK_SINT32 || format == MY_FLOAT32 ) dataSize = 4; - else if ( format == MY_FLOAT64 ) dataSize = 8; + if ( format == STK_SINT8 ) dataSize_ = 1; + else if ( format == STK_SINT16 ) dataSize_ = 2; + else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataSize_ = 4; + else if ( format == STK_FLOAT64 ) dataSize_ = 8; else { - sprintf( msg, "TcpWvOut: Unknown data type specified (%ld).", format ); - handleError(msg, StkError::FUNCTION_ARGUMENT); + errorString_ << "TcpWvOut::connect: unknown data type specified (" << format << ")."; + handleError( StkError::FUNCTION_ARGUMENT ); } - dataType = format; + dataType_ = format; - if ( !soket ) - soket = new Socket( port, hostname ); + if ( !soket_ ) + soket_ = new Socket( port, hostname ); else - soket->connect( port, hostname ); + soket_->connect( port, hostname ); // Allocate new memory if necessary. - if ( lastChannels < channels ) { - if ( data ) delete [] data; - data = (MY_FLOAT *) new MY_FLOAT[BUFFER_SIZE*channels]; - if ( buffer) delete [] buffer; - long bytes = dataSize * BUFFER_SIZE * channels; - buffer = (char *) new char[bytes]; + if ( lastChannels < channels_ ) { + data_.resize( BUFFER_SIZE*channels_ ); + if ( buffer_) delete [] buffer_; + long bytes = dataSize_ * BUFFER_SIZE * channels_; + buffer_ = (char *) new char[bytes]; } - counter = 0; + counter_ = 0; } void TcpWvOut :: disconnect(void) { - if ( soket ) { - writeData( counter ); - soket->close(); + if ( soket_ ) { + writeData( counter_ ); + soket_->close(); } } void TcpWvOut :: writeData( unsigned long frames ) { - if ( dataType == STK_SINT8 ) { - signed char *ptr = (signed char *) buffer; - for ( unsigned long k=0; kclipTest( data_[k] ); + *ptr++ = (signed char) (data_[k] * 127.0); + } } - else if ( dataType == STK_SINT16 ) { - SINT16 *ptr = (SINT16 *) buffer; - for ( unsigned long k=0; kclipTest( data_[k] ); + *ptr = (SINT16) (data_[k] * 32767.0); #ifdef __LITTLE_ENDIAN__ swap16 ((unsigned char *)ptr); #endif ptr++; } } - else if ( dataType == STK_SINT32 ) { - SINT32 *ptr = (SINT32 *) buffer; - for ( unsigned long k=0; kclipTest( data_[k] ); + *ptr = (SINT32) (data_[k] * 2147483647.0); #ifdef __LITTLE_ENDIAN__ swap32 ((unsigned char *)ptr); #endif ptr++; } } - else if ( dataType == MY_FLOAT32 ) { - FLOAT32 *ptr = (FLOAT32 *) buffer; - for ( unsigned long k=0; kclipTest( data_[k] ); + *ptr = (FLOAT32) data_[k]; #ifdef __LITTLE_ENDIAN__ swap32 ((unsigned char *)ptr); #endif ptr++; } } - else if ( dataType == MY_FLOAT64 ) { - FLOAT64 *ptr = (FLOAT64 *) buffer; - for ( unsigned long k=0; kclipTest( data_[k] ); + *ptr = (FLOAT64) data_[k]; #ifdef __LITTLE_ENDIAN__ swap64 ((unsigned char *)ptr); #endif @@ -143,62 +148,133 @@ void TcpWvOut :: writeData( unsigned long frames ) } } - long bytes = dataSize * frames * channels; - if ( soket->writeBuffer( (const void *)buffer, bytes, 0 ) < 0 ) { - sprintf(msg, "TcpWvOut: connection to socket server failed!"); - handleError( msg, StkError::PROCESS_SOCKET ); + long bytes = dataSize_ * frames * channels_; + if ( soket_->writeBuffer( (const void *)buffer_, bytes, 0 ) < 0 ) { + errorString_ << "TcpWvOut: connection to socket server failed!"; + handleError( StkError::PROCESS_SOCKET ); } } unsigned long TcpWvOut :: getFrames( void ) const { - return totalCount; + return totalCount_; } -MY_FLOAT TcpWvOut :: getTime( void ) const +StkFloat TcpWvOut :: getTime( void ) const { - return (MY_FLOAT) totalCount / Stk::sampleRate(); + return (StkFloat) totalCount_ / Stk::sampleRate(); } -void TcpWvOut :: tick(MY_FLOAT sample) +void TcpWvOut :: tick( const StkFloat sample ) { - if ( !soket || !soket->isValid( soket->socket() ) ) return; + if ( !soket_ || !soket_->isValid( soket_->id() ) ) return; - for ( unsigned int j=0; jisValid( soket->socket() ) ) return; + if ( !soket_ || !soket_->isValid( soket_->id() ) ) return; for (unsigned int i=0; iisValid( soket->socket() ) ) return; + if ( channel == 0 || frames.channels() < channel ) { + errorString_ << "TcpWvOut::tick(): channel argument (" << channel << ") is zero or > channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( !soket_ || !soket_->isValid( soket_->id() ) ) return; + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; iisValid( soket_->id() ) ) return; unsigned int j; for ( unsigned int i=0; iisValid( soket_->id() ) ) return; + + unsigned int j; + if ( channels_ == 1 || frames.interleaved() ) { + unsigned long iFrames = 0, iData = counter_; + for ( unsigned int i=0; isetRatio(0, 1.0 * 0.995); this->setRatio(1, 1.414 * 0.995); this->setRatio(2, 1.0 * 1.005); this->setRatio(3, 1.414 * 1.000); - gains[0] = __FM_gains[94]; - gains[1] = __FM_gains[76]; - gains[2] = __FM_gains[99]; - gains[3] = __FM_gains[71]; + gains_[0] = fmGains_[94]; + gains_[1] = fmGains_[76]; + gains_[2] = fmGains_[99]; + gains_[3] = fmGains_[71]; - adsr[0]->setAllTimes( 0.005, 4.0, 0.0, 0.04); - adsr[1]->setAllTimes( 0.005, 4.0, 0.0, 0.04); - adsr[2]->setAllTimes( 0.001, 2.0, 0.0, 0.04); - adsr[3]->setAllTimes( 0.004, 4.0, 0.0, 0.04); + adsr_[0]->setAllTimes( 0.005, 4.0, 0.0, 0.04); + adsr_[1]->setAllTimes( 0.005, 4.0, 0.0, 0.04); + adsr_[2]->setAllTimes( 0.001, 2.0, 0.0, 0.04); + adsr_[3]->setAllTimes( 0.004, 4.0, 0.0, 0.04); - twozero->setGain( 0.5 ); - vibrato->setFrequency( 2.0 ); + twozero_.setGain( 0.5 ); + vibrato_->setFrequency( 2.0 ); } TubeBell :: ~TubeBell() { } -void TubeBell :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void TubeBell :: noteOn(StkFloat frequency, StkFloat amplitude) { - gains[0] = amplitude * __FM_gains[94]; - gains[1] = amplitude * __FM_gains[76]; - gains[2] = amplitude * __FM_gains[99]; - gains[3] = amplitude * __FM_gains[71]; - this->setFrequency(frequency); + gains_[0] = amplitude * fmGains_[94]; + gains_[1] = amplitude * fmGains_[76]; + gains_[2] = amplitude * fmGains_[99]; + gains_[3] = amplitude * fmGains_[71]; + this->setFrequency( frequency ); this->keyOn(); #if defined(_STK_DEBUG_) - cerr << "TubeBell: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + errorString_ << "TubeBell::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT TubeBell :: tick() +StkFloat TubeBell :: tick() { - MY_FLOAT temp, temp2; + StkFloat temp, temp2; - temp = gains[1] * adsr[1]->tick() * waves[1]->tick(); - temp = temp * control1; + temp = gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; - waves[0]->addPhaseOffset(temp); - waves[3]->addPhaseOffset(twozero->lastOut()); - temp = gains[3] * adsr[3]->tick() * waves[3]->tick(); - twozero->tick(temp); + waves_[0]->addPhaseOffset( temp ); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick( temp ); - waves[2]->addPhaseOffset(temp); - temp = ( 1.0 - (control2 * 0.5)) * gains[0] * adsr[0]->tick() * waves[0]->tick(); - temp += control2 * 0.5 * gains[2] * adsr[2]->tick() * waves[2]->tick(); + waves_[2]->addPhaseOffset( temp ); + temp = ( 1.0 - (control2_ * 0.5)) * gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += control2_ * 0.5 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); // Calculate amplitude modulation and apply it to output. - temp2 = vibrato->tick() * modDepth; + temp2 = vibrato_->tick() * modDepth_; temp = temp * (1.0 + temp2); - lastOutput = temp * 0.5; - return lastOutput; + lastOutput_ = temp * 0.5; + return lastOutput_; +} + +StkFloat *TubeBell :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& TubeBell :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/TwoPole.cpp b/src/TwoPole.cpp index 3fff453..e3fd8a5 100644 --- a/src/TwoPole.cpp +++ b/src/TwoPole.cpp @@ -8,7 +8,7 @@ frequency response while maintaining a nearly constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -17,9 +17,10 @@ TwoPole :: TwoPole() : Filter() { - MY_FLOAT B = 1.0; - MY_FLOAT A[3] = {1.0, 0.0, 0.0}; - Filter::setCoefficients( 1, &B, 3, A ); + std::vector b(1, 1.0); + std::vector a(3, 0.0); + a[0] = 1.0; + Filter::setCoefficients( b, a ); } TwoPole :: ~TwoPole() @@ -31,63 +32,65 @@ void TwoPole :: clear(void) Filter::clear(); } -void TwoPole :: setB0(MY_FLOAT b0) +void TwoPole :: setB0(StkFloat b0) { - b[0] = b0; + b_[0] = b0; } -void TwoPole :: setA1(MY_FLOAT a1) +void TwoPole :: setA1(StkFloat a1) { - a[1] = a1; + a_[1] = a1; } -void TwoPole :: setA2(MY_FLOAT a2) +void TwoPole :: setA2(StkFloat a2) { - a[2] = a2; + a_[2] = a2; } -void TwoPole :: setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize) +void TwoPole :: setResonance(StkFloat frequency, StkFloat radius, bool normalize) { - a[2] = radius * radius; - a[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate()); + a_[2] = radius * radius; + a_[1] = (StkFloat) -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate()); if ( normalize ) { // Normalize the filter gain ... not terribly efficient. - MY_FLOAT real = 1 - radius + (a[2] - radius) * cos(TWO_PI * 2 * frequency / Stk::sampleRate()); - MY_FLOAT imag = (a[2] - radius) * sin(TWO_PI * 2 * frequency / Stk::sampleRate()); - b[0] = sqrt( pow(real, 2) + pow(imag, 2) ); + StkFloat real = 1 - radius + (a_[2] - radius) * cos(TWO_PI * 2 * frequency / Stk::sampleRate()); + StkFloat imag = (a_[2] - radius) * sin(TWO_PI * 2 * frequency / Stk::sampleRate()); + b_[0] = sqrt( pow(real, 2) + pow(imag, 2) ); } } -void TwoPole :: setGain(MY_FLOAT theGain) +void TwoPole :: setGain(StkFloat gain) { - Filter::setGain(theGain); + Filter::setGain(gain); } -MY_FLOAT TwoPole :: getGain(void) const +StkFloat TwoPole :: getGain(void) const { return Filter::getGain(); } -MY_FLOAT TwoPole :: lastOut(void) const +StkFloat TwoPole :: lastOut(void) const { return Filter::lastOut(); } -MY_FLOAT TwoPole :: tick(MY_FLOAT sample) +StkFloat TwoPole :: tick(StkFloat sample) { - inputs[0] = gain * sample; - outputs[0] = b[0] * inputs[0] - a[2] * outputs[2] - a[1] * outputs[1]; - outputs[2] = outputs[1]; - outputs[1] = outputs[0]; + inputs_[0] = gain_ * sample; + outputs_[0] = b_[0] * inputs_[0] - a_[2] * outputs_[2] - a_[1] * outputs_[1]; + outputs_[2] = outputs_[1]; + outputs_[1] = outputs_[0]; - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *TwoPole :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *TwoPole :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i b(3, 0.0); + b[0] = 1.0; + std::vector a(1, 1.0); + Filter::setCoefficients( b, a ); } TwoZero :: ~TwoZero() @@ -31,64 +32,66 @@ void TwoZero :: clear(void) Filter::clear(); } -void TwoZero :: setB0(MY_FLOAT b0) +void TwoZero :: setB0(StkFloat b0) { - b[0] = b0; + b_[0] = b0; } -void TwoZero :: setB1(MY_FLOAT b1) +void TwoZero :: setB1(StkFloat b1) { - b[1] = b1; + b_[1] = b1; } -void TwoZero :: setB2(MY_FLOAT b2) +void TwoZero :: setB2(StkFloat b2) { - b[2] = b2; + b_[2] = b2; } -void TwoZero :: setNotch(MY_FLOAT frequency, MY_FLOAT radius) +void TwoZero :: setNotch(StkFloat frequency, StkFloat radius) { - b[2] = radius * radius; - b[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate()); + b_[2] = radius * radius; + b_[1] = (StkFloat) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate()); // Normalize the filter gain. - if (b[1] > 0.0) // Maximum at z = 0. - b[0] = 1.0 / (1.0+b[1]+b[2]); + if (b_[1] > 0.0) // Maximum at z = 0. + b_[0] = 1.0 / (1.0+b_[1]+b_[2]); else // Maximum at z = -1. - b[0] = 1.0 / (1.0-b[1]+b[2]); - b[1] *= b[0]; - b[2] *= b[0]; + b_[0] = 1.0 / (1.0-b_[1]+b_[2]); + b_[1] *= b_[0]; + b_[2] *= b_[0]; } -void TwoZero :: setGain(MY_FLOAT theGain) +void TwoZero :: setGain(StkFloat gain) { - Filter::setGain(theGain); + Filter::setGain(gain); } -MY_FLOAT TwoZero :: getGain(void) const +StkFloat TwoZero :: getGain(void) const { return Filter::getGain(); } -MY_FLOAT TwoZero :: lastOut(void) const +StkFloat TwoZero :: lastOut(void) const { return Filter::lastOut(); } -MY_FLOAT TwoZero :: tick(MY_FLOAT sample) +StkFloat TwoZero :: tick(StkFloat sample) { - inputs[0] = gain * sample; - outputs[0] = b[2] * inputs[2] + b[1] * inputs[1] + b[0] * inputs[0]; - inputs[2] = inputs[1]; - inputs[1] = inputs[0]; + inputs_[0] = gain_ * sample; + outputs_[0] = b_[2] * inputs_[2] + b_[1] * inputs_[1] + b_[0] * inputs_[0]; + inputs_[2] = inputs_[1]; + inputs_[1] = inputs_[0]; - return outputs[0]; + return outputs_[0]; } -MY_FLOAT *TwoZero :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *TwoZero :: tick(StkFloat *vector, unsigned int vectorSize) { - for (unsigned int i=0; i -Vector3D :: Vector3D(double initX, double initY, double initZ) +Vector3D :: Vector3D(StkFloat initX, StkFloat initY, StkFloat initZ) { - myX = initX; - myY = initY; - myZ = initZ; + myX_ = initX; + myY_ = initY; + myZ_ = initZ; } Vector3D :: ~Vector3D() { } -double Vector3D :: getX() +StkFloat Vector3D :: getX() { - return myX; + return myX_; } -double Vector3D :: getY() +StkFloat Vector3D :: getY() { - return myY; + return myY_; } -double Vector3D :: getZ() +StkFloat Vector3D :: getZ() { - return myZ; + return myZ_; } -double Vector3D :: getLength() +StkFloat Vector3D :: getLength() { - double temp; - temp = myX * myX; - temp += myY * myY; - temp += myZ * myZ; + StkFloat temp; + temp = myX_ * myX_; + temp += myY_ * myY_; + temp += myZ_ * myZ_; temp = sqrt(temp); return temp; } -void Vector3D :: setXYZ(double anX, double aY, double aZ) +void Vector3D :: setXYZ(StkFloat x, StkFloat y, StkFloat z) { - myX = anX; - myY = aY; - myZ = aZ; + myX_ = z; + myY_ = y; + myZ_ = z; }; -void Vector3D :: setX(double aval) +void Vector3D :: setX(StkFloat x) { - myX = aval; + myX_ = x; } -void Vector3D :: setY(double aval) +void Vector3D :: setY(StkFloat y) { - myY = aval; + myY_ = y; } -void Vector3D :: setZ(double aval) +void Vector3D :: setZ(StkFloat z) { - myZ = aval; + myZ_ = z; } diff --git a/src/VoicForm.cpp b/src/VoicForm.cpp index 45ce141..4f487cc 100644 --- a/src/VoicForm.cpp +++ b/src/VoicForm.cpp @@ -21,7 +21,7 @@ - Vibrato Gain = 1 - Loudness (Spectral Tilt) = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -34,25 +34,18 @@ VoicForm :: VoicForm() : Instrmnt() { // 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 ); + voiced_ = new SingWave( (Stk::rawwavePath() + "impuls20.raw").c_str(), true ); + voiced_->setGainRate( 0.001 ); + voiced_->setGainTarget( 0.0 ); - noise = new Noise; - - for ( int i=0; i<4; i++ ) { - filters[i] = new FormSwep; - filters[i]->setSweepRate( 0.001 ); - } + for ( int i=0; i<4; i++ ) + filters_[i].setSweepRate( 0.001 ); - onezero = new OneZero; - onezero->setZero( -0.9 ); - onepole = new OnePole; - onepole->setPole( 0.9 ); + onezero_.setZero( -0.9 ); + onepole_.setPole( 0.9 ); - noiseEnv = new Envelope; - noiseEnv->setRate( 0.001 ); - noiseEnv->setTarget( 0.0 ); + noiseEnv_.setRate( 0.001 ); + noiseEnv_.setTarget( 0.0 ); this->setPhoneme( "eee" ); this->clear(); @@ -60,34 +53,28 @@ VoicForm :: VoicForm() : Instrmnt() VoicForm :: ~VoicForm() { - delete voiced; - delete noise; - delete onezero; - delete onepole; - delete noiseEnv; - for ( int i=0; i<4; i++ ) { - delete filters[i]; - } + delete voiced_; } void VoicForm :: clear() { - onezero->clear(); - onepole->clear(); + onezero_.clear(); + onepole_.clear(); for ( int i=0; i<4; i++ ) { - filters[i]->clear(); + filters_[i].clear(); } } -void VoicForm :: setFrequency(MY_FLOAT frequency) +void VoicForm :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency; + StkFloat freakency = frequency; if ( frequency <= 0.0 ) { - std::cerr << "VoicForm: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "VoicForm::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } - voiced->setFrequency( freakency ); + voiced_->setFrequency( freakency ); } bool VoicForm :: setPhoneme(const char *phoneme ) @@ -97,102 +84,118 @@ bool VoicForm :: setPhoneme(const char *phoneme ) while( i < 32 && !found ) { if ( !strcmp( Phonemes::name(i), phoneme ) ) { found = true; - filters[0]->setTargets( Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) ); - filters[1]->setTargets( Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) ); - filters[2]->setTargets( Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) ); - filters[3]->setTargets( Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) ); - setVoiced( Phonemes::voiceGain( i ) ); - setUnVoiced( Phonemes::noiseGain( i ) ); + filters_[0].setTargets( Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) ); + filters_[1].setTargets( Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) ); + filters_[2].setTargets( Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) ); + filters_[3].setTargets( Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) ); + this->setVoiced( Phonemes::voiceGain( i ) ); + this->setUnVoiced( Phonemes::noiseGain( i ) ); #if defined(_STK_DEBUG_) - cout << "VoicForm: found formant " << phoneme << " (number " << i << ")" << std::endl; + errorString_ << "VoicForm::setPhoneme: found formant " << phoneme << " (number " << i << ")."; + handleError( StkError::DEBUG_WARNING ); #endif } i++; } - if ( !found ) - std::cerr << "VoicForm: phoneme " << phoneme << " not found!" << std::endl; + if ( !found ) { + errorString_ << "VoicForm::setPhoneme: phoneme " << phoneme << " not found!"; + handleError( StkError::WARNING ); + } return found; } -void VoicForm :: setVoiced(MY_FLOAT vGain) +void VoicForm :: setVoiced(StkFloat vGain) { - voiced->setGainTarget(vGain); + voiced_->setGainTarget(vGain); } -void VoicForm :: setUnVoiced(MY_FLOAT nGain) +void VoicForm :: setUnVoiced(StkFloat nGain) { - noiseEnv->setTarget(nGain); + noiseEnv_.setTarget(nGain); } -void VoicForm :: setFilterSweepRate(int whichOne, MY_FLOAT rate) +void VoicForm :: setFilterSweepRate(unsigned int whichOne, StkFloat rate) { if ( whichOne < 0 || whichOne > 3 ) { - std::cerr << "VoicForm: setFilterSweepRate filter argument outside range 0-3!" << std::endl; + errorString_ << "VoicForm::setFilterSweepRate: filter select argument outside range 0-3!"; + handleError( StkError::WARNING ); return; } - filters[whichOne]->setSweepRate(rate); + filters_[whichOne].setSweepRate(rate); } -void VoicForm :: setPitchSweepRate(MY_FLOAT rate) +void VoicForm :: setPitchSweepRate(StkFloat rate) { - voiced->setSweepRate(rate); + voiced_->setSweepRate(rate); } void VoicForm :: speak() { - voiced->noteOn(); + voiced_->noteOn(); } void VoicForm :: quiet() { - voiced->noteOff(); - noiseEnv->setTarget( 0.0 ); + voiced_->noteOff(); + noiseEnv_.setTarget( 0.0 ); } -void VoicForm :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void VoicForm :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); - voiced->setGainTarget(amplitude); - onepole->setPole( 0.97 - (amplitude * 0.2) ); + this->setFrequency( frequency ); + voiced_->setGainTarget( amplitude ); + onepole_.setPole( 0.97 - (amplitude * 0.2) ); } -void VoicForm :: noteOff(MY_FLOAT amplitude) +void VoicForm :: noteOff(StkFloat amplitude) { this->quiet(); } -MY_FLOAT VoicForm :: tick() +StkFloat VoicForm :: tick() { - MY_FLOAT temp; - temp = onepole->tick( onezero->tick( voiced->tick() ) ); - temp += noiseEnv->tick() * noise->tick(); - lastOutput = filters[0]->tick(temp); - lastOutput += filters[1]->tick(temp); - lastOutput += filters[2]->tick(temp); - lastOutput += filters[3]->tick(temp); + StkFloat temp; + temp = onepole_.tick( onezero_.tick( voiced_->tick() ) ); + temp += noiseEnv_.tick() * noise_.tick(); + lastOutput_ = filters_[0].tick(temp); + lastOutput_ += filters_[1].tick(temp); + lastOutput_ += filters_[2].tick(temp); + lastOutput_ += filters_[3].tick(temp); /* - temp += noiseEnv->tick() * noise->tick(); - lastOutput = filters[0]->tick(temp); - lastOutput = filters[1]->tick(lastOutput); - lastOutput = filters[2]->tick(lastOutput); - lastOutput = filters[3]->tick(lastOutput); + temp += noiseEnv_.tick() * noise_.tick(); + lastOutput_ = filters_[0].tick(temp); + lastOutput_ = filters_[1].tick(lastOutput_); + lastOutput_ = filters_[2].tick(lastOutput_); + lastOutput_ = filters_[3].tick(lastOutput_); */ - return lastOutput; + return lastOutput_; +} + +StkFloat *VoicForm :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& VoicForm :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } -void VoicForm :: controlChange(int number, MY_FLOAT value) +void VoicForm :: controlChange(int number, StkFloat value) { - MY_FLOAT norm = value * ONE_OVER_128; + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "VoicForm: Control value less than zero!" << std::endl; + errorString_ << "VoicForm::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "VoicForm: Control value greater than 128.0!" << std::endl; + errorString_ << "VoicForm::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_Breath_) { // 2 @@ -200,7 +203,7 @@ void VoicForm :: controlChange(int number, MY_FLOAT value) this->setUnVoiced( 0.01 * norm ); } else if (number == __SK_FootControl_) { // 4 - MY_FLOAT temp = 0.0; + StkFloat temp = 0.0; unsigned int i = (int) value; if (i < 32) { temp = 0.9; @@ -221,25 +224,28 @@ void VoicForm :: controlChange(int number, MY_FLOAT value) i = 0; temp = 1.4; } - filters[0]->setTargets( temp * Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) ); - filters[1]->setTargets( temp * Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) ); - filters[2]->setTargets( temp * Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) ); - filters[3]->setTargets( temp * Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) ); - setVoiced( Phonemes::voiceGain( i ) ); - setUnVoiced( Phonemes::noiseGain( i ) ); + filters_[0].setTargets( temp * Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) ); + filters_[1].setTargets( temp * Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) ); + filters_[2].setTargets( temp * Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) ); + filters_[3].setTargets( temp * Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) ); + this->setVoiced( Phonemes::voiceGain( i ) ); + this->setUnVoiced( Phonemes::noiseGain( i ) ); } else if (number == __SK_ModFrequency_) // 11 - voiced->setVibratoRate( norm * 12.0); // 0 to 12 Hz + voiced_->setVibratoRate( norm * 12.0); // 0 to 12 Hz else if (number == __SK_ModWheel_) // 1 - voiced->setVibratoGain( norm * 0.2); + voiced_->setVibratoGain( norm * 0.2); else if (number == __SK_AfterTouch_Cont_) { // 128 - setVoiced( norm ); - onepole->setPole( 0.97 - ( norm * 0.2) ); + this->setVoiced( norm ); + onepole_.setPole( 0.97 - ( norm * 0.2) ); } - else - std::cerr << "VoicForm: Undefined Control Number (" << number << ")!!" << std::endl; + else { + errorString_ << "VoicForm::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "VoicForm: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "VoicForm::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Voicer.cpp b/src/Voicer.cpp index 6681d21..c37fce5 100644 --- a/src/Voicer.cpp +++ b/src/Voicer.cpp @@ -25,7 +25,7 @@ an ensemble. Alternately, control changes can be sent to all voices on a given channel. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -33,183 +33,167 @@ #include #include -Voicer :: Voicer( int maxInstruments, MY_FLOAT decayTime ) +Voicer :: Voicer( StkFloat decayTime ) { - nVoices = 0; - maxVoices = maxInstruments; - voices = (Voice *) new Voice[maxVoices]; - tags = 23456; - muteTime = (int) ( decayTime * Stk::sampleRate() ); + tags_ = 23456; + muteTime_ = (int) ( decayTime * Stk::sampleRate() ); } Voicer :: ~Voicer() { - delete [] voices; } void Voicer :: addInstrument( Instrmnt *instrument, int channel ) { - //voices = (Voice *) realloc( (void *) voices, nVoices+1 * sizeof( Voice ) ); - if ( nVoices == maxVoices ) { - std::cerr << "Voicer: Maximum number of voices already added!!" << std::endl; - return; - } - - voices[nVoices].instrument = instrument; - voices[nVoices].tag = 0; - voices[nVoices].channel = channel; - voices[nVoices].noteNumber = -1; - voices[nVoices].frequency = 0.0; - voices[nVoices].sounding = 0; - nVoices++; + Voicer::Voice voice; + voice.instrument = instrument; + voice.channel = channel; + voice.noteNumber = -1; + voices_.push_back( voice ); } void Voicer :: removeInstrument( Instrmnt *instrument ) { bool found = false; - for ( int i=0; i::iterator i; + for ( i=voices_.begin(); i!=voices_.end(); ++i ) { + if ( (*i).instrument != instrument ) continue; + voices_.erase( i ); + found = true; + break; } - if ( found ) - nVoices--; - //voices = (Voice *) realloc( voices, --nVoices * sizeof( Voice ) ); - + if ( !found ) { + errorString_ << "Voicer::removeInstrument: instrument pointer not found in current voices!"; + handleError( StkError::WARNING ); + } } -long Voicer :: noteOn(MY_FLOAT noteNumber, MY_FLOAT amplitude, int channel ) +long Voicer :: noteOn(StkFloat noteNumber, StkFloat amplitude, int channel ) { - int i; - MY_FLOAT frequency = (MY_FLOAT) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); - for ( i=0; inoteOn( frequency, amplitude * ONE_OVER_128 ); - voices[i].sounding = 1; - return voices[i].tag; + unsigned int i; + StkFloat frequency = (StkFloat) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); + for ( i=0; inoteOn( frequency, amplitude * ONE_OVER_128 ); + voices_[i].sounding = 1; + return voices_[i].tag; } } // All voices are sounding, so interrupt the oldest voice. int voice = -1; - for ( i=0; i= 0 ) { - voices[voice].tag = tags++; - voices[voice].channel = channel; - voices[voice].noteNumber = noteNumber; - voices[voice].frequency = frequency; - voices[voice].instrument->noteOn( frequency, amplitude * ONE_OVER_128 ); - voices[voice].sounding = 1; - return voices[voice].tag; + voices_[voice].tag = tags_++; + voices_[voice].channel = channel; + voices_[voice].noteNumber = noteNumber; + voices_[voice].frequency = frequency; + voices_[voice].instrument->noteOn( frequency, amplitude * ONE_OVER_128 ); + voices_[voice].sounding = 1; + return voices_[voice].tag; } return -1; } -void Voicer :: noteOff( MY_FLOAT noteNumber, MY_FLOAT amplitude, int channel ) +void Voicer :: noteOff( StkFloat noteNumber, StkFloat amplitude, int channel ) { - for ( int i=0; inoteOff( amplitude * ONE_OVER_128 ); - voices[i].sounding = -muteTime; + for ( unsigned int i=0; inoteOff( amplitude * ONE_OVER_128 ); + voices_[i].sounding = -muteTime_; } } } -void Voicer :: noteOff( long tag, MY_FLOAT amplitude ) +void Voicer :: noteOff( long tag, StkFloat amplitude ) { - for ( int i=0; inoteOff( amplitude * ONE_OVER_128 ); - voices[i].sounding = -muteTime; + for ( unsigned int i=0; inoteOff( amplitude * ONE_OVER_128 ); + voices_[i].sounding = -muteTime_; break; } } } -void Voicer :: setFrequency( MY_FLOAT noteNumber, int channel ) +void Voicer :: setFrequency( StkFloat noteNumber, int channel ) { - MY_FLOAT frequency = (MY_FLOAT) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); - for ( int i=0; isetFrequency( frequency ); + StkFloat frequency = (StkFloat) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); + for ( unsigned int i=0; isetFrequency( frequency ); } } } -void Voicer :: setFrequency( long tag, MY_FLOAT noteNumber ) +void Voicer :: setFrequency( long tag, StkFloat noteNumber ) { - MY_FLOAT frequency = (MY_FLOAT) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); - for ( int i=0; isetFrequency( frequency ); + StkFloat frequency = (StkFloat) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); + for ( unsigned int i=0; isetFrequency( frequency ); break; } } } -void Voicer :: pitchBend( MY_FLOAT value, int channel ) +void Voicer :: pitchBend( StkFloat value, int channel ) { - MY_FLOAT pitchScaler; + StkFloat pitchScaler; if ( value < 64.0 ) pitchScaler = pow(0.5, (64.0-value)/64.0); else pitchScaler = pow(2.0, (value-64.0)/64.0); - for ( int i=0; isetFrequency( (MY_FLOAT) (voices[i].frequency * pitchScaler) ); + for ( unsigned int i=0; isetFrequency( (StkFloat) (voices_[i].frequency * pitchScaler) ); } } -void Voicer :: pitchBend( long tag, MY_FLOAT value ) +void Voicer :: pitchBend( long tag, StkFloat value ) { - MY_FLOAT pitchScaler; + StkFloat pitchScaler; if ( value < 64.0 ) pitchScaler = pow(0.5, (64.0-value)/64.0); else pitchScaler = pow(2.0, (value-64.0)/64.0); - for ( int i=0; isetFrequency( (MY_FLOAT) (voices[i].frequency * pitchScaler) ); + for ( unsigned int i=0; isetFrequency( (StkFloat) (voices_[i].frequency * pitchScaler) ); break; } } } -void Voicer :: controlChange( int number, MY_FLOAT value, int channel ) +void Voicer :: controlChange( int number, StkFloat value, int channel ) { - for ( int i=0; icontrolChange( number, value ); + for ( unsigned int i=0; icontrolChange( number, value ); } } -void Voicer :: controlChange( long tag, int number, MY_FLOAT value ) +void Voicer :: controlChange( long tag, int number, StkFloat value ) { - for ( int i=0; icontrolChange( number, value ); + for ( unsigned int i=0; icontrolChange( number, value ); break; } } @@ -217,31 +201,31 @@ void Voicer :: controlChange( long tag, int number, MY_FLOAT value ) void Voicer :: silence( void ) { - for ( int i=0; i 0 ) - voices[i].instrument->noteOff( 0.5 ); + for ( unsigned int i=0; i 0 ) + voices_[i].instrument->noteOff( 0.5 ); } } -MY_FLOAT Voicer :: tick() +StkFloat Voicer :: tick() { - lastOutput = lastOutputLeft = lastOutputRight = 0.0; - for ( int i=0; itick(); - lastOutputLeft += voices[i].instrument->lastOutLeft(); - lastOutputRight += voices[i].instrument->lastOutRight(); + lastOutput_ = lastOutputLeft_ = lastOutputRight_ = 0.0; + for ( unsigned 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 ) - voices[i].noteNumber = -1; + if ( voices_[i].sounding < 0 ) { + voices_[i].sounding++; + if ( voices_[i].sounding == 0 ) + voices_[i].noteNumber = -1; } } - return lastOutput / nVoices; + return lastOutput_ / voices_.size(); } -MY_FLOAT *Voicer :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *Voicer :: tick(StkFloat *vector, unsigned int vectorSize) { for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; i -WaveLoop :: WaveLoop( const char *fileName, bool raw ) - : WvIn( fileName, raw ), phaseOffset(0.0) +WaveLoop :: WaveLoop( std::string 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= fileSize) - time -= fileSize; + while (time_ < 0.0) + time_ += fileSize_; + while (time_ >= fileSize_) + time_ -= fileSize_; } -void WaveLoop :: addPhase(MY_FLOAT anAngle) +void WaveLoop :: addPhase(StkFloat angle) { // Add a time in cycles (one cycle = fileSize). - time += fileSize * anAngle; + time_ += fileSize_ * angle; - while (time < 0.0) - time += fileSize; - while (time >= fileSize) - time -= fileSize; + while (time_ < 0.0) + time_ += fileSize_; + while (time_ >= fileSize_) + time_ -= fileSize_; } -void WaveLoop :: addPhaseOffset(MY_FLOAT anAngle) +void WaveLoop :: addPhaseOffset(StkFloat angle) { // Add a phase offset in cycles, where 1.0 = fileSize. - phaseOffset = fileSize * anAngle; + phaseOffset_ = fileSize_ * angle; } -const MY_FLOAT *WaveLoop :: tickFrame(void) +const StkFloat *WaveLoop :: tickFrame(void) { - register MY_FLOAT tyme, alpha; + register StkFloat tyme, alpha; register unsigned long i, index; // Check limits of time address ... if necessary, recalculate modulo fileSize. - while (time < 0.0) - time += fileSize; - while (time >= fileSize) - time -= fileSize; + while (time_ < 0.0) + time_ += fileSize_; + while (time_ >= fileSize_) + time_ -= fileSize_; - if (phaseOffset) { - tyme = time + phaseOffset; + if (phaseOffset_) { + tyme = time_ + phaseOffset_; while (tyme < 0.0) - tyme += fileSize; - while (tyme >= fileSize) - tyme -= fileSize; + tyme += fileSize_; + while (tyme >= fileSize_) + tyme -= fileSize_; } else { - tyme = time; + tyme = time_; } - if (chunking) { + if (chunking_) { // Check the time address vs. our current buffer limits. - if ( (tyme < chunkPointer) || (tyme >= chunkPointer+bufferSize) ) + if ( (tyme < chunkPointer_) || (tyme >= chunkPointer_+bufferSize_) ) this->readData((long) tyme); // Adjust index for the current buffer. - tyme -= chunkPointer; + tyme -= chunkPointer_; } // Always do linear interpolation here ... integer part of time address. index = (unsigned long) tyme; // Fractional part of time address. - alpha = tyme - (MY_FLOAT) index; - index *= channels; - for (i=0; i #include -#define CAN_RADIUS 100 -#define PEA_RADIUS 30 -#define BUMP_RADIUS 5 +const int CAN_RADIUS = 100; +const int PEA_RADIUS = 30; +const int BUMP_RADIUS = 5; -#define NORM_CAN_LOSS 0.97 -#define SLOW_CAN_LOSS 0.90 -#define GRAVITY 20.0 -// GRAVITY WAS 6.0 +const StkFloat NORM_CAN_LOSS = 0.97; +const StkFloat SLOW_CAN_LOSS = 0.90; +const StkFloat GRAVITY = 20.0; -#define NORM_TICK_SIZE 0.004 -#define SLOW_TICK_SIZE 0.0001 +const StkFloat NORM_TICK_SIZE = 0.004; +const StkFloat SLOW_TICK_SIZE = 0.0001; -#define ENV_RATE 0.001 +const StkFloat ENV_RATE = 0.001; Whistle :: Whistle() { - tempVector = new Vector3D(0,0,0); - can = new Sphere(CAN_RADIUS); - pea = new Sphere(PEA_RADIUS); - bumper = new Sphere(BUMP_RADIUS); - // Concatenate the STK rawwave path to the rawwave file - sine = new WaveLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), TRUE ); - sine->setFrequency(2800.0); + sine_ = new WaveLoop( ( Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + sine_->setFrequency( 2800.0 ); - can->setPosition(0, 0, 0); // set can location - can->setVelocity(0, 0, 0); // and the velocity + can_.setRadius( CAN_RADIUS ); + can_.setPosition(0, 0, 0); // set can location + can_.setVelocity(0, 0, 0); // and the velocity - onepole.setPole(0.95); // 0.99 + onepole_.setPole(0.95); // 0.99 - bumper->setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0); - bumper->setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0); - pea->setPosition(0, CAN_RADIUS/2, 0); - pea->setVelocity(35, 15, 0); + bumper_.setRadius( BUMP_RADIUS ); + bumper_.setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0); + bumper_.setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0); - envelope.setRate(ENV_RATE); - envelope.keyOn(); + pea_.setRadius( PEA_RADIUS ); + pea_.setPosition(0, CAN_RADIUS/2, 0); + pea_.setVelocity(35, 15, 0); - fippleFreqMod = 0.5; - fippleGainMod = 0.5; - blowFreqMod = 0.25; - noiseGain = 0.125; - maxPressure = (MY_FLOAT) 0.0; - baseFrequency = 2000; + envelope_.setRate( ENV_RATE ); + envelope_.keyOn(); - tickSize = NORM_TICK_SIZE; - canLoss = NORM_CAN_LOSS; + fippleFreqMod_ = 0.5; + fippleGainMod_ = 0.5; + blowFreqMod_ = 0.25; + noiseGain_ = 0.125; + baseFrequency_ = 2000; - subSample = 1; - subSampCount = subSample; + tickSize_ = NORM_TICK_SIZE; + canLoss_ = NORM_CAN_LOSS; + + subSample_ = 1; + subSampCount_ = subSample_; } Whistle :: ~Whistle() { - delete tempVector; - delete can; - delete pea; - delete bumper; - delete sine; + delete sine_; } void Whistle :: clear() { } -void Whistle :: setFrequency(MY_FLOAT frequency) +void Whistle :: setFrequency(StkFloat frequency) { - MY_FLOAT freakency = frequency * 4; // the whistle is a transposing instrument + StkFloat freakency = frequency * 4; // the whistle is a transposing instrument if ( frequency <= 0.0 ) { - std::cerr << "Whistle: setFrequency parameter is less than or equal to zero!" << std::endl; + errorString_ << "Whistle::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); freakency = 220.0; } - baseFrequency = freakency; + baseFrequency_ = freakency; } -void Whistle :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +void Whistle :: startBlowing(StkFloat amplitude, StkFloat rate) { - envelope.setRate(ENV_RATE); - envelope.setTarget(amplitude); + envelope_.setRate( ENV_RATE ); + envelope_.setTarget( amplitude ); } -void Whistle :: stopBlowing(MY_FLOAT rate) +void Whistle :: stopBlowing(StkFloat rate) { - envelope.setRate(rate); - envelope.keyOff(); + envelope_.setRate( rate ); + envelope_.keyOff(); } -void Whistle :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Whistle :: noteOn(StkFloat frequency, StkFloat amplitude) { - setFrequency(frequency); - startBlowing(amplitude*2.0 ,amplitude * 0.2); + this->setFrequency( frequency ); + this->startBlowing( amplitude*2.0 ,amplitude * 0.2 ); #if defined(_STK_DEBUG_) - std::cerr << "Whistle: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << std::endl; + errorString_ << "Whistle::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -void Whistle :: noteOff(MY_FLOAT amplitude) +void Whistle :: noteOff(StkFloat amplitude) { - this->stopBlowing(amplitude * 0.02); + this->stopBlowing( amplitude * 0.02 ); #if defined(_STK_DEBUG_) - std::cerr << "Whistle: NoteOff amplitude = " << amplitude << std::endl; + errorString_ << "Whistle::NoteOff: amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } int frameCount = 0; -MY_FLOAT Whistle :: tick() +StkFloat Whistle :: tick() { - MY_FLOAT soundMix, tempFreq; - double envOut = 0, temp, temp1, temp2, tempX, tempY; + StkFloat soundMix, tempFreq; + StkFloat envOut = 0, temp, temp1, temp2, tempX, tempY; double phi, cosphi, sinphi; double gain = 0.5, mod = 0.0; - if (--subSampCount <= 0) { - tempVectorP = pea->getPosition(); - subSampCount = subSample; - temp = bumper->isInside(tempVectorP); + if ( --subSampCount_ <= 0 ) { + tempVectorP_ = pea_.getPosition(); + subSampCount_ = subSample_; + temp = bumper_.isInside( tempVectorP_ ); #ifdef WHISTLE_ANIMATION frameCount += 1; - if (frameCount >= (1470 / subSample)) { + if ( frameCount >= (1470 / subSample_) ) { frameCount = 0; printf("%f %f %f\n",tempVectorP->getX(),tempVectorP->getY(),envOut); fflush(stdout); } #endif - envOut = envelope.tick(); + envOut = envelope_.tick(); if (temp < (BUMP_RADIUS + PEA_RADIUS)) { - tempX = envOut * tickSize * 2000 * noise.tick(); - tempY = -envOut * tickSize * 1000 * (1.0 + noise.tick()); - pea->addVelocity(tempX,tempY,0); - pea->tick(tickSize); + tempX = envOut * tickSize_ * 2000 * noise_.tick(); + tempY = -envOut * tickSize_ * 1000 * (1.0 + noise_.tick()); + pea_.addVelocity( tempX, tempY, 0 ); + pea_.tick( tickSize_ ); } - mod = exp(-temp * 0.01); // exp. distance falloff of fipple/pea effect - temp = onepole.tick(mod); // smooth it a little - gain = (1.0 - (fippleGainMod*0.5)) + (2.0 * fippleGainMod * temp); - gain *= gain; // squared distance/gain + mod = exp(-temp * 0.01); // exp. distance falloff of fipple/pea effect + temp = onepole_.tick(mod); // smooth it a little + gain = (1.0 - (fippleGainMod_*0.5)) + (2.0 * fippleGainMod_ * temp); + gain *= gain; // squared distance/gain // tempFreq = 1.0 // Normalized Base Freq - // + (fippleFreqMod * 0.25) - (fippleFreqMod * temp) // fippleModulation - // - (blowFreqMod) + (blowFreqMod * envOut); // blowingModulation + // + (fippleFreqMod_ * 0.25) - (fippleFreqMod_ * temp) // fippleModulation + // - (blowFreqMod_) + (blowFreqMod_ * envOut); // blowingModulation // short form of above - tempFreq = 1.0 + fippleFreqMod*(0.25-temp) + blowFreqMod*(envOut-1.0); - tempFreq *= baseFrequency; + tempFreq = 1.0 + fippleFreqMod_*(0.25-temp) + blowFreqMod_*(envOut-1.0); + tempFreq *= baseFrequency_; - sine->setFrequency(tempFreq); + sine_->setFrequency(tempFreq); - tempVectorP = pea->getPosition(); - temp = can->isInside(tempVectorP); + tempVectorP_ = pea_.getPosition(); + temp = can_.isInside(tempVectorP_); temp = -temp; // We know (hope) it's inside, just how much?? - if (temp < (PEA_RADIUS * 1.25)) { - pea->getVelocity(tempVector); // This is the can/pea collision - tempX = tempVectorP->getX(); // calculation. Could probably - tempY = tempVectorP->getY(); // simplify using tables, etc. + if (temp < (PEA_RADIUS * 1.25)) { + pea_.getVelocity( &tempVector_ ); // This is the can/pea collision + tempX = tempVectorP_->getX(); // calculation. Could probably + tempY = tempVectorP_->getY(); // simplify using tables, etc. phi = -atan2(tempY,tempX); + cosphi = cos(phi); sinphi = sin(phi); - temp1 = (cosphi*tempVector->getX()) - (sinphi*tempVector->getY()); - temp2 = (sinphi*tempVector->getX()) + (cosphi*tempVector->getY()); + temp1 = (cosphi*tempVector_.getX()) - (sinphi*tempVector_.getY()); + temp2 = (sinphi*tempVector_.getX()) + (cosphi*tempVector_.getY()); temp1 = -temp1; tempX = (cosphi*temp1) + (sinphi*temp2); tempY = (-sinphi*temp1) + (cosphi*temp2); - pea->setVelocity(tempX, tempY, 0); - pea->tick(tickSize); - pea->setVelocity(tempX*canLoss, tempY*canLoss, 0); - pea->tick(tickSize); + pea_.setVelocity(tempX, tempY, 0); + pea_.tick(tickSize_); + pea_.setVelocity( tempX*canLoss_, tempY*canLoss_, 0 ); + pea_.tick(tickSize_); } - - temp = tempVectorP->getLength(); - if (temp > 0.01) { - tempX = tempVectorP->getX(); - tempY = tempVectorP->getY(); - phi = atan2(tempY,tempX); + + temp = tempVectorP_->getLength(); + if (temp > 0.01) { + tempX = tempVectorP_->getX(); + tempY = tempVectorP_->getY(); + phi = atan2( tempY, tempX ); phi += 0.3 * temp / CAN_RADIUS; cosphi = cos(phi); sinphi = sin(phi); @@ -207,50 +203,64 @@ MY_FLOAT Whistle :: tick() tempY = 0.0; } - temp = (0.9 + 0.1*subSample*noise.tick()) * envOut * 0.6 * tickSize; - pea->addVelocity(temp * tempX, - (temp*tempY) - (GRAVITY*tickSize),0); - pea->tick(tickSize); + temp = (0.9 + 0.1*subSample_*noise_.tick()) * envOut * 0.6 * tickSize_; + pea_.addVelocity( temp * tempX, (temp*tempY) - (GRAVITY*tickSize_), 0 ); + pea_.tick( tickSize_ ); - // bumper->tick(0.0); + // bumper_.tick(0.0); } temp = envOut * envOut * gain / 2; - soundMix = temp * (sine->tick() + (noiseGain*noise.tick())); - lastOutput = 0.25 * soundMix; // should probably do one-zero filter here + soundMix = temp * ( sine_->tick() + ( noiseGain_*noise_.tick() ) ); + lastOutput_ = 0.25 * soundMix; // should probably do one-zero filter here - return lastOutput; + return lastOutput_; } -void Whistle :: controlChange(int number, MY_FLOAT value) +StkFloat *Whistle :: tick(StkFloat *vector, unsigned int vectorSize) { - MY_FLOAT norm = value * ONE_OVER_128; + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Whistle :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); +} + +void Whistle :: controlChange(int number, StkFloat value) +{ + StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { norm = 0.0; - std::cerr << "Whistle: Control value less than zero!" << std::endl; + errorString_ << "Whistle::controlChange: control value less than zero ... setting to zero!"; + handleError( StkError::WARNING ); } else if ( norm > 1.0 ) { norm = 1.0; - std::cerr << "Whistle: Control value greater than 128.0!" << std::endl; + errorString_ << "Whistle::controlChange: control value greater than 128.0 ... setting to 128.0!"; + handleError( StkError::WARNING ); } if (number == __SK_NoiseLevel_) // 4 - noiseGain = 0.25 * norm; + noiseGain_ = 0.25 * norm; else if (number == __SK_ModFrequency_) // 11 - fippleFreqMod = norm; + fippleFreqMod_ = norm; else if (number == __SK_ModWheel_) // 1 - fippleGainMod = norm; + fippleGainMod_ = norm; else if (number == __SK_AfterTouch_Cont_) // 128 - envelope.setTarget( norm * 2.0 ); + envelope_.setTarget( norm * 2.0 ); else if (number == __SK_Breath_) // 2 - blowFreqMod = norm * 0.5; + blowFreqMod_ = norm * 0.5; else if (number == __SK_Sustain_) // 64 - if (value < 1.0) subSample = 1; - else - std::cerr << "Whistle: Undefined Control Number (" << number << ")!!" << std::endl; + if (value < 1.0) subSample_ = 1; + else { + errorString_ << "Whistle::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } #if defined(_STK_DEBUG_) - std::cerr << "Whistle: controlChange number = " << number << ", value = " << value << std::endl; + errorString_ << "Whistle::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } diff --git a/src/Wurley.cpp b/src/Wurley.cpp index 4db05af..2dae379 100644 --- a/src/Wurley.cpp +++ b/src/Wurley.cpp @@ -26,7 +26,7 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -36,76 +36,87 @@ Wurley :: Wurley() : FM() { // 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 ); + for ( unsigned 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); this->setRatio(2, -510.0); this->setRatio(3, -510.0); - gains[0] = __FM_gains[99]; - gains[1] = __FM_gains[82]; - gains[2] = __FM_gains[92]; - gains[3] = __FM_gains[68]; + gains_[0] = fmGains_[99]; + gains_[1] = fmGains_[82]; + gains_[2] = fmGains_[92]; + gains_[3] = fmGains_[68]; - adsr[0]->setAllTimes( 0.001, 1.50, 0.0, 0.04); - adsr[1]->setAllTimes( 0.001, 1.50, 0.0, 0.04); - adsr[2]->setAllTimes( 0.001, 0.25, 0.0, 0.04); - adsr[3]->setAllTimes( 0.001, 0.15, 0.0, 0.04); + adsr_[0]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[1]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[2]->setAllTimes( 0.001, 0.25, 0.0, 0.04); + adsr_[3]->setAllTimes( 0.001, 0.15, 0.0, 0.04); - twozero->setGain( 2.0 ); - vibrato->setFrequency( 8.0 ); + twozero_.setGain( 2.0 ); + vibrato_->setFrequency( 8.0 ); } Wurley :: ~Wurley() { } -void Wurley :: setFrequency(MY_FLOAT frequency) +void Wurley :: setFrequency(StkFloat frequency) { - baseFrequency = frequency; - waves[0]->setFrequency(baseFrequency * ratios[0]); - waves[1]->setFrequency(baseFrequency * ratios[1]); - waves[2]->setFrequency(ratios[2]); // Note here a 'fixed resonance'. - waves[3]->setFrequency(ratios[3]); + baseFrequency_ = frequency; + waves_[0]->setFrequency( baseFrequency_ * ratios_[0]); + waves_[1]->setFrequency( baseFrequency_ * ratios_[1]); + waves_[2]->setFrequency( ratios_[2] ); // Note here a 'fixed resonance'. + waves_[3]->setFrequency( ratios_[3] ); } -void Wurley :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude) +void Wurley :: noteOn(StkFloat frequency, StkFloat amplitude) { - gains[0] = amplitude * __FM_gains[99]; - gains[1] = amplitude * __FM_gains[82]; - gains[2] = amplitude * __FM_gains[82]; - gains[3] = amplitude * __FM_gains[68]; - this->setFrequency(frequency); + gains_[0] = amplitude * fmGains_[99]; + gains_[1] = amplitude * fmGains_[82]; + gains_[2] = amplitude * fmGains_[82]; + gains_[3] = amplitude * fmGains_[68]; + this->setFrequency( frequency ); this->keyOn(); #if defined(_STK_DEBUG_) - cerr << "Wurley: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl; + errorString_ << "Wurley::NoteOn: frequency = " << frequency << ", amplitude = " << amplitude << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } -MY_FLOAT Wurley :: tick() +StkFloat Wurley :: tick() { - MY_FLOAT temp, temp2; + StkFloat temp, temp2; - temp = gains[1] * adsr[1]->tick() * waves[1]->tick(); - temp = temp * control1; + temp = gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; - waves[0]->addPhaseOffset(temp); - waves[3]->addPhaseOffset(twozero->lastOut()); - temp = gains[3] * adsr[3]->tick() * waves[3]->tick(); - twozero->tick(temp); + waves_[0]->addPhaseOffset( temp ); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); - waves[2]->addPhaseOffset(temp); - temp = ( 1.0 - (control2 * 0.5)) * gains[0] * adsr[0]->tick() * waves[0]->tick(); - temp += control2 * 0.5 * gains[2] * adsr[2]->tick() * waves[2]->tick(); + waves_[2]->addPhaseOffset( temp ); + temp = ( 1.0 - (control2_ * 0.5)) * gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += control2_ * 0.5 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); // Calculate amplitude modulation and apply it to output. - temp2 = vibrato->tick() * modDepth; + temp2 = vibrato_->tick() * modDepth_; temp = temp * (1.0 + temp2); - lastOutput = temp * 0.5; - return lastOutput; + lastOutput_ = temp * 0.5; + return lastOutput_; +} + +StkFloat *Wurley :: tick(StkFloat *vector, unsigned int vectorSize) +{ + return Instrmnt::tick( vector, vectorSize ); +} + +StkFrames& Wurley :: tick( StkFrames& frames, unsigned int channel ) +{ + return Instrmnt::tick( frames, channel ); } diff --git a/src/WvIn.cpp b/src/WvIn.cpp index 14e4d57..6f48217 100644 --- a/src/WvIn.cpp +++ b/src/WvIn.cpp @@ -10,29 +10,35 @@ subsequent output. Linear interpolation is used for fractional "read rates". - WvIn supports multi-channel data in interleaved - format. It is important to distinguish the - tick() methods, which return samples produced - by averaging across sample frames, from the - tickFrame() methods, which return pointers to - multi-channel sample frames. For single-channel - data, these methods return equivalent values. + WvIn supports multi-channel data in + interleaved format. It is important to + distinguish the tick() methods, which return + samples produced by averaging across sample + frames, from the tickFrame() methods, which + return pointers to multi-channel sample + frames. For single-channel data, these + methods return equivalent values. - Small files are completely read into local memory - during instantiation. Large files are read - incrementally from disk. The file size threshold - and the increment size values are defined in - WvIn.h. + Small files are completely read into local + memory during instantiation. Large files are + read incrementally from disk. The file size + threshold and the increment size values are + defined in WvIn.h. + + When the end of a file is reached, subsequent + calls to the tick() functions return the data + values at the end of the file. WvIn currently supports WAV, AIFF, SND (AU), MAT-file (Matlab), and STK RAW file formats. Signed integer (8-, 16-, and 32-bit) and floating- point (32- and 64-bit) data types are supported. - Uncompressed data types are not supported. If - using MAT-files, data should be saved in an array - with each data channel filling a matrix row. + Compressed data types are not supported. If using + MAT-files, data should be saved in an array with + each data channel filling a matrix row. The sample + rate for MAT-files is assumed to be 44100 Hz. - by Perry R. Cook and Gary P. Scavone, 1995 - 2002. + by Perry R. Cook and Gary P. Scavone, 1995 - 2004. */ /***************************************************/ @@ -40,122 +46,119 @@ #include #include #include -#include - -#include WvIn :: WvIn() { init(); } -WvIn :: WvIn( const char *fileName, bool raw, bool doNormalize ) +WvIn :: WvIn( std::string fileName, bool raw, bool doNormalize ) { init(); - openFile( fileName, raw ); + openFile( fileName, raw, doNormalize ); } WvIn :: ~WvIn() { - if (fd) - fclose(fd); + if (fd_) + fclose(fd_); - if (data) - delete [] data; + if (data_) + delete [] data_; - if (lastOutput) - delete [] lastOutput; + if (lastOutputs_) + delete [] lastOutputs_; } void WvIn :: init( void ) { - fd = 0; - data = 0; - lastOutput = 0; - chunking = false; - finished = true; - interpolate = false; - bufferSize = 0; - channels = 0; - time = 0.0; + fd_ = 0; + data_ = 0; + lastOutputs_ = 0; + chunking_ = false; + finished_ = true; + interpolate_ = false; + bufferSize_ = 0; + channels_ = 0; + time_ = 0.0; } void WvIn :: closeFile( void ) { - if ( fd ) fclose( fd ); - finished = true; + if ( fd_ ) fclose( fd_ ); + finished_ = true; } -void WvIn :: openFile( const char *fileName, bool raw, bool doNormalize ) +void WvIn :: openFile( std::string fileName, bool raw, bool doNormalize ) { closeFile(); // Try to open the file. - fd = fopen(fileName, "rb"); - if (!fd) { - sprintf(msg, "WvIn: Could not open or find file (%s).", fileName); - handleError(msg, StkError::FILE_NOT_FOUND); + fd_ = fopen(fileName.c_str(), "rb"); + if (!fd_) { + errorString_ << "WvIn::openFile: could not open or find file (" << fileName << ")!"; + handleError( StkError::FILE_NOT_FOUND ); } - unsigned long lastChannels = channels; - unsigned long samples, lastSamples = (bufferSize+1)*channels; + unsigned long lastChannels = channels_; + unsigned long samples, lastSamples = (bufferSize_+1)*channels_; bool result = false; if ( raw ) - result = getRawInfo( fileName ); + result = getRawInfo( fileName.c_str() ); else { char header[12]; - if ( fread(&header, 4, 3, fd) != 3 ) goto error; + if ( fread(&header, 4, 3, fd_) != 3 ) goto error; if ( !strncmp(header, "RIFF", 4) && !strncmp(&header[8], "WAVE", 4) ) - result = getWavInfo( fileName ); + result = getWavInfo( fileName.c_str() ); else if ( !strncmp(header, ".snd", 4) ) - result = getSndInfo( fileName ); + result = getSndInfo( fileName.c_str() ); else if ( !strncmp(header, "FORM", 4) && (!strncmp(&header[8], "AIFF", 4) || !strncmp(&header[8], "AIFC", 4) ) ) - result = getAifInfo( fileName ); + result = getAifInfo( fileName.c_str() ); else { - if ( fseek(fd, 126, SEEK_SET) == -1 ) goto error; - if ( fread(&header, 2, 1, fd) != 1 ) goto error; + if ( fseek(fd_, 126, SEEK_SET) == -1 ) goto error; + if ( fread(&header, 2, 1, fd_) != 1 ) goto error; if (!strncmp(header, "MI", 2) || !strncmp(header, "IM", 2) ) - result = getMatInfo( fileName ); + result = getMatInfo( fileName.c_str() ); else { - sprintf(msg, "WvIn: File (%s) format unknown.", fileName); - handleError(msg, StkError::FILE_UNKNOWN_FORMAT); + errorString_ << "WvIn::openFile: file (" << fileName << ") format unknown."; + handleError( StkError::FILE_UNKNOWN_FORMAT ); } } } if ( result == false ) - handleError(msg, StkError::FILE_ERROR); + handleError( StkError::FILE_ERROR ); - if ( fileSize == 0 ) { - sprintf(msg, "WvIn: File (%s) data size is zero!", fileName); - handleError(msg, StkError::FILE_ERROR); + if ( fileSize_ == 0 ) { + errorString_ << "WvIn::openFile: file (" << fileName << ") data size is zero!"; + handleError( StkError::FILE_ERROR ); } // Allocate new memory if necessary. - samples = (bufferSize+1)*channels; + samples = (bufferSize_+1)*channels_; if ( lastSamples < samples ) { - if ( data ) delete [] data; - data = (MY_FLOAT *) new MY_FLOAT[samples]; + if ( data_ ) delete [] data_; + data_ = (StkFloat *) new StkFloat[samples]; } - if ( lastChannels < channels ) { - if ( lastOutput ) delete [] lastOutput; - lastOutput = (MY_FLOAT *) new MY_FLOAT[channels]; + if ( lastChannels < channels_ ) { + if ( lastOutputs_ ) delete [] lastOutputs_; + lastOutputs_ = (StkFloat *) new StkFloat[channels_]; } - if ( fmod(rate, 1.0) != 0.0 ) interpolate = true; - chunkPointer = 0; + if ( fmod(rate_, 1.0) != 0.0 ) interpolate_ = true; + chunkPointer_ = 0; reset(); readData( 0 ); // Load file data. if ( doNormalize ) normalize(); - finished = false; + finished_ = false; return; error: - sprintf(msg, "WvIn: Error reading file (%s).", fileName); - handleError(msg, StkError::FILE_ERROR); + errorString_ << "WvIn::openFile: error reading file (" << fileName << ")!"; + handleError( StkError::FILE_ERROR ); } bool WvIn :: getRawInfo( const char *fileName ) @@ -163,30 +166,30 @@ bool WvIn :: getRawInfo( const char *fileName ) // Use the system call "stat" to determine the file length. struct stat filestat; if ( stat(fileName, &filestat) == -1 ) { - sprintf(msg, "WvIn: Could not stat RAW file (%s).", fileName); + errorString_ << "WvIn: Could not stat RAW file (" << fileName << ")."; return false; } - fileSize = (long) filestat.st_size / 2; // length in 2-byte samples - bufferSize = fileSize; - if (fileSize > CHUNK_THRESHOLD) { - chunking = true; - bufferSize = CHUNK_SIZE; - gain = 1.0 / 32768.0; + fileSize_ = (long) filestat.st_size / 2; // length in 2-byte samples + bufferSize_ = fileSize_; + if (fileSize_ > CHUNK_THRESHOLD) { + chunking_ = true; + bufferSize_ = CHUNK_SIZE; + gain_ = 1.0 / 32768.0; } // STK rawwave files have no header and are assumed to contain a // monophonic stream of 16-bit signed integers in big-endian byte // order with a sample rate of 22050 Hz. - channels = 1; - dataOffset = 0; - rate = (MY_FLOAT) 22050.0 / Stk::sampleRate(); - fileRate = 22050.0; - interpolate = false; - dataType = STK_SINT16; - byteswap = false; + channels_ = 1; + dataOffset_ = 0; + rate_ = (StkFloat) 22050.0 / Stk::sampleRate(); + fileRate_ = 22050.0; + interpolate_ = false; + dataType_ = STK_SINT16; + byteswap_ = false; #ifdef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; #endif return true; @@ -197,112 +200,112 @@ bool WvIn :: getWavInfo( const char *fileName ) // Find "format" chunk ... it must come before the "data" chunk. char id[4]; SINT32 chunkSize; - if ( fread(&id, 4, 1, fd) != 1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; while ( strncmp(id, "fmt ", 4) ) { - if ( fread(&chunkSize, 4, 1, fd) != 1 ) goto error; + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ swap32((unsigned char *)&chunkSize); #endif - if ( fseek(fd, chunkSize, SEEK_CUR) == -1 ) goto error; - if ( fread(&id, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; } // Check that the data is not compressed. SINT16 format_tag; - if ( fread(&chunkSize, 4, 1, fd) != 1 ) goto error; // Read fmt chunk size. - if ( fread(&format_tag, 2, 1, fd) != 1 ) goto error; + 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); + errorString_ << "WvIn: "<< fileName << " contains an unsupported data format type (" << format_tag << ")."; return false; } // Get number of channels from the header. SINT16 temp; - if ( fread(&temp, 2, 1, fd) != 1 ) goto error; + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ swap16((unsigned char *)&temp); #endif - channels = (unsigned int ) temp; + channels_ = (unsigned int ) temp; // Get file sample rate from the header. SINT32 srate; - if ( fread(&srate, 4, 1, fd) != 1 ) goto error; + if ( fread(&srate, 4, 1, fd_) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ swap32((unsigned char *)&srate); #endif - fileRate = (MY_FLOAT) srate; + fileRate_ = (StkFloat) srate; // Set default rate based on file sampling rate. - rate = (MY_FLOAT) ( srate / Stk::sampleRate() ); + rate_ = (StkFloat) ( srate / Stk::sampleRate() ); // Determine the data type. - dataType = 0; - if ( fseek(fd, 6, SEEK_CUR) == -1 ) goto error; // Locate bits_per_sample info. - if ( fread(&temp, 2, 1, fd) != 1 ) goto error; + dataType_ = 0; + if ( fseek(fd_, 6, SEEK_CUR) == -1 ) goto error; // Locate bits_per_sample info. + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ swap16((unsigned char *)&temp); #endif if ( format_tag == 1 ) { if (temp == 8) - dataType = STK_SINT8; + dataType_ = STK_SINT8; else if (temp == 16) - dataType = STK_SINT16; + dataType_ = STK_SINT16; else if (temp == 32) - dataType = STK_SINT32; + dataType_ = STK_SINT32; } else if ( format_tag == 3 ) { if (temp == 32) - dataType = MY_FLOAT32; + dataType_ = STK_FLOAT32; else if (temp == 64) - dataType = MY_FLOAT64; + dataType_ = STK_FLOAT64; } - if ( dataType == 0 ) { - sprintf(msg, "WvIn: %d bits per sample with data format %d are not supported (%s).", temp, format_tag, fileName); + if ( dataType_ == 0 ) { + errorString_ << "WvIn: " << temp << " bits per sample with data format " << format_tag << " are not supported (" << fileName << ")."; return false; } // Jump over any remaining part of the "fmt" chunk. - if ( fseek(fd, chunkSize-16, SEEK_CUR) == -1 ) goto error; + 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; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; while ( strncmp(id, "data", 4) ) { - if ( fread(&chunkSize, 4, 1, fd) != 1 ) goto error; + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ swap32((unsigned char *)&chunkSize); #endif - if ( fseek(fd, chunkSize, SEEK_CUR) == -1 ) goto error; - if ( fread(&id, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; } // Get length of data from the header. SINT32 bytes; - if ( fread(&bytes, 4, 1, fd) != 1 ) goto error; + if ( fread(&bytes, 4, 1, fd_) != 1 ) goto error; #ifndef __LITTLE_ENDIAN__ swap32((unsigned char *)&bytes); #endif - fileSize = 8 * bytes / temp / channels; // sample frames - bufferSize = fileSize; - if (fileSize > CHUNK_THRESHOLD) { - chunking = true; - bufferSize = CHUNK_SIZE; + fileSize_ = 8 * bytes / temp / channels_; // sample frames + bufferSize_ = fileSize_; + if (fileSize_ > CHUNK_THRESHOLD) { + chunking_ = true; + bufferSize_ = CHUNK_SIZE; } - dataOffset = ftell(fd); - byteswap = false; + dataOffset_ = ftell(fd_); + byteswap_ = false; #ifndef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; #endif return true; error: - sprintf(msg, "WvIn: Error reading WAV file (%s).", fileName); + errorString_ << "WvIn: error reading WAV file (" << fileName << ")."; return false; } @@ -310,67 +313,67 @@ bool WvIn :: getSndInfo( const char *fileName ) { // Determine the data type. SINT32 format; - if ( fseek(fd, 12, SEEK_SET) == -1 ) goto error; // Locate format - if ( fread(&format, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, 12, SEEK_SET) == -1 ) goto error; // Locate format + if ( fread(&format, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&format); #endif - 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 = MY_FLOAT32; - else if (format == 7) dataType = MY_FLOAT64; + 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 { - sprintf(msg, "WvIn: data format in file %s is not supported.", fileName); + errorString_ << "WvIn: data format in file " << fileName << " is not supported."; return false; } // Get file sample rate from the header. SINT32 srate; - if ( fread(&srate, 4, 1, fd) != 1 ) goto error; + if ( fread(&srate, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&srate); #endif - fileRate = (MY_FLOAT) srate; + fileRate_ = (StkFloat) srate; // Set default rate based on file sampling rate. - rate = (MY_FLOAT) ( srate / sampleRate() ); + rate_ = (StkFloat) ( srate / sampleRate() ); // Get number of channels from the header. SINT32 chans; - if ( fread(&chans, 4, 1, fd) != 1 ) goto error; + if ( fread(&chans, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&chans); #endif - channels = chans; + channels_ = chans; - if ( fseek(fd, 4, SEEK_SET) == -1 ) goto error; - if ( fread(&dataOffset, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, 4, SEEK_SET) == -1 ) goto error; + if ( fread(&dataOffset_, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ - swap32((unsigned char *)&dataOffset); + swap32((unsigned char *)&dataOffset_); #endif // Get length of data from the header. - if ( fread(&fileSize, 4, 1, fd) != 1 ) goto error; + if ( fread(&fileSize_, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ - swap32((unsigned char *)&fileSize); + swap32((unsigned char *)&fileSize_); #endif - fileSize /= 2 * channels; // Convert to sample frames. - bufferSize = fileSize; - if (fileSize > CHUNK_THRESHOLD) { - chunking = true; - bufferSize = CHUNK_SIZE; + fileSize_ /= 2 * channels_; // Convert to sample frames. + bufferSize_ = fileSize_; + if (fileSize_ > CHUNK_THRESHOLD) { + chunking_ = true; + bufferSize_ = CHUNK_SIZE; } - byteswap = false; + byteswap_ = false; #ifdef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; #endif return true; error: - sprintf(msg, "WvIn: Error reading SND file (%s).", fileName); + errorString_ << "WvIn: Error reading SND file (" << fileName << ")."; return false; } @@ -380,46 +383,46 @@ bool WvIn :: getAifInfo( const char *fileName ) char id[4]; // Determine whether this is AIFF or AIFC. - if ( fseek(fd, 8, SEEK_SET) == -1 ) goto error; - if ( fread(&id, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, 8, SEEK_SET) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; if ( !strncmp(id, "AIFC", 4) ) aifc = true; // Find "common" chunk SINT32 chunkSize; - if ( fread(&id, 4, 1, fd) != 1) goto error; + if ( fread(&id, 4, 1, fd_) != 1) goto error; while ( strncmp(id, "COMM", 4) ) { - if ( fread(&chunkSize, 4, 1, fd) != 1 ) goto error; + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&chunkSize); #endif - if ( fseek(fd, chunkSize, SEEK_CUR) == -1 ) goto error; - if ( fread(&id, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; } // Get number of channels from the header. SINT16 temp; - if ( fseek(fd, 4, SEEK_CUR) == -1 ) goto error; // Jump over chunk size - if ( fread(&temp, 2, 1, fd) != 1 ) goto error; + if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error; // Jump over chunk size + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap16((unsigned char *)&temp); #endif - channels = temp; + channels_ = temp; // Get length of data from the header. SINT32 frames; - if ( fread(&frames, 4, 1, fd) != 1 ) goto error; + if ( fread(&frames, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&frames); #endif - fileSize = frames; // sample frames - bufferSize = fileSize; - if (fileSize > CHUNK_THRESHOLD) { - chunking = true; - bufferSize = CHUNK_SIZE; + fileSize_ = frames; // sample frames + bufferSize_ = fileSize_; + if (fileSize_ > CHUNK_THRESHOLD) { + chunking_ = true; + bufferSize_ = CHUNK_SIZE; } // Read the number of bits per sample. - if ( fread(&temp, 2, 1, fd) != 1 ) goto error; + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap16((unsigned char *)&temp); #endif @@ -431,7 +434,7 @@ bool WvIn :: getAifInfo( const char *fileName ) unsigned char exp; unsigned long mantissa; unsigned long last; - if ( fread(&srate, 10, 1, fd) != 1 ) goto error; + if ( fread(&srate, 10, 1, fd_) != 1 ) goto error; mantissa = (unsigned long) *(unsigned long *)(srate+2); #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&mantissa); @@ -443,55 +446,55 @@ bool WvIn :: getAifInfo( const char *fileName ) mantissa >>= 1; } if (last & 0x00000001) mantissa++; - fileRate = (MY_FLOAT) mantissa; + fileRate_ = (StkFloat) mantissa; // Set default rate based on file sampling rate. - rate = (MY_FLOAT) ( fileRate / sampleRate() ); + rate_ = (StkFloat) ( fileRate_ / sampleRate() ); // Determine the data format. - dataType = 0; + dataType_ = 0; if ( aifc == false ) { - if ( temp == 8 ) dataType = STK_SINT8; - else if ( temp == 16 ) dataType = STK_SINT16; - else if ( temp == 32 ) dataType = STK_SINT32; + if ( temp == 8 ) dataType_ = STK_SINT8; + else if ( temp == 16 ) dataType_ = STK_SINT16; + else if ( temp == 32 ) dataType_ = STK_SINT32; } else { - if ( fread(&id, 4, 1, fd) != 1 ) goto error; - 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 ( 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 ( dataType == 0 ) { - sprintf(msg, "WvIn: %d bits per sample in file %s are not supported.", temp, fileName); + if ( dataType_ == 0 ) { + errorString_ << "WvIn: " << temp << " bits per sample in file " << fileName << " are not supported."; return false; } // Start at top to find data (SSND) chunk ... chunk order is undefined. - if ( fseek(fd, 12, SEEK_SET) == -1 ) goto error; + if ( fseek(fd_, 12, SEEK_SET) == -1 ) goto error; // Find data (SSND) chunk - if ( fread(&id, 4, 1, fd) != 1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; while ( strncmp(id, "SSND", 4) ) { - if ( fread(&chunkSize, 4, 1, fd) != 1 ) goto error; + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&chunkSize); #endif - if ( fseek(fd, chunkSize, SEEK_CUR) == -1 ) goto error; - if ( fread(&id, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; } // Skip over chunk size, offset, and blocksize fields - if ( fseek(fd, 12, SEEK_CUR) == -1 ) goto error; + if ( fseek(fd_, 12, SEEK_CUR) == -1 ) goto error; - dataOffset = ftell(fd); - byteswap = false; + dataOffset_ = ftell(fd_); + byteswap_ = false; #ifdef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; #endif return true; error: - sprintf(msg, "WvIn: Error reading AIFF file (%s).", fileName); + errorString_ << "WvIn: Error reading AIFF file (" << fileName << ")."; return false; } @@ -499,330 +502,329 @@ bool WvIn :: getMatInfo( const char *fileName ) { // Verify this is a version 5 MAT-file format. char head[4]; - if ( fseek(fd, 0, SEEK_SET) == -1 ) goto error; - if ( fread(&head, 4, 1, fd) != 1 ) goto error; + if ( fseek(fd_, 0, SEEK_SET) == -1 ) goto error; + if ( fread(&head, 4, 1, fd_) != 1 ) goto error; // If any of the first 4 characters of the header = 0, then this is // a Version 4 MAT-file. if ( strstr(head, "0") ) { - sprintf(msg, "WvIn: %s appears to be a Version 4 MAT-file, which is not currently supported.", - fileName); + errorString_ << "WvIn: " << fileName << " appears to be a Version 4 MAT-file, which is not currently supported."; return false; } // Determine the endian-ness of the file. char mi[2]; - byteswap = false; + byteswap_ = false; // Locate "M" and "I" characters in header. - if ( fseek(fd, 126, SEEK_SET) == -1 ) goto error; - if ( fread(&mi, 2, 1, fd) != 1) goto error; + if ( fseek(fd_, 126, SEEK_SET) == -1 ) goto error; + if ( fread(&mi, 2, 1, fd_) != 1) goto error; #ifdef __LITTLE_ENDIAN__ if ( !strncmp(mi, "MI", 2) ) - byteswap = true; + byteswap_ = true; else if ( strncmp(mi, "IM", 2) ) goto error; #else if ( !strncmp(mi, "IM", 2)) - byteswap = true; + byteswap_ = true; else if ( strncmp(mi, "MI", 2) ) goto error; #endif // Check the data element type SINT32 datatype; - if ( fread(&datatype, 4, 1, fd) != 1 ) goto error; - if ( byteswap ) swap32((unsigned char *)&datatype); + if ( fread(&datatype, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&datatype); if (datatype != 14) { - sprintf(msg, "WvIn: The file does not contain a single Matlab array (or matrix) data element."); + errorString_ << "WvIn: The file does not contain a single Matlab array (or matrix) data element."; return false; } // Determine the array data type. SINT32 tmp; SINT32 size; - if ( fseek(fd, 168, SEEK_SET) == -1 ) goto error; - if ( fread(&tmp, 4, 1, fd) != 1 ) goto error; - if (byteswap) swap32((unsigned char *)&tmp); + if ( fseek(fd_, 168, SEEK_SET) == -1 ) goto error; + if ( fread(&tmp, 4, 1, fd_) != 1 ) goto error; + if (byteswap_) swap32((unsigned char *)&tmp); if (tmp == 1) { // array name > 4 characters - if ( fread(&tmp, 4, 1, fd) != 1 ) goto error; // get array name length - if (byteswap) swap32((unsigned char *)&tmp); + if ( fread(&tmp, 4, 1, fd_) != 1 ) goto error; // get array name length + if (byteswap_) swap32((unsigned char *)&tmp); size = (SINT32) ceil((float)tmp / 8); - if ( fseek(fd, size*8, SEEK_CUR) == -1 ) goto error; // jump over array name + if ( fseek(fd_, size*8, SEEK_CUR) == -1 ) goto error; // jump over array name } else { // array name <= 4 characters, compressed data element - if ( fseek(fd, 4, SEEK_CUR) == -1 ) goto error; + if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error; } - if ( fread(&tmp, 4, 1, fd) != 1 ) goto error; - if (byteswap) swap32((unsigned char *)&tmp); - 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 = MY_FLOAT32; - else if ( tmp == 9 ) dataType = MY_FLOAT64; + if ( fread(&tmp, 4, 1, fd_) != 1 ) goto error; + if (byteswap_) swap32((unsigned char *)&tmp); + 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 { - sprintf(msg, "WvIn: The MAT-file array data format (%d) is not supported.", tmp); + errorString_ << "WvIn: The MAT-file array data format (" << tmp << ") is not supported."; return false; } // Get number of rows from the header. SINT32 rows; - if ( fseek(fd, 160, SEEK_SET) == -1 ) goto error; - if ( fread(&rows, 4, 1, fd) != 1 ) goto error; - if (byteswap) swap32((unsigned char *)&rows); + if ( fseek(fd_, 160, SEEK_SET) == -1 ) goto error; + if ( fread(&rows, 4, 1, fd_) != 1 ) goto error; + if (byteswap_) swap32((unsigned char *)&rows); // Get number of columns from the header. SINT32 columns; - if ( fread(&columns,4, 1, fd) != 1 ) goto error; - if (byteswap) swap32((unsigned char *)&columns); + if ( fread(&columns,4, 1, fd_) != 1 ) goto error; + if (byteswap_) swap32((unsigned char *)&columns); // Assume channels = smaller of rows or columns. if (rows < columns) { - channels = rows; - fileSize = columns; + channels_ = rows; + fileSize_ = columns; } else { - sprintf(msg, "WvIn: Transpose the MAT-file array so that audio channels fill matrix rows (not columns)."); + errorString_ << "WvIn: Transpose the MAT-file array so that audio channels fill matrix rows (not columns)."; return false; } - bufferSize = fileSize; - if (fileSize > CHUNK_THRESHOLD) { - chunking = true; - bufferSize = CHUNK_SIZE; + bufferSize_ = fileSize_; + if (fileSize_ > CHUNK_THRESHOLD) { + chunking_ = true; + bufferSize_ = CHUNK_SIZE; } // Move read pointer to the data in the file. SINT32 headsize; - if ( fseek(fd, 132, SEEK_SET) == -1 ) goto error; - if ( fread(&headsize, 4, 1, fd) != 1 ) goto error; // file size from 132nd byte - if (byteswap) swap32((unsigned char *)&headsize); - headsize -= fileSize * 8 * channels; - if ( fseek(fd, headsize, SEEK_CUR) == -1 ) goto error; - dataOffset = ftell(fd); + if ( fseek(fd_, 132, SEEK_SET) == -1 ) goto error; + if ( fread(&headsize, 4, 1, fd_) != 1 ) goto error; // file size from 132nd byte + if (byteswap_) swap32((unsigned char *)&headsize); + headsize -= fileSize_ * 8 * channels_; + if ( fseek(fd_, headsize, SEEK_CUR) == -1 ) goto error; + dataOffset_ = ftell(fd_); // Assume MAT-files have 44100 Hz sample rate. - fileRate = 44100.0; + fileRate_ = 44100.0; // Set default rate based on file sampling rate. - rate = (MY_FLOAT) ( fileRate / sampleRate() ); + rate_ = (StkFloat) ( fileRate_ / sampleRate() ); return true; error: - sprintf(msg, "WvIn: Error reading MAT-file (%s).", fileName); + errorString_ << "WvIn: Error reading MAT-file (" << fileName << ")."; return false; } void WvIn :: readData( unsigned long index ) { - while (index < (unsigned long)chunkPointer) { + while (index < (unsigned long)chunkPointer_) { // Negative rate. - chunkPointer -= CHUNK_SIZE; - bufferSize = CHUNK_SIZE; - if (chunkPointer < 0) { - bufferSize += chunkPointer; - chunkPointer = 0; + chunkPointer_ -= CHUNK_SIZE; + bufferSize_ = CHUNK_SIZE; + if (chunkPointer_ < 0) { + bufferSize_ += chunkPointer_; + chunkPointer_ = 0; } } - while (index >= chunkPointer+bufferSize) { + while (index >= chunkPointer_+bufferSize_) { // Positive rate. - chunkPointer += CHUNK_SIZE; - bufferSize = CHUNK_SIZE; - if ( (unsigned long)chunkPointer+CHUNK_SIZE >= fileSize) { - bufferSize = fileSize - chunkPointer; + chunkPointer_ += CHUNK_SIZE; + bufferSize_ = CHUNK_SIZE; + if ( (unsigned long)chunkPointer_+CHUNK_SIZE >= fileSize_) { + bufferSize_ = fileSize_ - chunkPointer_; } } - long i, length = bufferSize; - bool endfile = (chunkPointer+bufferSize == fileSize); + long i, length = bufferSize_; + bool endfile = (chunkPointer_+bufferSize_ == fileSize_); if ( !endfile ) length += 1; - // Read samples into data[]. Use MY_FLOAT data structure + // Read samples into data[]. Use StkFloat data structure // to store samples. - if ( dataType == STK_SINT16 ) { - SINT16 *buf = (SINT16 *)data; - if (fseek(fd, dataOffset+(long)(chunkPointer*channels*2), SEEK_SET) == -1) goto error; - if (fread(buf, length*channels, 2, fd) != 2 ) goto error; - if ( byteswap ) { + if ( dataType_ == STK_SINT16 ) { + SINT16 *buf = (SINT16 *)data_; + if (fseek(fd_, dataOffset_+(long)(chunkPointer_*channels_*2), SEEK_SET) == -1) goto error; + if (fread(buf, length*channels_, 2, fd_) != 2 ) goto error; + if ( byteswap_ ) { SINT16 *ptr = buf; - for (i=length*channels-1; i>=0; i--) + for (i=length*channels_-1; i>=0; i--) swap16((unsigned char *)(ptr++)); } - for (i=length*channels-1; i>=0; i--) - data[i] = buf[i]; + for (i=length*channels_-1; i>=0; i--) + data_[i] = buf[i]; } - else if ( dataType == STK_SINT32 ) { - SINT32 *buf = (SINT32 *)data; - if (fseek(fd, dataOffset+(long)(chunkPointer*channels*4), SEEK_SET) == -1) goto error; - if (fread(buf, length*channels, 4, fd) != 4 ) goto error; - if ( byteswap ) { + else if ( dataType_ == STK_SINT32 ) { + SINT32 *buf = (SINT32 *)data_; + if (fseek(fd_, dataOffset_+(long)(chunkPointer_*channels_*4), SEEK_SET) == -1) goto error; + if (fread(buf, length*channels_, 4, fd_) != 4 ) goto error; + if ( byteswap_ ) { SINT32 *ptr = buf; - for (i=length*channels-1; i>=0; i--) + for (i=length*channels_-1; i>=0; i--) swap32((unsigned char *)(ptr++)); } - for (i=length*channels-1; i>=0; i--) - data[i] = buf[i]; + for (i=length*channels_-1; i>=0; i--) + data_[i] = buf[i]; } - 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; - if ( byteswap ) { + else if ( dataType_ == STK_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; + if ( byteswap_ ) { FLOAT32 *ptr = buf; - for (i=length*channels-1; i>=0; i--) + for (i=length*channels_-1; i>=0; i--) swap32((unsigned char *)(ptr++)); } - for (i=length*channels-1; i>=0; i--) - data[i] = buf[i]; + for (i=length*channels_-1; i>=0; i--) + data_[i] = buf[i]; } - 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; - if ( byteswap ) { + else if ( dataType_ == STK_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; + if ( byteswap_ ) { FLOAT64 *ptr = buf; - for (i=length*channels-1; i>=0; i--) + for (i=length*channels_-1; i>=0; i--) swap64((unsigned char *)(ptr++)); } - for (i=length*channels-1; i>=0; i--) - data[i] = buf[i]; + for (i=length*channels_-1; i>=0; i--) + data_[i] = buf[i]; } - else if ( dataType == STK_SINT8 ) { - 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] - 128.0; // 8-bit WAV data is unsigned! + else if ( dataType_ == STK_SINT8 ) { + 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] - 128.0; // 8-bit WAV data is unsigned! } // If at end of file, repeat last sample frame for interpolation. if ( endfile ) { - for (unsigned int j=0; jnormalize((MY_FLOAT) 1.0); + this->normalize( 1.0 ); } // Normalize all channels equally by the greatest magnitude in all of the data. -void WvIn :: normalize(MY_FLOAT peak) +void WvIn :: normalize( StkFloat peak ) { - if (chunking) { - 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 == MY_FLOAT32 || dataType == MY_FLOAT64 ) gain = peak; + if (chunking_) { + 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; return; } unsigned long i; - MY_FLOAT max = (MY_FLOAT) 0.0; + StkFloat max = 0.0; - for (i=0; i max) - max = (MY_FLOAT) fabs((double) data[i]); + for (i=0; i max) + max = (StkFloat) fabs((double) data_[i]); } if (max > 0.0) { - max = (MY_FLOAT) 1.0 / max; + max = 1.0 / max; max *= peak; - for (i=0;i<=channels*bufferSize;i++) - data[i] *= max; + for (i=0;i<=channels_*bufferSize_;i++) + data_[i] *= max; } } unsigned long WvIn :: getSize(void) const { - return fileSize; + return fileSize_; } unsigned int WvIn :: getChannels(void) const { - return channels; + return channels_; } -MY_FLOAT WvIn :: getFileRate(void) const +StkFloat WvIn :: getFileRate(void) const { - return fileRate; + return fileRate_; } -bool WvIn :: isFinished(void) const +void WvIn :: setRate(StkFloat aRate) { - return finished; -} - -void WvIn :: setRate(MY_FLOAT aRate) -{ - rate = aRate; + rate_ = aRate; // If negative rate and at beginning of sound, move pointer to end // of sound. - if ( (rate < 0) && (time == 0.0) ) time += rate + fileSize; + if ( (rate_ < 0) && (time_ == 0.0) ) time_ += rate_ + fileSize_; - if (fmod(rate, 1.0) != 0.0) interpolate = true; - else interpolate = false; + if (fmod(rate_, 1.0) != 0.0) interpolate_ = true; + else interpolate_ = false; } -void WvIn :: addTime(MY_FLOAT aTime) +void WvIn :: addTime(StkFloat aTime) { // Add an absolute time in samples - time += aTime; + time_ += aTime; - if (time < 0.0) time = 0.0; - if (time >= fileSize) { - time = fileSize; - finished = true; + if (time_ < 0.0) time_ = 0.0; + if (time_ >= fileSize_) { + time_ = fileSize_; + finished_ = true; } } void WvIn :: setInterpolate(bool doInterpolate) { - interpolate = doInterpolate; + interpolate_ = doInterpolate; } -const MY_FLOAT *WvIn :: lastFrame(void) const +bool WvIn :: isFinished(void) const { - return lastOutput; + return finished_; } -MY_FLOAT WvIn :: lastOut(void) const +const StkFloat *WvIn :: lastFrame(void) const { - if ( channels == 1 ) - return *lastOutput; + return lastOutputs_; +} - MY_FLOAT output = 0.0; - for (unsigned int i=0; itickFrame(); + return this->lastOut(); } -MY_FLOAT *WvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize) +StkFloat *WvIn :: tick(StkFloat *vector, unsigned int vectorSize) { for ( unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } - if (finished) return lastOutput; + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; i= chunkPointer+bufferSize) ) + if ( (tyme < chunkPointer_) || (tyme >= chunkPointer_+bufferSize_) ) this->readData((long) tyme); // Adjust index for the current buffer. - tyme -= chunkPointer; + tyme -= chunkPointer_; } // Integer part of time address. - index = (long) tyme; + register unsigned long index = (unsigned long) tyme; - if (interpolate) { + register unsigned long i; + if (interpolate_) { // Linear interpolation ... fractional part of time address. - alpha = tyme - (MY_FLOAT) index; - index *= channels; - for (i=0; i= fileSize ) finished = true; + time_ += rate_; + if ( time_ < 0.0 || time_ >= fileSize_ ) + finished_ = true; - return lastOutput; + return lastOutputs_; } -MY_FLOAT *WvIn :: tickFrame(MY_FLOAT *frameVector, unsigned int frames) +StkFloat *WvIn :: tickFrame(StkFloat *frameVector, unsigned int frames) { unsigned int j; for ( unsigned int i=0; itickFrame(); + for ( j=0; j 1 && frames.interleaved() == false ) { + unsigned int jump = frames.frames(); + for ( unsigned int i=0; itickFrame(); + for ( j=0; jtickFrame(); + for ( j=0; j const WvOut::FILE_TYPE WvOut :: WVOUT_RAW = 1; const WvOut::FILE_TYPE WvOut :: WVOUT_WAV = 2; @@ -47,18 +46,18 @@ const WvOut::FILE_TYPE WvOut :: WVOUT_MAT = 5; // for information regarding format codes. struct wavhdr { char riff[4]; // "RIFF" - SINT32 file_size; // in bytes + SINT32 file_size; // in bytes char wave[4]; // "WAVE" char fmt[4]; // "fmt " - SINT32 chunk_size; // in bytes (16 for PCM) - SINT16 format_tag; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law - SINT16 num_chans; // 1=mono, 2=stereo + SINT32 chunk_size; // in bytes (16 for PCM) + SINT16 format_tag; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law + SINT16 num_chans; // 1=mono, 2=stereo SINT32 sample_rate; SINT32 bytes_per_sec; - SINT16 bytes_per_samp; // 2=16-bit mono, 4=16-bit stereo + SINT16 bytes_per_samp; // 2=16-bit mono, 4=16-bit stereo SINT16 bits_per_samp; char data[4]; // "data" - SINT32 data_length; // in bytes + SINT32 data_length; // in bytes }; // SND (AU) header structure (NeXT and Sun). @@ -75,204 +74,200 @@ struct sndhdr { // AIFF/AIFC header structure ... only the part common to both // formats. struct aifhdr { - char form[4]; // "FORM" - SINT32 form_size; // in bytes - char aiff[4]; // "AIFF" or "AIFC" - char comm[4]; // "COMM" - SINT32 comm_size; // "COMM" chunk size (18 for AIFF, 24 for AIFC) - SINT16 num_chans; // number of channels + char form[4]; // "FORM" + SINT32 form_size; // in bytes + char aiff[4]; // "AIFF" or "AIFC" + char comm[4]; // "COMM" + SINT32 comm_size; // "COMM" chunk size (18 for AIFF, 24 for AIFC) + SINT16 num_chans; // number of channels unsigned long sample_frames; // sample frames of audio data - SINT16 sample_size; // in bits - unsigned char srate[10]; // IEEE 754 floating point format + SINT16 sample_size; // in bits + unsigned char srate[10]; // IEEE 754 floating point format }; struct aifssnd { char ssnd[4]; // "SSND" - SINT32 ssnd_size; // "SSND" chunk size - unsigned long offset; // data offset in data block (should be 0) - unsigned long block_size; // not used by STK (should be 0) + SINT32 ssnd_size; // "SSND" chunk size + unsigned long offset; // data offset in data block (should be 0) + unsigned long block_size; // not used by STK (should be 0) }; // MAT-file 5 header structure. struct mathdr { char heading[124]; // Header text field - SINT16 hff[2]; // Header flag fields - SINT32 adf[11]; // Array data format fields + SINT16 hff[2]; // Header flag fields + SINT32 adf[11]; // Array data format fields // There's more, but it's of variable length }; WvOut :: WvOut() { - init(); + this->init(); } -WvOut::WvOut( const char *fileName, unsigned int nChannels, FILE_TYPE type, Stk::STK_FORMAT format ) +WvOut::WvOut( const char *fileName, unsigned int nChannels, FILE_TYPE type, Stk::StkFormat format ) { - init(); - openFile( fileName, nChannels, type, format ); + this->init(); + this->openFile( fileName, nChannels, type, format ); } WvOut :: ~WvOut() { - closeFile(); - - if (data) - delete [] data; + this->closeFile(); } void WvOut :: init() { - fd = 0; - data = 0; - fileType = 0; - dataType = 0; - channels = 0; - counter = 0; - totalCount = 0; + fd_ = 0; + fileType_ = 0; + dataType_ = 0; + channels_ = 0; + counter_ = 0; + totalCount_ = 0; + clipping_ = false; } void WvOut :: closeFile( void ) { - if ( fd ) { + if ( fd_ ) { // If there's an existing file, close it first. - writeData( counter ); + this->writeData( counter_ ); - if ( fileType == WVOUT_RAW ) - fclose( fd ); - else if ( fileType == WVOUT_WAV ) - closeWavFile(); - else if ( fileType == WVOUT_SND ) - closeSndFile(); - else if ( fileType == WVOUT_AIF ) - closeAifFile(); - else if ( fileType == WVOUT_MAT ) - closeMatFile(); - fd = 0; + if ( fileType_ == WVOUT_RAW ) + fclose( fd_ ); + else if ( fileType_ == WVOUT_WAV ) + this->closeWavFile(); + else if ( fileType_ == WVOUT_SND ) + this->closeSndFile(); + else if ( fileType_ == WVOUT_AIF ) + this->closeAifFile(); + else if ( fileType_ == WVOUT_MAT ) + this->closeMatFile(); + fd_ = 0; - printf("%f Seconds Computed\n\n", getTime() ); - totalCount = 0; + errorString_ << "WvOut: file closed, "<< getTime() << " seconds of data written."; + handleError( StkError::WARNING ); + totalCount_ = 0; } } -void WvOut :: openFile( const char *fileName, unsigned int nChannels, WvOut::FILE_TYPE type, Stk::STK_FORMAT format ) +void WvOut :: openFile( const char *fileName, unsigned int nChannels, WvOut::FILE_TYPE type, Stk::StkFormat format ) { closeFile(); if ( nChannels < 1 ) { - sprintf(msg, "WvOut: the channels argument must be greater than zero!"); - handleError( msg, StkError::FUNCTION_ARGUMENT ); + errorString_ << "WvOut::openFile: then channels argument must be greater than zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - unsigned int lastChannels = channels; - channels = nChannels; - fileType = type; + unsigned int lastChannels = channels_; + channels_ = nChannels; + fileType_ = type; if ( format != STK_SINT8 && format != STK_SINT16 && - format != STK_SINT32 && format != MY_FLOAT32 && - format != MY_FLOAT64 ) { - sprintf( msg, "WvOut: Unknown data type specified (%ld).", format ); - handleError(msg, StkError::FUNCTION_ARGUMENT); + format != STK_SINT32 && format != STK_FLOAT32 && + format != STK_FLOAT64 ) { + errorString_ << "WvOut::openFile: unknown data type (" << format << ") specified!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - dataType = format; + dataType_ = format; bool result = false; - if ( fileType == WVOUT_RAW ) { - if ( channels != 1 ) { - sprintf(msg, "WvOut: STK RAW files are, by definition, always monaural (channels = %d not supported)!", nChannels); - handleError( msg, StkError::FUNCTION_ARGUMENT ); + if ( fileType_ == WVOUT_RAW ) { + if ( channels_ != 1 ) { + errorString_ << "WvOut::openFile: STK RAW files are, by definition, always monaural (channels = " << nChannels << " not supported)!"; + handleError( StkError::FUNCTION_ARGUMENT ); } result = setRawFile( fileName ); } - else if ( fileType == WVOUT_WAV ) + else if ( fileType_ == WVOUT_WAV ) result = setWavFile( fileName ); - else if ( fileType == WVOUT_SND ) + else if ( fileType_ == WVOUT_SND ) result = setSndFile( fileName ); - else if ( fileType == WVOUT_AIF ) + else if ( fileType_ == WVOUT_AIF ) result = setAifFile( fileName ); - else if ( fileType == WVOUT_MAT ) + else if ( fileType_ == WVOUT_MAT ) result = setMatFile( fileName ); else { - sprintf(msg, "WvOut: Unknown file type specified (%ld).", fileType); - handleError(msg, StkError::FUNCTION_ARGUMENT); + errorString_ << "WvOut::openFile: unknown file type (" << fileType_ << ") specified!"; + handleError( StkError::FUNCTION_ARGUMENT ); } if ( result == false ) - handleError(msg, StkError::FILE_ERROR); + handleError( StkError::FILE_ERROR ); // Allocate new memory if necessary. - if ( lastChannels < channels ) { - if ( data ) delete [] data; - data = (MY_FLOAT *) new MY_FLOAT[BUFFER_SIZE*channels]; + if ( lastChannels < channels_ ) { + data_.resize( BUFFER_SIZE * channels_ ); } - counter = 0; + counter_ = 0; } bool WvOut :: setRawFile( const char *fileName ) { - char name[128]; - strncpy(name, fileName, 128); + char name[8192]; + strncpy(name, fileName, 8192); if ( strstr(name, ".raw") == NULL) strcat(name, ".raw"); - fd = fopen(name, "wb"); - if ( !fd ) { - sprintf(msg, "WvOut: Could not create RAW file: %s", name); + fd_ = fopen(name, "wb"); + if ( !fd_ ) { + errorString_ << "WvOut: could not create RAW file: " << name << '.'; return false; } - if ( dataType != STK_SINT16 ) { - dataType = STK_SINT16; - sprintf(msg, "WvOut: Using 16-bit signed integer data format for file %s.", name); - handleError(msg, StkError::WARNING); + if ( dataType_ != STK_SINT16 ) { + dataType_ = STK_SINT16; + errorString_ << "WvOut: using 16-bit signed integer data format for file " << name << '.'; + handleError( StkError::WARNING ); } - byteswap = false; + byteswap_ = false; #ifdef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; #endif - printf("\nCreating RAW file: %s\n", name); + errorString_ << "WvOut: creating RAW file: " << name; + handleError( StkError::WARNING ); return true; } bool WvOut :: setWavFile( const char *fileName ) { - char name[128]; - strncpy(name, fileName, 128); + char name[8192]; + strncpy(name, fileName, 8192); if ( strstr(name, ".wav") == NULL) strcat(name, ".wav"); - fd = fopen(name, "wb"); - if ( !fd ) { - sprintf(msg, "WvOut: Could not create WAV file: %s", name); + fd_ = fopen(name, "wb"); + if ( !fd_ ) { + errorString_ << "WvOut: could not create WAV file: " << name; return false; } - - struct wavhdr hdr = {"RIF", 44, "WAV", "fmt", 16, 1, 1, (SINT32) Stk::sampleRate(), 0, 2, 16, "dat", 0}; hdr.riff[3] = 'F'; hdr.wave[3] = 'E'; hdr.fmt[3] = ' '; hdr.data[3] = 'a'; - hdr.num_chans = (SINT16) channels; - if ( dataType == STK_SINT8 ) + hdr.num_chans = (SINT16) channels_; + if ( dataType_ == STK_SINT8 ) hdr.bits_per_samp = 8; - else if ( dataType == STK_SINT16 ) + else if ( dataType_ == STK_SINT16 ) hdr.bits_per_samp = 16; - else if ( dataType == STK_SINT32 ) + else if ( dataType_ == STK_SINT32 ) hdr.bits_per_samp = 32; - else if ( dataType == MY_FLOAT32 ) { + else if ( dataType_ == STK_FLOAT32 ) { hdr.format_tag = 3; hdr.bits_per_samp = 32; } - else if ( dataType == MY_FLOAT64 ) { + else if ( dataType_ == STK_FLOAT64 ) { hdr.format_tag = 3; hdr.bits_per_samp = 64; } - hdr.bytes_per_samp = (SINT16) (channels * hdr.bits_per_samp / 8); + hdr.bytes_per_samp = (SINT16) (channels_ * hdr.bits_per_samp / 8); hdr.bytes_per_sec = (SINT32) (hdr.sample_rate * hdr.bytes_per_samp); - byteswap = false; + byteswap_ = false; #ifndef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; swap32((unsigned char *)&hdr.file_size); swap32((unsigned char *)&hdr.chunk_size); swap16((unsigned char *)&hdr.format_tag); @@ -283,113 +278,115 @@ bool WvOut :: setWavFile( const char *fileName ) swap16((unsigned char *)&hdr.bits_per_samp); #endif - if ( fwrite(&hdr, 4, 11, fd) != 11 ) { - sprintf(msg, "WvOut: Could not write WAV header for file %s", name); + if ( fwrite(&hdr, 4, 11, fd_) != 11 ) { + errorString_ << "WvOut: could not write WAV header for file " << name << '.'; return false; } - printf("\nCreating WAV file: %s\n", name); + errorString_ << "WvOut: creating WAV file: " << name; + handleError( StkError::WARNING ); return true; } void WvOut :: closeWavFile( void ) { int bytes_per_sample = 1; - if ( dataType == STK_SINT16 ) + if ( dataType_ == STK_SINT16 ) bytes_per_sample = 2; - else if ( dataType == STK_SINT32 || dataType == MY_FLOAT32 ) + else if ( dataType_ == STK_SINT32 || dataType_ == STK_FLOAT32 ) bytes_per_sample = 4; - else if ( dataType == MY_FLOAT64 ) + else if ( dataType_ == STK_FLOAT64 ) bytes_per_sample = 8; - SINT32 bytes = totalCount * channels * bytes_per_sample; + SINT32 bytes = totalCount_ * channels_ * bytes_per_sample; #ifndef __LITTLE_ENDIAN__ swap32((unsigned char *)&bytes); #endif - fseek(fd, 40, SEEK_SET); // jump to data length - fwrite(&bytes, 4, 1, fd); + fseek(fd_, 40, SEEK_SET); // jump to data length + fwrite(&bytes, 4, 1, fd_); - bytes = totalCount * channels * bytes_per_sample + 44; + bytes = totalCount_ * channels_ * bytes_per_sample + 44; #ifndef __LITTLE_ENDIAN__ swap32((unsigned char *)&bytes); #endif - fseek(fd, 4, SEEK_SET); // jump to file size - fwrite(&bytes, 4, 1, fd); - fclose( fd ); + fseek(fd_, 4, SEEK_SET); // jump to file size + fwrite(&bytes, 4, 1, fd_); + fclose( fd_ ); } bool WvOut :: setSndFile( const char *fileName ) { - char name[128]; - strncpy(name, fileName, 128); + char name[8192]; + strncpy(name, fileName, 8192); if ( strstr(name, ".snd") == NULL) strcat(name, ".snd"); - fd = fopen(name, "wb"); - if ( !fd ) { - sprintf(msg, "WvOut: Could not create SND file: %s", name); + fd_ = fopen(name, "wb"); + if ( !fd_ ) { + errorString_ << "WvOut: could not create SND file: " << name; return false; } struct sndhdr hdr = {".sn", 40, 0, 3, (SINT32) Stk::sampleRate(), 1, "Created by STK"}; hdr.pref[3] = 'd'; - hdr.num_channels = channels; - if ( dataType == STK_SINT8 ) + hdr.num_channels = channels_; + if ( dataType_ == STK_SINT8 ) hdr.format = 2; - else if ( dataType == STK_SINT16 ) + else if ( dataType_ == STK_SINT16 ) hdr.format = 3; - else if ( dataType == STK_SINT32 ) + else if ( dataType_ == STK_SINT32 ) hdr.format = 5; - else if ( dataType == MY_FLOAT32 ) + else if ( dataType_ == STK_FLOAT32 ) hdr.format = 6; - else if ( dataType == MY_FLOAT64 ) + else if ( dataType_ == STK_FLOAT64 ) hdr.format = 7; - byteswap = false; + byteswap_ = false; #ifdef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; swap32 ((unsigned char *)&hdr.hdr_length); swap32 ((unsigned char *)&hdr.format); swap32 ((unsigned char *)&hdr.sample_rate); swap32 ((unsigned char *)&hdr.num_channels); #endif - if ( fwrite(&hdr, 4, 10, fd) != 10 ) { - sprintf(msg, "WvOut: Could not write SND header for file %s", name); + if ( fwrite(&hdr, 4, 10, fd_) != 10 ) { + errorString_ << "WvOut: Could not write SND header for file " << name << '.'; return false; } - printf("\nCreating SND file: %s\n", name); + errorString_ << "WvOut: creating SND file: " << name; + handleError( StkError::WARNING ); return true; } void WvOut :: closeSndFile( void ) { int bytes_per_sample = 1; - if ( dataType == STK_SINT16 ) + if ( dataType_ == STK_SINT16 ) bytes_per_sample = 2; - else if ( dataType == STK_SINT32 ) + else if ( dataType_ == STK_SINT32 ) bytes_per_sample = 4; - else if ( dataType == MY_FLOAT32 ) + else if ( dataType_ == STK_FLOAT32 ) bytes_per_sample = 4; - else if ( dataType == MY_FLOAT64 ) + else if ( dataType_ == STK_FLOAT64 ) bytes_per_sample = 8; - SINT32 bytes = totalCount * bytes_per_sample * channels; + SINT32 bytes = totalCount_ * bytes_per_sample * channels_; #ifdef __LITTLE_ENDIAN__ swap32 ((unsigned char *)&bytes); #endif - fseek(fd, 8, SEEK_SET); // jump to data size - fwrite(&bytes, 4, 1, fd); - fclose(fd); + fseek(fd_, 8, SEEK_SET); // jump to data size + fwrite(&bytes, 4, 1, fd_); + fclose(fd_); } bool WvOut :: setAifFile( const char *fileName ) { - char name[128]; - strncpy(name, fileName, 128); + char name[8192]; + strncpy(name, fileName, 8192); if ( strstr(name, ".aif") == NULL) strcat(name, ".aif"); - fd = fopen(name, "wb"); - if ( !fd ) { - sprintf(msg, "WvOut: Could not create AIF file: %s", name); + fd_ = fopen(name, "wb"); + if ( !fd_ ) { + errorString_ << "WvOut: could not create AIF file: " << name; return false; } @@ -400,19 +397,19 @@ bool WvOut :: setAifFile( const char *fileName ) hdr.aiff[3] = 'F'; hdr.comm[3] = 'M'; ssnd.ssnd[3] = 'D'; - hdr.num_chans = channels; - if ( dataType == STK_SINT8 ) + hdr.num_chans = channels_; + if ( dataType_ == STK_SINT8 ) hdr.sample_size = 8; - else if ( dataType == STK_SINT16 ) + else if ( dataType_ == STK_SINT16 ) hdr.sample_size = 16; - else if ( dataType == STK_SINT32 ) + else if ( dataType_ == STK_SINT32 ) hdr.sample_size = 32; - else if ( dataType == MY_FLOAT32 ) { + else if ( dataType_ == STK_FLOAT32 ) { hdr.aiff[3] = 'C'; hdr.sample_size = 32; hdr.comm_size = 24; } - else if ( dataType == MY_FLOAT64 ) { + else if ( dataType_ == STK_FLOAT64 ) { hdr.aiff[3] = 'C'; hdr.sample_size = 64; hdr.comm_size = 24; @@ -446,9 +443,9 @@ bool WvOut :: setAifFile( const char *fileName ) #endif *(unsigned long *)(hdr.srate+2) = (unsigned long) rate; - byteswap = false; + byteswap_ = false; #ifdef __LITTLE_ENDIAN__ - byteswap = true; + byteswap_ = true; swap32((unsigned char *)&hdr.form_size); swap32((unsigned char *)&hdr.comm_size); swap16((unsigned char *)&hdr.num_chans); @@ -459,93 +456,94 @@ bool WvOut :: setAifFile( const char *fileName ) #endif // The structure boundaries don't allow a single write of 54 bytes. - if ( fwrite(&hdr, 4, 5, fd) != 5 ) goto error; - if ( fwrite(&hdr.num_chans, 2, 1, fd) != 1 ) goto error; - if ( fwrite(&hdr.sample_frames, 4, 1, fd) != 1 ) goto error; - if ( fwrite(&hdr.sample_size, 2, 1, fd) != 1 ) goto error; - if ( fwrite(&hdr.srate, 10, 1, fd) != 1 ) goto error; + if ( fwrite(&hdr, 4, 5, fd_) != 5 ) goto error; + if ( fwrite(&hdr.num_chans, 2, 1, fd_) != 1 ) goto error; + if ( fwrite(&hdr.sample_frames, 4, 1, fd_) != 1 ) goto error; + if ( fwrite(&hdr.sample_size, 2, 1, fd_) != 1 ) goto error; + if ( fwrite(&hdr.srate, 10, 1, fd_) != 1 ) goto error; - if ( dataType == MY_FLOAT32 ) { + if ( dataType_ == STK_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; + if ( fwrite(&type, 4, 1, fd_) != 1 ) goto error; + if ( fwrite(&zeroes, 2, 1, fd_) != 1 ) goto error; } - else if ( dataType == MY_FLOAT64 ) { + else if ( dataType_ == STK_FLOAT64 ) { char type[4] = {'f','l','6','4'}; char zeroes[2] = { 0, 0 }; - if ( fwrite(&type, 4, 1, fd) != 1 ) goto error; - if ( fwrite(&zeroes, 2, 1, fd) != 1 ) goto error; + if ( fwrite(&type, 4, 1, fd_) != 1 ) goto error; + if ( fwrite(&zeroes, 2, 1, fd_) != 1 ) goto error; } - if ( fwrite(&ssnd, 4, 4, fd) != 4 ) goto error; + if ( fwrite(&ssnd, 4, 4, fd_) != 4 ) goto error; - printf("\nCreating AIF file: %s\n", name); + errorString_ << "WvOut: creating AIF file: " << name; + handleError( StkError::WARNING ); return true; error: - sprintf(msg, "WvOut: Could not write AIF header for file %s", name); + errorString_ << "WvOut: could not write AIF header for file: " << name; return false; } void WvOut :: closeAifFile( void ) { - unsigned long frames = (unsigned long) totalCount; + unsigned long frames = (unsigned long) totalCount_; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&frames); #endif - fseek(fd, 22, SEEK_SET); // jump to "COMM" sample_frames - fwrite(&frames, 4, 1, fd); + fseek(fd_, 22, SEEK_SET); // jump to "COMM" sample_frames + fwrite(&frames, 4, 1, fd_); int bytes_per_sample = 1; - if ( dataType == STK_SINT16 ) + if ( dataType_ == STK_SINT16 ) bytes_per_sample = 2; - else if ( dataType == STK_SINT32 || dataType == MY_FLOAT32 ) + else if ( dataType_ == STK_SINT32 || dataType_ == STK_FLOAT32 ) bytes_per_sample = 4; - else if ( dataType == MY_FLOAT64 ) + else if ( dataType_ == STK_FLOAT64 ) bytes_per_sample = 8; - unsigned long bytes = totalCount * bytes_per_sample * channels + 46; - if ( dataType == MY_FLOAT32 || dataType == MY_FLOAT64 ) bytes += 6; + unsigned long bytes = totalCount_ * bytes_per_sample * channels_ + 46; + if ( dataType_ == STK_FLOAT32 || dataType_ == STK_FLOAT64 ) bytes += 6; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&bytes); #endif - fseek(fd, 4, SEEK_SET); // jump to file size - fwrite(&bytes, 4, 1, fd); + fseek(fd_, 4, SEEK_SET); // jump to file size + fwrite(&bytes, 4, 1, fd_); - bytes = totalCount * bytes_per_sample * channels + 8; - if ( dataType == MY_FLOAT32 || dataType == MY_FLOAT64 ) bytes += 6; + bytes = totalCount_ * bytes_per_sample * channels_ + 8; + if ( dataType_ == STK_FLOAT32 || dataType_ == STK_FLOAT64 ) bytes += 6; #ifdef __LITTLE_ENDIAN__ swap32((unsigned char *)&bytes); #endif - if ( dataType == MY_FLOAT32 || dataType == MY_FLOAT64 ) - fseek(fd, 48, SEEK_SET); // jump to "SSND" chunk size + if ( dataType_ == STK_FLOAT32 || dataType_ == STK_FLOAT64 ) + fseek(fd_, 48, SEEK_SET); // jump to "SSND" chunk size else - fseek(fd, 42, SEEK_SET); // jump to "SSND" chunk size - fwrite(&bytes, 4, 1, fd); + fseek(fd_, 42, SEEK_SET); // jump to "SSND" chunk size + fwrite(&bytes, 4, 1, fd_); - fclose( fd ); + fclose( fd_ ); } bool WvOut :: setMatFile( const char *fileName ) { - char name[128]; - strncpy(name, fileName, 128); + char name[8192]; + strncpy(name, fileName, 8192); if ( strstr(name, ".mat") == NULL) strcat(name, ".mat"); - fd = fopen(name, "w+b"); - if ( !fd ) { - sprintf(msg, "WvOut: Could not create MAT file: %s", name); + fd_ = fopen(name, "w+b"); + if ( !fd_ ) { + errorString_ << "WvOut: could not create MAT file: " << name; return false; } - 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); + if ( dataType_ != STK_FLOAT64 ) { + dataType_ = STK_FLOAT64; + errorString_ << "WvOut: using 64-bit floating-point data format for file " << name << '.'; + handleError( StkError::WARNING ); } struct mathdr hdr; - strcpy(hdr.heading,"MATLAB 5.0 MAT-file, Generated using the Synthesis ToolKit in C++ (STK). By Perry R. Cook and Gary P. Scavone, 1995-2002."); + strcpy(hdr.heading,"MATLAB 5.0 MAT-file, Generated using the Synthesis ToolKit in C++ (STK). By Perry R. Cook and Gary P. Scavone, 1995-2004."); int i; for (i=strlen(hdr.heading);i<124;i++) hdr.heading[i] = ' '; @@ -569,7 +567,7 @@ bool WvOut :: setMatFile( const char *fileName ) // 2. Array Dimensions hdr.adf[6] = (SINT32) 5; // Matlab 32-bit signed integer data type value hdr.adf[7] = (SINT32) 8; // 8 bytes of data to follow (2D array) - hdr.adf[8] = (SINT32) channels; // This is the number of rows + hdr.adf[8] = (SINT32) channels_; // This is the number of rows hdr.adf[9] = (SINT32) 0; // This is the number of columns // 3. Array Name @@ -594,173 +592,293 @@ bool WvOut :: setMatFile( const char *fileName ) SINT32 headsize = 40; // Number of bytes in data element so far. // Write the fixed portion of the header - if ( fwrite(&hdr, 172, 1, fd) != 1 ) goto error; + if ( fwrite(&hdr, 172, 1, fd_) != 1 ) goto error; // Write MATLAB array name SINT32 tmp; if (namelength > 4) { - if ( fwrite(&namelength, 4, 1, fd) != 1) goto error; - if ( fwrite(arrayName, namelength, 1, fd) != 1 ) goto error; + if ( fwrite(&namelength, 4, 1, fd_) != 1) goto error; + if ( fwrite(arrayName, namelength, 1, fd_) != 1 ) goto error; tmp = (SINT32) ceil((float)namelength / 8); - if ( fseek(fd, tmp*8-namelength, SEEK_CUR) == -1 ) goto error; + if ( fseek(fd_, tmp*8-namelength, SEEK_CUR) == -1 ) goto error; headsize += tmp * 8; } else { // Compressed data element format - if ( fwrite(arrayName, namelength, 1, fd) != 1 ) goto error; + if ( fwrite(arrayName, namelength, 1, fd_) != 1 ) goto error; tmp = 4 - namelength; - if ( fseek(fd, tmp, SEEK_CUR) == -1 ) goto error; + if ( fseek(fd_, tmp, SEEK_CUR) == -1 ) goto error; } // Finish writing known header information tmp = 9; // Matlab IEEE 754 double data type - if ( fwrite(&tmp, 4, 1, fd) != 1 ) goto error; + if ( fwrite(&tmp, 4, 1, fd_) != 1 ) goto error; tmp = 0; // Size of real part subelement in bytes (8 per sample) - if ( fwrite(&tmp, 4, 1, fd) != 1 ) goto error; + if ( fwrite(&tmp, 4, 1, fd_) != 1 ) goto error; headsize += 8; // Total number of bytes in data element so far - if ( fseek(fd, 132, SEEK_SET) == -1 ) goto error; - if ( fwrite(&headsize, 4, 1, fd) != 1 ) goto error; // Write header size ... will update at end - if ( fseek(fd, 0, SEEK_END) == -1 ) goto error; + if ( fseek(fd_, 132, SEEK_SET) == -1 ) goto error; + if ( fwrite(&headsize, 4, 1, fd_) != 1 ) goto error; // Write header size ... will update at end + if ( fseek(fd_, 0, SEEK_END) == -1 ) goto error; - byteswap = false; + byteswap_ = false; printf("\nCreating MAT-file (%s) containing MATLAB array: %s\n", name, arrayName); + errorString_ << "WvOut: creating MAT-file (" << name << ") containing MATLAB array: " << arrayName; + handleError( StkError::WARNING ); + return true; error: - sprintf(msg, "WvOut: Could not write MAT-file header for file %s", name); + errorString_ << "WvOut: could not write MAT-file header for file " << name << '.'; return false; } void WvOut :: closeMatFile( void ) { - fseek(fd, 164, SEEK_SET); // jump to number of columns - fwrite(&totalCount, 4, 1, fd); + fseek(fd_, 164, SEEK_SET); // jump to number of columns + fwrite(&totalCount_, 4, 1, fd_); SINT32 headsize, temp; - fseek(fd, 132, SEEK_SET); // jump to header size - fread(&headsize, 4, 1, fd); + fseek(fd_, 132, SEEK_SET); // jump to header size + fread(&headsize, 4, 1, fd_); temp = headsize; - headsize += (SINT32) (totalCount * 8 * channels); - fseek(fd, 132, SEEK_SET); + headsize += (SINT32) (totalCount_ * 8 * channels_); + fseek(fd_, 132, SEEK_SET); // Write file size (minus some header info) - fwrite(&headsize, 4, 1, fd); + fwrite(&headsize, 4, 1, fd_); - fseek(fd, temp+132, SEEK_SET); // jumpt to data size (in bytes) - temp = totalCount * 8 * channels; - fwrite(&temp, 4, 1, fd); + fseek(fd_, temp+132, SEEK_SET); // jumpt to data size (in bytes) + temp = totalCount_ * 8 * channels_; + fwrite(&temp, 4, 1, fd_); - fclose(fd); + fclose(fd_); } unsigned long WvOut :: getFrames( void ) const { - return totalCount; + return totalCount_; } -MY_FLOAT WvOut :: getTime( void ) const +StkFloat WvOut :: getTime( void ) const { - return (MY_FLOAT) totalCount / Stk::sampleRate(); + return (StkFloat) totalCount_ / Stk::sampleRate(); } void WvOut :: writeData( unsigned long frames ) { - if ( dataType == STK_SINT8 ) { - if ( fileType == WVOUT_WAV ) { // 8-bit WAV data is unsigned! + if ( dataType_ == STK_SINT8 ) { + if ( fileType_ == WVOUT_WAV ) { // 8-bit WAV data is unsigned! unsigned char sample; - for ( unsigned long k=0; kclipTest( data_[k] ); + sample = (unsigned char) (data_[k] * 127.0 + 128.0); + if ( fwrite(&sample, 1, 1, fd_) != 1 ) goto error; } } else { signed char sample; - for ( unsigned long k=0; kclipTest( data_[k] ); + sample = (signed char) (data_[k] * 127.0); + //sample = ((signed char) (( data_[k] + 1.0 ) * 127.5 + 0.5)) - 128; + if ( fwrite(&sample, 1, 1, fd_) != 1 ) goto error; } } } - else if ( dataType == STK_SINT16 ) { + else if ( dataType_ == STK_SINT16 ) { SINT16 sample; - for ( unsigned long k=0; kclipTest( data_[k] ); + sample = (SINT16) (data_[k] * 32767.0); + //sample = ((SINT16) (( data_[k] + 1.0 ) * 32767.5 + 0.5)) - 32768; + if ( byteswap_ ) swap16( (unsigned char *)&sample ); + if ( fwrite(&sample, 2, 1, fd_) != 1 ) goto error; } } - else if ( dataType == STK_SINT32 ) { + else if ( dataType_ == STK_SINT32 ) { SINT32 sample; - for ( unsigned long k=0; kclipTest( data_[k] ); + sample = (SINT32) (data_[k] * 2147483647.0); + //sample = ((SINT32) (( data_[k] + 1.0 ) * 2147483647.5 + 0.5)) - 2147483648; + if ( byteswap_ ) swap32( (unsigned char *)&sample ); + if ( fwrite(&sample, 4, 1, fd_) != 1 ) goto error; } } - else if ( dataType == MY_FLOAT32 ) { + else if ( dataType_ == STK_FLOAT32 ) { FLOAT32 sample; - for ( unsigned long k=0; kclipTest( data_[k] ); + sample = (FLOAT32) (data_[k]); + if ( byteswap_ ) swap32( (unsigned char *)&sample ); + if ( fwrite(&sample, 4, 1, fd_) != 1 ) goto error; } } - else if ( dataType == MY_FLOAT64 ) { + else if ( dataType_ == STK_FLOAT64 ) { FLOAT64 sample; - for ( unsigned long k=0; kclipTest( data_[k] ); + sample = (FLOAT64) (data_[k]); + if ( byteswap_ ) swap64( (unsigned char *)&sample ); + if ( fwrite(&sample, 8, 1, fd_) != 1 ) goto error; } } return; error: - sprintf(msg, "WvOut: Error writing data to file."); - handleError(msg, StkError::FILE_ERROR); + errorString_ << "WvOut::writeData: error writing data to file!"; + handleError( StkError::FILE_ERROR ); } -void WvOut :: tick(const MY_FLOAT sample) +void WvOut :: clipTest( StkFloat& sample ) { - if ( !fd ) return; + register bool clip = false; + if ( sample > 1.0 ) { + sample = 1.0; + clip = true; + } + else if ( sample < -1.0 ) { + sample = -1.0; + clip = true; + } - for ( unsigned int j=0; jwriteData( BUFFER_SIZE ); + counter_ = 0; + } +} + +void WvOut :: tick( const StkFloat *vector, unsigned int vectorSize ) +{ + if ( !fd_ ) { + errorString_ << "WvOut::tickFrame(): no file open!"; + handleError( StkError::WARNING ); + return; + } for (unsigned int i=0; i channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( frames.channels() == 1 ) { + for ( unsigned int i=0; iwriteData( BUFFER_SIZE ); + counter_ = 0; + } + } +} + +void WvOut :: tickFrame( const StkFrames& frames ) +{ + if ( !fd_ ) { + errorString_ << "WvOut::tickFrame(): no file open!"; + handleError( StkError::WARNING ); + return; + } + + if ( channels_ != frames.channels() ) { + errorString_ << "WvOut::tickFrame(): incompatible channel value in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned int j; + if ( channels_ == 1 || frames.interleaved() ) { + unsigned long iFrames = 0, iData = counter_; + for ( unsigned int i=0; iwriteData( BUFFER_SIZE ); + counter_ = 0; + } + } + } + else { + unsigned int hop = frames.frames(); + unsigned long iData = counter_; + for ( unsigned int i=0; iwriteData( BUFFER_SIZE ); + counter_ = 0; + } } } } diff --git a/src/asio/asiolist.cpp b/src/asio/asiolist.cpp index 5a62f5b..b08fe9e 100644 --- a/src/asio/asiolist.cpp +++ b/src/asio/asiolist.cpp @@ -19,7 +19,7 @@ static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize) DWORD index; OFSTRUCT ofs; HFILE hfile; - BOOL found = FALSE; + BOOL found = false; CharLowerBuff(clsidstr,strlen(clsidstr)); if ((cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) { @@ -44,7 +44,7 @@ static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize) } RegCloseKey(hksub); } - found = TRUE; // break out + found = true; // break out } } } @@ -134,7 +134,7 @@ AsioDriverList::AsioDriverList () LPASIODRVSTRUCT pdl; LONG cr; DWORD index = 0; - BOOL fin = FALSE; + BOOL fin = false; numdrv = 0; lpdrvlist = 0; @@ -144,7 +144,7 @@ AsioDriverList::AsioDriverList () if ((cr = RegEnumKey(hkEnum,index++,(LPTSTR)keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) { lpdrvlist = newDrvStruct (hkEnum,keyname,0,lpdrvlist); } - else fin = TRUE; + else fin = true; } if (hkEnum) RegCloseKey(hkEnum);