diff --git a/INSTALL b/INSTALL index 84082f9..e273e92 100644 --- a/INSTALL +++ b/INSTALL @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. The Synthesis ToolKit in C++ can be used in a variety of ways, depending on your particular needs. Some people just choose the classes they need for a particular project and copy those to their project directory. Others like to compile and link to a library of object files. STK was not designed with one particular style of use in mind. diff --git a/README b/README index b050547..74e27f8 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. This distribution of the Synthesis ToolKit in C++ (STK) contains the following: @@ -146,7 +146,7 @@ LICENSE: STK WWW site: http://ccrma.stanford.edu/software/stk/ The Synthesis ToolKit in C++ (STK) -Copyright (c) 1995-2007 Perry R. Cook and Gary P. Scavone +Copyright (c) 1995-2009 Perry R. Cook and Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/bin/treesed b/bin/treesed new file mode 100755 index 0000000..06ff4dd --- /dev/null +++ b/bin/treesed @@ -0,0 +1,189 @@ +#!/usr/bin/perl + +# treesed +# Written January 1996 by Rick Jansen (rick@sara.nl) +# URL: http://www.sara.nl/rick + +# usage: treesed pattern1 pattern2 -tree +# treesed pattern1 pattern2 -files file1 file2 ... + +# example: treesed href HREF -files *.html + +# Treesed searches for pattern1 and replaces pattern1 by pattern2 +# if pattern2 supplied. If only pattern1 given treesed just searches. +# Treesed will search in all files and subdirectories of the current +# directory + + + +#-------------------------------------------------------- +# Parameters + +$DoEdit=0; +$search_pattern = $ARGV[0]; +$search_pattern =~ s/(\W)/\\$1/g; # escape regexp chars +shift; + +while ($#ARGV >= 0) { + + if ($ARGV[0] eq '-files') { + @temp_ls = @ARGV[1 .. $#ARGV]; + # Get list of files, skip dirs + foreach $file (@ARGV[1 .. $#ARGV]) { + if (-f $file) { + push(@ls, $file); + } + } + last; + } + elsif ($ARGV[0] eq '-tree') { + &Get_LS; + last; + } + + if (! -f $ARGV[0]) { + if (defined($replacement_pattern)) { + print "usage: treesed pattern1 -tree/-files \n"; + exit(1); + } + + $replacement_pattern = $ARGV[0]; + #$replacement_pattern =~ s/(\W)/\\$1/g; # escape regexp chars + $DoEdit=1; + shift; + } + +} + +# No files? +if ($#ls < 0) { + print "xx No input files\n"; + exit(1); +} + +print "search_pattern: $search_pattern\n"; +print "replacement_pattern: $replacement_pattern\n"; +if ($DoEdit) { + print "\n** EDIT MODE!\n\n"; } +else { + print "\n** Search mode\n\n"; +} + +#foreach $file (@ls) { +# print "$file \n"; +#} + + +#-------------------------------------------------------- +# Search list of files for pattern + +$linepos=0; + +$| = 1; # Force flush after every write +foreach $file (@ls) { + #print "$file\n"; + print '.'; + $linepos++; + if ($linepos > 50) { + $linepos=0; + print "\n"; + } + + if (!open(FILE, $file)) { + print "\nCould not open $file\n"; + next; + } + + $Found = 0; + $Count = 0; + $lineno = 0; + @lines = (); + while () { + $lineno++; + if (/$search_pattern/i) { + #print; + $Count++; + $Found = 1; + push(@lines, $lineno); + } + } + close(FILE); + if ($Found) { + print "\n$file: $Count lines on: @lines\n"; + } + + if ($Found && $DoEdit) { &Edit($file); } + +} +$| = 0; +print "\n"; + + +exit(0); + + +#-------------------------------------------------------- +# Edit file + +sub Edit { + +# Replace $ARGV[0] with $ARGV[1] in $file + +local($file) = @_; +local($bakfile) = $file.'.'.$$; + +# First create backup +open(FILE, $file) || die "Could not open $file for read\n"; +open(BAKFILE, ">$bakfile") || die "Could not open $bakfile for backup\n"; +while () { + print BAKFILE; +} +close(BAKFILE); +close(FILE); + +# Now replace $ARGV[0] by $ARGV[1] in the backupfile, +# result into $file +open(BAKFILE, $bakfile) || die "Could not open $bakfile for read\n"; +open(FILE,">$file") || die "Could not open $file for write\n"; +$Count=0; +while () { + if (/$search_pattern/i) { $Count++; } + s/$search_pattern/$replacement_pattern/gi; + print FILE; +} +close(BAKFILE); +close(FILE); + +print +"\nReplaced $search_pattern by $replacement_pattern on $Count lines in $file\n"; + +} #sub Edit + +#-------------------------------------------------------- + +sub Get_LS { + +# Get a list of full path names into array @ls + +local(@localls)=`ls -R1`; +local($item,$Dir); + +#print "localls: @localls\n"; +$Dir=''; +foreach $item (@localls) { + #print "$item\n"; + if ($item =~ /:$/) { + $Dir=$item; + chop($Dir); + $Dir =~ s/:$/\//; + } + else { + chop($item); + $item = $Dir.$item; + if ($item !~ /^\s*$/) { push(@ls, $item); } + } +} +@localls=(); + +} # sub Get_LS + diff --git a/config.guess b/config/config.guess similarity index 100% rename from config.guess rename to config/config.guess diff --git a/config.sub b/config/config.sub similarity index 100% rename from config.sub rename to config/config.sub diff --git a/install.sh b/config/install.sh similarity index 100% rename from install.sh rename to config/install.sh diff --git a/configure.ac b/configure.ac index 468b3ec..2da42ec 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(STK, 4.3, gary@music.mcgill.ca, stk) +AC_INIT(STK, 4.4, gary@music.mcgill.ca, stk) +AC_CONFIG_AUX_DIR(config) AC_CONFIG_SRCDIR(src/Stk.cpp) AC_CONFIG_FILES(src/Makefile projects/demo/Makefile projects/effects/Makefile projects/ragamatic/Makefile projects/examples/Makefile projects/examples/libMakefile) @@ -7,9 +8,12 @@ AC_CONFIG_FILES(src/Makefile projects/demo/Makefile projects/effects/Makefile pr AC_SUBST( GXX, ["no"] ) # Checks for programs. -AC_PROG_CC -AC_PROG_CXX(g++ CC c++ cxx) AC_PROG_CXX +AC_PROG_RANLIB +AC_PATH_PROG(AR, ar, no) +if [[ $AR = "no" ]] ; then + AC_MSG_ERROR("Could not find ar - needed to create a library"); +fi # Checks for header files. AC_HEADER_STDC @@ -42,121 +46,115 @@ AC_MSG_RESULT($realtime) # Check for math library AC_CHECK_LIB(m, cos, , AC_MSG_ERROR(math library is needed!)) -# Checks for functions -if test $realtime = yes; then - AC_CHECK_FUNCS(select socket) - AC_CHECK_FUNC(gettimeofday, [cflags=$cflags" -DHAVE_GETTIMEOFDAY"], ) -fi - # Check for debug AC_MSG_CHECKING(whether to compile debug version) AC_ARG_ENABLE(debug, [ --enable-debug = enable various debug output], - [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)]) + [AC_SUBST( cppflag, ["-D_STK_DEBUG_ -D__RTAUDIO_DEBUG__ -D__RTMIDI_DEBUG__"] ) AC_SUBST( cxxflag, ["-g"] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)], + [AC_SUBST( cppflag, [] ) AC_SUBST( cxxflag, [-O3] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)]) + +# Checks for functions +if test $realtime = yes; then + AC_CHECK_FUNCS(select socket) + AC_CHECK_FUNC(gettimeofday, [cppflag="$cppflag -DHAVE_GETTIMEOFDAY"], ) +fi + +# For -I and -D flags +CPPFLAGS="$CPPFLAGS $cppflag" + +# For debugging and optimization ... overwrite default because it has both -g and -O2 +CXXFLAGS="$cxxflag" # Check compiler and use -Wall if gnu. -if test $GXX = "yes" ; then - AC_SUBST( warn, ["-Wall -g -D__GXX__"] ) +if [test $GXX = "yes" ;] then + AC_SUBST( cxxflag, [-Wall] ) fi +CXXFLAGS="$CXXFLAGS $cxxflag" + if test $realtime = yes; then # Checks for package options and external software AC_CANONICAL_HOST AC_MSG_CHECKING(for audio API) case $host in *-*-linux*) - AC_SUBST( sound_api, [_NO_API_] ) + AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (mac and linux only)], [ + api="$api -D__UNIX_JACK__" + AC_MSG_RESULT(using JACK) + AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!)) + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(Jack support also requires the asound library!))], ) + + # Look for ALSA flag + AC_ARG_WITH(alsa, [ --with-alsa = choose native ALSA API support (linux only)], [ + api="$api -D__LINUX_ALSA__" + AC_MSG_RESULT(using ALSA) + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))], ) + + # Look for OSS flag + AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [ + api="$api -D__LINUX_OSS__" + AC_MSG_RESULT(using OSS)], ) + + # If no audio api flags specified, use ALSA + if [test "$api" == "";] then + AC_MSG_RESULT(using ALSA) + AC_SUBST( api, [-D__LINUX_ALSA__] ) + AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!)) + fi # 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__UNIX_JACK__] ) AC_MSG_RESULT(using JACK)] , ) - if [test $sound_api = -D__UNIX_JACK__;] then - TEMP_LIBS=$LIBS - AC_CHECK_LIB(jack, jack_client_new, , AC_MSG_ERROR(JACK support requires the jack library!)) - LIBS="`pkg-config --cflags --libs jack` $TEMP_LIBS -lasound" - audio_apis="-D__UNIX_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 - audio_apis="-D__LINUX_ALSA__ $audio_apis" - fi - - # Look for OSS flag - AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [AC_SUBST( sound_api, [-D__LINUX_OSS__] ) AC_MSG_RESULT(using OSS)], ) - if test $sound_api = -D__LINUX_OSS__; then - audio_apis="-D__LINUX_OSS__ $audio_apis" - fi - - # If no audio api flags specified, use ALSA - if [test $sound_api = _NO_API_;] then - AC_MSG_RESULT(using ALSA) - audio_apis="-D__LINUX_ALSA__ $audio_apis" - fi + api="$api -D__LINUX_ALSASEQ__" AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(realtime support requires the pthread library!)) ;; *-apple*) - AC_SUBST( sound_api, [_NO_API_] ) - - # Look for JACK flag - AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (unix only)], [AC_SUBST( sound_api, [-D__UNIX_JACK__] ) AC_MSG_RESULT(using JACK)], ) - if [test $sound_api = -D__UNIX_JACK__;] then - AC_CHECK_LIB(jack, jack_client_new, , AC_MSG_ERROR(JACK support requires the jack library!)) - audio_apis="-D__UNIX_JACK__" - fi + AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (unix only)], [ + api="$api -D__UNIX_JACK__" + AC_MSG_RESULT(using JACK) + AC_CHECK_LIB(jack, jack_client_new, , AC_MSG_ERROR(JACK support requires the jack library!))], ) # Look for Core flag - AC_ARG_WITH(core, [ --with-core = choose CoreAudio API support (mac only)], [AC_SUBST( sound_api, [-D__MACOSX_CORE__] ) AC_MSG_RESULT(using CoreAudio)], ) - if test $sound_api = -D__MACOSX_CORE__; then - AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] ) - AC_SUBST( frameworks, ["-framework CoreAudio -framework CoreFoundation -framework CoreMidi"] ) - audio_apis="-D__MACOSX_CORE__ $audio_apis" - fi + AC_ARG_WITH(core, [ --with-core = choose CoreAudio API support (mac only)], [ + api="$api -D__MACOSX_CORE__" + AC_MSG_RESULT(using CoreAudio) + AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] ) + LIBS="$LIBS -framework CoreAudio -framework CoreFoundation -framework CoreMidi" ], ) # If no audio api flags specified, use CoreAudio - if [test $sound_api = _NO_API_;] then - AC_SUBST( sound_api, [-D__MACOSX_CORE__] ) + if [test "$api" == ""; ] then + AC_SUBST( api, [-D__MACOSX_CORE__] ) AC_MSG_RESULT(using CoreAudio) AC_CHECK_HEADER(CoreAudio/CoreAudio.h, - [AC_SUBST( audio_apis, [-D__MACOSX_CORE__] )], - [AC_MSG_ERROR(CoreAudio header files not found!)] ) - AC_SUBST( frameworks, ["-framework CoreAudio -framework CoreFoundation -framework CoreMidi"] ) + [], + [AC_MSG_ERROR(CoreAudio header files not found!)] ) + AC_SUBST( LIBS, ["-framework CoreAudio -framework CoreFoundation -framework CoreMidi"] ) fi - AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(realtime support requires the pthread library!)) + AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!)) ;; *-mingw32*) - AC_SUBST( sound_api, [_NO_API_] ) - AC_ARG_WITH(asio, [ --with-asio = choose ASIO API support (windoze only)], [AC_SUBST( sound_api, [-D__WINDOWS_ASIO__] ) AC_MSG_RESULT(using ASIO)], ) - if [test $sound_api = -D__WINDOWS_ASIO__;] then - audio_apis="-D__WINDOWS_ASIO__" - AC_SUBST( objects, ["asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o"] ) - fi + AC_ARG_WITH(asio, [ --with-asio = choose ASIO API support (windoze only)], [ + api="$api -D__WINDOWS_ASIO__" + AC_MSG_RESULT(using ASIO) + AC_SUBST( objects, ["asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o"] ) ], ) # Look for DirectSound flag - AC_ARG_WITH(ds, [ --with-ds = choose DirectSound API support (windoze only)], [AC_SUBST( sound_api, [-D__WINDOWS_DS__] ) AC_MSG_RESULT(using DirectSound)], ) - if test $sound_api = -D__WINDOWS_DS__; then - audio_apis="-D__WINDOWS_DS__ $audio_apis" - LIBS="-ldsound $LIBS" - fi + AC_ARG_WITH(ds, [ --with-ds = choose DirectSound API support (windoze only)], [ + api="$api -D__WINDOWS_DS__" + AC_MSG_RESULT(using DirectSound) + LIBS="-ldsound -lwinmm $LIBS" ], ) # If no audio api flags specified, use DirectSound - if [test $sound_api = _NO_API_;] then - AC_SUBST( sound_api, [-D__WINDOWS_DS__] ) + if [test "$api" == "";] then + AC_SUBST( api, [-D__WINDOWS_DS__] ) AC_MSG_RESULT(using DirectSound) - audio_apis="-D__WINDOWS_DS__" - LIBS="-ldsound $LIBS" + LIBS="-ldsound -lwinmm $LIBS" fi - audio_apis="-D__WINDOWS_MM__ $audio_apis" + api="$api -D__WINDOWS_MM__" LIBS="-lole32 -lwinmm -lWsock32 $LIBS" ;; @@ -165,10 +163,8 @@ if test $realtime = yes; then AC_MSG_ERROR(Unknown system type for realtime support ... try --disable-realtime argument!) ;; esac + +CPPFLAGS="$CPPFLAGS $api" fi -# Checks for library functions. -AC_PROG_GCC_TRADITIONAL -AC_CHECK_FUNCS(strstr) - AC_OUTPUT diff --git a/doc/README-Linux.txt b/doc/README-Linux.txt index 89da290..0819a33 100644 --- a/doc/README-Linux.txt +++ b/doc/README-Linux.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. Please read the file README and INSTALL for more general STK information. diff --git a/doc/README-MacOSX.txt b/doc/README-MacOSX.txt index e07de5e..c78ecb0 100644 --- a/doc/README-MacOSX.txt +++ b/doc/README-MacOSX.txt @@ -1,12 +1,12 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. Please read the file README and INSTALL for more general STK information. -The default realtime support for Macintosh OS X uses the CoreAudio HAL API and is specified during compilation using the __MACOSX_CORE__ preprocessor definition. There is also support for the JACK low-latency audio server using the __UNIX_JACK__ preprocessor definition. +The default realtime support for Macintosh OS X uses the CoreAudio HAL API and is specified during compilation using the __MACOSX_CORE__ preprocessor definition. There is also support for the JACK audio server using the __UNIX_JACK__ preprocessor definition. -It is necessary to install the OS X developer kit in order to compile STK. STK was successfully tested on OS X versions 10.4. +It is necessary to install the OS X developer kit in order to compile STK. STK was successfully tested on OS X versions 10.5. 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. diff --git a/doc/README-NeXT.txt b/doc/README-NeXT.txt index 16c651e..e9f2c7f 100644 --- a/doc/README-NeXT.txt +++ b/doc/README-NeXT.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. Please read the file README and INSTALL for more general STK information. diff --git a/doc/README-SGI.txt b/doc/README-SGI.txt index 7ec2ad7..2031c64 100644 --- a/doc/README-SGI.txt +++ b/doc/README-SGI.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. Please read the file README and INSTALL for more general STK information. diff --git a/doc/README-Win.txt b/doc/README-Win.txt index bbfa781..442835c 100644 --- a/doc/README-Win.txt +++ b/doc/README-Win.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. Please read the file README for more general STK information. diff --git a/doc/ReleaseNotes.txt b/doc/ReleaseNotes.txt index 55ceef0..b658a0c 100644 --- a/doc/ReleaseNotes.txt +++ b/doc/ReleaseNotes.txt @@ -1,6 +1,20 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. + +v4.4: (30 April 2009) +- all classes embedded in the "stk" namespace (except RtAudio, RtMidi, and RtError) +- class WaveLoop renamed FileLoop +- significant efficiency improvements via code restructuring and inlining +- some class source (.cpp) files deleted as part of inlining (Generator, Filter, Function, WvIn, WvOut, Effect, Instrmnt, BowTable, ReedTable, JetTable, Vector3D) +- updates to RtAudio and RtMidi +- previous "tickFrame()" functions renamed "tick" for more consistent API +- more consistent and scalable approach to multichannel data and computations +- multichannel support added to Granulate class +- Filter class made abstract. New Iir and Fir classes made for non-order-specific filtering. +- new TapDelay class +- SubNoise class deleted (same as sub-sampled "ticking" of Noise class) + v4.3.1: (7 December 2007) - further headerless file support in FileRead @@ -14,6 +28,7 @@ v4.3.0: (13 August 2007) - an official MIT-like license - new functionality to automatically update class data when the STK sample rate changes (partly implemented) - updates for new RtAudio version 4.0 +- removed RtDuplex class, users should use RtAudio directly with a callback function - bug fix in interpolate() function in Stk.h for non-interleaved data - fixes / improvements to the Granulate class - fix in Whistle when doing animation diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 7916708..4075272 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -1,225 +1,1504 @@ -# Doxyfile 1.4.4 +# Doxyfile 1.5.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + PROJECT_NAME = STK -PROJECT_NUMBER = 4.3.1 -OUTPUT_DIRECTORY = . + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 4.4.0 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../html/ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. + OUTPUT_LANGUAGE = English -USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + MULTILINE_CPP_IS_BRIEF = NO -DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + INHERIT_DOCS = NO -DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + TAB_SIZE = 9 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + HIDE_IN_BODY_DOCS = YES + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + WARN_LOGFILE = + #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + INPUT = . \ ../../include + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + FILE_PATTERNS = *.txt \ *.msg \ *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + EXCLUDE = ../../src/asio + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + EXAMPLE_PATH = ../../projects/examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + FILTER_SOURCE_FILES = NO + #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + VERBATIM_HEADERS = YES + #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + IGNORE_PREFIX = + #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + HTML_OUTPUT = ../html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + HTML_HEADER = header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + HTML_FOOTER = footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + DISABLE_INDEX = YES + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + LATEX_HEADER = header.tex + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + LATEX_HIDE_INDICES = NO + #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + RTF_EXTENSIONS_FILE = + #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + MAN_LINKS = NO + #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + XML_PROGRAMLISTING = YES + #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + GENERATE_AUTOGEN_DEF = NO + #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + PERLMOD_MAKEVAR_PREFIX = + #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + ENABLE_PREPROCESSING = NO + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + SKIP_FUNCTION_MACROS = YES + #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + PERL_PATH = /usr/bin/perl + #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + DOT_PATH = /sw/bin + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + DOTFILE_DIRS = -MAX_DOT_GRAPH_WIDTH = 1024 -MAX_DOT_GRAPH_HEIGHT = 1024 + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + DOT_CLEANUP = YES + #--------------------------------------------------------------------------- -# Configuration::additions related to the search engine +# Options related to the search engine #--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + SEARCHENGINE = NO diff --git a/doc/doxygen/compile.txt b/doc/doxygen/compile.txt index ce5c05b..95ae3be 100644 --- a/doc/doxygen/compile.txt +++ b/doc/doxygen/compile.txt @@ -56,7 +56,7 @@ STK compiles with realtime support on the following flavors of the Unix operatin Macintosh OS X CoreAudio __MACOSX_CORE__ - pthread, CoreAudio, CoreMIDI, CoreFoundation + pthread, CoreAudio, CoreMidi, CoreFoundation @@ -69,13 +69,11 @@ a particular program into a project directory. Taking the 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, FileRead, FileWrite, WvIn, FileWvIn, WaveLoop, -WvOut, and FileWvOut. 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 FileRead.cpp FileWrite.cpp WvIn.cpp FileWvIn.cpp WaveLoop.cpp WvOut.cpp FileWvOut.cpp sineosc.cpp -\endcode +for the classes Stk, FileRead, FileWrite, FileWvIn, FileLoop, and +FileWvOut. 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 FileRead.cpp FileWrite.cpp FileWvIn.cpp FileLoop.cpp FileWvOut.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. @@ -95,7 +93,7 @@ g++ -Wall -D__LITTLE_ENDIAN__ -I/usr/include/stk -o sineosc sineosc.cpp -lstk With the header files in a standard search path, it is possible to modify the \#include statements in the sineosc.cpp program as follows: \code -#include "stk/WaveLoop.h" +#include "stk/FileLoop.h" #include "stk/FileWvOut.h" \endcode @@ -117,7 +115,7 @@ 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, FileRead, FileWrite, WvIn, FileWvIn, -WaveLoop, WvOut, and FileWvOut class files, as well as +FileLoop, WvOut, and FileWvOut class files, as well as sineosc.cpp, and make sure the sinewave.raw file is in the subdirectory rawwaves. diff --git a/doc/doxygen/control.txt b/doc/doxygen/control.txt index 5afa72f..6408655 100644 --- a/doc/doxygen/control.txt +++ b/doc/doxygen/control.txt @@ -19,9 +19,20 @@ NoteOff 1.000000 2 69.0 64.0 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. 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. +The class stk::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 stk::Skini::Message types (MIDI +messages are converted to the stk::Skini:Message format). The user +then uses the stk::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 +stk::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 SKINI messages read from a SKINI file. +In the following example, we'll modify the bethree.cpp program from the previous tutorial chapter and incorporate a stk::Messager class to allow control via SKINI messages read from a SKINI file. \include controlbee.cpp @@ -37,7 +48,7 @@ controlbee scores/bookert.ski 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 pipe, socket or MIDI connections. The Messager class provides Messager::startStdInput(), Messager::startSocketInput(), and Messager::startMidiInput() functions for this purpose. +This example could also be easily extended to accept "realtime" control input messages via pipe, socket or MIDI connections. The stk::Messager class provides stk::Messager::startStdInput(), stk::Messager::startSocketInput(), and stk::Messager::startMidiInput() functions for this purpose. [Main tutorial page]   [Next tutorial] */ diff --git a/doc/doxygen/crealtime.txt b/doc/doxygen/crealtime.txt index e0aa5aa..72d4e94 100644 --- a/doc/doxygen/crealtime.txt +++ b/doc/doxygen/crealtime.txt @@ -11,8 +11,8 @@ 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). -The previous section described the use of the RtWvOut class for -realtime audio output. The RtWvOut::tick() function writes data to a +The previous section described the use of the stk::RtWvOut class for +realtime audio output. The stk::RtWvOut::tick() function writes data to a large ring-buffer, from which data is periodically written to the computer's audio hardware via an underlying callback routine. diff --git a/doc/doxygen/download.txt b/doc/doxygen/download.txt index f50289a..d15e6c6 100644 --- a/doc/doxygen/download.txt +++ b/doc/doxygen/download.txt @@ -1,16 +1,31 @@ /*! \page download Download, Release Notes, and Bug Fixes -\section down Download Version 4.3.1 (7 December 2007): +\section down Download Version 4.4.0 (30 April 2009): \section notes Release Notes: -\subsection v4dot3dot0 Version 4.3.1 +\subsection v4dot4dot0 Version 4.4.0 + +
    +
  • All classes embedded in the "stk" namespace (except RtAudio, RtMidi, and RtError).
  • +
  • Class WaveLoop renamed FileLoop.
  • +
  • Significant efficiency improvements via code restructuring and inlining.
  • +
  • Some class source (.cpp) files deleted as part of inlining (Generator, Filter, Function, WvIn, WvOut, Effect, Instrmnt, BowTable, ReedTable, JetTable, Vector3D).
  • +
  • Updates to RtAudio and RtMidi.
  • +
  • Previous "tickFrame()" functions renamed "tick" for more consistent API.
  • +
  • More consistent and scalable approach to multichannel data and computations.
  • +
  • Multichannel support added to Granulate class.
  • +
  • Filter class made abstract. New Iir and Fir classes made for non-order-specific filtering.
  • +
  • New TapDelay class.
  • +
  • SubNoise class deleted (same as sub-sampled "ticking" of Noise class).
  • +
+ +\subsection v4dot3dot1 Version 4.3.1
  • Further headerless file support in FileRead.
  • @@ -20,13 +35,13 @@
  • Changes to channel assignment in demo.cpp.
- \subsection v4dot3dot0 Version 4.3.0
  • An official MIT-like license.
  • New functionality to automatically update class data when the STK sample rate changes (partly implemented).
  • Updates for new RtAudio version 4.0.
  • +
  • Removed RtDuplex class, users should use RtAudio directly with a callback function.
  • Bug fix in interpolate() function in Stk.h for non-interleaved data.
  • Fixes / improvements to the Granulate class.
  • Fix in Whistle when doing animation.
  • diff --git a/doc/doxygen/faq.txt b/doc/doxygen/faq.txt index cbb1686..a6832b4 100644 --- a/doc/doxygen/faq.txt +++ b/doc/doxygen/faq.txt @@ -2,8 +2,6 @@ - \ref license - \ref filerate -- \ref computesample -- \ref tickframe - \ref endianness - \ref xwindows @@ -26,7 +24,7 @@ work with any standard C++ compiler. STK WWW site: http://ccrma.stanford.edu/software/stk/ The Synthesis ToolKit in C++ (STK) -Copyright (c) 1995-2007 Perry R. Cook and Gary P. Scavone +Copyright (c) 1995-2009 Perry R. Cook and Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -65,21 +63,13 @@ Stk::setSampleRate( sampleRate ); // set a new STK sample rate based o With version 4.3 and higher of STK, the FileWvIn class will be notified of a sample rate change and it will automatically adjust its read rate accordingly. Previous versions of STK did not perform this change and thus, the read rate could end up being incorrect. If you do not want FileWvIn to perform this automatic adjustment, you can call the \c ignoreSampleRateChange() function for a given class instance. -\section tickframe What is the difference between the tick() and tickFrame() functions? - -\e tickFrame() functions are provided in classes that can handle multi-channel data. A sample frame of audio data represents a single "slice" in time across many audio channels. The WvIn and WvOut subclasses are the primary classes in STK that currently implement the \e tickFrame() functions. \e tick() functions are used for monophonic classes. Note, however, that the WvIn and WvOut classes also implement \e tick() functions though their behavior is dependent on the number of channels you are working with. For example, if using the FileWvIn class with a monophonic soundfile, then there is no difference between the \e tick() and \e tickFrame() functions (aside from the format of their arguments). But if you have a multi-channel file open, then the single value returned from the tick() function is an average of all the samples in the multi-channel sample frame. - -\section computesample Hey, why was the tick() function replaced by computeSample() in various STK classes? - -C++ doesn't like overloaded virtual functions. All STK classes that implement a single-sample \e tick() function also provide an overloaded version that takes an StkFrames argument (for vectorized computations). Further, many STK classes inherit from abstract base classes (Instrmnt, Generator, ...) and it is most convenient to define functionality common to all subclasses (like the \e tick() function that takes an StkFrames argument) in only the base class. So, to get around the overloaded virtual function problem, STK now uses the \e computeSample() function as a non-overloaded virtual function that is implemented in all subclasses (it essentially replaces the \e tick() function). Note, however, that the overloaded \e tick() functions are still available to the user ... they are implemented in the base classes. - \section endianness Why does the sound I generated with STK sound like *&#@!? If the resultant sound generated by an STK program sounds like noise (and you're not doing an MLS experiment), the problem is likely related to the byte "endianness" of your computer. By default, STK assumes "big endian" byte order. If you are working with STK classes on a PC (Windows or Linux), you \e must define the __LITTLE_ENDIAN__ preprocessor definition \e before compiling. If after reading this you realize you need to make this change, do not forget to recompile all STK classes from scratch. \section xwindows Why do I get a Tk display error message? -The following error will be printed to your terminal window if you attempt to start an STK tcl/tk interface without the X Server first running: +The following error may be printed to your terminal window (depending on the version of the tcl/tk interpreter you are running) if you attempt to start an STK tcl/tk interface without the X Server first running: \code Application initialization failed: this isn't a Tk applicationcouldn't connect to display ":0.0" diff --git a/doc/doxygen/filtering.txt b/doc/doxygen/filtering.txt index 29a93b7..67d689c 100644 --- a/doc/doxygen/filtering.txt +++ b/doc/doxygen/filtering.txt @@ -1,9 +1,10 @@ /*! \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. +In this section, we demonstrate the use of a few of the STK filter classes. The stk::Iir class provides functionality to implement a generalized infinite impulse response (IIR) digital filter, similar to the \c filter function in Matlab. In this example, we create an stk::Iir instance and initialize it with specific numerator and denominator coefficients. We then compute its impulse response for 20 samples. \code -#include "Filter.h" +#include "Iir.h" +using namespace stk; int main() { @@ -16,7 +17,7 @@ int main() denominator.push_back( 0.3 ); denominator.push_back( -0.5 ); - Filter filter( numerator, denominator ); + Iir filter( numerator, denominator ); filter.tick( output ); for ( unsigned int i=0; ivector, a container object provided by the C++ Standard Library. +where "b" values are numerator coefficients and "a" values are denominator coefficients. Note that if the first denominator coefficient is not 1.0, the Iir class automatically normalizes all filter coefficients by that value. The coefficient values are passed to the Iir class via a C++ vector, 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. +Most STK classes use more specific types of digital filters, such as the stk::OneZero, stk::OnePole, stk::TwoPole, or stk::BiQuad varieties. These classes inherit from the stk::Filter abstract base 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. +The STK stk::BiQuad and stk::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 stk::Noise generator. \code #include "BiQuad.h" #include "Noise.h" +using namespace stk; int main() { @@ -61,11 +63,12 @@ int main() } \endcode -By passing a boolian value of \c true as the third argument to the BiQuad::setResonance() function, the filter coefficients are automatically scaled to achieve unity gain at the resonance peak frequency. The previous code could be easily modified for "vector-based" calculations: +By passing a boolian value of \c true as the third argument to the stk::BiQuad::setResonance() function, the filter coefficients are automatically scaled to achieve unity gain at the resonance peak frequency. The previous code could be easily modified for "vector-based" calculations: \code #include "BiQuad.h" #include "Noise.h" +using namespace stk; int main() { diff --git a/doc/doxygen/footer.html b/doc/doxygen/footer.html index c536480..5f78789 100644 --- a/doc/doxygen/footer.html +++ b/doc/doxygen/footer.html @@ -2,7 +2,7 @@ - +
    The Synthesis ToolKit in C++ (STK)
    ©1995-2007 Perry R. Cook and Gary P. Scavone. All Rights Reserved.
    ©1995-2009 Perry R. Cook and Gary P. Scavone. All Rights Reserved.
    diff --git a/doc/doxygen/fundamentals.txt b/doc/doxygen/fundamentals.txt index 4b32507..9ada45e 100644 --- a/doc/doxygen/fundamentals.txt +++ b/doc/doxygen/fundamentals.txt @@ -1,15 +1,16 @@ /*! \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 that 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. +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 that 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. Starting with STK version 4.4, all STK classes except RtAudio, RtMidi, and RtError are defined within the stk namespace. \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: +In general, the computation and/or passing of values is performed on a "single-sample" basis. For example, the stk::Noise class outputs random floating-point numbers in the range +/-1.0. The computation of such values occurs in the stk::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" +using namespace stk; int main() { @@ -29,6 +30,7 @@ Nearly all STK classes implement tick() functions that take and/or retu \code #include "Noise.h" +using namespace stk; int main() { @@ -44,19 +46,19 @@ int main() } \endcode -In this way, it might be possible to achieve improved processing efficiency using vectorized computations. The StkFrames class is a relatively new addition to the ToolKit to provide a general "mechanism" for handling and passing vectorized, multi-channel audio data. The StkFrames "type" provides functions to set and/or determine the number of audio channels and sample frames it holds, as well as the format (interleaved or non-interleaved) of its data. Further, the StkFrames class provides data interpolation and subscripting functionality by frame/channel values. +In this way, it might be possible to achieve improved processing efficiency using vectorized computations. The StkFrames class is a relatively new addition to the ToolKit to provide a general "mechanism" for handling and passing vectorized, multi-channel audio data. The StkFrames "type" provides functions to set and/or determine the number of audio channels and sample frames it holds. Further, the StkFrames class provides data interpolation and subscripting functionality by frame/channel values. \section STK Inheritance: Nearly all STK classes inherit from the Stk abstract base class, which provides common functionality related to error reporting, sample rate control, and byte swapping. Several other base classes exist that roughly group many of the classes according to function as follows: -- Generator: source signal unit generator classes [Envelope, ADSR, Asymp, Noise, SubNoise, Modulate, SingWave, SineWave Blit, BlitSaw, BlitSquare, Granulate] -- Filter: digital filtering classes [OneZero, OnePole, PoleZero, TwoZero, TwoPole, BiQuad, FormSwep, Delay, DelayL, DelayA] -- Function: input to output function mappings [BowTable, JetTable, ReedTable] -- Instrmnt: sound synthesis algorithms, including physical, FM, modal, and particle models -- Effect: sound processing effect classes [Echo, Chorus, PitShift, PRCRev, JCRev, NRev] -- WvOut: audio data output classes [FileWvOut, RtWvOut, InetWvOut] -- WvIn: audio data input classes [FileWvIn, WaveLoop, RtWvIn, InetWvIn] +- stk::Generator: source signal unit generator classes [stk::Envelope, stk::ADSR, stk::Asymp, stk::Noise, stk::SubNoise, stk::Modulate, stk::SingWave, stk::SineWave, stk::Blit, stk::BlitSaw, stk::BlitSquare, stk::Granulate] +- stk::Filter: digital filtering classes [stk::OneZero, stk::OnePole, stk::PoleZero, stk::TwoZero, stk::TwoPole, stk::BiQuad, stk::FormSwep, stk::Delay, stk::DelayL, stk::DelayA, stk::TapDelay] +- stk::Function: input to output function mappings [stk::BowTable, stk::JetTable, stk::ReedTable] +- stk::Instrmnt: sound synthesis algorithms, including physical, FM, modal, and particle models +- stk::Effect: sound processing effect classes [stk::Echo, stk::Chorus, stk::PitShift, stk::PRCRev, stk::JCRev, stk::NRev] +- stk::WvOut: audio data output classes [stk::FileWvOut, stk::RtWvOut, stk::InetWvOut] +- stk::WvIn: audio data input classes [stk::FileWvIn, stk::FileLoop, stk::RtWvIn, stk::InetWvIn] [Main tutorial page]   [Next tutorial] diff --git a/doc/doxygen/hello.txt b/doc/doxygen/hello.txt index 724c420..e6cceaa 100644 --- a/doc/doxygen/hello.txt +++ b/doc/doxygen/hello.txt @@ -3,24 +3,25 @@ We'll continue our introduction to the Synthesis ToolKit with a simple sine-wave oscillator program. STK provides two different classes for sine-wave generation. We will first look at a generic waveform -oscillator class, WaveLoop, that can load a variety of common file +oscillator class, stk::FileLoop, that 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 -FileWvOut to write the result to a 16-bit, WAV formatted audio file. +stk::FileWvOut to write the result to a 16-bit, WAV formatted audio file. \code // sineosc.cpp -#include "WaveLoop.h" +#include "FileLoop.h" #include "FileWvOut.h" +using namespace stk; int main() { // Set the global sample rate before creating class instances. Stk::setSampleRate( 44100.0 ); - WaveLoop input; + FileLoop input; FileWvOut output; // Load the sine wave file. @@ -39,29 +40,37 @@ int main() } \endcode -WaveLoop is a subclass of FileWvIn, which supports 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. FileWvIn -provides interpolating, read-once ("oneshot") functionality, as well -as methods for setting the read rate and read position. +stk::FileLoop is a subclass of stk::FileWvIn, which supports 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. +stk::FileWvIn provides interpolating, read-once ("oneshot") +functionality, as well as methods for setting the read rate and read +position. -FileWvIn provides a "tick level" and interpolating interface to the -FileRead class. Likewise, FileWvOut provides a "tick level" interface -to the FileWrite class. FileRead and FileWrite both support 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. -FileWvOut does not currently offer data interpolation functionality. +stk::FileWvIn provides a "tick level" and interpolating interface to +the stk::FileRead class. Likewise, stk::FileWvOut provides a "tick +level" interface to the stk::FileWrite class. stk::FileRead and +FileWrite both support 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. stk::FileWvOut does not currently offer +data interpolation functionality. -The WvIn and WvOut parent classes and all subclasses 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. +A number of STK parent classes, including stk::WvIn, stk::WvOut, +stk::Instrmnt, stk::Generator, and stk::Effect, (and some or all of +their subclasses) support multi-channel sample frames. If a +single-sample version of the tick() function is called for +these classes, a full sample frame is computed but only a single value +is either input and/or output. For example, if the single-sample +tick() function is called for subclasses of stk::WvOut, the +sample argument is written to all channels in the one computed frame. +For classes returning values, an optional \c channel argument +specifies which channel value is returned from the computed frame (the +default is always channel 0). To input and/or output multichannel data +to these classes, the overloaded tick() functions taking +StkFrames reference arguments should be used. -Nearly all STK classes inherit from the Stk base class. Stk provides -a static sample rate that is queried by subclasses as needed. +Nearly all STK classes inherit from the stk::Stk base class. Stk +provides a static sample rate that 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. @@ -75,7 +84,11 @@ rewritten as shown below. \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. +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. [Main tutorial page]   [Next tutorial] */ diff --git a/doc/doxygen/index.txt b/doc/doxygen/index.txt index a6bc3de..c375074 100644 --- a/doc/doxygen/index.txt +++ b/doc/doxygen/index.txt @@ -1,4 +1,4 @@ -/*! \mainpage The Synthesis ToolKit in C++ (STK) +/*! \mainpage The Synthesis ToolKit in C++ (STK) \htmlonly @@ -15,7 +15,7 @@ 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 about 10 years +fact, the ToolKit has been working continuously for nearly 15 years now. STK currently runs with realtime support (audio and MIDI) on Linux, Macintosh OS X, and Windows computer platforms. Generic, non-realtime support has been tested under NeXTStep, Sun, and other diff --git a/doc/doxygen/information.txt b/doc/doxygen/information.txt index 7c6a4e7..738d91d 100644 --- a/doc/doxygen/information.txt +++ b/doc/doxygen/information.txt @@ -18,9 +18,9 @@ Here's a link to a book that includes an chapter on STK.

    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 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 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 15 years now. STK currently runs with realtime support (audio and MIDI) on 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). +The Synthesis ToolKit is free. 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.

    diff --git a/doc/doxygen/instruments.txt b/doc/doxygen/instruments.txt index 6ccc7f5..9ce7852 100644 --- a/doc/doxygen/instruments.txt +++ b/doc/doxygen/instruments.txt @@ -1,6 +1,6 @@ /*! \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 its frequency can be modified over time. +The ToolKit comes with a wide variety of synthesis algorithms, all of which inherit from the stk::Instrmnt class. In this example, we'll fire up an instance of the stk::BeeThree FM synthesis class and show how its frequency can be modified over time. \include bethree.cpp @@ -10,7 +10,7 @@ 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). -The noteOn() function initiates an instrument attack. Instruments that are continuously excited (e.g., Clarinet, BeeThree) will continue to sound until stopped with a noteOff(). Impulsively excited instrument sounds (e.g., Plucked, Wurley) typically decay within a few seconds time, requiring subsequent noteOn() messages for re-attack. +The noteOn() function initiates an instrument attack. Instruments that are continuously excited (e.g., stk::Clarinet, stk::BeeThree) will continue to sound until stopped with a noteOff(). Impulsively excited instrument sounds (e.g., stk::Plucked, stk::Wurley) typically decay within a few seconds time, requiring subsequent noteOn() messages for re-attack. Instrument parameters can be precisely controlled as demonstrated above. A more flexible approach to instrument control, allowing arbitrary scorefile or realtime updates, is described in the next tutorial chapter. diff --git a/doc/doxygen/links.txt b/doc/doxygen/links.txt index f8fe0a0..ff4734d 100644 --- a/doc/doxygen/links.txt +++ b/doc/doxygen/links.txt @@ -1,8 +1,8 @@ /*! \page links Miscellaneous Links -- The %RtAudio WWW site +- The %RtAudio WWW site -- The %RtMidi WWW site +- The %RtMidi WWW site - StkX: A Cocoa STK Framework for Mac OS X by Woon Seung Yeo diff --git a/doc/doxygen/multichannel.txt b/doc/doxygen/multichannel.txt index 15b6fa2..f2ba6eb 100644 --- a/doc/doxygen/multichannel.txt +++ b/doc/doxygen/multichannel.txt @@ -1,22 +1,25 @@ /*! \page multichannel Multi-Channel I/O -The ToolKit WvIn and WvOut classes (and their subclasses) support multi-channel audio data input and output. A set of interleaved audio samples representing a single time "slice" is referred to as a sample frame. At a sample rate of 44.1 kHz, a four-channel audio stream will have 44100 sample frames per second and a total of 176400 individual samples per second. +The ToolKit stk::WvIn and stk::WvOut classes (and their subclasses) support multi-channel audio data input and output. Several other abstract base classes, such as stk::Instrmnt, stk::Generator, and stk::Effect, also support multi-channel computations though not all of their subclasses produce or take multi-channel data. A set of interleaved audio samples representing a single time "slice" is referred to as a sample frame. At a sample rate of 44.1 kHz, a four-channel audio stream will have 44100 sample frames per second and a total of 176400 individual samples per second. Most STK classes process single-sample data streams via their -tick() function. In order to distinguish single-sample and -sample frame calculations, the WvIn and WvOut classes implement both -tick() and tickFrame() functions. The -tickFrame() functions take or return a reference to an StkFrames object -representing one or more sample frames. For single-channel -streams, the tick() and tickFrame() functions -produce equivalent results. When tick() is called for a -multi-channel stream, however, the function either returns a sample -frame average (WvIn) or writes a single sample argument to all -channels (WvOut). +tick() function. For classes supporting multi-channel data, +one must distinguish the tick() functions taking or producing +single \c StkFloat arguments from those taking stk::StkFrames& arguments. If +a single-sample version of the tick() function is called for +these classes, a full sample frame is computed but only a single value +is either input and/or output. For example, if the single-sample +tick() function is called for subclasses of WvOut, the sample +argument is written to all channels in the one computed frame. For +classes returning values, an optional \c channel argument specifies +which channel value is returned from the computed frame (the default +is always channel 0). To input and/or output multichannel data to +these classes, the overloaded tick() functions taking +StkFrames reference arguments should be used. Multi-channel support for realtime audio input and output is dependent on the audio device(s) available on your system. -The following example demonstrates the use of the FileWvOut class for +The following example demonstrates the use of the stk::FileWvOut 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. diff --git a/doc/doxygen/polyvoices.txt b/doc/doxygen/polyvoices.txt index 2c29393..405da76 100644 --- a/doc/doxygen/polyvoices.txt +++ b/doc/doxygen/polyvoices.txt @@ -1,10 +1,10 @@ /*! \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 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 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 stk::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 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. +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 "group" number. All sounding instrument outputs are summed and returned via the tick() function. The stk::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 stk::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. +In the following example, we modify the controlbee.cpp program to make use of three stk::BeeThree instruments, all controlled using a stk::Voicer. \include threebees.cpp @@ -16,7 +16,7 @@ threebees < scores/bachfugue.ski For more fun, surf to Kern Scores for a huge assortment of other scorefiles that can be downloaded in the SKINI format. -Another easy extension would be to add the \c Messager::startMidiInput() function to the program and then play the instruments via a MIDI keyboard. +Another easy extension would be to add the \c stk::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 38876e6..63cc6a8 100644 --- a/doc/doxygen/realtime.txt +++ b/doc/doxygen/realtime.txt @@ -2,36 +2,36 @@ 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. We also make use of the SineWave class as a -sine-wave oscillator. SineWave computes an internal, static sine-wave +computer system. We also make use of the stk::SineWave class as a +sine-wave oscillator. stk::SineWave computes an internal, static sine-wave table when its first instance is created. Subsequent instances make use of the same table. The default table length, specified in SineWave.h, is 2048 samples. \include rtsine.cpp -The class RtWvOut is a protected subclass of WvOut. A number of +The class stk::RtWvOut is a protected subclass of stk::WvOut. A number of optional constructor arguments can be used to fine tune its -performance for a given system. RtWvOut provides a "single-sample", -blocking interface to the RtAudio class. Note that RtWvOut (as well -as the RtWvIn class described below) makes use of RtAudio's callback +performance for a given system. stk::RtWvOut provides a "single-sample", +blocking interface to the RtAudio class. Note that stk::RtWvOut (as well +as the stk::RtWvIn class described below) makes use of RtAudio's callback input/output functionality by creating a large ring-buffer into which data is written. These classes should not be used when low-latency and robust performance is necessary -Though not used here, an RtWvIn class exists as well that can be used +Though not used here, an stk::RtWvIn class exists as well that 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 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 +It may be possible to use an instance of stk::RtWvOut and an instance of +stk::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 RtAudio to achieve this behavior, as described in the next section. See the effects project or the duplex.cpp example program in the examples project for more information. -When using any realtime STK class (RtAudio, RtWvOut, RtWvIn, RtDuplex, RtMidi, InetWvIn, InetWvOut, Socket, UdpSocket, TcpServer, TcpClient, 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): +When using any realtime STK class (RtAudio, stk::RtWvOut, stk::RtWvIn, RtMidi, stk::InetWvIn, stk::InetWvOut, stk::Socket, stk::UdpSocket, stk::TcpServer, stk::TcpClient, and stk::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 Generator.cpp SineWave.cpp WvOut.cpp \ diff --git a/doc/doxygen/system.txt b/doc/doxygen/system.txt index a015d23..026bbfe 100644 --- a/doc/doxygen/system.txt +++ b/doc/doxygen/system.txt @@ -15,10 +15,9 @@ Macintosh OS X (specific):
      -
    • 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 that 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.
    • +
    • A C++ compiler is not installed 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.
    • 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. +
    • The tcl/tk interpreter does not ship by default with OS X and 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. 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.
    • @@ -32,11 +31,6 @@ It appears that socket support in Tcl/Tk uses the Nagle algorithm, which produce
    • For compiling the source (if not already in your system):
      • dsound.h header file (DirectX 6.1) - put somewhere in your header search path
      • dsound.lib library file (DirectX 6.1) - put somewhere in your library search path
    -WindowsNT (specific): -
      -
    • 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.
    • -
    -

    */ diff --git a/doc/doxygen/usage.txt b/doc/doxygen/usage.txt index bfa4cc2..fab8eec 100644 --- a/doc/doxygen/usage.txt +++ b/doc/doxygen/usage.txt @@ -45,8 +45,6 @@ This release of STK comes with four separate "project" directories:

    • 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++ .NET 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.
    • -
    • Unix Systems: A GNU configure shell script is included in the distribution for unix-based systems. From the top-level distribution directory, type './configure' and the script will create Makefiles in each project directory specific to the characteristics of the host computer. Then from within any given project directory (example demo), type 'make' to compile the project. In addition, an STK library can be compiled from within the src directory. Several options can be supplied to the configure script to customize the build behavior: diff --git a/doc/hierarchy.txt b/doc/hierarchy.txt index a4f2f47..b766194 100644 --- a/doc/hierarchy.txt +++ b/doc/hierarchy.txt @@ -5,10 +5,7 @@ By Perry R. Cook and Gary P. Scavone, 1995-2007. STK Classes - See the HTML documentation in the html directory for complete information. - .- Generator - (Modulate, Noise, SingWave, Envelope, SineWave, Blit, BlitSaw, BlitSquare, Granulate) - | | | - | SubNoise ADSR - | Asymp + .- Generator - (Modulate, Noise, SingWave, Envelope, ADSR, Asymp, SineWave, Blit, BlitSaw, BlitSquare, Granulate) | |- Function - (BowTable, JetTable, ReedTable) | @@ -16,14 +13,11 @@ STK Classes - See the HTML documentation in the html directory for complete info | |- WvIn - (FileWvIn, RtWvIn, InetWvIn) | | - | WaveLoop + | FileLoop | |- WvOut - (FileWvOut, RtWvOut, TcpWvOut) | - |- Filter - (OnePole, OneZero, Delay, TwoPole, TwoZero, PoleZero, Biquad) - | | | - | DelayL FormSwep - | DelayA + |- Filter - (OnePole, OneZero, TwoPole, TwoZero, PoleZero, Biquad, FormSwep, Delay, DelayL, DelayA, TapDelay) | |- RtAudio, RtMidi, RtDuplex, Socket, Thread, Mutex | | @@ -68,34 +62,35 @@ Stk -| UdpSocket Master Class: Stk.cpp Sample rate, byte-swapping, error handling functionality -Sources: Generator.cpp Abstract base class for various source signal classes - Function.cpp Abstract base class for various input/output mapping classes +Sources: Generator.h Abstract base class for various source signal classes + Function.h Abstract base class for various input/output mapping classes Envelope.cpp Linearly goes to target by rate - ADSR.cpp ADSR flavor of Envelope + ADSR.cpp ADSR envelope Asymp.cpp Exponentially approaches target Noise.cpp Random number generator - SubNoise.cpp Random numbers each N samples SineWave.cpp Sinusoidal oscillator with internally computed static table Blit.cpp Bandlimited impulse train BlitSaw.cpp Bandlimited sawtooth generator BlitSquare.cpp Bandlimited square wave generator Granulate.cpp Granular synthesis class that processes a monophonic audio file FileRead.cpp Audio file input class (no internal data storage) for RAW, WAV, SND (AU), AIFF, MAT-file files - WvIn.cpp Abstract base class for audio data input classes + WvIn.h Abstract base class for audio data input classes FileWvIn.cpp Audio file input interface class with interpolation - WaveLoop.cpp Wavetable looping (subclass of FileWvIn) + FileLoop.cpp Wavetable looping (subclass of FileWvIn) RtWvIn.cpp Realtime audio input class (subclass of WvIn) InetWvIn.cpp Audio streaming (socket server) input class (subclass of WvIn) Sinks: FileWrite.cpp Audio file output class (no internal data storage) for RAW, WAV, SND (AU), AIFF, MAT-file files - WvOut.cpp Abstract base class for audio data output classes + WvOut.h Abstract base class for audio data output classes FileWvOut.cpp Audio file output interface class to FileWrite RtWvOut.cpp Realtime audio output class (subclass of WvOut) InetWvOut.cpp Audio streaming (socket client) output class (subclass of WvOut) Duplex: RtDuplex.cpp Synchronous realtime audio input/output class (blocking) -Filters: Filter.cpp Filter master class +Filters: Filter.h Filter master class + Iir.h General infinite-impulse response filter + Fir.h General finite-impulse response filter OneZero.cpp One zero filter OnePole.cpp One pole filter PoleZero.cpp One pole/one zero filter @@ -104,15 +99,16 @@ Filters: Filter.cpp Filter master class BiQuad.cpp Two pole/two zero filter FormSwep.cpp Sweepable biquad filter (goes to target by rate) Delay.cpp Non-interpolating delay line class - DelayL.cpp Linearly interpolating delay line (subclass of Delay) - DelayA.cpp Allpass interpolating delay line (subclass of Delay) + DelayL.cpp Linearly interpolating delay line + DelayA.cpp Allpass interpolating delay line + TapDelay.cpp Multi-tap non-interpolating delay line class -Non-Linear: JetTabl.cpp Cubic jet non-linearity - BowTabl.cpp x^(-3) Bow non-linearity - ReedTabl.cpp One breakpoint saturating reed non-linearity +Non-Linear: JetTabl.h Cubic jet non-linearity + BowTabl.h x^(-3) Bow non-linearity + ReedTabl.h One breakpoint saturating reed non-linearity -Derived: Modulate.cpp Periodic and random vibrato: WvIn, SubNoise, OnePole - SingWave.cpp Looping wave table with randomness: Modulate, WaveLoop, Envelope +Derived: Modulate.cpp Periodic and random vibrato: WvIn, Noise, OnePole + SingWave.cpp Looping wave table with randomness: Modulate, FileLoop, Envelope ********** INSTRUMENTS AND ALGORITHMS ************** @@ -153,7 +149,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. -Effect.cpp Effects Processor Base Class +Effect.h 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 diff --git a/doc/treesed.html b/doc/treesed.html new file mode 100644 index 0000000..6fb9187 --- /dev/null +++ b/doc/treesed.html @@ -0,0 +1,175 @@ + + +Treesed Usage + + + +
      + +

      How to Use Treesed

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

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

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

      Searching

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

      Replacing

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

      Old Versions

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

      About Treesed

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

      + +

      About This Document

      +This usage document was created by the Division of Information Technology Services at The +University of Western Ontario. + + \ No newline at end of file diff --git a/include/ADSR.h b/include/ADSR.h index c1971e8..afa6040 100644 --- a/include/ADSR.h +++ b/include/ADSR.h @@ -1,86 +1,163 @@ +#ifndef STK_ADSR_H +#define STK_ADSR_H + +#include "Generator.h" + +namespace stk { + /***************************************************/ /*! \class ADSR \brief STK ADSR envelope class. - This Envelope subclass implements a - traditional ADSR (Attack, Decay, - Sustain, Release) envelope. It - responds to simple keyOn and keyOff - messages, keeping track of its state. - The \e state = ADSR::DONE after the - envelope value reaches 0.0 in the - ADSR::RELEASE state. + This class implements a traditional ADSR (Attack, Decay, Sustain, + Release) envelope. It responds to simple keyOn and keyOff + messages, keeping track of its state. The \e state = ADSR::DONE + after the envelope value reaches 0.0 in the ADSR::RELEASE state. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_ADSR_H -#define STK_ADSR_H - -#include "Envelope.h" - -class ADSR : public Envelope +class ADSR : public Generator { public: - //! Envelope states. - enum { ATTACK, DECAY, SUSTAIN, RELEASE, DONE }; + //! ADSR envelope states. + enum { + ATTACK, /*!< Attack */ + DECAY, /*!< Decay */ + SUSTAIN, /*!< Sustain */ + RELEASE, /*!< Release */ + DONE /*!< End of release */ + }; //! Default constructor. - ADSR(void); + ADSR( void ); //! Class destructor. - ~ADSR(void); + ~ADSR( void ); //! Set target = 1, state = \e ADSR::ATTACK. - void keyOn(void); + void keyOn( void ); //! Set target = 0, state = \e ADSR::RELEASE. - void keyOff(void); + void keyOff( void ); //! Set the attack rate. - void setAttackRate(StkFloat rate); + void setAttackRate( StkFloat rate ); //! Set the decay rate. - void setDecayRate(StkFloat rate); + void setDecayRate( StkFloat rate ); //! Set the sustain level. - void setSustainLevel(StkFloat level); + void setSustainLevel( StkFloat level ); //! Set the release rate. - void setReleaseRate(StkFloat rate); + void setReleaseRate( StkFloat rate ); //! Set the attack rate based on a time duration. - void setAttackTime(StkFloat time); + void setAttackTime( StkFloat time ); //! Set the decay rate based on a time duration. - void setDecayTime(StkFloat time); + void setDecayTime( StkFloat time ); //! Set the release rate based on a time duration. - void setReleaseTime(StkFloat time); + void setReleaseTime( StkFloat time ); //! Set sustain level and attack, decay, and release time durations. - void setAllTimes(StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime); + void setAllTimes( StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime ); //! Set the target value. - void setTarget(StkFloat target); + void setTarget( StkFloat target ); //! Return the current envelope \e state (ATTACK, DECAY, SUSTAIN, RELEASE, DONE). - int getState(void) const; + int getState( void ) const { return state_; }; - //! Set to state = ADSR::SUSTAIN with current and target values of \e aValue. - void setValue(StkFloat value); + //! Set to state = ADSR::SUSTAIN with current and target values of \e value. + void setValue( StkFloat value ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + int state_; + StkFloat value_; + StkFloat target_; StkFloat attackRate_; StkFloat decayRate_; - StkFloat sustainLevel_; StkFloat releaseRate_; + StkFloat sustainLevel_; }; +inline StkFloat ADSR :: tick( void ) +{ + switch ( state_ ) { + + case ATTACK: + value_ += attackRate_; + if ( value_ >= target_ ) { + value_ = target_; + target_ = sustainLevel_; + state_ = DECAY; + } + lastFrame_[0] = value_; + break; + + case DECAY: + value_ -= decayRate_; + if ( value_ <= sustainLevel_ ) { + value_ = sustainLevel_; + state_ = SUSTAIN; + } + lastFrame_[0] = value_; + break; + + case RELEASE: + value_ -= releaseRate_; + if ( value_ <= 0.0 ) { + value_ = (StkFloat) 0.0; + state_ = DONE; + } + lastFrame_[0] = value_; + + } + + return value_; +} + +inline StkFrames& ADSR :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "ADSR::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 0). /*! @@ -53,21 +55,89 @@ class Asymp : public Envelope fast approach rates, while values greater than 1.0 produce rather slow rates. */ - void setTau(StkFloat tau); + void setTau( StkFloat tau ); //! Set the asymptotic rate based on a time duration (must be > 0). - void setTime(StkFloat time); + void setTime( StkFloat time ); //! Set the target value. - void setTarget(StkFloat target); + void setTarget( StkFloat target ); + + //! Set current and target values to \e value. + void setValue( StkFloat value ); + + //! Return the current envelope \e state (0 = at target, 1 otherwise). + int getState( void ) const { return state_; }; + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + StkFloat value_; + StkFloat target_; StkFloat factor_; StkFloat constant_; + int state_; }; +inline 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; + } + } + lastFrame_[0] = value_; + } + + return value_; +} + +inline StkFrames& Asymp :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Asymp::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 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 ); + + 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(); + + lastFrame_[0] = temp * 0.125; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/BiQuad.h b/include/BiQuad.h index d1951f6..c4640de 100644 --- a/include/BiQuad.h +++ b/include/BiQuad.h @@ -1,23 +1,23 @@ -/***************************************************/ -/*! \class BiQuad - \brief STK biquad (two-pole, two-zero) filter class. - - This protected Filter subclass implements a - two-pole, two-zero digital filter. A method - is provided for creating a resonance in the - frequency response while maintaining a constant - filter gain. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - #ifndef STK_BIQUAD_H #define STK_BIQUAD_H #include "Filter.h" -class BiQuad : protected Filter +namespace stk { + +/***************************************************/ +/*! \class BiQuad + \brief STK biquad (two-pole, two-zero) filter class. + + This class implements a two-pole, two-zero digital filter. + Methods are provided for creating a resonance or notch in the + frequency response while maintaining a constant filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + +class BiQuad : public Filter { public: @@ -25,28 +25,28 @@ public: BiQuad(); //! Class destructor. - virtual ~BiQuad(); + ~BiQuad(); //! A function to enable/disable the automatic updating of class data when the STK sample rate changes. void ignoreSampleRateChange( bool ignore = true ) { ignoreSampleRateChange_ = ignore; }; - //! Clears all internal states of the filter. - void clear(void); + //! Set all filter coefficients. + void setCoefficients( StkFloat b0, StkFloat b1, StkFloat b2, StkFloat a1, StkFloat a2, bool clearState = false ); //! Set the b[0] coefficient value. - void setB0(StkFloat b0); + void setB0( StkFloat b0 ) { b_[0] = b0; }; //! Set the b[1] coefficient value. - void setB1(StkFloat b1); + void setB1( StkFloat b1 ) { b_[1] = b1; }; //! Set the b[2] coefficient value. - void setB2(StkFloat b2); + void setB2( StkFloat b2 ) { b_[2] = b2; }; //! Set the a[1] coefficient value. - void setA1(StkFloat a1); + void setA1( StkFloat a1 ) { a_[1] = a1; }; //! Set the a[2] coefficient value. - void setA2(StkFloat a2); + void setA2( StkFloat a2 ) { a_[2] = a2; }; //! Sets the filter coefficients for a resonance at \e frequency (in Hz). /*! @@ -60,7 +60,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(StkFloat frequency, StkFloat 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). /*! @@ -69,7 +69,7 @@ public: and \e radius from the z-plane origin. No filter normalization is attempted. */ - void setNotch(StkFloat frequency, StkFloat radius); + void setNotch( StkFloat frequency, StkFloat radius ); //! Sets the filter zeroes for equal resonance gain. /*! @@ -78,62 +78,106 @@ public: where R is the pole radius setting. */ - void setEqualGainZeroes(); - - //! 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(StkFloat gain); - - //! Return the current filter gain. - StkFloat getGain(void) const; + void setEqualGainZeroes( void ); //! Return the last computed output value. - StkFloat lastOut(void) const; + StkFloat lastOut( void ) const { return lastFrame_[0]; }; - //! Input one sample to the filter and return one output. - virtual StkFloat tick(StkFloat sample); + //! Input one sample to the filter and return a reference to one output. + StkFloat tick( StkFloat input ); //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. /*! - The \c channel argument should be zero or greater (the first - channel is specified by 0). An StkError will be thrown if the \c - channel argument is equal to or greater than the number of - channels in the StkFrames object. + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. */ - virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); protected: - // This function must be implemented in all subclasses. It is used - // to get around a C++ problem with overloaded virtual functions. - virtual StkFloat computeSample( StkFloat input ); virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); }; -inline StkFloat BiQuad :: computeSample( StkFloat input ) +inline StkFloat BiQuad :: tick( StkFloat input ) { inputs_[0] = gain_ * input; - outputs_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2]; - outputs_[0] -= a_[2] * outputs_[2] + a_[1] * outputs_[1]; + lastFrame_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2]; + lastFrame_[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]; + outputs_[1] = lastFrame_[0]; - return outputs_[0]; -} - -inline StkFloat BiQuad :: tick( StkFloat input ) -{ - return this->computeSample( input ); + return lastFrame_[0]; } inline StkFrames& BiQuad :: tick( StkFrames& frames, unsigned int channel ) { - return Filter::tick( frames, channel ); +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "BiQuad::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "BiQuad::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } #endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i +#include + +namespace stk { + /***************************************************/ /*! \class Blit \brief STK band-limited impulse train class. @@ -21,11 +30,6 @@ */ /***************************************************/ -#ifndef STK_BLIT_H -#define STK_BLIT_H - -#include "Generator.h" - class Blit: public Generator { public: @@ -68,10 +72,25 @@ class Blit: public Generator */ void setHarmonics( unsigned int nHarmonics = 0 ); + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + protected: void updateHarmonics( void ); - StkFloat computeSample( void ); unsigned int nHarmonics_; unsigned int m_; @@ -81,4 +100,52 @@ class Blit: public Generator }; +inline StkFloat Blit :: tick( void ) +{ + // The code below implements the SincM algorithm of Stilson and + // Smith with an additional scale factor of P / M applied to + // normalize the output. + + // A fully optimized version of this code would replace the two sin + // calls with a pair of fast sin oscillators, for which stable fast + // two-multiply algorithms are well known. In the spirit of STK, + // which favors clarity over performance, the optimization has not + // been made here. + + // Avoid a divide by zero at the sinc peak, which has a limiting + // value of 1.0. + StkFloat tmp, denominator = sin( phase_ ); + if ( denominator <= std::numeric_limits::epsilon() ) + tmp = 1.0; + else { + tmp = sin( m_ * phase_ ); + tmp /= m_ * denominator; + } + + phase_ += rate_; + if ( phase_ >= PI ) phase_ -= PI; + + lastFrame_[0] = tmp; + return lastFrame_[0]; +} + +inline StkFrames& Blit :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Blit::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i +#include + +namespace stk { + /***************************************************/ /*! \class BlitSaw \brief STK band-limited sawtooth wave class. @@ -19,11 +28,6 @@ */ /***************************************************/ -#ifndef STK_BLITSAW_H -#define STK_BLITSAW_H - -#include "Generator.h" - class BlitSaw: public Generator { public: @@ -54,10 +58,25 @@ class BlitSaw: public Generator */ void setHarmonics( unsigned int nHarmonics = 0 ); + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + protected: void updateHarmonics( void ); - StkFloat computeSample( void ); unsigned int nHarmonics_; unsigned int m_; @@ -70,4 +89,60 @@ class BlitSaw: public Generator }; +inline StkFloat BlitSaw :: tick( void ) +{ + // The code below implements the BLIT algorithm of Stilson and + // Smith, followed by a summation and filtering operation to produce + // a sawtooth waveform. After experimenting with various approaches + // to calculate the average value of the BLIT over one period, I + // found that an estimate of C2_ = 1.0 / period (in samples) worked + // most consistently. A "leaky integrator" is then applied to the + // difference of the BLIT output and C2_. (GPS - 1 October 2005) + + // A fully optimized version of this code would replace the two sin + // calls with a pair of fast sin oscillators, for which stable fast + // two-multiply algorithms are well known. In the spirit of STK, + // which favors clarity over performance, the optimization has + // not been made here. + + // Avoid a divide by zero, or use of a denormalized divisor + // at the sinc peak, which has a limiting value of m_ / p_. + StkFloat tmp, denominator = sin( phase_ ); + if ( fabs(denominator) <= std::numeric_limits::epsilon() ) + tmp = a_; + else { + tmp = sin( m_ * phase_ ); + tmp /= p_ * denominator; + } + + tmp += state_ - C2_; + state_ = tmp * 0.995; + + phase_ += rate_; + if ( phase_ >= PI ) phase_ -= PI; + + lastFrame_[0] = tmp; + return lastFrame_[0]; +} + +inline StkFrames& BlitSaw :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "BlitSaw::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i +#include + +namespace stk { + /***************************************************/ /*! \class BlitSquare \brief STK band-limited square wave class. @@ -30,11 +39,6 @@ */ /***************************************************/ -#ifndef STK_BLITSQUARE_H -#define STK_BLITSQUARE_H - -#include "Generator.h" - class BlitSquare: public Generator { public: @@ -77,10 +81,25 @@ class BlitSquare: public Generator */ void setHarmonics( unsigned int nHarmonics = 0 ); + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + protected: void updateHarmonics( void ); - StkFloat computeSample( void ); unsigned int nHarmonics_; unsigned int m_; @@ -92,4 +111,60 @@ class BlitSquare: public Generator StkFloat dcbState_; }; +inline StkFloat BlitSquare :: tick( void ) +{ + StkFloat temp = lastBlitOutput_; + + // A fully optimized version of this would replace the two sin calls + // with a pair of fast sin oscillators, for which stable fast + // two-multiply algorithms are well known. In the spirit of STK, + // which favors clarity over performance, the optimization has + // not been made here. + + // Avoid a divide by zero, or use of a denomralized divisor + // at the sinc peak, which has a limiting value of 1.0. + StkFloat denominator = sin( phase_ ); + if ( fabs( denominator ) < std::numeric_limits::epsilon() ) { + // Inexact comparison safely distinguishes betwen *close to zero*, and *close to PI*. + if ( phase_ < 0.1f || phase_ > TWO_PI - 0.1f ) + lastBlitOutput_ = a_; + else + lastBlitOutput_ = -a_; + } + else { + lastBlitOutput_ = sin( m_ * phase_ ); + lastBlitOutput_ /= p_ * denominator; + } + + lastBlitOutput_ += temp; + + // Now apply DC blocker. + lastFrame_[0] = lastBlitOutput_ - dcbState_ + 0.999 * lastFrame_[0]; + dcbState_ = lastBlitOutput_; + + phase_ += rate_; + if ( phase_ >= TWO_PI ) phase_ -= TWO_PI; + + return lastFrame_[0]; +} + +inline StkFrames& BlitSquare :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "BlitSquare::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i + +namespace stk { + /***************************************************/ /*! \class BowTable \brief STK bowed string table class. @@ -5,23 +13,15 @@ This class implements a simple bowed string non-linear function, as described by Smith (1986). - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_BOWTABL_H -#define STK_BOWTABL_H - -#include "Function.h" - class BowTable : public Function { public: //! Default constructor. - BowTable(); - - //! Class destructor. - ~BowTable(); + BowTable( void ) : offset_(0.0), slope_(0.1) {}; //! Set the table offset value. /*! @@ -30,22 +30,111 @@ public: friction to vary with direction, use a non-zero value for the offset. The default value is zero. */ - void setOffset(StkFloat offset); + void setOffset( StkFloat offset ) { offset_ = 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); + void setSlope( StkFloat slope ) { slope_ = slope; }; + + //! Take one sample input and map to one sample of output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the table and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the table and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); protected: - StkFloat computeSample( StkFloat input ); - StkFloat offset_; StkFloat slope_; }; +inline StkFloat BowTable :: tick( StkFloat input ) +{ + // The input represents differential string vs. bow velocity. + StkFloat sample = input + offset_; // add bias to input + sample *= slope_; // then scale it + lastFrame_[0] = (StkFloat) fabs( (double) sample ) + (StkFloat) 0.75; + lastFrame_[0] = (StkFloat) pow( lastFrame_[0], (StkFloat) -4.0 ); + + // Set minimum friction to 0.0 + // if ( lastFrame_[0] < 0.0 ) lastFrame_[0] = 0.0; + + // Set maximum friction to 1.0. + if ( lastFrame_[0] > 1.0 ) lastFrame_[0] = (StkFloat) 1.0; + + return lastFrame_[0]; +} + +inline StkFrames& BowTable :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "BowTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 1.0) *samples = 1.0; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& BowTable :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "BowTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0) *oSamples = 1.0; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + #endif diff --git a/include/Bowed.h b/include/Bowed.h index e077ba9..b0b0fba 100644 --- a/include/Bowed.h +++ b/include/Bowed.h @@ -1,3 +1,16 @@ +#ifndef STK_BOWED_H +#define STK_BOWED_H + +#include "Instrmnt.h" +#include "DelayL.h" +#include "BowTable.h" +#include "OnePole.h" +#include "BiQuad.h" +#include "SineWave.h" +#include "ADSR.h" + +namespace stk { + /***************************************************/ /*! \class Bowed \brief STK bowed string instrument class. @@ -17,58 +30,48 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_BOWED_H -#define STK_BOWED_H - -#include "Instrmnt.h" -#include "DelayL.h" -#include "BowTable.h" -#include "OnePole.h" -#include "BiQuad.h" -#include "SineWave.h" -#include "ADSR.h" - class Bowed : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Bowed(StkFloat lowestFrequency); + Bowed( StkFloat lowestFrequency ); //! Class destructor. - ~Bowed(); + ~Bowed( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Set vibrato gain. - void setVibrato(StkFloat gain); + void setVibrato( StkFloat gain ); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBowing(StkFloat amplitude, StkFloat rate); + void startBowing( StkFloat amplitude, StkFloat rate ); //! Decrease breath pressure with given rate of decrease. - void stopBowing(StkFloat rate); + void stopBowing( StkFloat rate ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayL neckDelay_; DelayL bridgeDelay_; BowTable bowTable_; @@ -83,4 +86,27 @@ class Bowed : public Instrmnt }; +inline StkFloat Bowed :: tick( unsigned int ) +{ + StkFloat bowVelocity = maxVelocity_ * adsr_.tick(); + StkFloat bridgeRefl = -stringFilter_.tick( bridgeDelay_.lastOut() ); + StkFloat nutRefl = -neckDelay_.lastOut(); + StkFloat stringVel = bridgeRefl + nutRefl; // Sum is string velocity + StkFloat velDiff = bowVelocity - stringVel; // Differential velocity + StkFloat 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_ * (1.0 - betaRatio_) ) + + (baseDelay_ * vibratoGain_ * vibrato_.tick()) ); + } + + lastFrame_[0] = bodyFilter_.tick( bridgeDelay_.lastOut() ); + + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Brass.h b/include/Brass.h index da13dde..76633d1 100644 --- a/include/Brass.h +++ b/include/Brass.h @@ -1,3 +1,15 @@ +#ifndef STK_BRASS_H +#define STK_BRASS_H + +#include "Instrmnt.h" +#include "DelayA.h" +#include "BiQuad.h" +#include "PoleZero.h" +#include "ADSR.h" +#include "SineWave.h" + +namespace stk { + /***************************************************/ /*! \class Brass \brief STK simple brass instrument class. @@ -16,20 +28,10 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_BRASS_H -#define STK_BRASS_H - -#include "Instrmnt.h" -#include "DelayA.h" -#include "BiQuad.h" -#include "PoleZero.h" -#include "ADSR.h" -#include "SineWave.h" - class Brass: public Instrmnt { public: @@ -37,39 +39,40 @@ class Brass: public Instrmnt /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - Brass(StkFloat lowestFrequency); + Brass( StkFloat lowestFrequency ); //! Class destructor. - ~Brass(); + ~Brass( ); //! Reset and clear all internal state. - void clear(); + void clear( ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Set the lips frequency. - void setLip(StkFloat frequency); + void setLip( StkFloat frequency ); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBlowing(StkFloat amplitude, StkFloat rate); + void startBlowing( StkFloat amplitude, StkFloat rate ); //! Decrease breath pressure with given rate of decrease. - void stopBlowing(StkFloat rate); + void stopBlowing( StkFloat rate ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayA delayLine_; BiQuad lipFilter_; PoleZero dcBlock_; @@ -83,4 +86,25 @@ class Brass: public Instrmnt }; +inline StkFloat Brass :: tick( unsigned int ) +{ + StkFloat breathPressure = maxPressure_ * adsr_.tick(); + breathPressure += vibratoGain_ * vibrato_.tick(); + + 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. + lastFrame_[0] = deltaPressure * mouthPressure + ( 1.0 - deltaPressure) * borePressure; + lastFrame_[0] = delayLine_.tick( dcBlock_.tick( lastFrame_[0] ) ); + + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Chorus.h b/include/Chorus.h index 046f6c8..cab498a 100644 --- a/include/Chorus.h +++ b/include/Chorus.h @@ -1,20 +1,23 @@ +#ifndef STK_CHORUS_H +#define STK_CHORUS_H + +#include "Effect.h" +#include "DelayL.h" +#include "SineWave.h" + +namespace stk { + /***************************************************/ /*! \class Chorus \brief STK chorus effect class. - This class implements a chorus effect. + This class implements a chorus effect. It takes a monophonic + input signal and produces a stereo output signal. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_CHORUS_H -#define STK_CHORUS_H - -#include "Effect.h" -#include "DelayL.h" -#include "SineWave.h" - class Chorus : public Effect { public: @@ -24,22 +27,62 @@ class Chorus : public Effect */ Chorus( StkFloat baseDelay = 6000 ); - //! Class destructor. - ~Chorus(); - //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set modulation depth. - void setModDepth(StkFloat depth); + void setModDepth( StkFloat depth ) { modDepth_ = depth; }; //! Set modulation frequency. - void setModFrequency(StkFloat frequency); + void setModFrequency( StkFloat frequency ); + + //! Return the specified channel value of the last computed stereo frame. + /*! + Use the lastFrame() function to get both values of the last + computed stereo frame. The \c channel argument must be 0 or 1 + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Input one sample to the effect and return the specified \c channel value of the computed stereo frame. + /*! + Use the lastFrame() function to get both values of the computed + stereo output frame. The \c channel argument must be 0 or 1 (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFloat tick( StkFloat input, unsigned int channel = 0 ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with stereo outputs. + /*! + The StkFrames argument reference is returned. The stereo + outputs are written to the StkFrames argument starting at the + specified \c channel. Therefore, the \c channel argument must be + less than ( channels() - 1 ) of the StkFrames argument (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the effect and write stereo outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. The \c iChannel + argument must be less than the number of channels in the \c + iFrames argument (the first channel is specified by 0). The \c + oChannel argument must be less than ( channels() - 1 ) of the \c + oFrames argument. However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); protected: - StkFloat computeSample( StkFloat input ); - DelayL delayLine_[2]; SineWave mods_[2]; StkFloat baseLength_; @@ -47,5 +90,79 @@ class Chorus : public Effect }; +inline StkFloat Chorus :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + errorString_ << "Chorus::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFloat Chorus :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + errorString_ << "Chorus::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + delayLine_[0].setDelay( baseLength_ * 0.707 * ( 1.0 + modDepth_ * mods_[0].tick() ) ); + delayLine_[1].setDelay( baseLength_ * 0.5 * ( 1.0 - modDepth_ * mods_[1].tick() ) ); + lastFrame_[0] = effectMix_ * ( delayLine_[0].tick( input ) - input ) + input; + lastFrame_[1] = effectMix_ * ( delayLine_[1].tick( input ) - input ) + input; + return lastFrame_[channel]; +} + +inline StkFrames& Chorus :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + errorString_ << "Chorus::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels() - 1; + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + errorString_ << "Chorus::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + errorString_ << "Delay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } #endif + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "Delay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + errorString_ << "DelayA::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "DelayA::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + errorString_ << "DelayL::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "DelayL::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i soundOrder_; @@ -60,4 +63,30 @@ class Drummer : public Instrmnt int nSounding_; }; +inline StkFloat Drummer :: tick( unsigned int ) +{ + lastFrame_[0] = 0.0; + if ( nSounding_ == 0 ) return lastFrame_[0]; + + for ( int i=0; i= 0 ) { + if ( waves_[i].isFinished() ) { + // Re-order the list. + for ( int j=0; j soundOrder_[i] ) + soundOrder_[j] -= 1; + } + soundOrder_[i] = -1; + nSounding_--; + } + else + lastFrame_[0] += filters_[i].tick( waves_[i].tick() ); + } + } + + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Echo.h b/include/Echo.h index f9a6fff..2f57f45 100644 --- a/include/Echo.h +++ b/include/Echo.h @@ -1,19 +1,21 @@ +#ifndef STK_ECHO_H +#define STK_ECHO_H + +#include "Effect.h" +#include "Delay.h" + +namespace stk { + /***************************************************/ /*! \class Echo \brief STK echo effect class. This class implements an echo effect. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_ECHO_H -#define STK_ECHO_H - -#include "Effect.h" -#include "Delay.h" - class Echo : public Effect { public: @@ -23,9 +25,6 @@ class Echo : public Effect */ Echo( unsigned long maximumDelay = (unsigned long) Stk::sampleRate() ); - //! Class destructor. - ~Echo(); - //! Reset and clear all internal state. void clear(); @@ -35,14 +34,87 @@ class Echo : public Effect //! Set the delay line length in samples. void setDelay( unsigned long delay ); - protected: + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; - StkFloat computeSample( StkFloat input ); + //! Input one sample to the effect and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the effect and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: Delay delayLine_; unsigned long length_; }; +inline StkFloat Echo :: tick( StkFloat input ) +{ + lastFrame_[0] = effectMix_ * ( delayLine_.tick( input ) - input ) + input; + return lastFrame_[0]; +} + +inline StkFrames& Echo :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Echo::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "Echo::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i + +namespace stk { + /***************************************************/ /*! \class Effect \brief STK abstract effects parent class. - This class provides common functionality for - STK effects subclasses. + This class provides common functionality for STK effects + subclasses. It is general enough to support both monophonic and + polyphonic input/output classes. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#include "Stk.h" - -#ifndef STK_EFFECT_H -#define STK_EFFECT_H - class Effect : public Stk { public: //! Class constructor. - Effect(); + Effect( void ) { lastFrame_.resize( 1, 1, 0.0 ); }; - //! Class destructor. - virtual ~Effect(); + //! Return the number of output channels for the class. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; + + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; //! 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; - - //! Take one sample input and compute one sample of output. - StkFloat tick( StkFloat input ); - - //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. - /*! - The \c channel argument should be zero or greater (the first - channel is specified by 0). An StkError will be thrown if the \c - channel argument is equal to or greater than the number of - channels in the StkFrames object. - */ - StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + //! Set the mixture of input and "effected" levels in the output (0.0 = input only, 1.0 = effect only). + void setEffectMix( StkFloat mix ); protected: - // This abstract function must be implemented in all subclasses. - // It is used to get around a C++ problem with overloaded virtual - // functions. - virtual StkFloat computeSample( StkFloat input ) = 0; - // Returns true if argument value is prime. - bool isPrime( int number ); + bool isPrime( unsigned int number ); - StkFloat lastOutput_[2]; + StkFrames lastFrame_; StkFloat effectMix_; }; +inline 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; +} + +inline bool Effect :: isPrime( unsigned int number ) +{ + if ( number == 2 ) return true; + if ( number & 1 ) { + for ( int i=3; i<(int)sqrt((double)number)+1; i+=2 ) + if ( (number % i) == 0 ) return false; + return true; // prime + } + else return false; // even +} + +} // stk namespace + #endif diff --git a/include/Envelope.h b/include/Envelope.h index 23c97d6..7530dd2 100644 --- a/include/Envelope.h +++ b/include/Envelope.h @@ -1,64 +1,76 @@ -/***************************************************/ -/*! \class Envelope - \brief STK envelope base class. - - This class implements a simple envelope - generator which is capable of ramping to - a target value by a specified \e rate. - It also responds to simple \e keyOn and - \e keyOff messages, ramping to 1.0 on - keyOn and to 0.0 on keyOff. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - #ifndef STK_ENVELOPE_H #define STK_ENVELOPE_H #include "Generator.h" +namespace stk { + +/***************************************************/ +/*! \class Envelope + \brief STK linear line envelope class. + + This class implements a simple linear line envelope generator + which is capable of ramping to an arbitrary target value by a + specified \e rate. It also responds to simple \e keyOn and \e + keyOff messages, ramping to 1.0 on keyOn and to 0.0 on keyOff. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + class Envelope : public Generator { public: //! Default constructor. - Envelope(void); - - //! Copy constructor. - Envelope( const Envelope& e ); + Envelope( void ); //! Class destructor. - virtual ~Envelope(void); + ~Envelope( void ); //! Assignment operator. Envelope& operator= ( const Envelope& e ); //! Set target = 1. - virtual void keyOn(void); + void keyOn( void ) { this->setTarget( 1.0 ); }; //! Set target = 0. - virtual void keyOff(void); + void keyOff( void ) { this->setTarget( 0.0 ); }; //! Set the \e rate. - void setRate(StkFloat rate); + void setRate( StkFloat rate ); //! Set the \e rate based on a time duration. - void setTime(StkFloat time); + void setTime( StkFloat time ); //! Set the target value. - virtual void setTarget(StkFloat target); + void setTarget( StkFloat target ); - //! Set current and target values to \e aValue. - virtual void setValue(StkFloat value); + //! Set current and target values to \e value. + void setValue( StkFloat value ); //! Return the current envelope \e state (0 = at target, 1 otherwise). - virtual int getState(void) const; + int getState( void ) const { return state_; }; + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); protected: - virtual StkFloat computeSample( void ); - virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); StkFloat value_; StkFloat target_; @@ -66,4 +78,85 @@ class Envelope : public Generator int state_; }; +inline void Envelope :: setRate( StkFloat rate ) +{ +#if defined(_STK_DEBUG_) + if ( rate < 0.0 ) { + errorString_ << "Envelope::setRate: negative rates not allowed ... correcting!"; + handleError( StkError::WARNING ); + rate_ = -rate; + } + else +#endif + rate_ = rate; +} + +inline void Envelope :: setTime( StkFloat time ) +{ +#if defined(_STK_DEBUG_) + if ( time < 0.0 ) { + errorString_ << "Envelope::setTime: negative times not allowed ... correcting!"; + handleError( StkError::WARNING ); + rate_ = 1.0 / ( -time * Stk::sampleRate() ); + } + else +#endif + rate_ = 1.0 / ( time * Stk::sampleRate() ); +} + +inline void Envelope :: setTarget( StkFloat target ) +{ + target_ = target; + if ( value_ != target_ ) state_ = 1; +} + +inline void Envelope :: setValue( StkFloat value ) +{ + state_ = 0; + target_ = value; + value_ = value; +} + +inline StkFloat Envelope :: tick( void ) +{ + if ( state_ ) { + if ( target_ > value_ ) { + value_ += rate_; + if ( value_ >= target_ ) { + value_ = target_; + state_ = 0; + } + } + else { + value_ -= rate_; + if ( value_ <= target_ ) { + value_ = target_; + state_ = 0; + } + } + lastFrame_[0] = value_; + } + + return value_; +} + +inline StkFrames& Envelope :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Envelope::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i adsr_; - std::vector waves_; + std::vector waves_; SineWave vibrato_; TwoZero twozero_; unsigned int nOperators_; @@ -104,4 +107,6 @@ class FM : public Instrmnt }; +} // stk namespace + #endif diff --git a/include/FMVoices.h b/include/FMVoices.h index ec8dff1..a25583a 100644 --- a/include/FMVoices.h +++ b/include/FMVoices.h @@ -1,3 +1,10 @@ +#ifndef STK_FMVOICES_H +#define STK_FMVOICES_H + +#include "FM.h" + +namespace stk { + /***************************************************/ /*! \class FMVoices \brief STK singing FM synthesis instrument. @@ -26,15 +33,10 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_FMVOICES_H -#define STK_FMVOICES_H - -#include "FM.h" - class FMVoices : public FM { public: @@ -42,27 +44,55 @@ class FMVoices : public FM /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - FMVoices(); + FMVoices( void ); //! Class destructor. - ~FMVoices(); + ~FMVoices( void ); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - int currentVowel_; StkFloat tilt_[3]; StkFloat mods_[3]; }; +inline StkFloat FMVoices :: tick( unsigned int ) +{ + register StkFloat temp, temp2; + + 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]->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(); + + lastFrame_[0] = temp * 0.33; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/FileLoop.h b/include/FileLoop.h new file mode 100644 index 0000000..0050070 --- /dev/null +++ b/include/FileLoop.h @@ -0,0 +1,161 @@ +#ifndef STK_FILELOOP_H +#define STK_FILELOOP_H + +#include "FileWvIn.h" + +namespace stk { + +/***************************************************/ +/*! \class FileLoop + \brief STK file looping / oscillator class. + + This class provides audio file looping functionality. Any audio + file that can be loaded by FileRead can be looped using this + class. + + FileLoop supports multi-channel data. It is important to + distinguish the tick() method that computes a single frame (and + returns only the specified sample of a multi-channel frame) from + the overloaded one that takes an StkFrames object for + multi-channel and/or multi-frame data. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + +class FileLoop : protected FileWvIn +{ + public: + //! Default constructor. + FileLoop( unsigned long chunkThreshold = 1000000, unsigned long chunkSize = 1024 ); + + //! Class constructor that opens a specified file. + FileLoop( std::string fileName, bool raw = false, bool doNormalize = true, + unsigned long chunkThreshold = 1000000, unsigned long chunkSize = 1024 ); + + //! Class destructor. + ~FileLoop( void ); + + //! Open the specified file and load its data. + /*! + Data from a previously opened file will be overwritten by this + function. An StkError will be thrown if the file is not found, + its format is unknown, or a read error occurs. If the file data + is to be loaded incrementally from disk and normalization is + specified, a scaling will be applied with respect to fixed-point + limits. If the data format is floating-point, no scaling is + performed. + */ + void openFile( std::string fileName, bool raw = false, bool doNormalize = true ); + + //! Close a file if one is open. + void closeFile( void ) { FileWvIn::closeFile(); }; + + //! Clear outputs and reset time (file) pointer to zero. + void reset( void ) { FileWvIn::reset(); }; + + //! Normalize data to a maximum of +-1.0. + /*! + This function has no effect when data is incrementally loaded + from disk. + */ + void normalize( void ) { FileWvIn::normalize( 1.0 ); }; + + //! Normalize data to a maximum of \e +-peak. + /*! + This function has no effect when data is incrementally loaded + from disk. + */ + void normalize( StkFloat peak ) { FileWvIn::normalize( peak ); }; + + //! Return the file size in sample frames. + unsigned long getSize( void ) const { return data_.frames(); }; + + //! Return the input file sample rate in Hz (not the data read rate). + /*! + WAV, SND, and AIF formatted files specify a sample rate in + 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. + */ + StkFloat getFileRate( void ) const { return data_.dataRate(); }; + + //! Set the data read rate in samples. The rate can be negative. + /*! + If the rate value is negative, the data is read in reverse order. + */ + void setRate( StkFloat rate ); + + //! Set the data interpolation rate based on a looping frequency. + /*! + This function determines the interpolation rate based on the file + size and the current Stk::sampleRate. The \e frequency value + corresponds to file cycles per second. The frequency can be + negative, in which case the loop is read in reverse order. + */ + void setFrequency( StkFloat frequency ) { this->setRate( file_.fileSize() * frequency / Stk::sampleRate() ); }; + + //! Increment the read pointer by \e time samples, modulo file size. + void addTime( StkFloat time ); + + //! Increment current read pointer by \e angle, relative to a looping frequency. + /*! + This function increments the read pointer based on the file + size and the current Stk::sampleRate. The \e anAngle value + is a multiple of file size. + */ + void addPhase( StkFloat angle ); + + //! Add a phase offset to the current read pointer. + /*! + This function determines a time offset based on the file + size and the current Stk::sampleRate. The \e angle value + is a multiple of file size. + */ + void addPhaseOffset( StkFloat angle ); + + //! Return the specified channel value of the last computed frame. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the last computed frame. If no file data is + loaded, the returned value is 0.0. The \c channel argument must + be less than the number of channels in the file data (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFloat lastOut( unsigned int channel = 0 ) { return FileWvIn::lastOut( channel ); }; + + //! Compute a sample frame and return the specified \c channel value. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the computed frame. If no file data is loaded, + the returned value is 0.0. The \c channel argument must be less + than the number of channels in the file data (the first channel is + specified by 0). However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames argument with computed frames and return the same reference. + /*! + The number of channels in the StkFrames argument should equal + the number of channels in the file data. However, this is only + checked if _STK_DEBUG_ is defined during compilation, in which + case an incompatibility will trigger an StkError exception. If no + file data is loaded, the function does nothing (a warning will be + issued if _STK_DEBUG_ is defined during compilation and + Stk::showWarnings() has been set to \e true). + */ + StkFrames& tick( StkFrames& frames ); + + protected: + + StkFrames firstFrame_; + StkFloat phaseOffset_; + +}; + +} // stk namespace + +#endif diff --git a/include/FileRead.h b/include/FileRead.h index 7bd04d4..7bf8414 100644 --- a/include/FileRead.h +++ b/include/FileRead.h @@ -1,3 +1,10 @@ +#ifndef STK_FILEREAD_H +#define STK_FILEREAD_H + +#include "Stk.h" + +namespace stk { + /***************************************************/ /*! \class FileRead \brief STK audio file input class. @@ -25,20 +32,15 @@ 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_FILEREAD_H -#define STK_FILEREAD_H - -#include "Stk.h" - class FileRead : public Stk { public: //! Default constructor. - FileRead(); + FileRead( void ); //! Overloaded constructor that opens a file during instantiation. /*! @@ -51,7 +53,7 @@ public: StkFormat format = STK_SINT16, StkFloat rate = 22050.0 ); //! Class destructor. - ~FileRead(); + ~FileRead( void ); //! Open the specified file and determine its formatting. /*! @@ -126,4 +128,6 @@ protected: StkFloat fileRate_; }; +} // stk namespace + #endif diff --git a/include/FileWrite.h b/include/FileWrite.h index c43db1b..663f852 100644 --- a/include/FileWrite.h +++ b/include/FileWrite.h @@ -1,3 +1,10 @@ +#ifndef STK_FILEWRITE_H +#define STK_FILEWRITE_H + +#include "Stk.h" + +namespace stk { + /***************************************************/ /*! \class FileWrite \brief STK audio file output class. @@ -17,15 +24,10 @@ type, the data type will automatically be modified. Compressed data types are not supported. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_FILEWRITE_H -#define STK_FILEWRITE_H - -#include "Stk.h" - class FileWrite : public Stk { public: @@ -39,7 +41,7 @@ class FileWrite : public Stk static const FILE_TYPE FILE_MAT; /*!< Matlab MAT-file type. */ //! Default constructor. - FileWrite(); + FileWrite( void ); //! Overloaded constructor used to specify a file name, type, and data format with this object. /*! @@ -109,4 +111,6 @@ class FileWrite : public Stk }; +} // stk namespace + #endif diff --git a/include/FileWvIn.h b/include/FileWvIn.h index ec54032..dc0caab 100644 --- a/include/FileWvIn.h +++ b/include/FileWvIn.h @@ -1,17 +1,26 @@ +#ifndef STK_FILEWVIN_H +#define STK_FILEWVIN_H + +#include "WvIn.h" +#include "FileRead.h" + +namespace stk { + /***************************************************/ /*! \class FileWvIn \brief STK audio file input class. This class inherits from WvIn. It provides a "tick-level" interface to the FileRead class. It also provides variable-rate - "playback" functionality. Audio file support is provided by the - FileRead class. Linear interpolation is used for fractional "read - rates". + playback functionality. Audio file support is provided by the + FileRead class. Linear interpolation is used for fractional read + rates. - FileWvIn supports multi-channel data. It is important to distinguish - the tick() methods, which return samples produced by averaging - across sample frames, from the tickFrame() methods, which return - references to multi-channel sample frames. + FileWvIn supports multi-channel data. It is important to + distinguish the tick() method that computes a single frame (and + returns only the specified sample of a multi-channel frame) from + the overloaded one that takes an StkFrames object for + multi-channel and/or multi-frame data. FileWvIn will either load the entire content of an audio file into local memory or incrementally read file data from disk in chunks. @@ -21,22 +30,15 @@ chunks of \e chunkSize each (also in sample frames). When the file end is reached, subsequent calls to the tick() - functions return zero-valued data and isFinished() returns \e - true. + functions return zeros and isFinished() returns \e true. See the FileRead class for a description of the supported audio file formats. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_FILEWVIN_H -#define STK_FILEWVIN_H - -#include "WvIn.h" -#include "FileRead.h" - class FileWvIn : public WvIn { public: @@ -52,7 +54,7 @@ public: unsigned long chunkThreshold = 1000000, unsigned long chunkSize = 1024 ); //! Class destructor. - virtual ~FileWvIn(); + ~FileWvIn( void ); //! Open the specified file and load its data. /*! @@ -64,30 +66,30 @@ public: limits. If the data format is floating-point, no scaling is performed. */ - void openFile( std::string fileName, bool raw = false, bool doNormalize = true ); + virtual void openFile( std::string fileName, bool raw = false, bool doNormalize = true ); //! Close a file if one is open. - void closeFile( void ); + virtual void closeFile( void ); //! Clear outputs and reset time (file) pointer to zero. - void reset( void ); + virtual void reset( void ); //! Normalize data to a maximum of +-1.0. /*! This function has no effect when data is incrementally loaded from disk. */ - void normalize( void ); + virtual void normalize( void ); //! Normalize data to a maximum of \e +-peak. /*! This function has no effect when data is incrementally loaded from disk. */ - void normalize( StkFloat peak ); + virtual void normalize( StkFloat peak ); //! Return the file size in sample frames. - unsigned long getSize( void ) const { return data_.frames(); }; + virtual unsigned long getSize( void ) const { return data_.frames(); }; //! Return the input file sample rate in Hz (not the data read rate). /*! @@ -95,7 +97,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. */ - StkFloat getFileRate( void ) const { return data_.dataRate(); }; + virtual StkFloat getFileRate( void ) const { return data_.dataRate(); }; //! Query whether reading is complete. bool isFinished( void ) const { return finished_; }; @@ -104,7 +106,7 @@ public: /*! If the rate value is negative, the data is read in reverse order. */ - void setRate( StkFloat rate ); + virtual void setRate( StkFloat rate ); //! Increment the read pointer by \e time samples. /*! @@ -121,12 +123,44 @@ public: */ void setInterpolate( bool doInterpolate ) { interpolate_ = doInterpolate; }; - StkFloat lastOut( void ) const; + //! Return the specified channel value of the last computed frame. + /*! + If no file is loaded, the returned value is 0.0. The \c + channel argument must be less than the number of output channels, + which can be determined with the channelsOut() function (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. \sa + lastFrame() + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Compute a sample frame and return the specified \c channel value. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the computed frame. If no file data is loaded, + the returned value is 0.0. The \c channel argument must be less + than the number of channels in the file data (the first channel is + specified by 0). However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + virtual StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames argument with computed frames and return the same reference. + /*! + The number of channels in the StkFrames argument must equal + the number of channels in the file data. However, this is only + checked if _STK_DEBUG_ is defined during compilation, in which + case an incompatibility will trigger an StkError exception. If no + file data is loaded, the function does nothing (a warning will be + issued if _STK_DEBUG_ is defined during compilation). + */ + virtual StkFrames& tick( StkFrames& frames ); protected: - virtual void computeFrame( void ); - virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); FileRead file_; bool finished_; @@ -141,4 +175,19 @@ protected: }; +inline StkFloat FileWvIn :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= data_.channels() ) { + errorString_ << "FileWvIn::lastOut(): channel argument and soundfile data are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + if ( finished_ ) return 0.0; + return lastFrame_[channel]; +} + +} // stk namespace + #endif diff --git a/include/FileWvOut.h b/include/FileWvOut.h index 3af786f..eed517f 100644 --- a/include/FileWvOut.h +++ b/include/FileWvOut.h @@ -1,3 +1,11 @@ +#ifndef STK_FILEWVOUT_H +#define STK_FILEWVOUT_H + +#include "WvOut.h" +#include "FileWrite.h" + +namespace stk { + /***************************************************/ /*! \class FileWvOut \brief STK audio file output class. @@ -7,9 +15,9 @@ FileWvOut writes samples to an audio file and supports multi-channel data. It is important to distinguish the tick() - methods, which output single samples to all channels in a sample - frame, from the tickFrame() methods, which take a pointer or - reference to multi-channel sample frame data. + method that outputs a single sample to all channels in a sample + frame from the overloaded one that takes a reference to an + StkFrames object for multi-channel and/or multi-frame data. See the FileWrite class for a description of the supported audio file formats. @@ -17,16 +25,10 @@ Currently, FileWvOut is non-interpolating and the output rate is always Stk::sampleRate(). - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_FILEWVOUT_H -#define STK_FILEWVOUT_H - -#include "WvOut.h" -#include "FileWrite.h" - class FileWvOut : public WvOut { public: @@ -69,12 +71,23 @@ class FileWvOut : public WvOut */ void closeFile( void ); + //! Output a single sample to all channels in a sample frame. + /*! + An StkError is thrown if an output error occurs. + */ + void tick( const StkFloat sample ); + + //! Output the StkFrames data. + /*! + An StkError will be thrown if an output error occurs. An + StkError will also be thrown if _STK_DEBUG_ is defined during + compilation and there is an incompatability between the number of + channels in the FileWvOut object and that in the StkFrames object. + */ + void tick( const StkFrames& frames ); + protected: - void computeSample( const StkFloat sample ); - - void computeFrames( const StkFrames& frames ); - void incrementFrame( void ); FileWrite file_; @@ -84,4 +97,6 @@ class FileWvOut : public WvOut }; +} // stk namespace + #endif diff --git a/include/Filter.h b/include/Filter.h index 2ed9106..76403f0 100644 --- a/include/Filter.h +++ b/include/Filter.h @@ -1,122 +1,86 @@ -/***************************************************/ -/*! \class Filter - \brief STK filter class. - - This class implements a generic structure that - can be used to create a wide range of filters. - It can function independently or be subclassed - to provide more specific controls based on a - particular filter type. - - In particular, this class implements the standard - difference equation: - - 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 - are normalized by a[0]. - - The \e gain parameter is applied at the filter - input and does not affect the coefficient values. - The default gain value is 1.0. This structure - 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 - 2007. -*/ -/***************************************************/ - #ifndef STK_FILTER_H #define STK_FILTER_H #include "Stk.h" #include +namespace stk { + +/***************************************************/ +/*! \class Filter + \brief STK abstract filter class. + + This class provides limited common functionality for STK digital + filter subclasses. It is general enough to support both + monophonic and polyphonic input/output classes. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + class Filter : public Stk { public: - //! Default constructor creates a zero-order pass-through "filter". - Filter(void); + //! Class constructor. + Filter( void ) { gain_ = 1.0; channelsIn_ = 1; lastFrame_.resize( 1, 1, 0.0 ); }; - //! Overloaded constructor which takes filter coefficients. - /*! - 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( std::vector &bCoefficients, std::vector &aCoefficients ); + //! Return the number of input channels for the class. + unsigned int channelsIn( void ) const { return channelsIn_; }; - //! Class destructor. - virtual ~Filter(void); + //! Return the number of output channels for the class. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; - //! Sets all internal states of the filter to zero. - void clear(void); - - //! Set filter coefficients. - /*! - 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 not cleared unless the - \e clearState flag is \c true. - */ - void setCoefficients( std::vector &bCoefficients, std::vector &aCoefficients, bool clearState = false ); - - //! Set numerator coefficients. - /*! - 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 not - cleared unless the \e clearState flag is \c true. - */ - void setNumerator( std::vector &bCoefficients, bool clearState = false ); - - //! Set denominator coefficients. - /*! - 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 not cleared - unless the \e clearState flag is \c true. - */ - void setDenominator( std::vector &aCoefficients, bool clearState = false ); + //! Clears all internal states of the filter. + virtual void clear( void ); //! 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(StkFloat gain); + void setGain( StkFloat gain ) { gain_ = gain; }; //! Return the current filter gain. - virtual StkFloat getGain(void) const; + StkFloat getGain( void ) const { return gain_; }; - //! Return the last computed output value. - virtual StkFloat lastOut(void) const; - - //! Input one sample to the filter and return one output. - virtual StkFloat tick( StkFloat input ); + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. /*! - The \c channel argument should be zero or greater (the first - channel is specified by 0). An StkError will be thrown if the \c - channel argument is equal to or greater than the number of - channels in the StkFrames object. + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. */ - virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ) = 0; protected: + unsigned int channelsIn_; + StkFrames lastFrame_; + StkFloat gain_; std::vector b_; std::vector a_; - std::vector outputs_; - std::vector inputs_; + StkFrames outputs_; + StkFrames inputs_; }; +inline void Filter :: clear( void ) +{ + unsigned int i; + for ( i=0; i &coefficients ); + + //! Class destructor. + ~Fir( void ); + + //! Set filter coefficients. + /*! + An StkError can be thrown if the coefficient vector size is + zero. The internal state of the filter is not cleared unless the + \e clearState flag is \c true. + */ + void setCoefficients( std::vector &coefficients, bool clearState = false ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + +protected: + +}; + +inline StkFloat Fir :: tick( StkFloat input ) +{ + lastFrame_[0] = 0.0; + inputs_[0] = gain_ * input; + + for ( unsigned int i=b_.size()-1; i>0; i-- ) { + lastFrame_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + lastFrame_[0] += b_[0] * inputs_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& Fir :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Fir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int i, hop = frames.channels(); + for ( unsigned int j=0; j0; i-- ) { + *samples += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + *samples += b_[0] * inputs_[0]; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& Fir :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "Fir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int i, iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int j=0; j0; i-- ) { + *oSamples += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + *oSamples += b_[0] * inputs_[0]; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + +#endif diff --git a/include/Flute.h b/include/Flute.h index 9b95f9f..fb4d904 100644 --- a/include/Flute.h +++ b/include/Flute.h @@ -1,3 +1,17 @@ +#ifndef STK_FLUTE_H +#define STK_FLUTE_H + +#include "Instrmnt.h" +#include "JetTable.h" +#include "DelayL.h" +#include "OnePole.h" +#include "PoleZero.h" +#include "Noise.h" +#include "ADSR.h" +#include "SineWave.h" + +namespace stk { + /***************************************************/ /*! \class Flute \brief STK flute physical model class. @@ -18,22 +32,10 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_FLUTE_H -#define STK_FLUTE_H - -#include "Instrmnt.h" -#include "JetTable.h" -#include "DelayL.h" -#include "OnePole.h" -#include "PoleZero.h" -#include "Noise.h" -#include "ADSR.h" -#include "SineWave.h" - class Flute : public Instrmnt { public: @@ -41,45 +43,46 @@ class Flute : public Instrmnt /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - Flute(StkFloat lowestFrequency); + Flute( StkFloat lowestFrequency ); //! Class destructor. - ~Flute(); + ~Flute( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Set the reflection coefficient for the jet delay (-1.0 - 1.0). - void setJetReflection(StkFloat coefficient); + void setJetReflection( StkFloat coefficient ); //! Set the reflection coefficient for the air column delay (-1.0 - 1.0). - void setEndReflection(StkFloat 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(StkFloat aRatio); + void setJetDelay( StkFloat aRatio ); //! Apply breath velocity to instrument with given amplitude and rate of increase. - void startBlowing(StkFloat amplitude, StkFloat rate); + void startBlowing( StkFloat amplitude, StkFloat rate ); //! Decrease breath velocity with given rate of decrease. - void stopBlowing(StkFloat rate); + void stopBlowing( StkFloat rate ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayL jetDelay_; DelayL boreDelay_; JetTable jetTable_; @@ -100,4 +103,27 @@ class Flute : public Instrmnt }; +inline StkFloat Flute :: tick( unsigned int ) +{ + StkFloat pressureDiff; + StkFloat breathPressure; + + // Calculate the breath pressure (envelope + noise + vibrato) + breathPressure = maxPressure_ * adsr_.tick(); + breathPressure += breathPressure * ( noiseGain_ * noise_.tick() + vibratoGain_ * vibrato_.tick() ); + + 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); + lastFrame_[0] = (StkFloat) 0.3 * boreDelay_.tick( pressureDiff ); + + lastFrame_[0] *= outputGain_; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/FormSwep.h b/include/FormSwep.h index f7daf56..905d3db 100644 --- a/include/FormSwep.h +++ b/include/FormSwep.h @@ -1,32 +1,35 @@ +#ifndef STK_FORMSWEP_H +#define STK_FORMSWEP_H + +#include "Filter.h" + +namespace stk { + /***************************************************/ /*! \class FormSwep \brief STK sweepable formant filter class. - This public BiQuad filter subclass implements - a formant (resonance) which can be "swept" - over time from one frequency setting to another. - It provides methods for controlling the sweep - rate and target frequency. + This class implements a formant (resonance) which can be "swept" + over time from one frequency setting to another. It provides + methods for controlling the sweep rate and target frequency. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_FORMSWEP_H -#define STK_FORMSWEP_H - -#include "BiQuad.h" - -class FormSwep : public BiQuad +class FormSwep : public Filter { public: //! Default constructor creates a second-order pass-through filter. - FormSwep(); + FormSwep( void ); //! Class destructor. ~FormSwep(); + //! A function to enable/disable the automatic updating of class data when the STK sample rate changes. + void ignoreSampleRateChange( bool ignore = true ) { ignoreSampleRateChange_ = ignore; }; + //! Sets the filter coefficients for a resonance at \e frequency (in Hz). /*! This method determines the filter coefficients corresponding to @@ -39,13 +42,13 @@ class FormSwep : public BiQuad the unit-circle (\e radius close to one), the narrower the resulting resonance width. */ - void setResonance(StkFloat frequency, StkFloat radius); + void setResonance( StkFloat frequency, StkFloat radius ); //! Set both the current and target resonance parameters. - void setStates(StkFloat frequency, StkFloat radius, StkFloat gain = 1.0); + void setStates( StkFloat frequency, StkFloat radius, StkFloat gain = 1.0 ); //! Set target resonance parameters. - void setTargets(StkFloat frequency, StkFloat radius, StkFloat gain = 1.0); + void setTargets( StkFloat frequency, StkFloat radius, StkFloat gain = 1.0 ); //! Set the sweep rate (between 0.0 - 1.0). /*! @@ -56,7 +59,7 @@ class FormSwep : public BiQuad target values. A sweep rate of 0.0 will produce no change in resonance parameters. */ - void setSweepRate(StkFloat rate); + void setSweepRate( StkFloat rate ); //! Set the sweep rate in terms of a time value in seconds. /*! @@ -64,11 +67,39 @@ class FormSwep : public BiQuad given time for the formant parameters to reach their target values. */ - void setSweepTime(StkFloat time); + void setSweepTime( StkFloat time ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return a reference to one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); protected: - StkFloat computeSample( StkFloat input ); + virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); bool dirty_; StkFloat frequency_; @@ -87,4 +118,71 @@ class FormSwep : public BiQuad }; +inline StkFloat FormSwep :: tick( StkFloat input ) +{ + if ( dirty_ ) { + sweepState_ += sweepRate_; + if ( sweepState_ >= 1.0 ) { + sweepState_ = 1.0; + dirty_ = false; + radius_ = targetRadius_; + frequency_ = targetFrequency_; + gain_ = targetGain_; + } + else { + radius_ = startRadius_ + (deltaRadius_ * sweepState_); + frequency_ = startFrequency_ + (deltaFrequency_ * sweepState_); + gain_ = startGain_ + (deltaGain_ * sweepState_); + } + this->setResonance( frequency_, radius_ ); + } + + inputs_[0] = gain_ * input; + lastFrame_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2]; + lastFrame_[0] -= a_[2] * outputs_[2] + a_[1] * outputs_[1]; + inputs_[2] = inputs_[1]; + inputs_[1] = inputs_[0]; + outputs_[2] = outputs_[1]; + outputs_[1] = lastFrame_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& FormSwep :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "FormSwep::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "FormSwep::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= lastFrame_.channels() ) { + errorString_ << "Granulate::lastOut(): channel argument is invalid!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFrames& Granulate :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + errorString_ << "Granulate::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + for ( unsigned int i=0; isetFrequency(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 ); + + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = (1.0 - (control2_ * 0.5)) * gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); + + 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(); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Iir.h b/include/Iir.h new file mode 100644 index 0000000..f349cc7 --- /dev/null +++ b/include/Iir.h @@ -0,0 +1,202 @@ +#ifndef STK_IIR_H +#define STK_IIR_H + +#include "Filter.h" + +namespace stk { + +/***************************************************/ +/*! \class Iir + \brief STK general infinite impulse response filter class. + + This class provides a generic digital filter structure that can be + used to implement IIR filters. For filters containing only + feedforward terms, the Fir class is slightly more efficient. + + In particular, this class implements the standard difference + equation: + + 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 are normalized + by a[0]. + + The \e gain parameter is applied at the filter input and does not + affect the coefficient values. The default gain value is 1.0. + This structure 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 - 2009. +*/ +/***************************************************/ + +class Iir : public Filter +{ +public: + //! Default constructor creates a zero-order pass-through "filter". + Iir( void ); + + //! Overloaded constructor which takes filter coefficients. + /*! + An StkError can be thrown if either of the coefficient vector + sizes is zero, or if the a[0] coefficient is equal to zero. + */ + Iir( std::vector &bCoefficients, std::vector &aCoefficients ); + + //! Class destructor. + ~Iir( void ); + + //! Set filter coefficients. + /*! + 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 not cleared unless the + \e clearState flag is \c true. + */ + void setCoefficients( std::vector &bCoefficients, std::vector &aCoefficients, bool clearState = false ); + + //! Set numerator coefficients. + /*! + 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 not + cleared unless the \e clearState flag is \c true. + */ + void setNumerator( std::vector &bCoefficients, bool clearState = false ); + + //! Set denominator coefficients. + /*! + 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 not cleared + unless the \e clearState flag is \c true. + */ + void setDenominator( std::vector &aCoefficients, bool clearState = false ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + +protected: + +}; + +inline StkFloat Iir :: tick( StkFloat input ) +{ + unsigned int i; + + outputs_[0] = 0.0; + inputs_[0] = gain_ * input; + 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]; + + for ( i=a_.size()-1; i>0; i-- ) { + outputs_[0] += -a_[i] * outputs_[i]; + outputs_[i] = outputs_[i-1]; + } + + lastFrame_[0] = outputs_[0]; + return lastFrame_[0]; +} + +inline StkFrames& Iir :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Iir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int i, hop = frames.channels(); + for ( unsigned int j=0; j0; i-- ) { + outputs_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + outputs_[0] += b_[0] * inputs_[0]; + + for ( i=a_.size()-1; i>0; i-- ) { + outputs_[0] += -a_[i] * outputs_[i]; + outputs_[i] = outputs_[i-1]; + } + + *samples = outputs_[0]; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& Iir :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "Iir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int i, iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int j=0; j0; i-- ) { + outputs_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + outputs_[0] += b_[0] * inputs_[0]; + + for ( i=a_.size()-1; i>0; i-- ) { + outputs_[0] += -a_[i] * outputs_[i]; + outputs_[i] = outputs_[i-1]; + } + + *oSamples = outputs_[0]; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + +#endif diff --git a/include/InetWvIn.h b/include/InetWvIn.h index c2aee83..71c10c3 100644 --- a/include/InetWvIn.h +++ b/include/InetWvIn.h @@ -1,3 +1,14 @@ +#ifndef STK_INETWVIN_H +#define STK_INETWVIN_H + +#include "WvIn.h" +#include "TcpServer.h" +#include "UdpSocket.h" +#include "Thread.h" +#include "Mutex.h" + +namespace stk { + /***************************************************/ /*! \class InetWvIn \brief STK internet streaming input class. @@ -8,10 +19,10 @@ supported. InetWvIn supports multi-channel data. It is important to - distinguish the tick() methods, which return samples produced by - averaging across sample frames, from the tickFrame() methods, - which return references or pointers to multi-channel sample - frames. + distinguish the tick() method that computes a single frame (and + returns only the specified sample of a multi-channel frame) from + the overloaded one that takes an StkFrames object for + multi-channel and/or multi-frame data. This class implements a socket server. When using the TCP protocol, the server "listens" for a single remote connection @@ -20,19 +31,10 @@ data type for the incoming stream is signed 16-bit integers, though any of the defined StkFormats are permissible. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_INETWVIN_H -#define STK_INETWVIN_H - -#include "WvIn.h" -#include "TcpServer.h" -#include "UdpSocket.h" -#include "Thread.h" -#include "Mutex.h" - typedef struct { bool finished; void *object; @@ -69,9 +71,46 @@ public: */ bool isConnected( void ); + //! Return the specified channel value of the last computed frame. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the last computed frame. If no connection exists, + the returned value is 0.0. The \c channel argument must be less + than the number of channels in the data stream (the first channel + is specified by 0). However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Compute a sample frame and return the specified \c channel value. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the computed frame. If no connection exists, the + returned value is 0.0 (and a warning will be issued if _STK_DEBUG_ + is defined during compilation). The \c channel argument must be + less than the number of channels in the data stream (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames argument with computed frames and return the same reference. + /*! + The number of channels in the StkFrames argument must equal the + number of channels specified in the listen() function. However, + this is only checked if _STK_DEBUG_ is defined during compilation, + in which case an incompatibility will trigger an StkError + exception. If no connection exists, the function does + nothing (a warning will be issued if _STK_DEBUG_ is defined during + compilation). + */ + StkFrames& tick( 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 - // had to be made public for access from the thread. + // must be public for access from the thread. void receive( void ); protected: @@ -79,8 +118,6 @@ protected: // Read buffered socket data into the data buffer ... will block if none available. int readData( void ); - void computeFrame( void ); - Socket *soket_; Thread thread_; Mutex mutex_; @@ -100,4 +137,21 @@ protected: }; +inline StkFloat InetWvIn :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= data_.channels() ) { + errorString_ << "InetWvIn::lastOut(): channel argument and data stream are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + // If no connection and we've output all samples in the queue, return. + if ( !connected_ && bytesFilled_ == 0 && bufferCounter_ == 0 ) return 0.0; + + return lastFrame_[channel]; +} + +} // stk namespace + #endif diff --git a/include/InetWvOut.h b/include/InetWvOut.h index 96c273e..fe4b996 100644 --- a/include/InetWvOut.h +++ b/include/InetWvOut.h @@ -1,3 +1,11 @@ +#ifndef STK_INETWVOUT_H +#define STK_INETWVOUT_H + +#include "WvOut.h" +#include "Socket.h" + +namespace stk { + /***************************************************/ /*! \class InetWvOut \brief STK internet streaming output class. @@ -7,25 +15,20 @@ order, if necessary, before being transmitted. InetWvOut supports multi-channel data. It is important to - distinguish the tick() methods, which output single samples to all - channels in a sample frame, from the tickFrame() method, which - takes a reference to multi-channel sample frame data. + distinguish the tick() method that outputs a single sample to all + channels in a sample frame from the overloaded one that takes a + reference to an StkFrames object for multi-channel and/or + multi-frame data. This class connects to a socket server, the 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 StkFormats are permissible. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_INETWVOUT_H -#define STK_INETWVOUT_H - -#include "WvOut.h" -#include "Socket.h" - class InetWvOut : public WvOut { public: @@ -51,14 +54,30 @@ class InetWvOut : public WvOut std::string 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); + void disconnect( void ); + + //! Output a single sample to all channels in a sample frame. + /*! + An StkError is thrown if an output error occurs. If a socket + connection does not exist, the function does nothing (a warning + will be issued if _STK_DEBUG_ is defined during compilation). + */ + void tick( const StkFloat sample ); + + //! Output the StkFrames data. + /*! + An StkError will be thrown if an output error occurs. An + StkError will also be thrown if _STK_DEBUG_ is defined during + compilation and there is an incompatability between the number of + channels in the FileWvOut object and that in the StkFrames object. + If a socket connection does not exist, the function does nothing + (a warning will be issued if _STK_DEBUG_ is defined during + compilation). + */ + void tick( const StkFrames& frames ); protected: - void computeSample( const StkFloat sample ); - - void computeFrames( const StkFrames& frames ); - void incrementFrame( void ); // Write a buffer of length frames via the socket connection. @@ -74,4 +93,6 @@ class InetWvOut : public WvOut Stk::StkFormat dataType_; }; +} // stk namespace + #endif diff --git a/include/Instrmnt.h b/include/Instrmnt.h index 85b2c7e..ecec1ca 100644 --- a/include/Instrmnt.h +++ b/include/Instrmnt.h @@ -1,70 +1,129 @@ -/***************************************************/ -/*! \class Instrmnt - \brief STK instrument abstract base class. - - This class provides a common interface for - all STK instruments. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - #ifndef STK_INSTRMNT_H #define STK_INSTRMNT_H #include "Stk.h" +namespace stk { + +/***************************************************/ +/*! \class Instrmnt + \brief STK instrument abstract base class. + + This class provides a common interface for + all STK instruments. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + class Instrmnt : public Stk { public: - //! Default constructor. - Instrmnt(); - - //! Class destructor. - virtual ~Instrmnt(); + //! Class constructor. + Instrmnt( void ) { lastFrame_.resize( 1, 1, 0.0 ); }; //! Start a note with the given frequency and amplitude. - virtual void noteOn(StkFloat frequency, StkFloat amplitude) = 0; + virtual void noteOn( StkFloat frequency, StkFloat amplitude ) = 0; //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(StkFloat amplitude) = 0; + virtual void noteOff( StkFloat amplitude ) = 0; //! Set instrument parameters for a particular frequency. - virtual void setFrequency(StkFloat frequency); - - //! 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; - - //! Compute one sample and output. - StkFloat tick( void ); - - //! Fill a channel of the StkFrames object with computed outputs. - /*! - The \c channel argument should be zero or greater (the first - channel is specified by 0). An StkError will be thrown if the \c - channel argument is equal to or greater than the number of - channels in the StkFrames object. - */ - StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + virtual void setFrequency( StkFloat frequency ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). virtual void controlChange(int number, StkFloat value); + //! Return the number of output channels for the class. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; + + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; + + //! Return the specified channel value of the last computed frame. + /*! + The \c channel argument must be less than the number of output + channels, which can be determined with the channelsOut() function + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. \sa lastFrame() + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Compute one sample frame and return the specified \c channel value. + /*! + For monophonic instruments, the \c channel argument is ignored. + */ + virtual StkFloat tick( unsigned int channel = 0 ) = 0; + + //! Fill the StkFrames object with computed sample frames, starting at the specified channel. + /*! + The \c channel argument plus the number of output channels must + be less than the number of channels in the StkFrames argument (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + protected: - // This abstract function must be implemented in all subclasses. - // It is used to get around a C++ problem with overloaded virtual - // functions. - virtual StkFloat computeSample( void ) = 0; - - StkFloat lastOutput_; + StkFrames lastFrame_; }; +inline void Instrmnt :: setFrequency(StkFloat frequency) +{ + errorString_ << "Instrmnt::setFrequency: virtual setFrequency function call!"; + handleError( StkError::WARNING ); +} + +inline StkFloat Instrmnt :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= lastFrame_.channels() ) { + errorString_ << "Instrmnt::lastOut(): channel argument is invalid!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFrames& Instrmnt :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + errorString_ << "Instrmnt::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i 1 ) { + errorString_ << "JCRev::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFloat JCRev :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + errorString_ << "JCRev::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + 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; + + temp = allpassDelays_[1].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += temp0; + allpassDelays_[1].tick(temp1); + temp1 = -(allpassCoefficient_ * temp1) + temp; + + temp = allpassDelays_[2].lastOut(); + temp2 = allpassCoefficient_ * temp; + temp2 += temp1; + 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()); + + combDelays_[0].tick(temp3); + combDelays_[1].tick(temp4); + combDelays_[2].tick(temp5); + combDelays_[3].tick(temp6); + + filtout = temp3 + temp4 + temp5 + temp6; + + lastFrame_[0] = effectMix_ * (outLeftDelay_.tick(filtout)); + lastFrame_[1] = effectMix_ * (outRightDelay_.tick(filtout)); + temp = (1.0 - effectMix_) * input; + lastFrame_[0] += temp; + lastFrame_[1] += temp; + + return lastFrame_[channel]; +} + +} // stk namespace + #endif diff --git a/include/JetTable.h b/include/JetTable.h index 2a3f298..8059595 100644 --- a/include/JetTable.h +++ b/include/JetTable.h @@ -1,3 +1,10 @@ +#ifndef STK_JETTABL_H +#define STK_JETTABL_H + +#include "Function.h" + +namespace stk { + /***************************************************/ /*! \class JetTable \brief STK jet table class. @@ -9,28 +16,97 @@ Consult Fletcher and Rossing, Karjalainen, Cook, and others for more information. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_JETTABL_H -#define STK_JETTABL_H - -#include "Function.h" - class JetTable : public Function { public: - //! Default constructor. - JetTable(); - //! Class destructor. - ~JetTable(); + //! Take one sample input and map to one sample of output. + StkFloat tick( StkFloat input ); -protected: + //! Take a channel of the StkFrames object as inputs to the table and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); - StkFloat computeSample( StkFloat input ); + //! Take a channel of the \c iFrames object as inputs to the table and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); }; +inline StkFloat JetTable :: tick( StkFloat input ) +{ + // Perform "table lookup" using a polynomial + // calculation (x^3 - x), which approximates + // the jet sigmoid behavior. + lastFrame_[0] = input * (input * input - 1.0); + + // Saturate at +/- 1.0. + if ( lastFrame_[0] > 1.0 ) lastFrame_[0] = 1.0; + if ( lastFrame_[0] < -1.0 ) lastFrame_[0] = -1.0; + return lastFrame_[0]; +} + +inline StkFrames& JetTable :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "JetTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 1.0) *samples = 1.0; + if ( *samples < -1.0) *samples = -1.0; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& JetTable :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "JetTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0) *oSamples = 1.0; + if ( *oSamples < -1.0) *oSamples = -1.0; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + #endif diff --git a/include/Mandolin.h b/include/Mandolin.h index dee9637..9ceabe4 100644 --- a/include/Mandolin.h +++ b/include/Mandolin.h @@ -1,3 +1,11 @@ +#ifndef STK_MANDOLIN_H +#define STK_MANDOLIN_H + +#include "PluckTwo.h" +#include "FileWvIn.h" + +namespace stk { + /***************************************************/ /*! \class Mandolin \brief STK mandolin instrument model class. @@ -23,48 +31,75 @@ - String Detuning = 1 - Microphone Position = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_MANDOLIN_H -#define STK_MANDOLIN_H - -#include "PluckTwo.h" -#include "FileWvIn.h" - class Mandolin : public PluckTwo { public: //! Class constructor, taking the lowest desired playing frequency. - Mandolin(StkFloat lowestFrequency); + Mandolin( StkFloat lowestFrequency ); //! Class destructor. - ~Mandolin(); + ~Mandolin( void ); //! Pluck the strings with the given amplitude (0.0 - 1.0) using the current frequency. - void pluck(StkFloat amplitude); + void pluck( StkFloat amplitude ); //! Pluck the strings with the given amplitude (0.0 - 1.0) and position (0.0 - 1.0). - void pluck(StkFloat amplitude,StkFloat position); + void pluck( StkFloat amplitude,StkFloat position ); //! Start a note with the given frequency and amplitude (0.0 - 1.0). - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Set the body size (a value of 1.0 produces the "default" size). - void setBodySize(StkFloat size); + void setBodySize( StkFloat size ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - FileWvIn *soundfile_[12]; int mic_; long dampTime_; bool waveDone_; }; +inline StkFloat Mandolin :: tick( unsigned int ) +{ + 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(); + } + + // Damping hack to help avoid overflow on re-plucking. + if ( dampTime_ >=0 ) { + dampTime_ -= 1; + // Calculate 1st delay filtered reflection plus pluck excitation. + lastFrame_[0] = delayLine_.tick( filter_.tick( temp + (delayLine_.lastOut() * 0.7) ) ); + // Calculate 2nd delay just like the 1st. + lastFrame_[0] += delayLine2_.tick( filter2_.tick( temp + (delayLine2_.lastOut() * 0.7) ) ); + } + else { // No damping hack after 1 period. + // Calculate 1st delay filtered reflection plus pluck excitation. + lastFrame_[0] = delayLine_.tick( filter_.tick( temp + (delayLine_.lastOut() * loopGain_) ) ); + // Calculate 2nd delay just like the 1st. + lastFrame_[0] += delayLine2_.tick( filter2_.tick( temp + (delayLine2_.lastOut() * loopGain_) ) ); + } + + lastFrame_[0] *= 0.3; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Mesh2D.h b/include/Mesh2D.h index e580367..91b83e4 100644 --- a/include/Mesh2D.h +++ b/include/Mesh2D.h @@ -1,3 +1,11 @@ +#ifndef STK_MESH2D_H +#define STK_MESH2D_H + +#include "Instrmnt.h" +#include "OnePole.h" + +namespace stk { + /***************************************************/ /*! \class Mesh2D \brief Two-dimensional rectilinear waveguide mesh class. @@ -24,12 +32,6 @@ */ /***************************************************/ -#ifndef STK_MESH2D_H -#define STK_MESH2D_H - -#include "Instrmnt.h" -#include "OnePole.h" - const short NXMAX = 12; const short NYMAX = 12; @@ -37,45 +39,46 @@ class Mesh2D : public Instrmnt { public: //! Class constructor, taking the x and y dimensions in samples. - Mesh2D(short nX, short nY); + Mesh2D( short nX, short nY ); //! Class destructor. - ~Mesh2D(); + ~Mesh2D( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set the x dimension size in samples. - void setNX(short lenX); + void setNX( short lenX ); //! Set the y dimension size in samples. - void setNY(short lenY); + void setNY( short lenY ); //! Set the x, y input position on a 0.0 - 1.0 scale. - void setInputPosition(StkFloat xFactor, StkFloat yFactor); + void setInputPosition( StkFloat xFactor, StkFloat yFactor ); //! Set the loss filters gains (0.0 - 1.0). - void setDecay(StkFloat decayFactor); + void setDecay( StkFloat decayFactor ); //! Impulse the mesh with the given amplitude (frequency ignored). - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay) ... currently ignored. - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Calculate and return the signal energy stored in the mesh. - StkFloat energy(); + StkFloat energy( void ); //! Input a sample to the mesh and compute one output sample. StkFloat inputTick( StkFloat input ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - StkFloat tick0(); StkFloat tick1(); void clearMesh(); @@ -99,4 +102,6 @@ class Mesh2D : public Instrmnt int counter_; // time in samples }; +} // stk namespace + #endif diff --git a/include/Messager.h b/include/Messager.h index 4607c8a..1a16db8 100644 --- a/include/Messager.h +++ b/include/Messager.h @@ -1,3 +1,21 @@ +#ifndef STK_MESSAGER_H +#define STK_MESSAGER_H + +#include "Stk.h" +#include "Skini.h" +#include + +#if defined(__STK_REALTIME__) + +#include "Mutex.h" +#include "Thread.h" +#include "TcpServer.h" +#include "RtMidi.h" + +#endif // __STK_REALTIME__ + +namespace stk { + /***************************************************/ /*! \class Messager \brief STK input control message parser. @@ -28,32 +46,12 @@ 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_MESSAGER_H -#define STK_MESSAGER_H - -#include "Stk.h" -#include "Skini.h" -#include - const int DEFAULT_QUEUE_LIMIT = 200; -#if defined(__STK_REALTIME__) - -#include "Mutex.h" -#include "Thread.h" -#include "TcpServer.h" -#include "RtMidi.h" - -extern "C" THREAD_RETURN THREAD_TYPE stdinHandler(void * ptr); - -extern "C" THREAD_RETURN THREAD_TYPE socketHandler(void * ptr); - -#endif // __STK_REALTIME__ - class Messager : public Stk { public: @@ -163,4 +161,6 @@ class Messager : public Stk }; +} // stk namespace + #endif diff --git a/include/MidiFileIn.h b/include/MidiFileIn.h index b687b65..218bf2a 100644 --- a/include/MidiFileIn.h +++ b/include/MidiFileIn.h @@ -1,3 +1,14 @@ +#ifndef STK_MIDIFILEIN_H +#define STK_MIDIFILEIN_H + +#include "Stk.h" +#include +#include +#include +#include + +namespace stk { + /**********************************************************************/ /*! \class MidiFileIn \brief A standard MIDI file reading/parsing class. @@ -11,19 +22,10 @@ Tempo changes are internally tracked by the class and reflected in the values returned by the function getTickSeconds(). - by Gary P. Scavone, 2003. + by Gary P. Scavone, 2003 - 2009. */ /**********************************************************************/ -#ifndef STK_MIDIFILEIN_H -#define STK_MIDIFILEIN_H - -#include "Stk.h" -#include -#include -#include -#include - class MidiFileIn : public Stk { public: @@ -128,4 +130,6 @@ class MidiFileIn : public Stk std::vector trackTempoIndex_; }; +} // stk namespace + #endif diff --git a/include/Modal.h b/include/Modal.h index 576d931..77ead48 100644 --- a/include/Modal.h +++ b/include/Modal.h @@ -1,26 +1,28 @@ +#ifndef STK_MODAL_H +#define STK_MODAL_H + +#include "Instrmnt.h" +#include "Envelope.h" +#include "FileLoop.h" +#include "SineWave.h" +#include "BiQuad.h" +#include "OnePole.h" + +namespace stk { + /***************************************************/ /*! \class Modal - \brief STK resonance model instrument. + \brief STK resonance model abstract base class. This class contains an excitation wavetable, an envelope, an oscillator, and N resonances (non-sweeping BiQuad filters), where N is set during instantiation. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_MODAL_H -#define STK_MODAL_H - -#include "Instrmnt.h" -#include "Envelope.h" -#include "WaveLoop.h" -#include "SineWave.h" -#include "BiQuad.h" -#include "OnePole.h" - class Modal : public Instrmnt { public: @@ -31,45 +33,46 @@ public: Modal( unsigned int modes = 4 ); //! Class destructor. - virtual ~Modal(); + virtual ~Modal( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(StkFloat frequency); + virtual void setFrequency( StkFloat frequency ); //! Set the ratio and radius for a specified mode filter. - void setRatioAndRadius(unsigned int modeIndex, StkFloat ratio, StkFloat radius); + void setRatioAndRadius( unsigned int modeIndex, StkFloat ratio, StkFloat radius ); //! Set the master gain. - void setMasterGain(StkFloat aGain); + void setMasterGain( StkFloat aGain ) { masterGain_ = aGain; }; //! Set the direct gain. - void setDirectGain(StkFloat aGain); + void setDirectGain( StkFloat aGain ) { directGain_ = aGain; }; //! Set the gain for a specified mode filter. - void setModeGain(unsigned int modeIndex, StkFloat gain); + void setModeGain( unsigned int modeIndex, StkFloat gain ); //! Initiate a strike with the given amplitude (0.0 - 1.0). - virtual void strike(StkFloat amplitude); + virtual void strike( StkFloat amplitude ); //! Damp modes with a given decay factor (0.0 - 1.0). - void damp(StkFloat amplitude); + void damp( StkFloat amplitude ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, StkFloat value) = 0; + virtual void controlChange( int number, StkFloat value ) = 0; + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - Envelope envelope_; FileWvIn *wave_; BiQuad **filters_; @@ -88,4 +91,27 @@ protected: StkFloat baseFrequency_; }; +inline StkFloat Modal :: tick( unsigned int ) +{ + StkFloat temp = masterGain_ * onepole_.tick( wave_->tick() * envelope_.tick() ); + + StkFloat temp2 = 0.0; + for ( unsigned int i=0; itick(temp); + + temp2 -= temp2 * directGain_; + temp2 += directGain_ * temp; + + if ( vibratoGain_ != 0.0 ) { + // Calculate AM and apply to master out + temp = 1.0 + ( vibrato_.tick() * vibratoGain_ ); + temp2 = temp * temp2; + } + + lastFrame_[0] = temp2; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/ModalBar.h b/include/ModalBar.h index aca13b8..7c9dd24 100644 --- a/include/ModalBar.h +++ b/include/ModalBar.h @@ -1,3 +1,10 @@ +#ifndef STK_MODALBAR_H +#define STK_MODALBAR_H + +#include "Modal.h" + +namespace stk { + /***************************************************/ /*! \class ModalBar \brief STK resonant bar instrument class. @@ -24,38 +31,35 @@ - Two Fixed = 7 - Clump = 8 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_MODALBAR_H -#define STK_MODALBAR_H - -#include "Modal.h" - class ModalBar : public Modal { public: //! Class constructor. - ModalBar(); + ModalBar( void ); //! Class destructor. - ~ModalBar(); + ~ModalBar( void ); //! Set stick hardness (0.0 - 1.0). - void setStickHardness(StkFloat hardness); + void setStickHardness( StkFloat hardness ); //! Set stick position (0.0 - 1.0). - void setStrikePosition(StkFloat position); + void setStrikePosition( StkFloat position ); //! Select a bar preset (currently modulo 9). - void setPreset(int preset); + void setPreset( int preset ); //! Set the modulation (vibrato) depth. - void setModulationDepth(StkFloat mDepth); + void setModulationDepth( StkFloat mDepth ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); }; +} // stk namespace + #endif diff --git a/include/Modulate.h b/include/Modulate.h index 6573964..7429985 100644 --- a/include/Modulate.h +++ b/include/Modulate.h @@ -1,3 +1,13 @@ +#ifndef STK_MODULATE_H +#define STK_MODULATE_H + +#include "Generator.h" +#include "SineWave.h" +#include "Noise.h" +#include "OnePole.h" + +namespace stk { + /***************************************************/ /*! \class Modulate \brief STK periodic/random modulator. @@ -6,18 +16,10 @@ modulations to give a nice, natural human modulation function. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_MODULATE_H -#define STK_MODULATE_H - -#include "Generator.h" -#include "SineWave.h" -#include "SubNoise.h" -#include "OnePole.h" - class Modulate : public Generator { public: @@ -25,33 +27,82 @@ class Modulate : public Generator /*! An StkError can be thrown if the rawwave path is incorrect. */ - Modulate(); + Modulate( void ); //! Class destructor. - ~Modulate(); + ~Modulate( void ); //! Reset internal state. - void reset(); + void reset( void ) { lastFrame_[0] = 0.0; }; //! Set the periodic (vibrato) rate or frequency in Hz. - void setVibratoRate(StkFloat rate); + void setVibratoRate( StkFloat rate ) { vibrato_.setFrequency( rate ); }; //! Set the periodic (vibrato) gain. - void setVibratoGain(StkFloat gain); + void setVibratoGain( StkFloat gain ) { vibratoGain_ = gain; }; //! Set the random modulation gain. - void setRandomGain(StkFloat gain); + void setRandomGain( StkFloat gain ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); + void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); SineWave vibrato_; - SubNoise noise_; + Noise noise_; OnePole filter_; StkFloat vibratoGain_; StkFloat randomGain_; + unsigned int noiseRate_; + unsigned int noiseCounter_; }; +inline StkFloat Modulate :: tick( void ) +{ + // Compute periodic and random modulations. + lastFrame_[0] = vibratoGain_ * vibrato_.tick(); + if ( noiseCounter_++ >= noiseRate_ ) { + noise_.tick(); + noiseCounter_ = 0; + } + lastFrame_[0] += filter_.tick( noise_.lastOut() ); + return lastFrame_[0]; +} + +inline StkFrames& Modulate :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Modulate::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; isetFrequency( mSpeed ); }; //! Set the modulation (vibrato) depth. - void setModulationDepth(StkFloat mDepth); + void setModulationDepth( StkFloat mDepth ) { modDepth_ = mDepth * 0.5; }; //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - FormSwep filters_[2]; StkFloat modDepth_; StkFloat filterQ_; @@ -62,4 +65,24 @@ class Moog : public Sampler }; +inline StkFloat Moog :: tick( unsigned int ) +{ + StkFloat temp; + + if ( modDepth_ != 0.0 ) { + temp = loops_[1]->tick() * modDepth_; + loops_[0]->setFrequency( baseFrequency_ * (1.0 + temp) ); + } + + temp = attackGain_ * attacks_[0]->tick(); + temp += loopGain_ * loops_[0]->tick(); + temp = filter_.tick( temp ); + temp *= adsr_.tick(); + temp = filters_[0].tick( temp ); + lastFrame_[0] = filters_[1].tick( temp ); + return lastFrame_[0] * 6.0; +} + +} // stk namespace + #endif diff --git a/include/Mutex.h b/include/Mutex.h index ac04384..7f40c6d 100644 --- a/include/Mutex.h +++ b/include/Mutex.h @@ -1,16 +1,3 @@ -/***************************************************/ -/*! \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 - 2007. -*/ -/***************************************************/ - #ifndef STK_MUTEX_H #define STK_MUTEX_H @@ -31,6 +18,21 @@ #endif +namespace stk { + +/***************************************************/ +/*! \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 - 2009. +*/ +/***************************************************/ + class Mutex : public Stk { public: @@ -67,4 +69,6 @@ class Mutex : public Stk }; +} // stk namespace + #endif diff --git a/include/NRev.h b/include/NRev.h index dce31cd..f2d68df 100644 --- a/include/NRev.h +++ b/include/NRev.h @@ -1,45 +1,85 @@ +#ifndef STK_NREV_H +#define STK_NREV_H + +#include "Effect.h" +#include "Delay.h" + +namespace stk { + /***************************************************/ /*! \class NRev \brief CCRMA's NRev reverberator class. - This class is derived from the CLM NRev - function, which is based on the use of - networks of simple allpass and comb delay - filters. This particular arrangement consists - of 6 comb filters in parallel, followed by 3 - allpass filters, a lowpass filter, and another - allpass in series, followed by two allpass - filters in parallel with corresponding right - and left outputs. + This class takes a monophonic input signal and produces a stereo + output signal. It is derived from the CLM NRev function, which is + based on the use of networks of simple allpass and comb delay + filters. This particular arrangement consists of 6 comb filters + in parallel, followed by 3 allpass filters, a lowpass filter, and + another allpass in series, followed by two allpass filters in + parallel with corresponding right and left outputs. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_NREV_H -#define STK_NREV_H - -#include "Effect.h" -#include "Delay.h" - class NRev : public Effect { public: //! Class constructor taking a T60 decay time argument (one second default value). NRev( StkFloat T60 = 1.0 ); - //! Class destructor. - ~NRev(); - //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set the reverberation T60 decay time. void setT60( StkFloat T60 ); - protected: + //! Return the specified channel value of the last computed stereo frame. + /*! + Use the lastFrame() function to get both values of the last + computed stereo frame. The \c channel argument must be 0 or 1 + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. + */ + StkFloat lastOut( unsigned int channel = 0 ); - StkFloat computeSample( StkFloat input ); + //! Input one sample to the effect and return the specified \c channel value of the computed stereo frame. + /*! + Use the lastFrame() function to get both values of the computed + stereo output frame. The \c channel argument must be 0 or 1 (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFloat tick( StkFloat input, unsigned int channel = 0 ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with stereo outputs. + /*! + The StkFrames argument reference is returned. The stereo + outputs are written to the StkFrames argument starting at the + specified \c channel. Therefore, the \c channel argument must be + less than ( channels() - 1 ) of the StkFrames argument (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the effect and write stereo outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. The \c iChannel + argument must be less than the number of channels in the \c + iFrames argument (the first channel is specified by 0). The \c + oChannel argument must be less than ( channels() - 1 ) of the \c + oFrames argument. However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: Delay allpassDelays_[8]; Delay combDelays_[6]; @@ -49,5 +89,72 @@ class NRev : public Effect }; +inline StkFloat NRev :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + errorString_ << "NRev::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFloat NRev :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + errorString_ << "NRev::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + 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); + } + + for ( i=0; i<3; i++ ) { + temp = allpassDelays_[i].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += temp0; + 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; + + temp = allpassDelays_[4].lastOut(); + temp2 = allpassCoefficient_ * temp; + temp2 += temp1; + allpassDelays_[4].tick( temp2 ); + lastFrame_[0] = effectMix_*( -( allpassCoefficient_ * temp2 ) + temp ); + + temp = allpassDelays_[5].lastOut(); + temp3 = allpassCoefficient_ * temp; + temp3 += temp1; + allpassDelays_[5].tick( temp3 ); + lastFrame_[1] = effectMix_*( - ( allpassCoefficient_ * temp3 ) + temp ); + + temp = ( 1.0 - effectMix_ ) * input; + lastFrame_[0] += temp; + lastFrame_[1] += temp; + + return lastFrame_[channel]; +} + +} // stk namespace + #endif diff --git a/include/Noise.h b/include/Noise.h index 6d154e8..135bd32 100644 --- a/include/Noise.h +++ b/include/Noise.h @@ -1,3 +1,10 @@ +#ifndef STK_NOISE_H +#define STK_NOISE_H + +#include "Generator.h" + +namespace stk { + /***************************************************/ /*! \class Noise \brief STK noise generator. @@ -6,31 +13,20 @@ C rand() function. The quality of the rand() function varies from one OS to another. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_NOISE_H -#define STK_NOISE_H - -#include "Generator.h" - class Noise : public Generator { public: - //! Default constructor which seeds the random number generator with the system time. - Noise(); - - //! Constructor which seeds the random number generator with a given seed. + //! Default constructor that can also take a specific seed value. /*! - If the seed value is zero, the random number generator is + If the seed value is zero (the default value), the random number generator is seeded with the system time. */ - Noise( unsigned int seed ); - - //! Class destructor. - virtual ~Noise(); + Noise( unsigned int seed = 0 ); //! Seed the random number generator with a specific seed value. /*! @@ -39,10 +35,49 @@ public: */ void setSeed( unsigned int seed = 0 ); -protected: + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; - virtual StkFloat computeSample( void ); + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + +protected: }; +inline StkFloat Noise :: tick( void ) +{ + return lastFrame_[0] = (StkFloat) ( 2.0 * rand() / (RAND_MAX + 1.0) - 1.0 ); +} + +inline StkFrames& Noise :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "Noise::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + errorString_ << "OnePole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "OnePole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + errorString_ << "OneZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } #endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "OneZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1 ) { + errorString_ << "PRCRev::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + + inline StkFloat PRCRev :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + errorString_ << "PRCRev::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat temp, temp0, temp1, temp2, temp3; + + temp = allpassDelays_[0].lastOut(); + temp0 = allpassCoefficient_ * temp; + temp0 += input; + allpassDelays_[0].tick(temp0); + temp0 = -(allpassCoefficient_ * temp0) + temp; + + temp = allpassDelays_[1].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += temp0; + allpassDelays_[1].tick(temp1); + temp1 = -(allpassCoefficient_ * temp1) + temp; + + temp2 = temp1 + (combCoefficient_[0] * combDelays_[0].lastOut()); + temp3 = temp1 + (combCoefficient_[1] * combDelays_[1].lastOut()); + + lastFrame_[0] = effectMix_ * (combDelays_[0].tick(temp2)); + lastFrame_[1] = effectMix_ * (combDelays_[1].tick(temp3)); + temp = (1.0 - effectMix_) * input; + lastFrame_[0] += temp; + lastFrame_[1] += temp; + + return lastFrame_[channel]; +} + +} // stk namespace + #endif diff --git a/include/PercFlut.h b/include/PercFlut.h index 5745466..8ddc7f8 100644 --- a/include/PercFlut.h +++ b/include/PercFlut.h @@ -1,3 +1,10 @@ +#ifndef STK_PERCFLUT_H +#define STK_PERCFLUT_H + +#include "FM.h" + +namespace stk { + /***************************************************/ /*! \class PercFlut \brief STK percussive flute FM synthesis instrument. @@ -22,15 +29,10 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_PERCFLUT_H -#define STK_PERCFLUT_H - -#include "FM.h" - class PercFlut : public FM { public: @@ -38,20 +40,51 @@ class PercFlut : public FM /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - PercFlut(); + PercFlut( void ); //! Class destructor. - ~PercFlut(); + ~PercFlut( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); }; +inline StkFloat PercFlut :: tick( unsigned int ) +{ + 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]); + + 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(); + + 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(); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Phonemes.h b/include/Phonemes.h index 21773ad..94a0214 100644 --- a/include/Phonemes.h +++ b/include/Phonemes.h @@ -1,3 +1,10 @@ +#ifndef STK_PHONEMES_H +#define STK_PHONEMES_H + +#include "Stk.h" + +namespace stk { + /***************************************************/ /*! \class Phonemes \brief STK phonemes table. @@ -6,15 +13,10 @@ set of 32 static phoneme formant parameters and provide access to those values. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_PHONEMES_H -#define STK_PHONEMES_H - -#include "Stk.h" - class Phonemes : public Stk { public: @@ -47,4 +49,6 @@ private: static const StkFloat phonemeParameters[][4][3]; }; +} // stk namespace + #endif diff --git a/include/PitShift.h b/include/PitShift.h index 521bb57..ab6b63f 100644 --- a/include/PitShift.h +++ b/include/PitShift.h @@ -1,3 +1,11 @@ +#ifndef STK_PITSHIFT_H +#define STK_PITSHIFT_H + +#include "Effect.h" +#include "DelayL.h" + +namespace stk { + /***************************************************/ /*! \class PitShift \brief STK simple pitch shifter effect class. @@ -5,43 +13,95 @@ This class implements a simple pitch shifter using delay lines. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_PITSHIFT_H -#define STK_PITSHIFT_H - -#include "Effect.h" -#include "DelayL.h" +const int maxDelay = 5024; class PitShift : public Effect { public: //! Class constructor. - PitShift(); - - //! Class destructor. - ~PitShift(); + PitShift( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set the pitch shift factor (1.0 produces no shift). - void setShift(StkFloat shift); + void setShift( StkFloat shift ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the effect and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the effect and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); protected: - StkFloat computeSample( StkFloat input ); - DelayL delayLine_[2]; StkFloat delay_[2]; StkFloat env_[2]; StkFloat rate_; - unsigned long delayLength; - unsigned long halfLength; + unsigned long delayLength_; + unsigned long halfLength_; }; +inline StkFloat PitShift :: tick( StkFloat input ) +{ + // 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( delay_[0] ); + delayLine_[1].setDelay( 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. + lastFrame_[0] = env_[0] * delayLine_[0].tick( input ); + lastFrame_[0] += env_[1] * delayLine_[1].tick( input ); + + // Compute effect mix and output. + lastFrame_[0] *= effectMix_; + lastFrame_[0] += ( 1.0 - effectMix_ ) * input; + + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/PluckTwo.h b/include/PluckTwo.h index 3e479ec..95616ac 100644 --- a/include/PluckTwo.h +++ b/include/PluckTwo.h @@ -1,3 +1,13 @@ +#ifndef STK_PLUCKTWO_H +#define STK_PLUCKTWO_H + +#include "Instrmnt.h" +#include "DelayL.h" +#include "DelayA.h" +#include "OneZero.h" + +namespace stk { + /***************************************************/ /*! \class PluckTwo \brief STK enhanced plucked string model class. @@ -14,41 +24,33 @@ use possibly subject to patents held by Stanford University, Yamaha, and others. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_PLUCKTWO_H -#define STK_PLUCKTWO_H - -#include "Instrmnt.h" -#include "DelayL.h" -#include "DelayA.h" -#include "OneZero.h" - class PluckTwo : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - PluckTwo(StkFloat lowestFrequency); + PluckTwo( StkFloat lowestFrequency ); //! Class destructor. - virtual ~PluckTwo(); + virtual ~PluckTwo( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(StkFloat frequency); + virtual void setFrequency( StkFloat frequency ); //! Detune the two strings by the given factor. A value of 1.0 produces unison strings. - void setDetune(StkFloat detune); + void setDetune( StkFloat detune ); //! Efficient combined setting of frequency and detuning. - void setFreqAndDetune(StkFloat frequency, StkFloat detune); + void setFreqAndDetune( StkFloat frequency, StkFloat detune ); //! Set the pluck or "excitation" position along the string (0.0 - 1.0). - void setPluckPosition(StkFloat position); + void setPluckPosition( StkFloat position ); //! Set the base loop gain. /*! @@ -56,15 +58,15 @@ class PluckTwo : public Instrmnt Because of high-frequency loop filter roll-off, higher frequency settings have greater loop gains. */ - void setBaseLoopGain(StkFloat aGain); + void setBaseLoopGain( StkFloat aGain ); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(StkFloat amplitude); + virtual void noteOff( StkFloat amplitude ); + + virtual StkFloat tick( unsigned int channel = 0 ) = 0; protected: - virtual StkFloat computeSample( void ) = 0; - DelayA delayLine_; DelayA delayLine2_; DelayL combDelay_; @@ -82,4 +84,6 @@ class PluckTwo : public Instrmnt }; +} // stk namespace + #endif diff --git a/include/Plucked.h b/include/Plucked.h index ceba36c..c081883 100644 --- a/include/Plucked.h +++ b/include/Plucked.h @@ -1,3 +1,14 @@ +#ifndef STK_PLUCKED_H +#define STK_PLUCKED_H + +#include "Instrmnt.h" +#include "DelayA.h" +#include "OneZero.h" +#include "OnePole.h" +#include "Noise.h" + +namespace stk { + /***************************************************/ /*! \class Plucked \brief STK plucked string model class. @@ -13,47 +24,39 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_PLUCKED_H -#define STK_PLUCKED_H - -#include "Instrmnt.h" -#include "DelayA.h" -#include "OneZero.h" -#include "OnePole.h" -#include "Noise.h" - class Plucked : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - Plucked(StkFloat lowestFrequency); + Plucked( StkFloat lowestFrequency ); //! Class destructor. - ~Plucked(); + ~Plucked( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Pluck the string with the given amplitude using the current frequency. - void pluck(StkFloat amplitude); + void pluck( StkFloat amplitude ); //! Start a note with the given frequency and amplitude. - virtual void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayA delayLine_; OneZero loopFilter_; OnePole pickFilter_; @@ -63,5 +66,13 @@ class Plucked : public Instrmnt }; +inline StkFloat Plucked :: tick( unsigned int ) +{ + // Here's the whole inner loop of the instrument!! + return lastFrame_[0] = 3.0 * delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) ); +} + +} // stk namespace + #endif diff --git a/include/PoleZero.h b/include/PoleZero.h index 5f1b520..a064a87 100644 --- a/include/PoleZero.h +++ b/include/PoleZero.h @@ -1,23 +1,24 @@ -/***************************************************/ -/*! \class PoleZero - \brief STK one-pole, one-zero filter class. - - This protected Filter subclass implements - a one-pole, one-zero digital filter. A - method is provided for creating an allpass - filter with a given coefficient. Another - method is provided to create a DC blocking filter. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - #ifndef STK_POLEZERO_H #define STK_POLEZERO_H #include "Filter.h" -class PoleZero : protected Filter +namespace stk { + +/***************************************************/ +/*! \class PoleZero + \brief STK one-pole, one-zero filter class. + + This class implements a one-pole, one-zero digital filter. A + method is provided for creating an allpass filter with a given + coefficient. Another method is provided to create a DC blocking + filter. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + +class PoleZero : public Filter { public: @@ -27,17 +28,17 @@ class PoleZero : protected Filter //! Class destructor. ~PoleZero(); - //! Clears the internal states of the filter. - void clear(void); - //! Set the b[0] coefficient value. - void setB0(StkFloat b0); + void setB0( StkFloat b0 ) { b_[0] = b0; }; //! Set the b[1] coefficient value. - void setB1(StkFloat b1); + void setB1( StkFloat b1 ) { b_[1] = b1; }; //! Set the a[1] coefficient value. - void setA1(StkFloat a1); + void setA1( StkFloat a1 ) { a_[1] = a1; }; + + //! Set all filter coefficients. + void setCoefficients( StkFloat b0, StkFloat b1, StkFloat a1, bool clearState = false ); //! Set the filter for allpass behavior using \e coefficient. /*! @@ -45,7 +46,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(StkFloat coefficient); + void setAllpass( StkFloat coefficient ); //! Create a DC blocking filter with the given pole position in the z-plane. /*! @@ -54,33 +55,58 @@ class PoleZero : protected Filter close to one to minimize low-frequency attenuation. */ - 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( StkFloat gain ); - - //! Return the current filter gain. - StkFloat getGain( void ) const; + void setBlockZero( StkFloat thePole = 0.99 ); //! Return the last computed output value. - StkFloat lastOut( void ) const; + StkFloat lastOut( void ) const { return lastFrame_[0]; }; //! Input one sample to the filter and return one output. - StkFloat tick( StkFloat sample ); + StkFloat tick( StkFloat input ); //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. /*! - The \c channel argument should be zero or greater (the first - channel is specified by 0). An StkError will be thrown if the \c - channel argument is equal to or greater than the number of - channels in the StkFrames object. + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. */ StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); }; +inline StkFloat PoleZero :: tick( StkFloat input ) +{ + inputs_[0] = gain_ * input; + lastFrame_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] - a_[1] * outputs_[1]; + inputs_[1] = inputs_[0]; + outputs_[1] = lastFrame_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& PoleZero :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "PoleZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 1, the reed has slammed shut and the + // reflection function value saturates at 1.0. + if ( lastFrame_[0] > 1.0) lastFrame_[0] = (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 ( lastFrame_[0] < -1.0) lastFrame_[0] = (StkFloat) -1.0; + + return lastFrame_[0]; +} + +inline StkFrames& ReedTable :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "ReedTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 1.0) *samples = 1.0; + if ( *samples < -1.0) *samples = -1.0; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& ReedTable :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "ReedTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0) *oSamples = 1.0; + if ( *oSamples < -1.0) *oSamples = -1.0; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + #endif diff --git a/include/Resonate.h b/include/Resonate.h index b5b3f05..4152ad8 100644 --- a/include/Resonate.h +++ b/include/Resonate.h @@ -1,3 +1,13 @@ +#ifndef STK_RESONATE_H +#define STK_RESONATE_H + +#include "Instrmnt.h" +#include "ADSR.h" +#include "BiQuad.h" +#include "Noise.h" + +namespace stk { + /***************************************************/ /*! \class Resonate \brief STK noise driven formant filter. @@ -13,58 +23,51 @@ - Zero Radii = 1 - Envelope Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_RESONATE_H -#define STK_RESONATE_H - -#include "Instrmnt.h" -#include "ADSR.h" -#include "BiQuad.h" -#include "Noise.h" - class Resonate : public Instrmnt { public: //! Class constructor. - Resonate(); + Resonate( void ); //! Class destructor. - ~Resonate(); + ~Resonate( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set the filter for a resonance at the given frequency (Hz) and radius. - void setResonance(StkFloat frequency, StkFloat radius); + void setResonance( StkFloat frequency, StkFloat radius ); //! Set the filter for a notch at the given frequency (Hz) and radius. - void setNotch(StkFloat frequency, StkFloat radius); + void setNotch( StkFloat frequency, StkFloat radius ); //! Set the filter zero coefficients for contant resonance gain. - void setEqualGainZeroes(); + void setEqualGainZeroes( void ) { filter_.setEqualGainZeroes(); }; //! Initiate the envelope with a key-on event. - void keyOn(); + void keyOn( void ) { adsr_.keyOn(); }; //! Signal a key-off event to the envelope. - void keyOff(); + void keyOff( void ) { adsr_.keyOff(); }; //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - ADSR adsr_; BiQuad filter_; Noise noise_; @@ -75,4 +78,13 @@ class Resonate : public Instrmnt }; +inline StkFloat Resonate :: tick( unsigned int ) +{ + lastFrame_[0] = filter_.tick( noise_.tick() ); + lastFrame_[0] *= adsr_.tick(); + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Rhodey.h b/include/Rhodey.h index d7ab2b3..9f845cb 100644 --- a/include/Rhodey.h +++ b/include/Rhodey.h @@ -1,3 +1,10 @@ +#ifndef STK_RHODEY_H +#define STK_RHODEY_H + +#include "FM.h" + +namespace stk { + /***************************************************/ /*! \class Rhodey \brief STK Fender Rhodes electric piano FM @@ -26,15 +33,10 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_RHODEY_H -#define STK_RHODEY_H - -#include "FM.h" - class Rhodey : public FM { public: @@ -42,20 +44,48 @@ class Rhodey : public FM /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - Rhodey(); + Rhodey( void ); //! Class destructor. - ~Rhodey(); + ~Rhodey( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); }; +inline StkFloat Rhodey :: tick( unsigned int ) +{ + StkFloat temp, temp2; + + 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_[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_; + temp = temp * (1.0 + temp2); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/RtAudio.h b/include/RtAudio.h index 5acc9a5..5c5fd6f 100644 --- a/include/RtAudio.h +++ b/include/RtAudio.h @@ -4,13 +4,13 @@ RtAudio provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, Jack, - and OSS), SGI, Macintosh OS X (CoreAudio and Jack), and Windows + and OSS), Macintosh OS X (CoreAudio and Jack), and Windows (DirectSound and ASIO) operating systems. RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes - Copyright (c) 2001-2007 Gary P. Scavone + Copyright (c) 2001-2009 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -42,7 +42,7 @@ \file RtAudio.h */ -// RtAudio: Version 4.0.3 +// RtAudio: Version 4.0.5 #ifndef __RTAUDIO_H #define __RTAUDIO_H @@ -108,11 +108,15 @@ static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/mi If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to open the input and/or output stream device(s) for exclusive use. Note that this is not possible with all supported audio APIs. + + If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt + to select realtime scheduling (round-robin) for the callback thread. */ typedef unsigned int RtAudioStreamFlags; static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved). static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency. static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others. +static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread. /*! \typedef typedef unsigned long RtAudioStreamStatus; \brief RtAudio stream status (over- or underflow) flags. @@ -240,9 +244,10 @@ class RtAudio The following flags can be OR'ed together to allow a client to make changes to the default stream behavior: - - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). - - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. - - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. + - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). + - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. + - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. + - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread. By default, RtAudio streams pass and receive audio data from the client in an interleaved format. By passing the @@ -268,6 +273,11 @@ class RtAudio open the input and/or output stream device(s) for exclusive use. Note that this is not possible with all supported audio APIs. + If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt + to select realtime scheduling (round-robin) for the callback thread. + The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME + flag is set. It defines the thread's realtime priority. + The \c numberOfBuffers parameter can be used to control stream latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs only. A value of two is usually the smallest allowed. Larger @@ -285,10 +295,11 @@ class RtAudio RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE). */ unsigned int numberOfBuffers; /*!< Number of stream buffers. */ std::string streamName; /*!< A stream name (currently used only in Jack). */ + int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */ // Default constructor. StreamOptions() - : flags(0), numberOfBuffers(0) {} + : flags(0), numberOfBuffers(0), priority(0) {} }; //! A static function to determine the available compiled audio APIs. @@ -440,10 +451,10 @@ class RtAudio void abortStream( void ); //! Returns true if a stream is open and false if not. - bool isStreamOpen( void ) throw(); + bool isStreamOpen( void ) const throw(); //! Returns true if the stream is running and false if it is stopped or not open. - bool isStreamRunning( void ) throw(); + bool isStreamRunning( void ) const throw(); //! Returns the number of elapsed seconds since the stream was started. /*! @@ -462,6 +473,14 @@ class RtAudio */ long getStreamLatency( void ); + //! Returns actual sample rate in use by the stream. + /*! + On some systems, the sample rate used may be slightly different + than that specified in the stream parameters. If a stream is not + open, an RtError (type = INVALID_USE) will be thrown. + */ + unsigned int getStreamSampleRate( void ); + //! Specify whether warning messages should be printed to stderr. void showWarnings( bool value = true ) throw(); @@ -551,9 +570,10 @@ public: virtual void stopStream( void ) = 0; virtual void abortStream( void ) = 0; long getStreamLatency( void ); + unsigned int getStreamSampleRate( void ); virtual double getStreamTime( void ); - bool isStreamOpen( void ) { return stream_.state != STREAM_CLOSED; }; - bool isStreamRunning( void ) { return stream_.state == STREAM_RUNNING; }; + bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; }; + bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; }; void showWarnings( bool value ) { showWarnings_ = value; }; @@ -617,7 +637,7 @@ protected: #endif RtApiStream() - :apiHandle(0), deviceBuffer(0) {} + :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; } }; typedef signed short Int16; @@ -688,9 +708,10 @@ inline void RtAudio :: closeStream( void ) throw() { return rtapi_->closeStream( inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); } inline void RtAudio :: stopStream( void ) { return rtapi_->stopStream(); } inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); } -inline bool RtAudio :: isStreamOpen( void ) throw() { return rtapi_->isStreamOpen(); } -inline bool RtAudio :: isStreamRunning( void ) throw() { return rtapi_->isStreamRunning(); } +inline bool RtAudio :: isStreamOpen( void ) const throw() { return rtapi_->isStreamOpen(); } +inline bool RtAudio :: isStreamRunning( void ) const throw() { return rtapi_->isStreamRunning(); } inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); } +inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); }; inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); } inline void RtAudio :: showWarnings( bool value ) throw() { rtapi_->showWarnings( value ); } @@ -867,6 +888,8 @@ public: private: + std::vector devices_; + void saveDeviceInfo( void ); bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, diff --git a/include/RtMidi.h b/include/RtMidi.h index e443e2a..2928eaf 100644 --- a/include/RtMidi.h +++ b/include/RtMidi.h @@ -8,7 +8,7 @@ RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2007 Gary P. Scavone + Copyright (c) 2003-2009 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -35,7 +35,7 @@ */ /**********************************************************************/ -// RtMidi: Version 1.0.7 +// RtMidi: Version 1.0.9 #ifndef RTMIDI_H #define RTMIDI_H @@ -48,7 +48,7 @@ class RtMidi public: //! Pure virtual openPort() function. - virtual void openPort( unsigned int portNumber = 0 ) = 0; + virtual void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi" ) ) = 0; //! Pure virtual openVirtualPort() function. virtual void openVirtualPort( const std::string portName = std::string( "RtMidi" ) ) = 0; @@ -91,7 +91,7 @@ class RtMidi to open a virtual input port to which other MIDI software clients can connect. - by Gary P. Scavone, 2003-2004. + by Gary P. Scavone, 2003-2008. */ /**********************************************************************/ @@ -105,11 +105,11 @@ class RtMidiIn : public RtMidi //! User callback function type definition. typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); - //! Default constructor. + //! Default constructor that allows an optional client name. /*! An exception will be thrown if a MIDI system initialization error occurs. */ - RtMidiIn(); + RtMidiIn( const std::string clientName = std::string( "RtMidi Input Client") ); //! If a MIDI connection is still open, it will be closed by the destructor. ~RtMidiIn(); @@ -119,7 +119,7 @@ class RtMidiIn : public RtMidi 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 ); + void openPort( unsigned int portNumber = 0, const std::string Portname = std::string( "RtMidi Input" ) ); //! Create a virtual input port, with optional name, to allow software connections (OS X and ALSA only). /*! @@ -200,6 +200,7 @@ class RtMidiIn : public RtMidi // the MIDI input handling function or thread. struct RtMidiInData { std::queue queue; + MidiMessage message; unsigned int queueLimit; unsigned char ignoreFlags; bool doInput; @@ -208,16 +209,18 @@ class RtMidiIn : public RtMidi bool usingCallback; void *userCallback; void *userData; + bool continueSysex; // Default constructor. RtMidiInData() : queueLimit(1024), ignoreFlags(7), doInput(false), firstMessage(true), - apiData(0), usingCallback(false), userCallback(0), userData(0) {} + apiData(0), usingCallback(false), userCallback(0), userData(0), + continueSysex(false) {} }; private: - void initialize( void ); + void initialize( const std::string& clientName ); RtMidiInData inputData_; }; @@ -232,7 +235,7 @@ class RtMidiIn : public RtMidi 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. + by Gary P. Scavone, 2003-2008. */ /**********************************************************************/ @@ -240,11 +243,11 @@ class RtMidiOut : public RtMidi { public: - //! Default constructor. + //! Default constructor that allows an optional client name. /*! An exception will be thrown if a MIDI system initialization error occurs. */ - RtMidiOut(); + RtMidiOut( const std::string clientName = std::string( "RtMidi Output Client" ) ); //! The destructor closes any open MIDI connections. ~RtMidiOut(); @@ -256,7 +259,7 @@ class RtMidiOut : public RtMidi exception is thrown if an error occurs while attempting to make the port connection. */ - void openPort( unsigned int portNumber = 0 ); + void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Output" ) ); //! Close an open MIDI connection (if one exists). void closePort(); @@ -290,7 +293,7 @@ class RtMidiOut : public RtMidi private: - void initialize( void ); + void initialize( const std::string& clientName ); }; #endif diff --git a/include/RtWvIn.h b/include/RtWvIn.h index 8b625a2..8cc5cd0 100644 --- a/include/RtWvIn.h +++ b/include/RtWvIn.h @@ -1,3 +1,12 @@ +#ifndef STK_RTWVIN_H +#define STK_RTWVIN_H + +#include "WvIn.h" +#include "RtAudio.h" +#include "Mutex.h" + +namespace stk { + /***************************************************/ /*! \class RtWvIn \brief STK realtime audio (blocking) input class. @@ -10,36 +19,28 @@ RtWvIn supports multi-channel data in both interleaved and non-interleaved formats. It is important to distinguish the - tick() methods, which return samples produced by averaging across - sample frames, from the tickFrame() methods, which return - references or pointers to multi-channel sample frames. + tick() method that computes a single frame (and returns only the + specified sample of a multi-channel frame) from the overloaded one + that takes an StkFrames object for multi-channel and/or + multi-frame data. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_RTWVIN_H -#define STK_RTWVIN_H - -#include "WvIn.h" -#include "RtAudio.h" - class RtWvIn : public WvIn { public: //! Default constructor. /*! - The \e device argument is passed to RtAudio during - instantiation. The default value (zero) will select the default - device on your system or the first device found meeting the - specified parameters. On systems with multiple - soundcards/devices, values greater than zero can be specified in - accordance with the order that the devices are enumerated by the - underlying audio API. The default buffer size of RT_BUFFER_SIZE - is defined in Stk.h. An StkError will be thrown if an error - occurs duing instantiation. + The default \e device argument value (zero) will select the + default input device on your system. The first device enumerated + by the underlying audio API is specified with a value of one. The + default buffer size of RT_BUFFER_SIZE is defined in Stk.h. An + StkError will be thrown if an error occurs duing instantiation. */ - RtWvIn( unsigned int nChannels = 1, StkFloat sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 20 ); + RtWvIn( unsigned int nChannels = 1, StkFloat sampleRate = Stk::sampleRate(), + int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 20 ); //! Class destructor. ~RtWvIn(); @@ -58,15 +59,48 @@ public: */ void stop( void ); - // This function is not intended for general use but had to be made + //! Return the specified channel value of the last computed frame. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the last computed frame. If the device is + stopped, the returned value is 0.0. The \c channel argument must + be less than the number of channels in the audio stream (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Compute a sample frame and return the specified \c channel value. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the computed frame. If the device is "stopped", + it is "started". The \c channel argument must be less than the + number of channels in the audio stream (the first channel is + specified by 0). However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames argument with computed frames and return the same reference. + /*! + If the device is "stopped", it is "started". The number of + channels in the StkFrames argument must equal the number of + channels specified during instantiation. However, this is only + checked if _STK_DEBUG_ is defined during compilation, in which + case an incompatibility will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames ); + + // This function is not intended for general use but must be // public for access from the audio callback function. void fillBuffer( void *buffer, unsigned int nFrames ); protected: - void computeFrame( void ); - RtAudio adc_; + Mutex mutex_; bool stopped_; unsigned int readIndex_; unsigned int writeIndex_; @@ -74,4 +108,18 @@ protected: }; +inline StkFloat RtWvIn :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= data_.channels() ) { + errorString_ << "RtWvIn::lastOut(): channel argument and audio stream are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +} // stk namespace + #endif diff --git a/include/RtWvOut.h b/include/RtWvOut.h index 7e8545c..fdaa150 100644 --- a/include/RtWvOut.h +++ b/include/RtWvOut.h @@ -1,3 +1,12 @@ +#ifndef STK_RTWVOUT_H +#define STK_RTWVOUT_H + +#include "WvOut.h" +#include "RtAudio.h" +#include "Mutex.h" + +namespace stk { + /***************************************************/ /*! \class RtWvOut \brief STK realtime audio (blocking) output class. @@ -9,35 +18,26 @@ low-latency is desired. RtWvOut 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, from the tickFrame() - method, which take a pointer or reference to multi-channel sample - frame data. + important to distinguish the tick() method that outputs a single + sample to all channels in a sample frame from the overloaded one + that takes a reference to an StkFrames object for multi-channel + and/or multi-frame data. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_RTWVOUT_H -#define STK_RTWVOUT_H - -#include "WvOut.h" -#include "RtAudio.h" - class RtWvOut : public WvOut { public: //! Default constructor. /*! - The \e device argument is passed to RtAudio during - instantiation. The default value (zero) will select the default - device on your system. On systems with multiple - soundcards/devices, values greater than zero can be specified in - accordance with the order that the devices are enumerated by the - underlying audio API. The default buffer size of RT_BUFFER_SIZE - is defined in Stk.h. An StkError will be thrown if an error - occurs duing instantiation. + The default \e device argument value (zero) will select the + default output device on your system. The first device enumerated + by the underlying audio API is specified with a value of one. The + default buffer size of RT_BUFFER_SIZE is defined in Stk.h. An + StkError will be thrown if an error occurs duing instantiation. */ RtWvOut( unsigned int nChannels = 1, StkFloat sampleRate = Stk::sampleRate(), int device = 0, int bufferFrames = RT_BUFFER_SIZE, int nBuffers = 20 ); @@ -48,7 +48,7 @@ class RtWvOut : public WvOut //! Start the audio output stream. /*! The stream is started automatically, if necessary, when a - tick() or tickFrame method is called. + tick() method is called. */ void start( void ); @@ -59,16 +59,30 @@ class RtWvOut : public WvOut */ void stop( void ); - // This function is not intended for general use but had to be made + //! Output a single sample to all channels in a sample frame. + /*! + If the device is "stopped", it is "started". + */ + void tick( const StkFloat sample ); + + //! Output the StkFrames data. + /*! + If the device is "stopped", it is "started". The number of + channels in the StkFrames argument must equal the number of + channels specified during instantiation. However, this is only + checked if _STK_DEBUG_ is defined during compilation, in which + case an incompatibility will trigger an StkError exception. + */ + void tick( StkFrames& frames ); + + // This function is not intended for general use but must be // public for access from the audio callback function. int readBuffer( void *buffer, unsigned int frameCount ); protected: - void computeSample( const StkFloat sample ); - void computeFrames( const StkFrames& frames ); - RtAudio dac_; + Mutex mutex_; bool stopped_; unsigned int readIndex_; unsigned int writeIndex_; @@ -77,4 +91,6 @@ class RtWvOut : public WvOut }; +} // stk namespace + #endif diff --git a/include/SKINI.msg b/include/SKINI.msg index 72bacf1..a161df9 100644 --- a/include/SKINI.msg +++ b/include/SKINI.msg @@ -9,10 +9,12 @@ where is the string used in the SKINI stream. - by Perry R. Cook, 1995 - 2004. + by Perry R. Cook, 1995 - 2009. */ /*********************************************************/ +namespace stk { + #define NOPE -32767 #define YEP 1 #define SK_DBL -32766 @@ -124,4 +126,4 @@ #define __SK_SINGER_RndVibAmt_ 3008 #define __SK_SINGER_VibFreq_ __SK_Expression_ - +} // stk namespace diff --git a/include/SKINI.tbl b/include/SKINI.tbl index e2a1877..6c9d47a 100644 --- a/include/SKINI.tbl +++ b/include/SKINI.tbl @@ -1,6 +1,8 @@ #include "SKINI.msg" +namespace stk { + #define __SK_MaxMsgTypes_ 80 struct SkiniSpec { char messageString[32]; @@ -129,3 +131,5 @@ struct SkiniSpec skini_msgs[__SK_MaxMsgTypes_] = /* message. */ /* */ /*************************************************************************/ + +} // stk namespace diff --git a/include/Sampler.h b/include/Sampler.h index 0aa8e3b..433d85b 100644 --- a/include/Sampler.h +++ b/include/Sampler.h @@ -1,56 +1,59 @@ -/***************************************************/ -/*! \class Sampler - \brief STK sampling synthesis abstract base class. - - 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 - 2007. -*/ -/***************************************************/ - #ifndef STK_SAMPLER_H #define STK_SAMPLER_H #include "Instrmnt.h" #include "ADSR.h" -#include "WaveLoop.h" +#include "FileLoop.h" #include "OnePole.h" +namespace stk { + +/***************************************************/ +/*! \class Sampler + \brief STK sampling synthesis abstract base class. + + This instrument provides an ADSR envelope, a one-pole filter, and + structures for an arbitrary number of attack and looped files. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + class Sampler : public Instrmnt { public: //! Default constructor. - Sampler(); + Sampler( void ); //! Class destructor. - virtual ~Sampler(); + virtual ~Sampler( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(StkFloat frequency) = 0; + virtual void setFrequency( StkFloat frequency ) = 0; //! Initiate the envelopes with a key-on event and reset the attack waves. - void keyOn(); + void keyOn( void ); //! Signal a key-off event to the envelopes. - void keyOff(); + void keyOff( void ); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(StkFloat amplitude); + virtual void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - virtual void controlChange(int number, StkFloat value) = 0; + virtual void controlChange( int number, StkFloat value ) = 0; + + //! Compute and return one output sample. + virtual StkFloat tick( unsigned int channel = 0 ) = 0; protected: - virtual StkFloat computeSample( void ) = 0; - ADSR adsr_; std::vector attacks_; - std::vector loops_; + std::vector loops_; OnePole filter_; StkFloat baseFrequency_; std::vector attackRatios_; @@ -60,4 +63,6 @@ class Sampler : public Instrmnt }; +} // stk namespace + #endif diff --git a/include/Saxofony.h b/include/Saxofony.h index 4f5b337..56e07dc 100644 --- a/include/Saxofony.h +++ b/include/Saxofony.h @@ -1,3 +1,16 @@ +#ifndef STK_SAXOFONY_H +#define STK_SAXOFONY_H + +#include "Instrmnt.h" +#include "DelayL.h" +#include "ReedTable.h" +#include "OneZero.h" +#include "Envelope.h" +#include "Noise.h" +#include "SineWave.h" + +namespace stk { + /***************************************************/ /*! \class Saxofony \brief STK faux conical bore reed instrument class. @@ -31,21 +44,10 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_SAXOFONY_H -#define STK_SAXOFONY_H - -#include "Instrmnt.h" -#include "DelayL.h" -#include "ReedTable.h" -#include "OneZero.h" -#include "Envelope.h" -#include "Noise.h" -#include "SineWave.h" - class Saxofony : public Instrmnt { public: @@ -53,39 +55,40 @@ class Saxofony : public Instrmnt /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - Saxofony(StkFloat lowestFrequency); + Saxofony( StkFloat lowestFrequency ); //! Class destructor. - ~Saxofony(); + ~Saxofony( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Set the "blowing" position between the air column terminations (0.0 - 1.0). - void setBlowPosition(StkFloat aPosition); + void setBlowPosition( StkFloat aPosition ); //! Apply breath pressure to instrument with given amplitude and rate of increase. - void startBlowing(StkFloat amplitude, StkFloat rate); + void startBlowing( StkFloat amplitude, StkFloat rate ); //! Decrease breath pressure with given rate of decrease. - void stopBlowing(StkFloat rate); + void stopBlowing( StkFloat rate ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayL delays_[2]; ReedTable reedTable_; OneZero filter_; @@ -100,4 +103,27 @@ class Saxofony : public Instrmnt }; +inline StkFloat Saxofony :: tick( unsigned int ) +{ + 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(); + + temp = -0.95 * filter_.tick( delays_[0].lastOut() ); + lastFrame_[0] = temp - delays_[1].lastOut(); + pressureDiff = breathPressure - lastFrame_[0]; + delays_[1].tick( temp ); + delays_[0].tick( breathPressure - (pressureDiff * reedTable_.tick(pressureDiff)) - temp ); + + lastFrame_[0] *= outputGain_; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Shakers.h b/include/Shakers.h index e41ded4..ecf6d84 100644 --- a/include/Shakers.h +++ b/include/Shakers.h @@ -1,3 +1,10 @@ +#ifndef STK_SHAKERS_H +#define STK_SHAKERS_H + +#include "Instrmnt.h" + +namespace stk { + /***************************************************/ /*! \class Shakers \brief PhISEM and PhOLIES class. @@ -48,15 +55,10 @@ - Little Rocks = 21 - Tuned Bamboo Chimes = 22 - by Perry R. Cook, 1996 - 2004. + by Perry R. Cook, 1996 - 2009. */ /***************************************************/ -#ifndef STK_SHAKERS_H -#define STK_SHAKERS_H - -#include "Instrmnt.h" - const int MAX_FREQS = 8; const int NUM_INSTR = 24; @@ -64,35 +66,37 @@ class Shakers : public Instrmnt { public: //! Class constructor. - Shakers(); + Shakers( void ); //! Class destructor. - ~Shakers(); + ~Shakers( void ); //! Start a note with the given instrument and amplitude. /*! Use the instrument numbers above, converted to frequency values as if MIDI note numbers, to select a particular instrument. */ - void noteOn(StkFloat instrument, StkFloat amplitude); + void noteOn( StkFloat instrument, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - int setupName(char* instr); - int setupNum(int inst); - 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 setupName( char* instr ); + int setupNum( int inst ); + 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( void ); + StkFloat tbamb_tick( void ); + StkFloat ratchet_tick( void ); int instType_; int ratchetPos_, lastRatchetPos_; @@ -122,4 +126,6 @@ class Shakers : public Instrmnt }; +} // stk namespace + #endif diff --git a/include/Simple.h b/include/Simple.h index 9230264..d38af25 100644 --- a/include/Simple.h +++ b/include/Simple.h @@ -1,3 +1,15 @@ +#ifndef STK_SIMPLE_H +#define STK_SIMPLE_H + +#include "Instrmnt.h" +#include "ADSR.h" +#include "FileLoop.h" +#include "OnePole.h" +#include "BiQuad.h" +#include "Noise.h" + +namespace stk { + /***************************************************/ /*! \class Simple \brief STK wavetable/noise instrument. @@ -13,20 +25,10 @@ - Envelope Rate = 11 - Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_SIMPLE_H -#define STK_SIMPLE_H - -#include "Instrmnt.h" -#include "ADSR.h" -#include "WaveLoop.h" -#include "OnePole.h" -#include "BiQuad.h" -#include "Noise.h" - class Simple : public Instrmnt { public: @@ -34,38 +36,39 @@ class Simple : public Instrmnt /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - Simple(); + Simple( void ); //! Class destructor. - ~Simple(); + ~Simple( void ); //! Clear internal states. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Start envelope toward "on" target. - void keyOn(); + void keyOn( void ); //! Start envelope toward "off" target. - void keyOff(); + void keyOff( void ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - ADSR adsr_; - WaveLoop *loop_; + FileLoop *loop_; OnePole filter_; BiQuad biquad_; Noise noise_; @@ -74,4 +77,16 @@ class Simple : public Instrmnt }; +inline StkFloat Simple :: tick( unsigned int ) +{ + lastFrame_[0] = loopGain_ * loop_->tick(); + biquad_.tick( noise_.tick() ); + lastFrame_[0] += (1.0 - loopGain_) * biquad_.lastOut(); + lastFrame_[0] = filter_.tick( lastFrame_[0] ); + lastFrame_[0] *= adsr_.tick(); + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/SineWave.h b/include/SineWave.h index ca1a0cd..cb7785f 100644 --- a/include/SineWave.h +++ b/include/SineWave.h @@ -1,3 +1,12 @@ +#ifndef STK_SINEWAVE_H +#define STK_SINEWAVE_H + +const unsigned long TABLE_SIZE = 2048; + +#include "Generator.h" + +namespace stk { + /***************************************************/ /*! \class SineWave \brief STK sinusoid oscillator class. @@ -13,13 +22,6 @@ */ /***************************************************/ -#ifndef STK_SINEWAVE_H -#define STK_SINEWAVE_H - -const unsigned long TABLE_SIZE = 2048; - -#include "Generator.h" - class SineWave : public Generator { public: @@ -27,7 +29,7 @@ public: SineWave( void ); //! Class destructor. - virtual ~SineWave( void ); + ~SineWave( void ); //! Clear output and reset time pointer to zero. void reset( void ); @@ -47,35 +49,111 @@ public: */ void setFrequency( StkFloat frequency ); - //! Increment the read pointer by \e time samples, modulo file size. + //! Increment the read pointer by \e time in samples, modulo the table size. void addTime( StkFloat time ); - //! Increment current read pointer by \e angle, relative to a looping frequency. + //! Increment the read pointer by a normalized \e phase value. /*! - This function increments the read pointer based on the file - size and the current Stk::sampleRate. The \e anAngle value - is a multiple of file size. + This function increments the read pointer by a normalized phase + value, such that \e phase = 1.0 corresponds to a 360 degree phase + shift. Positive or negative values are possible. */ - void addPhase( StkFloat angle ); + void addPhase( StkFloat phase ); - //! Add a phase offset to the current read pointer. + //! Add a normalized phase offset to the read pointer. /*! - This function determines a time offset based on the file - size and the current Stk::sampleRate. The \e angle value - is a multiple of file size. + A \e phaseOffset = 1.0 corresponds to a 360 degree phase + offset. Positive or negative values are possible. */ - void addPhaseOffset( StkFloat angle ); + void addPhaseOffset( StkFloat phaseOffset ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); static StkFrames table_; StkFloat time_; StkFloat rate_; StkFloat phaseOffset_; + unsigned int iIndex_; + StkFloat alpha_; }; +inline StkFloat SineWave :: tick( void ) +{ + // Check limits of time address ... if necessary, recalculate modulo + // TABLE_SIZE. + while ( time_ < 0.0 ) + time_ += TABLE_SIZE; + while ( time_ >= TABLE_SIZE ) + time_ -= TABLE_SIZE; + + iIndex_ = (unsigned int) time_; + alpha_ = time_ - iIndex_; + StkFloat tmp = table_[ iIndex_ ]; + tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); + + // Increment time, which can be negative. + time_ += rate_; + + lastFrame_[0] = tmp; + return lastFrame_[0]; +} + +inline StkFrames& SineWave :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "SineWave::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } #endif + + StkFloat *samples = &frames[channel]; + StkFloat tmp = 0.0; + + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= TABLE_SIZE ) + time_ -= TABLE_SIZE; + + iIndex_ = (unsigned int) time_; + alpha_ = time_ - iIndex_; + tmp = table_[ iIndex_ ]; + tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); + *samples = tmp; + + // Increment time, which can be negative. + time_ += rate_; + } + + lastFrame_[0] = tmp; + return frames; +} + +} // stk namespace + +#endif + diff --git a/include/SingWave.h b/include/SingWave.h index 34ff920..e330e39 100644 --- a/include/SingWave.h +++ b/include/SingWave.h @@ -1,79 +1,98 @@ +#ifndef STK_SINGWAVE_H +#define STK_SINGWAVE_H + +#include "FileLoop.h" +#include "Modulate.h" +#include "Envelope.h" + +namespace stk { + /***************************************************/ /*! \class SingWave \brief STK "singing" looped soundfile class. - This class contains all that is needed to make - a pitched musical sound, like a simple voice - or violin. In general, it will not be used - alone because of munchkinification effects - from pitch shifting. It will be used as an - excitation source for other instruments. + This class loops a specified soundfile and modulates it both + periodically and randomly to produce a pitched musical sound, like + a simple voice or violin. In general, it is not be used alone + because of "munchkinification" effects from pitch shifting. + Within STK, it is used as an excitation source for other + instruments. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_SINGWAVE_H -#define STK_SINGWAVE_H - -#include "WaveLoop.h" -#include "Modulate.h" -#include "Envelope.h" - 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, a read error occurs, or the rawwave path is incorrectly set. + An StkError will be thrown if the file is not found, its format + is unknown, or a read error occurs. If the soundfile has no + header, the second argument should be \e true and the file data + will be assumed to consist of 16-bit signed integers in big-endian + byte order at a sample rate of 22050 Hz. */ - SingWave( std::string fileName, bool raw = false); + SingWave( std::string fileName, bool raw = false ); //! Class destructor. - ~SingWave(); + ~SingWave( void ); //! Reset file to beginning. - void reset(); + void reset( void ) { wave_.reset(); lastFrame_[0] = 0.0; }; //! Normalize the file to a maximum of +-1.0. - void normalize(); + void normalize( void ) { wave_.normalize(); }; //! Normalize the file to a maximum of \e +- peak. - void normalize(StkFloat peak); + void normalize( StkFloat peak ) { wave_.normalize( peak ); }; - //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + //! Set looping parameters for a particular frequency. + void setFrequency( StkFloat frequency ); //! Set the vibrato frequency in Hz. - void setVibratoRate(StkFloat rate); + void setVibratoRate( StkFloat rate ) { modulator_.setVibratoRate( rate ); }; //! Set the vibrato gain. - void setVibratoGain(StkFloat gain); + void setVibratoGain( StkFloat gain ) { modulator_.setVibratoGain( gain ); }; //! Set the random-ness amount. - void setRandomGain(StkFloat gain); + void setRandomGain( StkFloat gain ) { modulator_.setRandomGain( gain ); }; //! Set the sweep rate. - void setSweepRate(StkFloat rate); + void setSweepRate( StkFloat rate ) { sweepRate_ = rate; }; //! Set the gain rate. - void setGainRate(StkFloat rate); + void setGainRate( StkFloat rate ) { envelope_.setRate( rate ); }; //! Set the gain target value. - void setGainTarget(StkFloat target); + void setGainTarget( StkFloat target ) { envelope_.setTarget( target ); }; //! Start a note. - void noteOn(); + void noteOn( void ) { envelope_.keyOn(); }; //! Stop a note. - void noteOff(); + void noteOff( void ) { envelope_.keyOff(); }; + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - - WaveLoop *wave_; + FileLoop wave_; Modulate modulator_; Envelope envelope_; Envelope pitchEnvelope_; @@ -82,4 +101,36 @@ class SingWave : public Generator }; +inline StkFloat SingWave :: tick( void ) +{ + // Set the wave rate. + StkFloat newRate = pitchEnvelope_.tick(); + newRate += newRate * modulator_.tick(); + wave_.setRate( newRate ); + + lastFrame_[0] = wave_.tick(); + lastFrame_[0] *= envelope_.tick(); + + return lastFrame_[0]; +} + +inline StkFrames& SingWave :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "SingWave::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i + +namespace stk { + /***************************************************/ /*! \class Sitar \brief STK sitar string model class. @@ -13,19 +25,10 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_SITAR_H -#define STK_SITAR_H - -#include "Instrmnt.h" -#include "DelayA.h" -#include "OneZero.h" -#include "Noise.h" -#include "ADSR.h" - class Sitar : public Instrmnt { public: @@ -33,27 +36,28 @@ class Sitar : public Instrmnt Sitar( StkFloat lowestFrequency = 20 ); //! Class destructor. - ~Sitar(); + ~Sitar( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Pluck the string with the given amplitude using the current frequency. - void pluck(StkFloat amplitude); + void pluck( StkFloat amplitude ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayA delayLine_; OneZero loopFilter_; Noise noise_; @@ -66,5 +70,23 @@ class Sitar : public Instrmnt }; +inline StkFloat Sitar :: tick( unsigned int ) +{ + if ( fabs(targetDelay_ - delay_) > 0.001 ) { + if ( targetDelay_ < delay_ ) + delay_ *= 0.99999; + else + delay_ *= 1.00001; + delayLine_.setDelay( delay_ ); + } + + lastFrame_[0] = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) + + (amGain_ * envelope_.tick() * noise_.tick())); + + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Skini.h b/include/Skini.h index a7e92fe..a988c22 100644 --- a/include/Skini.h +++ b/include/Skini.h @@ -1,3 +1,13 @@ +#ifndef STK_SKINI_H +#define STK_SKINI_H + +#include "Stk.h" +#include +#include +#include + +namespace stk { + /***************************************************/ /*! \class Skini \brief STK SKINI parsing class @@ -21,18 +31,10 @@ \sa \ref skini - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_SKINI_H -#define STK_SKINI_H - -#include "Stk.h" -#include -#include -#include - class Skini : public Stk { public: @@ -112,6 +114,8 @@ static const double Midi2Pitch[129] = { 8372.02,8869.84,9397.27,9956.06,10548.08,11175.30,11839.82,12543.85, 13289.75}; +} // stk namespace + #endif diff --git a/include/Socket.h b/include/Socket.h index fec4cde..6f59eae 100644 --- a/include/Socket.h +++ b/include/Socket.h @@ -1,16 +1,3 @@ -/***************************************************/ -/*! \class Socket - \brief STK internet socket abstract base class. - - This class provides common functionality for TCP and UDP internet - socket server and client subclasses. This class also provides a - number of static functions for use with external socket - descriptors. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - #ifndef STK_SOCKET_H #define STK_SOCKET_H @@ -33,6 +20,21 @@ #endif +namespace stk { + +/***************************************************/ +/*! \class Socket + \brief STK internet socket abstract base class. + + This class provides common functionality for TCP and UDP internet + socket server and client subclasses. This class also provides a + number of static functions for use with external socket + descriptors. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + class Socket : public Stk { public: @@ -52,13 +54,13 @@ class Socket : public Stk static void close( int socket ); //! Return the socket descriptor. - int id( void ) const; + int id( void ) const { return soket_; }; //! Return the socket port number. - int port( void ) const; + int port( void ) const { return port_; }; //! Returns true if the socket descriptor is valid. - static bool isValid( int socket ); + static bool isValid( int socket ) { return socket != -1; }; //! If enable = false, the socket is set to non-blocking mode. When first created, sockets are by default in blocking mode. static void setBlocking( int socket, bool enable ); @@ -82,4 +84,6 @@ class Socket : public Stk }; -#endif // defined(STK_SOCKET_H) +} // stk namespace + +#endif diff --git a/include/Sphere.h b/include/Sphere.h index d399165..246a145 100644 --- a/include/Sphere.h +++ b/include/Sphere.h @@ -1,3 +1,11 @@ +#ifndef STK_SPHERE_H +#define STK_SPHERE_H + +#include "Stk.h" +#include "Vector3D.h" + +namespace stk { + /***************************************************/ /*! \class Sphere \brief STK sphere class. @@ -5,60 +13,51 @@ This class implements a spherical ball with radius, mass, position, and velocity parameters. - by Perry R. Cook, 1995 - 2004. + by Perry R. Cook, 1995 - 2009. */ /***************************************************/ -#ifndef STK_SPHERE_H -#define STK_SPHERE_H - -#include "Stk.h" -#include "Vector3D.h" - class Sphere : public Stk { public: //! Constructor taking an initial radius value. - Sphere(StkFloat radius = 1.0 ); - - //! Class destructor. - ~Sphere(); + Sphere( StkFloat radius = 1.0 ) { radius_ = radius; mass_ = 1.0; }; //! Set the 3D center position of the sphere. - void setPosition(StkFloat x, StkFloat y, StkFloat z); + void setPosition( StkFloat x, StkFloat y, StkFloat z ) { position_.setXYZ(x, y, z); }; //! Set the 3D velocity of the sphere. - void setVelocity(StkFloat x, StkFloat y, StkFloat z); + void setVelocity( StkFloat x, StkFloat y, StkFloat z ) { velocity_.setXYZ(x, y, z); }; //! Set the radius of the sphere. - void setRadius(StkFloat radius); + void setRadius( StkFloat radius ) { radius_ = radius; }; //! Set the mass of the sphere. - void setMass(StkFloat mass); + void setMass( StkFloat mass ) { mass_ = mass; }; //! Get the current position of the sphere as a 3D vector. - Vector3D* getPosition(); + Vector3D* getPosition( void ) { return &position_; }; //! Get the relative position of the given point to the sphere as a 3D vector. - Vector3D* getRelativePosition(Vector3D *position); + Vector3D* getRelativePosition( Vector3D *position ); //! Set the velcoity of the sphere as a 3D vector. - StkFloat getVelocity(Vector3D* velocity); + StkFloat getVelocity( Vector3D* velocity ); //! Returns the distance from the sphere boundary to the given position (< 0 if inside). - StkFloat isInside(Vector3D *position); + StkFloat isInside( Vector3D *position ); //! Get the current sphere radius. - StkFloat getRadius(); + StkFloat getRadius( void ) { return radius_; }; //! Get the current sphere mass. - StkFloat getMass(); + StkFloat getMass( void ) { return mass_; }; //! Increase the current sphere velocity by the given 3D components. - void addVelocity(StkFloat x, StkFloat y, StkFloat z); + void addVelocity( StkFloat x, StkFloat y, StkFloat z ); //! Move the sphere for the given time increment. - void tick(StkFloat timeIncrement); + void tick( StkFloat timeIncrement ); private: Vector3D position_; @@ -68,4 +67,13 @@ private: StkFloat mass_; }; +inline void Sphere::tick( StkFloat timeIncrement ) +{ + position_.setX(position_.getX() + (timeIncrement * velocity_.getX())); + position_.setY(position_.getY() + (timeIncrement * velocity_.getY())); + position_.setZ(position_.getZ() + (timeIncrement * velocity_.getZ())); +}; + +} // stk namespace + #endif diff --git a/include/StifKarp.h b/include/StifKarp.h index 6f106b9..a0621d2 100644 --- a/include/StifKarp.h +++ b/include/StifKarp.h @@ -1,3 +1,15 @@ +#ifndef STK_STIFKARP_H +#define STK_STIFKARP_H + +#include "Instrmnt.h" +#include "DelayL.h" +#include "DelayA.h" +#include "OneZero.h" +#include "Noise.h" +#include "BiQuad.h" + +namespace stk { + /***************************************************/ /*! \class StifKarp \brief STK plucked stiff string instrument. @@ -17,40 +29,30 @@ - String Sustain = 11 - String Stretch = 1 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_STIFKARP_H -#define STK_STIFKARP_H - -#include "Instrmnt.h" -#include "DelayL.h" -#include "DelayA.h" -#include "OneZero.h" -#include "Noise.h" -#include "BiQuad.h" - class StifKarp : public Instrmnt { public: //! Class constructor, taking the lowest desired playing frequency. - StifKarp(StkFloat lowestFrequency); + StifKarp( StkFloat lowestFrequency ); //! Class destructor. - ~StifKarp(); + ~StifKarp( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Set the stretch "factor" of the string (0.0 - 1.0). - void setStretch(StkFloat stretch); + void setStretch( StkFloat stretch ); //! Set the pluck or "excitation" position along the string (0.0 - 1.0). - void setPickupPosition(StkFloat position); + void setPickupPosition( StkFloat position ); //! Set the base loop gain. /*! @@ -58,24 +60,25 @@ class StifKarp : public Instrmnt Because of high-frequency loop filter roll-off, higher frequency settings have greater loop gains. */ - void setBaseLoopGain(StkFloat aGain); + void setBaseLoopGain( StkFloat aGain ); //! Pluck the string with the given amplitude using the current frequency. - void pluck(StkFloat amplitude); + void pluck( StkFloat amplitude ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayA delayLine_; DelayL combDelay_; OneZero filter_; @@ -93,4 +96,22 @@ class StifKarp : public Instrmnt }; +inline StkFloat StifKarp :: tick( unsigned int ) +{ + StkFloat temp = delayLine_.lastOut() * loopGain_; + + // Calculate allpass stretching. + for (int i=0; i<4; i++) + temp = biquad_[i].tick(temp); + + // Moving average filter. + temp = filter_.tick(temp); + + lastFrame_[0] = delayLine_.tick(temp); + lastFrame_[0] = lastFrame_[0] - combDelay_.tick( lastFrame_[0] ); + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Stk.h b/include/Stk.h index 0f0fadd..3f2aa6e 100644 --- a/include/Stk.h +++ b/include/Stk.h @@ -1,3 +1,20 @@ +#ifndef STK_STK_H +#define STK_STK_H + +#include +#include +#include +#include +#include + +/*! \namespace stk + \brief The STK namespace. + + Most Stk classes are defined within the STK namespace. Exceptions + to this include the classes RtAudio, RtMidi, and RtError. +*/ +namespace stk { + /***************************************************/ /*! \class Stk \brief STK base class @@ -22,7 +39,7 @@ STK WWW site: http://ccrma.stanford.edu/software/stk/ The Synthesis ToolKit in C++ (STK) - Copyright (c) 1995-2007 Perry R. Cook and Gary P. Scavone + Copyright (c) 1995-2009 Perry R. Cook and Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -50,13 +67,7 @@ */ /***************************************************/ -#ifndef STK_STK_H -#define STK_STK_H - -#include -#include -#include -#include +//#define _STK_DEBUG_ // Most data in STK is passed and calculated with the // following user-definable floating-point type. You @@ -129,15 +140,15 @@ public: 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 method that returns the current STK sample rate. static StkFloat sampleRate( void ) { return srate_; } //! Static method that sets the STK sample rate. /*! The sample rate set using this method is queried by all STK - classes which depend on its value. It is initialized to the + classes that depend on its value. It is initialized to the default SRATE set in Stk.h. Many STK classes use the sample rate - during instantiation. Therefore, if you wish to use a rate which + during instantiation. Therefore, if you wish to use a rate that is different from the default rate, it is imperative that it be set \e BEFORE STK objects are instantiated. A few classes that make use of the global STK sample rate are automatically notified @@ -160,19 +171,19 @@ public: */ void ignoreSampleRateChange( bool ignore = true ) { ignoreSampleRateChange_ = ignore; }; - //! Static method which returns the current rawwave path. + //! Static method that returns the current rawwave path. static std::string rawwavePath(void) { return rawwavepath_; } - //! Static method which sets the STK rawwave path. + //! Static method that sets the STK rawwave path. static void setRawwavePath( std::string path ); - //! Static method which byte-swaps a 16-bit data type. + //! Static method that byte-swaps a 16-bit data type. static void swap16( unsigned char *ptr ); - //! Static method which byte-swaps a 32-bit data type. + //! Static method that byte-swaps a 32-bit data type. static void swap32( unsigned char *ptr ); - //! Static method which byte-swaps a 64-bit data type. + //! Static method that byte-swaps a 64-bit data type. static void swap64( unsigned char *ptr ); //! Static cross-platform method to sleep for a number of milliseconds. @@ -217,7 +228,7 @@ protected: //! Remove class pointer from list for sample rate change notification. void removeSampleRateAlert( Stk *ptr ); - //! Internal function for error reporting which assumes message in \c errorString_ variable. + //! Internal function for error reporting that assumes message in \c errorString_ variable. void handleError( StkError::Type type ); }; @@ -226,16 +237,24 @@ protected: /*! \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. In an effort to maintain efficiency, no out-of-bounds - checks are performed in this class. + This class can hold single- or multi-channel audio data. The data + type is always StkFloat and the channel format is always + interleaved. In an effort to maintain efficiency, no + out-of-bounds checks are performed in this class unless + _STK_DEBUG_ is defined. + + Internally, the data is stored in a one-dimensional C array. An + indexing operator is available to set and retrieve data values. + Alternately, one can use pointers to access the data, using the + index operator to get an address for a particular location in the + data: + + StkFloat* ptr = &myStkFrames[0]; Possible future improvements in this class could include functions - to inter- or de-interleave the data and to convert to and return - other data types. + to convert to and return other data types. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ @@ -244,17 +263,23 @@ class StkFrames public: //! The default constructor initializes the frame data structure to size zero. - StkFrames( unsigned int nFrames = 0, unsigned int nChannels = 0, bool interleaved = true ); + StkFrames( unsigned int nFrames = 0, unsigned int nChannels = 0 ); //! Overloaded constructor that initializes the frame data to the specified size with \c value. - StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels, bool interleaved = true ); + StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels ); //! The destructor. ~StkFrames(); - //! Subscript operator which returns a reference to element \c n of self. + // A copy constructor. + StkFrames( const StkFrames& f ); + + // Assignment operator that returns a reference to self. + StkFrames& operator= ( const StkFrames& f ); + + //! Subscript operator that returns a reference to element \c n of self. /*! - The result can be used as an lvalue . This reference is valid + 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 unless _STK_DEBUG_ is defined. @@ -268,6 +293,22 @@ public: */ StkFloat operator[] ( size_t n ) const; + //! Assignment by sum operator into self. + /*! + The dimensions of the argument are expected to be the same as + self. No range checking is performed unless _STK_DEBUG_ is + defined. + */ + void operator+= ( StkFrames& f ); + + //! Assignment by product operator into self. + /*! + The dimensions of the argument are expected to be the same as + self. No range checking is performed unless _STK_DEBUG_ is + defined. + */ + void operator*= ( StkFrames& f ); + //! Channel / frame subscript operator that returns a reference. /*! The result can be used as an lvalue. This reference is valid @@ -341,17 +382,6 @@ public: */ StkFloat dataRate( void ) const { return dataRate_; }; - //! 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. - /*! - Note that this function does not modify the internal data order - with respect to the argument value. It simply changes the - indicator flag value. - */ - void setInterleaved( bool isInterleaved ) { interleaved_ = isInterleaved; }; - private: StkFloat *data_; @@ -360,10 +390,98 @@ private: unsigned int nChannels_; size_t size_; size_t bufferSize_; - bool interleaved_; }; +inline bool StkFrames :: empty() const +{ + if ( size_ > 0 ) return false; + else return true; +} + +inline StkFloat& StkFrames :: operator[] ( size_t n ) +{ +#if defined(_STK_DEBUG_) + if ( n >= size_ ) { + std::ostringstream error; + error << "StkFrames::operator[]: invalid index (" << n << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[n]; +} + +inline StkFloat StkFrames :: operator[] ( size_t n ) const +{ +#if defined(_STK_DEBUG_) + if ( n >= size_ ) { + std::ostringstream error; + error << "StkFrames::operator[]: invalid index (" << n << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[n]; +} + +inline StkFloat& StkFrames :: operator() ( size_t frame, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( frame >= nFrames_ || channel >= nChannels_ ) { + std::ostringstream error; + error << "StkFrames::operator(): invalid frame (" << frame << ") or channel (" << channel << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[ frame * nChannels_ + channel ]; +} + +inline StkFloat StkFrames :: operator() ( size_t frame, unsigned int channel ) const +{ +#if defined(_STK_DEBUG_) + if ( frame >= nFrames_ || channel >= nChannels_ ) { + std::ostringstream error; + error << "StkFrames::operator(): invalid frame (" << frame << ") or channel (" << channel << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[ frame * nChannels_ + channel ]; +} + +inline void StkFrames :: operator+= ( StkFrames& f ) +{ +#if defined(_STK_DEBUG_) + if ( f.frames() != nFrames_ || f.channels() != nChannels_ ) { + std::ostringstream error; + error << "StkFrames::operator+=: frames argument must be of equal dimensions!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + StkFloat *fptr = &f[0]; + StkFloat *dptr = data_; + for ( unsigned int i=0; i taps = std::vector( 1, 0 ), unsigned long maxDelay = 4095 ); + + //! Class destructor. + ~TapDelay(); + + //! 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 tap lengths. + /*! + The valid range for each tap length is from 0 to the maximum delay-line length. + */ + void setTapDelays( std::vector taps ); + + //! Return the current delay-line length. + std::vector getTapDelays( void ) const { return delays_; }; + + //! Return the specified tap value of the last computed frame. + /*! + Use the lastFrame() function to get all tap values from the + last computed frame. The \c tap argument must be less than the + number of delayline taps (the first tap is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFloat lastOut( unsigned int tap = 0 ) const; + + //! Input one sample to the delayline and return outputs at all tap positions. + /*! + The StkFrames argument reference is returned. The output + values are ordered according to the tap positions set using the + setTapDelays() function (no sorting is performed). The StkFrames + argument must contain at least as many channels as the number of + taps. However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFloat input, StkFrames& outputs ); + + //! Take a channel of the StkFrames object as inputs to the filter and write outputs back to the same object. + /*! + The StkFrames argument reference is returned. The output + values are ordered according to the tap positions set using the + setTapDelays() function (no sorting is performed). The StkFrames + argument must contain at least as many channels as the number of + taps. However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. The output values + are ordered according to the tap positions set using the + setTapDelays() function (no sorting is performed). The \c + iChannel argument must be less than the number of channels in + the \c iFrames argument (the first channel is specified by 0). + The \c oFrames argument must contain at least as many channels as + the number of taps. However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0 ); + + protected: + + unsigned long inPoint_; + std::vector outPoint_; + std::vector delays_; + +}; + +inline StkFloat TapDelay :: lastOut( unsigned int tap ) const +{ +#if defined(_STK_DEBUG_) + if ( tap >= lastFrame_.size() ) ) { + errorString_ << "TapDelay::lastOut(): tap argument and number of taps are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[tap]; +} + +inline StkFrames& TapDelay :: tick( StkFloat input, StkFrames& outputs ) +{ +#if defined(_STK_DEBUG_) + if ( outputs.channels() < outPoint_.size() ) { + errorString_ << "TapDelay::tick(): number of taps > channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + inputs_[inPoint_++] = input * gain_; + + // Check for end condition + if ( inPoint_ == inputs_.size() ) + inPoint_ = 0; + + // Read out next values + StkFloat *outs = &outputs[0]; + for ( unsigned int i=0; i= frames.channels() ) { + errorString_ << "TapDelay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + if ( frames.channels() < outPoint_.size() ) { + errorString_ << "TapDelay::tick(): number of taps > channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &frames[channel]; + StkFloat *oSamples = &frames[0]; + unsigned int j, iHop = frames.channels(), oHop = frames.channels() - outPoint_.size(); + for ( unsigned int i=0; i= iFrames.channels() ) { + errorString_ << "TapDelay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + if ( oFrames.channels() < outPoint_.size() ) { + errorString_ << "TapDelay::tick(): number of taps > channels in output StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[0]; + unsigned int j, iHop = iFrames.channels(), oHop = oFrames.channels() - outPoint_.size(); + for ( unsigned int i=0; itick() * 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_[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_; + temp = temp * (1.0 + temp2); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/TwoPole.h b/include/TwoPole.h index b8060ea..f5ab03d 100644 --- a/include/TwoPole.h +++ b/include/TwoPole.h @@ -1,28 +1,28 @@ -/***************************************************/ -/*! \class TwoPole - \brief STK two-pole filter class. - - This protected Filter subclass implements - a two-pole digital filter. A method is - provided for creating a resonance in the - frequency response while maintaining a nearly - constant filter gain. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - #ifndef STK_TWOPOLE_H #define STK_TWOPOLE_H #include "Filter.h" -class TwoPole : protected Filter +namespace stk { + +/***************************************************/ +/*! \class TwoPole + \brief STK two-pole filter class. + + This class implements a two-pole digital filter. A method is + provided for creating a resonance in the frequency response while + maintaining a nearly constant filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + +class TwoPole : public Filter { public: //! Default constructor creates a second-order pass-through filter. - TwoPole(); + TwoPole( void ); //! Class destructor. ~TwoPole(); @@ -30,17 +30,17 @@ class TwoPole : protected Filter //! A function to enable/disable the automatic updating of class data when the STK sample rate changes. void ignoreSampleRateChange( bool ignore = true ) { ignoreSampleRateChange_ = ignore; }; - //! Clears the internal states of the filter. - void clear(void); - //! Set the b[0] coefficient value. - void setB0(StkFloat b0); + void setB0( StkFloat b0 ) { b_[0] = b0; }; //! Set the a[1] coefficient value. - void setA1(StkFloat a1); + void setA1( StkFloat a1 ) { a_[1] = a1; }; //! Set the a[2] coefficient value. - void setA2(StkFloat a2); + void setA2( StkFloat a2 ) { a_[2] = a2; }; + + //! Set all filter coefficients. + void setCoefficients( StkFloat b0, StkFloat a1, StkFloat a2, bool clearState = false ); //! Sets the filter coefficients for a resonance at \e frequency (in Hz). /*! @@ -58,34 +58,94 @@ class TwoPole : protected Filter */ 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(StkFloat gain); - - //! Return the current filter gain. - StkFloat getGain(void) const; - //! Return the last computed output value. - StkFloat lastOut(void) const; + StkFloat lastOut( void ) const { return lastFrame_[0]; }; //! Input one sample to the filter and return one output. - StkFloat tick(StkFloat sample); + StkFloat tick( StkFloat input ); //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. /*! - The \c channel argument should be zero or greater (the first - channel is specified by 0). An StkError will be thrown if the \c - channel argument is equal to or greater than the number of - channels in the StkFrames object. + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. */ StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + protected: virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); }; +inline StkFloat TwoPole :: tick( StkFloat input ) +{ + inputs_[0] = gain_ * input; + lastFrame_[0] = b_[0] * inputs_[0] - a_[1] * outputs_[1] - a_[2] * outputs_[2]; + outputs_[2] = outputs_[1]; + outputs_[1] = lastFrame_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& TwoPole :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "TwoPole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "TwoPole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + errorString_ << "TwoZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "TwoZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i + +namespace stk { + /***************************************************/ /*! \class Vector3D \brief STK 3D vector class. This class implements a three-dimensional vector. - by Perry R. Cook, 1995 - 2004. + by Perry R. Cook, 1995 - 2009. */ /***************************************************/ -#ifndef STK_VECTOR3D_H -#define STK_VECTOR3D_H - -#include "Stk.h" - class Vector3D : public Stk { public: //! Default constructor taking optional initial X, Y, and Z values. - Vector3D(StkFloat initX=0.0, StkFloat initY=0.0, StkFloat initZ=0.0); - - //! Class destructor. - ~Vector3D(); + Vector3D( StkFloat x = 0.0, StkFloat y = 0.0, StkFloat z = 0.0 ) { setXYZ( x, y, z ); }; //! Get the current X value. - StkFloat getX(); + StkFloat getX( void ) { return X_; }; //! Get the current Y value. - StkFloat getY(); + StkFloat getY( void ) { return Y_; }; //! Get the current Z value. - StkFloat getZ(); + StkFloat getZ( void ) { return Z_; }; //! Calculate the vector length. - StkFloat getLength(); + StkFloat getLength( void ); //! Set the X, Y, and Z values simultaniously. - void setXYZ(StkFloat x, StkFloat y, StkFloat z); + void setXYZ( StkFloat x, StkFloat y, StkFloat z ) { X_ = x; Y_ = y; Z_ = z; }; //! Set the X value. - void setX(StkFloat x); + void setX( StkFloat x ) { X_ = x; }; //! Set the Y value. - void setY(StkFloat y); + void setY( StkFloat y ) { Y_ = y; }; //! Set the Z value. - void setZ(StkFloat z); + void setZ( StkFloat z ) { Z_ = z; }; protected: - StkFloat myX_; - StkFloat myY_; - StkFloat myZ_; + StkFloat X_; + StkFloat Y_; + StkFloat Z_; }; +inline StkFloat Vector3D :: getLength( void ) +{ + StkFloat temp; + temp = X_ * X_; + temp += Y_ * Y_; + temp += Z_ * Z_; + temp = sqrt( temp ); + return temp; +} + +} // stk namespace + #endif diff --git a/include/VoicForm.h b/include/VoicForm.h index 41f47b0..5653589 100644 --- a/include/VoicForm.h +++ b/include/VoicForm.h @@ -1,3 +1,16 @@ +#ifndef STK_VOICFORM_H +#define STK_VOICFORM_H + +#include "Instrmnt.h" +#include "Envelope.h" +#include "Noise.h" +#include "SingWave.h" +#include "FormSwep.h" +#include "OnePole.h" +#include "OneZero.h" + +namespace stk { + /***************************************************/ /*! \class VoicForm \brief Four formant synthesis instrument. @@ -21,21 +34,10 @@ - Vibrato Gain = 1 - Loudness (Spectral Tilt) = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_VOICFORM_H -#define STK_VOICFORM_H - -#include "Instrmnt.h" -#include "Envelope.h" -#include "Noise.h" -#include "SingWave.h" -#include "FormSwep.h" -#include "OnePole.h" -#include "OneZero.h" - class VoicForm : public Instrmnt { public: @@ -43,51 +45,52 @@ class VoicForm : public Instrmnt /*! An StkError will be thrown if the rawwave path is incorrectly set. */ - VoicForm(); + VoicForm( void ); //! Class destructor. - ~VoicForm(); + ~VoicForm( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Set instrument parameters for the given phoneme. Returns false if phoneme not found. - bool setPhoneme(const char* phoneme); + bool setPhoneme( const char* phoneme ); //! Set the voiced component gain. - void setVoiced(StkFloat vGain); + void setVoiced( StkFloat vGain ) { voiced_->setGainTarget(vGain); }; //! Set the unvoiced component gain. - void setUnVoiced(StkFloat nGain); + void setUnVoiced( StkFloat nGain ) { noiseEnv_.setTarget(nGain); }; //! Set the sweep rate for a particular formant filter (0-3). - void setFilterSweepRate(unsigned int whichOne, StkFloat rate); + void setFilterSweepRate( unsigned int whichOne, StkFloat rate ); //! Set voiced component pitch sweep rate. - void setPitchSweepRate(StkFloat rate); + void setPitchSweepRate( StkFloat rate ) { voiced_->setSweepRate(rate); }; //! Start the voice. - void speak(); + void speak( void ) { voiced_->noteOn(); }; //! Stop the voice. - void quiet(); + void quiet( void ); //! Start a note with the given frequency and amplitude. - void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ) { this->quiet(); }; //! Perform the control change specified by \e number and \e value (0.0 - 128.0). - void controlChange(int number, StkFloat value); + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - SingWave *voiced_; Noise noise_; Envelope noiseEnv_; @@ -97,4 +100,25 @@ protected: }; +inline StkFloat VoicForm :: tick( unsigned int ) +{ + StkFloat temp; + temp = onepole_.tick( onezero_.tick( voiced_->tick() ) ); + temp += noiseEnv_.tick() * noise_.tick(); + lastFrame_[0] = filters_[0].tick(temp); + lastFrame_[0] += filters_[1].tick(temp); + lastFrame_[0] += filters_[2].tick(temp); + lastFrame_[0] += filters_[3].tick(temp); + /* + temp += noiseEnv_.tick() * noise_.tick(); + lastFrame_[0] = filters_[0].tick(temp); + lastFrame_[0] = filters_[1].tick(lastFrame_[0]); + lastFrame_[0] = filters_[2].tick(lastFrame_[0]); + lastFrame_[0] = filters_[3].tick(lastFrame_[0]); + */ + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/Voicer.h b/include/Voicer.h index ba404a4..5a4fd2a 100644 --- a/include/Voicer.h +++ b/include/Voicer.h @@ -1,80 +1,73 @@ -/***************************************************/ -/*! \class Voicer - \brief STK voice manager class. - - This class can be used to manage a group of - STK instrument classes. Individual voices can - be controlled via unique note tags. - Instrument groups can be controlled by channel - number. - - A previously constructed STK instrument class - is linked with a voice manager using the - addInstrument() function. An optional channel - number argument can be specified to the - addInstrument() function as well (default - channel = 0). The voice manager does not - delete any instrument instances ... it is the - responsibility of the user to allocate and - deallocate all instruments. - - The tick() function returns the mix of all - sounding voices. Each noteOn returns a unique - tag (credits to the NeXT MusicKit), so you can - send control changes to specific voices within - an ensemble. Alternately, control changes can - be sent to all voices on a given channel. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - #ifndef STK_VOICER_H #define STK_VOICER_H #include "Instrmnt.h" #include +namespace stk { + +/***************************************************/ +/*! \class Voicer + \brief STK voice manager class. + + This class can be used to manage a group of STK instrument + classes. Individual voices can be controlled via unique note + tags. Instrument groups can be controlled by group number. + + A previously constructed STK instrument class is linked with a + voice manager using the addInstrument() function. An optional + group number argument can be specified to the addInstrument() + function as well (default group = 0). The voice manager does not + delete any instrument instances ... it is the responsibility of + the user to allocate and deallocate all instruments. + + The tick() function returns the mix of all sounding voices. Each + noteOn returns a unique tag (credits to the NeXT MusicKit), so you + can send control changes to specific voices within an ensemble. + Alternately, control changes can be sent to all voices in a given + group. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + class Voicer : public Stk { -public: + public: //! Class constructor taking an optional note decay time (in seconds). - Voicer( StkFloat decayTime=0.2 ); + Voicer( StkFloat decayTime = 0.2 ); - //! Class destructor. - ~Voicer(); - - //! Add an instrument with an optional channel number to the voice manager. + //! Add an instrument with an optional group number to the voice manager. /*! - A set of instruments can be grouped by channel number and - controlled via the functions which take a channel number argument. + A set of instruments can be grouped by group number and + controlled via the functions that take a group number argument. */ - void addInstrument( Instrmnt *instrument, int channel=0 ); + void addInstrument( Instrmnt *instrument, int group=0 ); //! Remove the given instrument pointer from the voice manager's control. /*! It is important that any instruments which are to be deleted by the user while the voice manager is running be first removed from the manager's control via this function!! - */ + */ void removeInstrument( Instrmnt *instrument ); //! Initiate a noteOn event with the given note number and amplitude and return a unique note tag. /*! Send the noteOn message to the first available unused voice. If all voices are sounding, the oldest voice is interrupted and - sent the noteOn message. If the optional channel argument is - non-zero, only voices on that channel are used. If no voices are - found for a specified non-zero channel value, the function returns + sent the noteOn message. If the optional group argument is + non-zero, only voices in that group are used. If no voices are + found for a specified non-zero group value, the function returns -1. The amplitude value should be in the range 0.0 - 128.0. */ - long noteOn( StkFloat noteNumber, StkFloat amplitude, int channel=0 ); + long noteOn( StkFloat noteNumber, StkFloat amplitude, int group=0 ); - //! Send a noteOff to all voices having the given noteNumber and optional channel (default channel = 0). + //! Send a noteOff to all voices having the given noteNumber and optional group (default group = 0). /*! The amplitude value should be in the range 0.0 - 128.0. */ - void noteOff( StkFloat noteNumber, StkFloat amplitude, int channel=0 ); + void noteOff( StkFloat noteNumber, StkFloat amplitude, int group=0 ); //! Send a noteOff to the voice with the given note tag. /*! @@ -82,26 +75,26 @@ public: */ void noteOff( long tag, StkFloat amplitude ); - //! Send a frequency update message to all voices assigned to the optional channel argument (default channel = 0). + //! Send a frequency update message to all voices assigned to the optional group argument (default group = 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( StkFloat noteNumber, int channel=0 ); + */ + void setFrequency( StkFloat noteNumber, int group=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, StkFloat noteNumber ); - //! Send a pitchBend message to all voices assigned to the optional channel argument (default channel = 0). - void pitchBend( StkFloat value, int channel=0 ); + //! Send a pitchBend message to all voices assigned to the optional group argument (default group = 0). + void pitchBend( StkFloat value, int group=0 ); //! Send a pitchBend message to the voice with the given note tag. 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, StkFloat value, int channel=0 ); + //! Send a controlChange to all instruments assigned to the optional group argument (default group = 0). + void controlChange( int number, StkFloat value, int group=0 ); //! Send a controlChange to the voice with the given note tag. void controlChange( long tag, int number, StkFloat value ); @@ -109,31 +102,46 @@ public: //! Send a noteOff message to all existing voices. void silence( void ); - //! Mix the output for all sounding voices. - StkFloat tick(); + //! Return the current number of output channels. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; - //! Compute \e vectorSize output mixes and return them in \e vector. - StkFloat *tick(StkFloat *vector, unsigned int vectorSize); + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; - //! Fill a channel of the StkFrames object with computed outputs. + //! Return the specified channel value of the last computed frame. /*! - 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. + The \c channel argument must be less than the number of output + channels, which can be determined with the channelsOut() function + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. \sa lastFrame() */ - virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 1 ); + StkFloat lastOut( unsigned int channel = 0 ); - //! Return the last output value. - StkFloat lastOut() const; + //! Mix one sample frame of all sounding voices and return the specified \c channel value. + /*! + The \c channel argument must be less than the number of output + channels, which can be determined with the channelsOut() function + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. + */ + StkFloat tick( unsigned int channel = 0 ); - //! Return the last left output value. - StkFloat lastOutLeft() const; + //! Fill the StkFrames argument with computed frames and return the same reference. + /*! + The number of channels in the StkFrames argument must equal + the number of channels in the file data. However, this is only + checked if _STK_DEBUG_ is defined during compilation, in which + case an incompatibility will trigger an StkError exception. If no + file data is loaded, the function does nothing (a warning will be + issued if _STK_DEBUG_ is defined during compilation). + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); - //! Return the last right output value. - StkFloat lastOutRight() const; - -protected: + protected: struct Voice { Instrmnt *instrument; @@ -141,20 +149,72 @@ protected: StkFloat noteNumber; StkFloat frequency; int sounding; - int channel; + int group; // Default constructor. Voice() - :instrument(0), tag(0), noteNumber(-1.0), frequency(0.0), - sounding(0), channel(0) {} + :instrument(0), tag(0), noteNumber(-1.0), frequency(0.0), sounding(0), group(0) {} }; std::vector voices_; long tags_; int muteTime_; - StkFloat lastOutput_; - StkFloat lastOutputLeft_; - StkFloat lastOutputRight_; + StkFrames lastFrame_; }; +inline StkFloat Voicer :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= lastFrame_.channels() ) { + errorString_ << "Voicer::lastOut(): channel argument is invalid!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + + +inline StkFloat Voicer :: tick( unsigned int channel ) +{ + unsigned int j; + for ( j=0; jtick(); + for ( j=0; jchannelsOut(); j++ ) lastFrame_[j] += voices_[i].instrument->lastOut( j ); + } + if ( voices_[i].sounding < 0 ) { + voices_[i].sounding++; + if ( voices_[i].sounding == 0 ) + voices_[i].noteNumber = -1; + } + } + + return lastFrame_[channel]; +} + +inline StkFrames& Voicer :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + errorString_ << "Voicer::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + for ( unsigned int i=0; itick() * 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_[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_; + temp = temp * (1.0 + temp2); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/include/WvIn.h b/include/WvIn.h index 7722b80..651bbc2 100644 --- a/include/WvIn.h +++ b/include/WvIn.h @@ -1,3 +1,10 @@ +#ifndef STK_WVIN_H +#define STK_WVIN_H + +#include "Stk.h" + +namespace stk { + /***************************************************/ /*! \class WvIn \brief STK audio input abstract base class. @@ -5,83 +12,32 @@ This class provides common functionality for a variety of audio data input subclasses. - WvIn supports multi-channel data. It is important to distinguish - the tick() methods, which return samples produced by averaging - across sample frames, from the tickFrame() methods, which return - references or pointers to multi-channel sample frames. - - Both interleaved and non-interleaved data is supported via the use - of StkFrames objects. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_WVIN_H -#define STK_WVIN_H - -#include "Stk.h" -#include - class WvIn : public Stk { public: - //! Default constructor. - WvIn(); + //! Return the number of audio channels in the data or stream. + unsigned int channelsOut( void ) const { return data_.channels(); }; - //! Class destructor. - virtual ~WvIn(); - - //! Return the number of audio channels in the data. - unsigned int getChannels( void ) const { return data_.channels(); }; - - //! Return the average across the last output sample frame. - /*! - If no file data is loaded, the returned value is 0.0. - */ - StkFloat lastOut( void ) const; - - //! Return an StkFrames reference to the last output sample frame. + //! Return an StkFrames reference to the last computed sample frame. /*! If no file data is loaded, an empty container is returned. */ - const StkFrames& lastFrame( void ) const { return lastOutputs_; }; + const StkFrames& lastFrame( void ) const { return lastFrame_; }; - //! Read out the average across one sample frame of data. - /*! - If no file data is loaded, the returned value is 0.0. - */ - StkFloat tick( void ); - - //! Fill a channel of the StkFrames object with averaged sample frames. - /*! - The \c channel argument should be zero or greater (the first - channel is specified by 0). An StkError will be thrown if the \c - channel argument is greater than or equal to the number of - channels in the StkFrames object. If no file data is loaded, the - container is filled with zeroes. - */ - StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); - - //! Fill the StkFrames argument with data and return the same reference. - /*! - An StkError will be thrown if there is an incompatability - between the number of channels in the loaded data and that in the - StkFrames argument. If no file data is loaded, the container is - filled with zeroes. - */ - StkFrames& tickFrame( StkFrames& frames ); + //! Compute one sample frame and return the specified \c channel value. + virtual StkFloat tick( unsigned int channel = 0 ) = 0; protected: - // This abstract function must be implemented in all subclasses. - // It is used to get around a C++ problem with overloaded virtual - // functions. - virtual void computeFrame( void ) = 0; - StkFrames data_; - StkFrames lastOutputs_; + StkFrames lastFrame_; }; +} // stk namespace + #endif diff --git a/include/WvOut.h b/include/WvOut.h index d2b4820..1339319 100644 --- a/include/WvOut.h +++ b/include/WvOut.h @@ -1,3 +1,10 @@ +#ifndef STK_WVOUT_H +#define STK_WVOUT_H + +#include "Stk.h" + +namespace stk { + /***************************************************/ /*! \class WvOut \brief STK audio output abstract base class. @@ -5,42 +12,25 @@ This class provides common functionality for a variety of audio data output subclasses. - WvOut supports multi-channel data. It is important to distinguish - the tick() methods, which output single samples to all channels in - a sample frame, from the tickFrame() methods, which take a pointer - or reference to multi-channel sample frame data. - - Both interleaved and non-interleaved data is supported via the use - of StkFrames objects. - Currently, WvOut is non-interpolating and the output rate is always Stk::sampleRate(). - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_WVOUT_H -#define STK_WVOUT_H - -#include "Stk.h" -#include - class WvOut : public Stk { public: //! Default constructor. - WvOut(); - - //! Class destructor. - virtual ~WvOut(); + WvOut( void ) : frameCounter_(0), clipping_(false) {}; //! Return the number of sample frames output. - unsigned long getFrameCount( void ) const; + unsigned long getFrameCount( void ) const { return frameCounter_; }; //! Return the number of seconds of data output. - StkFloat getTime( void ) const; + StkFloat getTime( void ) const { return (StkFloat) frameCounter_ / Stk::sampleRate(); }; //! Returns \c true if clipping has been detected during output since instantiation or the last reset. bool clipStatus( void ) { return clipping_; }; @@ -52,34 +42,10 @@ class WvOut : public Stk /*! An StkError is thrown if an output error occurs. */ - void tick( const StkFloat sample ); - - //! Output a channel of the StkFrames object to all channels of the WvOut object. - /*! - The \c channel argument should be 0 or greater (the first - channel is specified by 0). An StkError will be thrown if an - output error occurs or if the \c channel argument is equal to or - greater than the number of channels in the StkFrames object. - */ - void tick( const StkFrames& frames, unsigned int channel = 0 ); - - //! Output the StkFrames data. - /*! - An StkError will be thrown if an output error occurs 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 ); + virtual void tick( const StkFloat sample ) = 0; protected: - // These abstract functions must be implemented in all subclasses. - // They are used to get around a C++ problem with overloaded virtual - // functions. - virtual void computeSample( const StkFloat sample ) = 0; - - virtual void computeFrames( const StkFrames& frames ) = 0; - // Check for sample clipping and clamp. StkFloat& clipTest( StkFloat& sample ); @@ -89,4 +55,28 @@ class WvOut : public Stk }; +inline StkFloat& WvOut :: clipTest( StkFloat& sample ) +{ + bool clip = false; + if ( sample > 1.0 ) { + sample = 1.0; + clip = true; + } + else if ( sample < -1.0 ) { + sample = -1.0; + clip = true; + } + + if ( clip == true && clipping_ == false ) { + // First occurrence of clipping since instantiation or reset. + clipping_ = true; + errorString_ << "WvOut: data value(s) outside +-1.0 detected ... clamping at outer bound!"; + handleError( StkError::WARNING ); + } + + return sample; +} + +} // stk namespace + #endif diff --git a/projects/demo/Makefile.in b/projects/demo/Makefile.in index 31af87a..ffb8448 100644 --- a/projects/demo/Makefile.in +++ b/projects/demo/Makefile.in @@ -7,16 +7,16 @@ SRC_PATH = ../../src OBJECT_PATH = @object_path@ vpath %.o $(OBJECT_PATH) -OBJECTS = Stk.o Generator.o Noise.o SubNoise.o Envelope.o ADSR.o \ +OBJECTS = Stk.o Noise.o Envelope.o ADSR.o \ Modulate.o SingWave.o SineWave.o FileRead.o FileWrite.o \ - WvIn.o FileWvIn.o WaveLoop.o WvOut.o FileWvOut.o \ - Filter.o OneZero.o OnePole.o PoleZero.o TwoZero.o \ + FileWvIn.o FileLoop.o FileWvOut.o \ + OneZero.o OnePole.o PoleZero.o TwoZero.o \ BiQuad.o FormSwep.o Delay.o DelayL.o DelayA.o \ - Function.o ReedTable.o JetTable.o BowTable.o \ - Effect.o PRCRev.o \ + ReedTable.o JetTable.o BowTable.o \ + PRCRev.o \ Voicer.o Vector3D.o Sphere.o \ \ - Instrmnt.o Clarinet.o BlowHole.o Saxofony.o Flute.o Brass.o BlowBotl.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 \ FM.o Rhodey.o Wurley.o TubeBell.o HevyMetl.o PercFlut.o BeeThree.o FMVoices.o \ Sampler.o Moog.o Simple.o Drummer.o Shakers.o \ @@ -31,18 +31,16 @@ endif vpath %.h $(INCLUDE) CC = @CXX@ -DEFS = @byte_order@ -DEFS += @debug@ -CFLAGS = @cflags@ -CFLAGS += @warn@ -I$(INCLUDE) -I../../src/include +DEFS = @CPPFLAGS@ +DEFS += @byte_order@ +CFLAGS = @CXXFLAGS@ +CFLAGS += -I$(INCLUDE) -I$(INCLUDE)/../src/include LIBRARY = @LIBS@ -LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) PROGRAMS += demo OBJECTS += RtMidi.o RtAudio.o Thread.o Mutex.o Socket.o TcpServer.o @objects@ - DEFS += @audio_apis@ endif RAWWAVES = @rawwaves@ @@ -76,6 +74,7 @@ $(OBJECTS) : Stk.h clean : -rm $(OBJECT_PATH)/*.o -rm $(PROGRAMS) *.exe + -rm -fR *~ *.dSYM strip : strip $(PROGRAMS) diff --git a/projects/demo/Md2Skini.cpp b/projects/demo/Md2Skini.cpp index 41c4f14..5062685 100644 --- a/projects/demo/Md2Skini.cpp +++ b/projects/demo/Md2Skini.cpp @@ -12,9 +12,8 @@ #include "RtMidi.h" #include "SKINI.msg" -#include #include -#include +#include void usage(void) { std::cout << "\nuseage: Md2Skini \n\n"; @@ -161,6 +160,8 @@ void midiCallback( double deltatime, std::vector< unsigned char > *bytes, void * if ( file != NULL ) fprintf( file, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1, (float)databyte2 ); } + + fflush( stdout ); } int main( int argc,char *argv[] ) diff --git a/projects/demo/demo.cpp b/projects/demo/demo.cpp index b048dee..f7bcd63 100644 --- a/projects/demo/demo.cpp +++ b/projects/demo/demo.cpp @@ -28,13 +28,15 @@ using std::min; bool done; static void finish(int ignore){ done = true; } +using namespace stk; + // 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; + PRCRev reverb; Messager messager; Skini::Message message; StkFloat volume; @@ -51,7 +53,7 @@ struct TickData { // Default constructor. TickData() - : wvout(0), instrument(0), voicer(0), reverb(0), volume(1.0), t60(1.0), + : wvout(0), instrument(0), voicer(0), volume(1.0), t60(1.0), nWvOuts(0), nVoices(1), currentVoice(0), channels(2), counter(0), realtime( false ), settling( false ), haveMessage( false ) {} }; @@ -90,7 +92,7 @@ void processMessage( TickData* data ) case __SK_ControlChange_: if (value1 == 44.0) - data->reverb->setEffectMix(value2 * ONE_OVER_128); + data->reverb.setEffectMix(value2 * ONE_OVER_128); else if (value1 == 7.0) data->volume = value2 * ONE_OVER_128; else if (value1 == 49.0) @@ -183,7 +185,7 @@ int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, counter = min( nTicks, data->counter ); data->counter -= counter; for ( int i=0; ivolume * data->reverb->tick( data->voicer->tick() ); + sample = data->volume * 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; @@ -212,6 +214,10 @@ int main( int argc, char *argv[] ) // specified in the command line, it will override this setting. Stk::setSampleRate( 44100.0 ); + // Depending on how you compile STK, you may need to explicitly set + // the path to the rawwave directory. + Stk::setRawwavePath( "../../rawwaves/" ); + // By default, warning messages are not printed. If we want to see // them, we need to specify that here. Stk::showWarnings( true ); @@ -267,8 +273,8 @@ int main( int argc, char *argv[] ) #endif // Set the reverb parameters. - data.reverb = new PRCRev( data.t60 ); - data.reverb->setEffectMix(0.2); + data.reverb.setT60( data.t60 ); + data.reverb.setEffectMix(0.2); // Install an interrupt handler function. (void) signal(SIGINT, finish); @@ -315,7 +321,6 @@ int main( int argc, char *argv[] ) for ( i=0; i<(int)data.nWvOuts; i++ ) delete data.wvout[i]; free( data.wvout ); - delete data.reverb; delete data.voicer; for ( i=0; i { -bind . { - noteOn $pitch $press -} - # Bind an X windows "close" event with the Exit routine bind . +myExit diff --git a/projects/demo/utilities.cpp b/projects/demo/utilities.cpp index 853c25c..65b6a0b 100644 --- a/projects/demo/utilities.cpp +++ b/projects/demo/utilities.cpp @@ -36,6 +36,8 @@ #include "Resonate.h" #include "Whistle.h" +using namespace stk; + #define NUM_INSTS 28 // The order of the following list is important. The location of a particular diff --git a/projects/demo/utilities.h b/projects/demo/utilities.h index e80ddb2..1885687 100644 --- a/projects/demo/utilities.h +++ b/projects/demo/utilities.h @@ -6,9 +6,9 @@ #include "FileWvOut.h" #include "Messager.h" -int voiceByNumber(int number, Instrmnt **instrument); +int voiceByNumber(int number, stk::Instrmnt **instrument); -int voiceByName(char *name, Instrmnt **instrument); +int voiceByName(char *name, stk::Instrmnt **instrument); void usage(char *function); @@ -16,4 +16,4 @@ int checkArgs(int numArgs, char *args[]); int countVoices(int nArgs, char *args[]); -bool parseArgs(int numArgs, char *args[], WvOut **output, Messager& messager); +bool parseArgs(int numArgs, char *args[], stk::WvOut **output, stk::Messager& messager); diff --git a/projects/effects/Makefile.in b/projects/effects/Makefile.in index 39d60f5..8cc7bb5 100644 --- a/projects/effects/Makefile.in +++ b/projects/effects/Makefile.in @@ -20,18 +20,16 @@ endif vpath %.h $(INCLUDE) CC = @CXX@ -DEFS = @byte_order@ -DEFS += @debug@ -CFLAGS = @cflags@ -CFLAGS += @warn@ -I$(INCLUDE) -I../../src/include +DEFS = @CPPFLAGS@ +DEFS += @byte_order@ +CFLAGS = @CXXFLAGS@ +CFLAGS += -I$(INCLUDE) -I$(INCLUDE)/../src/include LIBRARY = @LIBS@ -LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) PROGRAMS += effects OBJECTS += RtMidi.o RtAudio.o Thread.o Mutex.o Socket.o TcpServer.o @objects@ - DEFS += @audio_apis@ endif RAWWAVES = @rawwaves@ @@ -59,6 +57,7 @@ $(OBJECTS) : Stk.h clean : -rm $(OBJECT_PATH)/*.o -rm $(PROGRAMS) *.exe + -rm -fR *.dSYM strip : strip $(PROGRAMS) diff --git a/projects/effects/README-effects.txt b/projects/effects/README-effects.txt index 6fa532a..d4d7e62 100644 --- a/projects/effects/README-effects.txt +++ b/projects/effects/README-effects.txt @@ -1,6 +1,6 @@ The Synthesis ToolKit in C++ (STK) -By Perry R. Cook and Gary P. Scavone, 1995-2007. +By Perry R. Cook and Gary P. Scavone, 1995-2009. EFFECTS PROJECT: diff --git a/projects/effects/effects.cpp b/projects/effects/effects.cpp index 985dcf3..9553fa4 100644 --- a/projects/effects/effects.cpp +++ b/projects/effects/effects.cpp @@ -13,10 +13,13 @@ #include "RtAudio.h" #include +#include #include #include using std::min; +using namespace stk; + void usage(void) { // Error function in case of incorrect command-line argument specifications std::cout << "\nuseage: effects flags \n"; @@ -33,7 +36,7 @@ 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; + unsigned int effectId; PRCRev prcrev; JCRev jcrev; NRev nrev; @@ -51,7 +54,7 @@ struct TickData { // Default constructor. TickData() - : effect(0), t60(1.0), counter(0), + : effectId(0), t60(1.0), counter(0), settling( false ), haveMessage( false ) {} }; @@ -90,18 +93,7 @@ void processMessage( TickData* data ) 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); + data->effectId = (unsigned int) type; break; } @@ -153,6 +145,7 @@ int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, TickData *data = (TickData *) dataPointer; register StkFloat *oSamples = (StkFloat *) outputBuffer, *iSamples = (StkFloat *) inputBuffer; register StkFloat sample; + Effect *effect; int i, counter, nTicks = (int) nBufferFrames; while ( nTicks > 0 && !done ) { @@ -170,9 +163,35 @@ int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, counter = min( nTicks, data->counter ); data->counter -= counter; for ( i=0; ienvelope.tick() * data->effect->tick( *iSamples++ ); - *oSamples++ = sample; // two channels interleaved - *oSamples++ = sample; + if ( data->effectId < 2 ) { // Echo and PitShift ... mono output + if ( data->effectId == 0 ) + sample = data->envelope.tick() * data->echo.tick( *iSamples++ ); + else + sample = data->envelope.tick() * data->shifter.tick( *iSamples++ ); + *oSamples++ = sample; // two channels interleaved + *oSamples++ = sample; + } + else { // Chorus or a reverb ... stereo output + if ( data->effectId == 2 ) { + data->chorus.tick( *iSamples++ ); + effect = (Effect *) &(data->chorus); + } + else if ( data->effectId == 3 ) { + data->prcrev.tick( *iSamples++ ); + effect = (Effect *) &(data->prcrev); + } + else if ( data->effectId == 4 ) { + data->jcrev.tick( *iSamples++ ); + effect = (Effect *) &(data->jcrev); + } + else { + data->nrev.tick( *iSamples++ ); + effect = (Effect *) &(data->nrev); + } + const StkFrames& samples = effect->lastFrame(); + *oSamples++ = data->envelope.tick() * samples[0]; + *oSamples++ = data->envelope.lastOut() * samples[1]; + } nTicks--; } if ( nTicks == 0 ) break; @@ -184,7 +203,6 @@ int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, return 0; } - int main( int argc, char *argv[] ) { TickData data; @@ -230,7 +248,6 @@ int main( int argc, char *argv[] ) } data.envelope.setRate( 0.001 ); - data.effect = &( data.echo ); // Install an interrupt handler function. (void) signal( SIGINT, finish ); diff --git a/projects/effects/effects.dsp b/projects/effects/effects.dsp index abb9228..1183e1f 100644 --- a/projects/effects/effects.dsp +++ b/projects/effects/effects.dsp @@ -118,10 +118,6 @@ 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 @@ -154,18 +150,10 @@ SOURCE=..\..\include\FileWvIn.h # End Source File # Begin Source File -SOURCE=..\..\src\Filter.cpp -# End Source File -# Begin Source File - 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 @@ -282,15 +270,11 @@ SOURCE=..\..\include\Thread.h # End Source File # Begin Source File -SOURCE=..\..\src\WaveLoop.cpp +SOURCE=..\..\src\FileLoop.cpp # End Source File # Begin Source File -SOURCE=..\..\include\WaveLoop.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp +SOURCE=..\..\include\FileLoop.h # End Source File # Begin Source File diff --git a/projects/examples/Makefile.in b/projects/examples/Makefile.in index 17b360c..3c522b4 100644 --- a/projects/examples/Makefile.in +++ b/projects/examples/Makefile.in @@ -14,17 +14,15 @@ endif vpath %.h $(INCLUDE) CC = @CXX@ -DEFS = @byte_order@ -DEFS += @debug@ -CFLAGS = @cflags@ -CFLAGS += @warn@ -I$(INCLUDE) -I../../src/include +DEFS = @CPPFLAGS@ +DEFS += @byte_order@ +CFLAGS = @CXXFLAGS@ +CFLAGS += -I$(INCLUDE) -I$(INCLUDE)/../src/include LIBRARY = @LIBS@ -LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) PROGRAMS += play record audioprobe midiprobe duplex inetIn inetOut rtsine crtsine bethree controlbee threebees playsmf grains - DEFS += @audio_apis@ endif RAWWAVES = @rawwaves@ @@ -46,6 +44,7 @@ $(OBJECTS) : Stk.h clean : -rm $(OBJECT_PATH)/*.o -rm $(PROGRAMS) *.exe + -rm -fR *~ *.dSYM strip : strip $(PROGRAMS) @@ -56,47 +55,47 @@ audioprobe: RtAudio.o @objects@ midiprobe: RtMidi.o $(CC) $(CFLAGS) $(DEFS) -o midiprobe midiprobe.cpp $(OBJECT_PATH)/RtMidi.o $(LIBRARY) -play: play.cpp Stk.o FileRead.o WvIn.o FileWvIn.o RtAudio.o - $(CC) $(CFLAGS) $(DEFS) -o play play.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +play: play.cpp Stk.o FileRead.o FileWvIn.o RtAudio.o + $(CC) $(CFLAGS) $(DEFS) -o play play.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -record: record.cpp Stk.o WvIn.o FileWrite.o WvOut.o FileWvOut.o RtWvIn.o RtAudio.o - $(CC) $(CFLAGS) $(DEFS) -o record record.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/FileWvOut.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvIn.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +record: record.cpp Stk.o FileWrite.o FileWvOut.o RtWvIn.o RtAudio.o Mutex.o + $(CC) $(CFLAGS) $(DEFS) -o record record.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/FileWvOut.o $(OBJECT_PATH)/RtWvIn.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -sine: sine.cpp Stk.o Generator.o SineWave.o FileWrite.o WvOut.o FileWvOut.o - $(CC) $(CFLAGS) $(DEFS) -o sine sine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/FileWvOut.o $(LIBRARY) +sine: sine.cpp Stk.o SineWave.o FileWrite.o FileWvOut.o + $(CC) $(CFLAGS) $(DEFS) -o sine sine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/FileWvOut.o $(LIBRARY) duplex: duplex.cpp RtAudio.o $(CC) $(CFLAGS) $(DEFS) -o duplex duplex.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -inetIn: inetIn.cpp Stk.o WvIn.o InetWvIn.o WvOut.o RtWvOut.o RtAudio.o Socket.o TcpServer.o UdpSocket.o Thread.o Mutex.o - $(CC) $(CFLAGS) $(DEFS) -o inetIn inetIn.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/InetWvIn.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/TcpServer.o $(OBJECT_PATH)/UdpSocket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +inetIn: inetIn.cpp Stk.o InetWvIn.o RtWvOut.o RtAudio.o Socket.o TcpServer.o UdpSocket.o Thread.o Mutex.o + $(CC) $(CFLAGS) $(DEFS) -o inetIn inetIn.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/InetWvIn.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/TcpServer.o $(OBJECT_PATH)/UdpSocket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -inetOut: inetOut.cpp Stk.o FileRead.o WvIn.o FileWvIn.o WvOut.o InetWvOut.o Socket.o TcpClient.o UdpSocket.o Thread.o Mutex.o - $(CC) $(CFLAGS) $(DEFS) -o inetOut inetOut.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/TcpClient.o $(OBJECT_PATH)/UdpSocket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/InetWvOut.o $(LIBRARY) +inetOut: inetOut.cpp Stk.o FileRead.o FileWvIn.o InetWvOut.o Socket.o TcpClient.o UdpSocket.o Thread.o Mutex.o + $(CC) $(CFLAGS) $(DEFS) -o inetOut inetOut.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/TcpClient.o $(OBJECT_PATH)/UdpSocket.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/InetWvOut.o $(LIBRARY) -sineosc: sineosc.cpp Stk.o FileRead.o WvIn.o FileWvIn.o WaveLoop.o FileWrite.o WvOut.o FileWvOut.o - $(CC) $(CFLAGS) $(DEFS) -o sineosc sineosc.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/FileWvOut.o $(OBJECT_PATH)/WaveLoop.o $(LIBRARY) +sineosc: sineosc.cpp Stk.o FileRead.o FileWvIn.o FileLoop.o FileWrite.o FileWvOut.o + $(CC) $(CFLAGS) $(DEFS) -o sineosc sineosc.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/FileWvOut.o $(OBJECT_PATH)/FileLoop.o $(LIBRARY) -rtsine: rtsine.cpp Stk.o Generator.o SineWave.o WvOut.o RtWvOut.o RtAudio.o - $(CC) $(CFLAGS) $(DEFS) -o rtsine rtsine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +rtsine: rtsine.cpp Stk.o SineWave.o RtWvOut.o RtAudio.o Mutex.o + $(CC) $(CFLAGS) $(DEFS) -o rtsine rtsine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/RtWvOut.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/Mutex.o $(LIBRARY) -crtsine: crtsine.cpp Stk.o Generator.o SineWave.o RtAudio.o - $(CC) $(CFLAGS) $(DEFS) -o crtsine crtsine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +crtsine: crtsine.cpp Stk.o SineWave.o RtAudio.o + $(CC) $(CFLAGS) $(DEFS) -o crtsine crtsine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) -bethree: bethree.cpp Stk.o WvIn.o FileRead.o FileWvIn.o WaveLoop.o FM.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Generator.o SineWave.o Envelope.o ADSR.o BeeThree.o - $(CC) $(CFLAGS) $(DEFS) -o bethree bethree.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.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)/SineWave.o $(OBJECT_PATH)/Envelope.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(LIBRARY) +bethree: bethree.cpp Stk.o FileRead.o FileWvIn.o FileLoop.o FM.o RtAudio.o TwoZero.o SineWave.o ADSR.o BeeThree.o + $(CC) $(CFLAGS) $(DEFS) -o bethree bethree.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/FileLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(LIBRARY) -controlbee: controlbee.cpp Stk.o WvIn.o FileRead.o FileWvIn.o WaveLoop.o FM.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Generator.o SineWave.o Envelope.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o TcpServer.o Thread.o Mutex.o Skini.o - $(CC) $(CFLAGS) $(DEFS) -o controlbee controlbee.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/WvIn.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.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)/SineWave.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)/TcpServer.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/Skini.o $(LIBRARY) +controlbee: controlbee.cpp Stk.o FileRead.o FileWvIn.o FileLoop.o FM.o RtAudio.o TwoZero.o SineWave.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o TcpServer.o Thread.o Mutex.o Skini.o + $(CC) $(CFLAGS) $(DEFS) -o controlbee controlbee.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/FileLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(OBJECT_PATH)/Messager.o $(OBJECT_PATH)/RtMidi.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/TcpServer.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/Skini.o $(LIBRARY) -foursine: foursine.cpp Stk.o Generator.o SineWave.o FileWrite.o WvOut.o FileWvOut.o - $(CC) $(CFLAGS) $(DEFS) -o foursine foursine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/WvOut.o $(OBJECT_PATH)/FileWvOut.o $(LIBRARY) +foursine: foursine.cpp Stk.o SineWave.o FileWrite.o FileWvOut.o + $(CC) $(CFLAGS) $(DEFS) -o foursine foursine.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/FileWrite.o $(OBJECT_PATH)/FileWvOut.o $(LIBRARY) -threebees: threebees.cpp Stk.o FileRead.o WvIn.o FileWvIn.o WaveLoop.o FM.o RtAudio.o Instrmnt.o Filter.o TwoZero.o Generator.o SineWave.o Envelope.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o TcpServer.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)/FileRead.o $(OBJECT_PATH)/FileWvIn.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)/SineWave.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)/TcpServer.o $(OBJECT_PATH)/Thread.o $(OBJECT_PATH)/Mutex.o $(OBJECT_PATH)/Skini.o $(OBJECT_PATH)/Voicer.o $(LIBRARY) +threebees: threebees.cpp Stk.o FileRead.o FileWvIn.o FileLoop.o FM.o RtAudio.o TwoZero.o SineWave.o ADSR.o BeeThree.o Messager.o RtMidi.o Socket.o TcpServer.o Thread.o Mutex.o Skini.o Voicer.o + $(CC) $(CFLAGS) $(DEFS) -o threebees threebees.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/FileWvIn.o $(OBJECT_PATH)/FileLoop.o $(OBJECT_PATH)/FM.o $(OBJECT_PATH)/RtAudio.o $(OBJECT_PATH)/TwoZero.o $(OBJECT_PATH)/SineWave.o $(OBJECT_PATH)/ADSR.o $(OBJECT_PATH)/BeeThree.o $(OBJECT_PATH)/Messager.o $(OBJECT_PATH)/RtMidi.o $(OBJECT_PATH)/Socket.o $(OBJECT_PATH)/TcpServer.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) -grains: grains.cpp Stk.o Generator.o Granulate.o Noise.o FileRead.o RtAudio.o - $(CC) $(CFLAGS) $(DEFS) -o grains grains.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/Generator.o $(OBJECT_PATH)/Granulate.o $(OBJECT_PATH)/Noise.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) +grains: grains.cpp Stk.o Granulate.o Noise.o FileRead.o RtAudio.o + $(CC) $(CFLAGS) $(DEFS) -o grains grains.cpp $(OBJECT_PATH)/Stk.o $(OBJECT_PATH)/Granulate.o $(OBJECT_PATH)/Noise.o $(OBJECT_PATH)/FileRead.o $(OBJECT_PATH)/RtAudio.o $(LIBRARY) diff --git a/projects/examples/audioprobe.dsp b/projects/examples/audioprobe.dsp index bdb557c..b6a43dc 100755 --- a/projects/examples/audioprobe.dsp +++ b/projects/examples/audioprobe.dsp @@ -160,3 +160,4 @@ SOURCE=..\..\include\RtError.h # End Group # End Target # End Project + diff --git a/projects/examples/bethree.cpp b/projects/examples/bethree.cpp index 0767c03..5869bc6 100644 --- a/projects/examples/bethree.cpp +++ b/projects/examples/bethree.cpp @@ -2,6 +2,7 @@ #include "BeeThree.h" #include "RtAudio.h" +using namespace stk; // The TickData structure holds all the class instances and data that // are shared by the various processing functions. diff --git a/projects/examples/bethree.dsp b/projects/examples/bethree.dsp index c5f4401..56d55dc 100755 --- a/projects/examples/bethree.dsp +++ b/projects/examples/bethree.dsp @@ -111,19 +111,11 @@ SOURCE=..\..\src\FileWvIn.cpp # End Source File # Begin Source File -SOURCE=..\..\src\Filter.cpp -# End Source File -# Begin Source File - SOURCE=..\..\src\FM.cpp # End Source File -# Begin Source File - -SOURCE=..\..\src\Generator.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\Instrmnt.cpp +# Begin Source File + +SOURCE=..\..\src\Mutex.cpp # End Source File # Begin Source File @@ -147,15 +139,7 @@ SOURCE=..\..\src\TwoZero.cpp # End Source File # Begin Source File -SOURCE=..\..\src\WaveLoop.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp +SOURCE=..\..\src\FileLoop.cpp # End Source File # End Group # Begin Group "Header Files" @@ -197,6 +181,10 @@ SOURCE=..\..\include\Generator.h SOURCE=..\..\include\Instrmnt.h # End Source File +# Begin Source File + +SOURCE=..\..\src\Mutex.h +# End Source File # Begin Source File SOURCE=..\..\include\RtAudio.h @@ -215,7 +203,7 @@ SOURCE=..\..\include\TwoZero.h # End Source File # Begin Source File -SOURCE=..\..\include\WaveLoop.h +SOURCE=..\..\include\FileLoop.h # End Source File # Begin Source File diff --git a/projects/examples/controlbee.cpp b/projects/examples/controlbee.cpp index 8902172..f2ba4b1 100644 --- a/projects/examples/controlbee.cpp +++ b/projects/examples/controlbee.cpp @@ -8,6 +8,8 @@ #include using std::min; +using namespace stk; + void usage(void) { // Error function in case of incorrect command-line // argument specifications. diff --git a/projects/examples/controlbee.dsp b/projects/examples/controlbee.dsp index 557262b..6b41d03 100755 --- a/projects/examples/controlbee.dsp +++ b/projects/examples/controlbee.dsp @@ -112,22 +112,10 @@ SOURCE=..\..\src\FileWvIn.cpp # End Source File # Begin Source File -SOURCE=..\..\src\Filter.cpp -# End Source File -# Begin Source File - 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 - SOURCE=..\..\src\Messager.cpp # End Source File # Begin Source File @@ -176,15 +164,7 @@ SOURCE=..\..\src\TwoZero.cpp # End Source File # Begin Source File -SOURCE=..\..\src\WaveLoop.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp +SOURCE=..\..\src\FileLoop.cpp # End Source File # End Group # Begin Group "Header Files" @@ -252,7 +232,7 @@ SOURCE=..\..\include\TwoZero.h # End Source File # Begin Source File -SOURCE=..\..\include\WaveLoop.h +SOURCE=..\..\include\FileLoop.h # End Source File # Begin Source File diff --git a/projects/examples/crtsine.cpp b/projects/examples/crtsine.cpp index b959c08..26087b9 100644 --- a/projects/examples/crtsine.cpp +++ b/projects/examples/crtsine.cpp @@ -2,6 +2,7 @@ #include "SineWave.h" #include "RtAudio.h" +using namespace stk; // This tick() function handles sample computation only. It will be // called automatically when the system needs a new buffer of audio diff --git a/projects/examples/crtsine.dsp b/projects/examples/crtsine.dsp index 004e7c7..f56b8ae 100755 --- a/projects/examples/crtsine.dsp +++ b/projects/examples/crtsine.dsp @@ -91,18 +91,6 @@ SOURCE=.\crtsine.cpp # End Source File # Begin Source File -SOURCE=..\..\src\FileRead.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\FileWvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\Generator.cpp -# End Source File -# Begin Source File - SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File @@ -113,14 +101,6 @@ SOURCE=..\..\src\SineWave.cpp 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" @@ -133,13 +113,9 @@ SOURCE=..\..\include\RtAudio.h SOURCE=..\..\include\Stk.h # End Source File -# Begin Source File - -SOURCE=..\..\include\WaveLoop.h -# End Source File -# Begin Source File - -SOURCE=..\..\include\WvIn.h +# Begin Source File + +SOURCE=..\..\include\Generator.h # End Source File # End Group # Begin Group "Resource Files" diff --git a/projects/examples/duplex.cpp b/projects/examples/duplex.cpp index 77cf961..5edceae 100644 --- a/projects/examples/duplex.cpp +++ b/projects/examples/duplex.cpp @@ -10,6 +10,8 @@ #include "RtAudio.h" #include +#include +#include /* typedef signed long MY_TYPE; @@ -86,10 +88,16 @@ int main(int argc, char *argv[]) // Set the same number of channels for both input and output. unsigned int bufferFrames = 512; RtAudio::StreamParameters iParams, oParams; - iParams.deviceId = iDevice; + if ( iDevice == 0 ) + iParams.deviceId = adac.getDefaultInputDevice(); + else + iParams.deviceId = iDevice - 1; iParams.nChannels = channels; iParams.firstChannel = iOffset; - oParams.deviceId = oDevice; + if ( oDevice == 0 ) + oParams.deviceId = adac.getDefaultOutputDevice(); + else + oParams.deviceId = oDevice - 1; oParams.nChannels = channels; oParams.firstChannel = oOffset; diff --git a/projects/examples/foursine.cpp b/projects/examples/foursine.cpp index 7e64c88..d0f8e61 100644 --- a/projects/examples/foursine.cpp +++ b/projects/examples/foursine.cpp @@ -2,6 +2,7 @@ #include "SineWave.h" #include "FileWvOut.h" +using namespace stk; int main() { @@ -29,7 +30,7 @@ int main() for ( i=0; i<4; i++ ) inputs[i].tick( frames, i ); - output.tickFrame( frames ); + output.tick( frames ); // Now write the first sine to all four channels for two seconds for ( i=0; i<88200; i++ ) { diff --git a/projects/examples/foursine.dsp b/projects/examples/foursine.dsp index 220fd1b..9fcd11a 100755 --- a/projects/examples/foursine.dsp +++ b/projects/examples/foursine.dsp @@ -85,18 +85,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=..\..\src\FileRead.cpp -# End Source File -# Begin Source File - SOURCE=..\..\src\FileWrite.cpp # End Source File # Begin Source File -SOURCE=..\..\src\FileWvIn.cpp -# End Source File -# Begin Source File - SOURCE=..\..\src\FileWvOut.cpp # End Source File # Begin Source File @@ -105,28 +97,12 @@ SOURCE=.\foursine.cpp # End Source File # Begin Source File -SOURCE=..\..\src\Generator.cpp -# End Source File -# Begin Source File - SOURCE=..\..\src\SineWave.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 -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp -# End Source File # End Group # Begin Group "Header Files" @@ -145,14 +121,6 @@ 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 -# Begin Source File - SOURCE=..\..\include\WvOut.h # End Source File # End Group diff --git a/projects/examples/grains.cpp b/projects/examples/grains.cpp index 71f2f45..801e112 100644 --- a/projects/examples/grains.cpp +++ b/projects/examples/grains.cpp @@ -5,6 +5,8 @@ #include "Granulate.h" #include "RtAudio.h" +using namespace stk; + // This tick() function handles sample computation only. It will be // called automatically when the system needs a new buffer of audio // samples. @@ -13,9 +15,15 @@ int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, { Granulate *grani = (Granulate *) dataPointer; register StkFloat *samples = (StkFloat *) outputBuffer; + const StkFrames& lastframe = grani->lastFrame(); + unsigned int nChannels = lastframe.channels(); - for ( unsigned int i=0; itick(); + unsigned int j; + for ( unsigned int i=0; itick(); + for ( j=0; jtickFrame( input.tickFrame( frame ) ); + output->tick( input.tick( frame ) ); cleanup: delete output; diff --git a/projects/examples/inetIn.dsp b/projects/examples/inetIn.dsp index 5e4f4e6..8ce3ebb 100755 --- a/projects/examples/inetIn.dsp +++ b/projects/examples/inetIn.dsp @@ -125,14 +125,6 @@ SOURCE=..\..\src\Thread.cpp SOURCE=..\..\src\UdpSocket.cpp # End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp -# End Source File # End Group # Begin Group "Header Files" diff --git a/projects/examples/inetOut.cpp b/projects/examples/inetOut.cpp index 211fd1c..1de041e 100644 --- a/projects/examples/inetOut.cpp +++ b/projects/examples/inetOut.cpp @@ -20,6 +20,8 @@ #include "FileWvIn.h" #include "InetWvOut.h" +using namespace stk; + void usage(void) { // Error function in case of incorrect command-line // argument specifications. @@ -57,7 +59,7 @@ int main( int argc, char *argv[] ) input.setRate( rate ); // Find out how many channels we have. - int channels = input.getChannels(); + int channels = input.channelsOut(); StkFrames frames( 4096, channels ); // Attempt to connect to the socket server. @@ -71,7 +73,7 @@ int main( int argc, char *argv[] ) // Here's the runtime loop while ( !input.isFinished() ) - output.tickFrame( input.tickFrame( frames ) ); + output.tick( input.tick( frames ) ); return 0; } diff --git a/projects/examples/inetOut.dsp b/projects/examples/inetOut.dsp index 6ecf774..bdd3e8e 100755 --- a/projects/examples/inetOut.dsp +++ b/projects/examples/inetOut.dsp @@ -117,14 +117,6 @@ SOURCE=..\..\src\TcpClient.cpp SOURCE=..\..\src\UdpSocket.cpp # End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp -# End Source File # End Group # Begin Group "Header Files" diff --git a/projects/examples/libMakefile.in b/projects/examples/libMakefile.in index d6e995d..6dab44b 100644 --- a/projects/examples/libMakefile.in +++ b/projects/examples/libMakefile.in @@ -10,17 +10,15 @@ endif vpath %.h $(INCLUDE) CC = @CXX@ -DEFS = @byte_order@ -DEFS += @debug@ -CFLAGS = @cflags@ -CFLAGS += @warn@ -I$(INCLUDE) -I../../src/include +DEFS = @CPPFLAGS@ +DEFS += @byte_order@ +CFLAGS = @CXXFLAGS@ +CFLAGS += -I$(INCLUDE) -Iinclude LIBRARY = @LIBS@ -LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) PROGRAMS += audioprobe midiprobe duplex play record inetIn inetOut rtsine crtsine bethree controlbee threebees playsmf grains - DEFS += @audio_apis@ endif RAWWAVES = @rawwaves@ diff --git a/projects/examples/midiprobe.cpp b/projects/examples/midiprobe.cpp index 7b4dc8c..9fdb242 100644 --- a/projects/examples/midiprobe.cpp +++ b/projects/examples/midiprobe.cpp @@ -5,6 +5,7 @@ // by Gary Scavone, 2003-2004. #include +#include #include "RtMidi.h" int main() diff --git a/projects/examples/play.cpp b/projects/examples/play.cpp index c0cd29b..fef3e63 100644 --- a/projects/examples/play.cpp +++ b/projects/examples/play.cpp @@ -19,6 +19,8 @@ #include #include +using namespace stk; + // Eewww ... global variables! :-) bool done; StkFrames frames; @@ -44,7 +46,7 @@ int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, FileWvIn *input = (FileWvIn *) userData; register StkFloat *samples = (StkFloat *) outputBuffer; - input->tickFrame( frames ); + input->tick( frames ); for ( unsigned int i=0; i -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=probe - 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 "probe.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 "probe.mak" CFG="probe - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "probe - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "probe - 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)" == "probe - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "probe___Win32_Release" -# PROP BASE Intermediate_Dir "probe___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "" -# 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 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib 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)" == "probe - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "probe___Win32_Debug" -# PROP BASE Intermediate_Dir "probe___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 /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# SUBTRACT LINK32 /pdb:none - -!ENDIF - -# Begin Target - -# Name "probe - Win32 Release" -# Name "probe - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\asio\asio.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\asiodrivers.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\asiolist.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\iasiothiscallresolver.cpp -# End Source File -# Begin Source File - -SOURCE=.\probe.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\RtAudio.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\..\src\asio\asio.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\asiodrivers.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\asiodrvr.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\asiolist.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\asiosys.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\ginclude.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\iasiodrv.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\asio\iasiothiscallresolver.h -# End Source File -# Begin Source File - -SOURCE=..\..\include\RtAudio.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/record.cpp b/projects/examples/record.cpp index d7b49cc..d3b6a7e 100644 --- a/projects/examples/record.cpp +++ b/projects/examples/record.cpp @@ -25,6 +25,8 @@ #include "RtWvIn.h" #include "FileWvOut.h" +using namespace stk; + void usage( void ) { // Error function in case of incorrect command-line // argument specifications. @@ -75,7 +77,7 @@ int main( int argc, char *argv[] ) // Here's the runtime loop samples = (long) ( time * Stk::sampleRate() ); for ( i=0; itickFrame( input->tickFrame( frame ) ); + output->tick( input->tick( frame ) ); } cleanup: diff --git a/projects/examples/record.dsp b/projects/examples/record.dsp index fe537e7..ebca7eb 100755 --- a/projects/examples/record.dsp +++ b/projects/examples/record.dsp @@ -101,7 +101,11 @@ SOURCE=.\record.cpp # End Source File # Begin Source File -SOURCE=..\..\src\RtAudio.cpp +SOURCE=..\..\src\Mutex.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File @@ -111,14 +115,6 @@ SOURCE=..\..\src\RtWvIn.cpp SOURCE=..\..\src\Stk.cpp # End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp -# End Source File # End Group # Begin Group "Header Files" @@ -131,6 +127,10 @@ SOURCE=..\..\include\RtAudio.h SOURCE=..\..\include\RtWvIn.h # End Source File +# Begin Source File + +SOURCE=..\..\src\Mutex.h +# End Source File # Begin Source File SOURCE=..\..\include\Stk.h diff --git a/projects/examples/rtsine.cpp b/projects/examples/rtsine.cpp index 96a9773..bb42403 100644 --- a/projects/examples/rtsine.cpp +++ b/projects/examples/rtsine.cpp @@ -2,6 +2,7 @@ #include "SineWave.h" #include "RtWvOut.h" +using namespace stk; int main() { @@ -9,6 +10,7 @@ int main() Stk::setSampleRate( 44100.0 ); Stk::showWarnings( true ); + int nFrames = 100000; SineWave sine; RtWvOut *dac = 0; @@ -22,8 +24,19 @@ int main() sine.setFrequency( 441.0 ); - // Play the oscillator for 40000 samples - for ( int i=0; i<40000; i++ ) { + // Option 1: Use StkFrames + /* + StkFrames frames( nFrames, 1 ); + try { + dac->tick( sine.tick( frames ) ); + } + catch ( StkError & ) { + goto cleanup; + } + */ + + // Option 2: Single-sample computations + for ( int i=0; itick( sine.tick() ); } diff --git a/projects/examples/rtsine.dsp b/projects/examples/rtsine.dsp index cab3ae3..50f0c0f 100755 --- a/projects/examples/rtsine.dsp +++ b/projects/examples/rtsine.dsp @@ -87,18 +87,6 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=..\..\src\FileRead.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\FileWvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\Generator.cpp -# End Source File -# Begin Source File - SOURCE=..\..\src\RtAudio.cpp # End Source File # Begin Source File @@ -107,7 +95,11 @@ SOURCE=.\rtsine.cpp # End Source File # Begin Source File -SOURCE=..\..\src\RtWvOut.cpp +SOURCE=..\..\src\Mutex.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\RtWvOut.cpp # End Source File # Begin Source File @@ -117,32 +109,12 @@ SOURCE=..\..\src\SineWave.cpp 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 -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp -# End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=..\..\include\FileRead.h -# End Source File -# Begin Source File - -SOURCE=..\..\include\FileWvIn.h -# End Source File -# Begin Source File - SOURCE=..\..\include\RtAudio.h # End Source File # Begin Source File @@ -153,13 +125,9 @@ SOURCE=..\..\include\RtWvOut.h SOURCE=..\..\include\Stk.h # End Source File -# Begin Source File - -SOURCE=..\..\include\WaveLoop.h -# End Source File -# Begin Source File - -SOURCE=..\..\include\WvIn.h +# Begin Source File + +SOURCE=..\..\include\Mutex.h # End Source File # Begin Source File diff --git a/projects/examples/sine.cpp b/projects/examples/sine.cpp index 84fb59d..84ca398 100644 --- a/projects/examples/sine.cpp +++ b/projects/examples/sine.cpp @@ -14,6 +14,7 @@ #include "SineWave.h" #include "FileWvOut.h" +using namespace stk; void usage(void) { // Error function in case of incorrect command-line @@ -73,7 +74,7 @@ int main( int argc, char *argv[] ) for ( i=0; itick( frames, i ); - output.tickFrame( frames ); + output.tick( frames ); cleanup: for ( i=0; i using std::min; +using namespace stk; + // The TickData structure holds all the class instances and data that // are shared by the various processing functions. struct TickData { diff --git a/projects/examples/threebees.dsp b/projects/examples/threebees.dsp index f8c9a24..54e98f2 100755 --- a/projects/examples/threebees.dsp +++ b/projects/examples/threebees.dsp @@ -107,22 +107,10 @@ SOURCE=..\..\src\FileWvIn.cpp # End Source File # Begin Source File -SOURCE=..\..\src\Filter.cpp -# End Source File -# Begin Source File - 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 - SOURCE=..\..\src\Messager.cpp # End Source File # Begin Source File @@ -179,15 +167,7 @@ SOURCE=..\..\src\Voicer.cpp # End Source File # Begin Source File -SOURCE=..\..\src\WaveLoop.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvOut.cpp +SOURCE=..\..\src\FileLoop.cpp # End Source File # End Group # Begin Group "Header Files" @@ -259,7 +239,7 @@ SOURCE=..\..\include\Voicer.h # End Source File # Begin Source File -SOURCE=..\..\include\WaveLoop.h +SOURCE=..\..\include\FileLoop.h # End Source File # Begin Source File diff --git a/projects/ragamatic/Drone.cpp b/projects/ragamatic/Drone.cpp index 61e411f..bcf903e 100644 --- a/projects/ragamatic/Drone.cpp +++ b/projects/ragamatic/Drone.cpp @@ -13,15 +13,17 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Drone.h" +namespace stk { + Drone :: Drone( StkFloat lowestFrequency ) { - length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + length_ = (unsigned long) ( Stk::sampleRate() / lowestFrequency + 1 ); loopGain_ = 0.999; delayLine_.setMaximumDelay( length_ ); delayLine_.setDelay( 0.5 * length_ ); @@ -29,11 +31,11 @@ Drone :: Drone( StkFloat lowestFrequency ) this->clear(); } -Drone :: ~Drone() +Drone :: ~Drone( void ) { } -void Drone :: clear() +void Drone :: clear( void ) { delayLine_.clear(); loopFilter_.clear(); @@ -95,11 +97,4 @@ void Drone :: noteOff( StkFloat amplitude ) #endif } -StkFloat Drone :: computeSample() -{ - // 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_; -} - +} // stk namespace diff --git a/projects/ragamatic/Drone.h b/projects/ragamatic/Drone.h index 6563cd8..9d1cd6e 100644 --- a/projects/ragamatic/Drone.h +++ b/projects/ragamatic/Drone.h @@ -1,3 +1,14 @@ +#ifndef STK_DRONE_H +#define STK_DRONE_H + +#include "Instrmnt.h" +#include "DelayA.h" +#include "OneZero.h" +#include "ADSR.h" +#include "Noise.h" + +namespace stk { + /***************************************************/ /*! \class Drone \brief STK "drone" plucked string model. @@ -13,19 +24,10 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_DRONE_H -#define STK_DRONE_H - -#include "Instrmnt.h" -#include "DelayA.h" -#include "OneZero.h" -#include "ADSR.h" -#include "Noise.h" - class Drone : public Instrmnt { public: @@ -33,27 +35,28 @@ class Drone : public Instrmnt Drone( StkFloat lowestFrequency = 20 ); //! Class destructor. - ~Drone(); + ~Drone( void ); //! Reset and clear all internal state. - void clear(); + void clear( void ); //! Set instrument parameters for a particular frequency. - virtual void setFrequency(StkFloat frequency); + void setFrequency( StkFloat frequency ); //! Pluck the string with the given amplitude using the current frequency. - void pluck(StkFloat amplitude); + void pluck( StkFloat amplitude ); //! Start a note with the given frequency and amplitude. - virtual void noteOn(StkFloat frequency, StkFloat amplitude); + void noteOn( StkFloat frequency, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - virtual void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - DelayA delayLine_; OneZero loopFilter_; ADSR envelope_; @@ -63,5 +66,15 @@ class Drone : public Instrmnt }; +inline StkFloat Drone :: tick( unsigned int ) +{ + // Here's the whole inner loop of the instrument!! + lastFrame_[0] = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) + + ( 0.005 * envelope_.tick() * noise_.tick() ) ); + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/projects/ragamatic/Makefile.in b/projects/ragamatic/Makefile.in index cc65a20..22a5308 100644 --- a/projects/ragamatic/Makefile.in +++ b/projects/ragamatic/Makefile.in @@ -7,12 +7,12 @@ SRC_PATH = ../../src OBJECT_PATH = @object_path@ vpath %.o $(OBJECT_PATH) -OBJECTS = Stk.o Generator.o Noise.o Envelope.o ADSR.o \ - Filter.o DelayA.o Delay.o \ +OBJECTS = Stk.o Noise.o ADSR.o \ + DelayA.o Delay.o \ OnePole.o OneZero.o Skini.o \ - Tabla.o Instrmnt.o Sitar.o \ - Drone.o VoicDrum.o FileRead.o WvOut.o WvIn.o FileWvIn.o \ - Effect.o JCRev.o Messager.o + Tabla.o Sitar.o \ + Drone.o VoicDrum.o FileRead.o FileWvIn.o \ + JCRev.o Messager.o INCLUDE = @include@ ifeq ($(strip $(INCLUDE)), ) @@ -21,18 +21,16 @@ endif vpath %.h $(INCLUDE) CC = @CXX@ -DEFS = @byte_order@ -DEFS += @debug@ -CFLAGS = @cflags@ -CFLAGS += @warn@ -I$(INCLUDE) -I../../src/include +DEFS = @CPPFLAGS@ +DEFS += @byte_order@ +CFLAGS = @CXXFLAGS@ +CFLAGS += -I$(INCLUDE) -I$(INCLUDE)/../src/include LIBRARY = @LIBS@ -LIBRARY += @frameworks@ REALTIME = @realtime@ ifeq ($(REALTIME),yes) PROGRAMS = ragamat OBJECTS += RtMidi.o RtAudio.o RtWvOut.o Thread.o Mutex.o Socket.o TcpServer.o @objects@ - DEFS += @audio_apis@ endif RAWWAVES = @rawwaves@ @@ -60,6 +58,7 @@ $(OBJECTS) : Stk.h clean : -rm $(OBJECT_PATH)/*.o -rm $(PROGRAMS) *.exe + -rm -fR *.dSYM strip : strip $(PROGRAMS) diff --git a/projects/ragamatic/Tabla.cpp b/projects/ragamatic/Tabla.cpp index a9998b0..32320c5 100644 --- a/projects/ragamatic/Tabla.cpp +++ b/projects/ragamatic/Tabla.cpp @@ -8,14 +8,16 @@ sample rates. You can specify the maximum polyphony (maximum number of simultaneous voices) in Tabla.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Tabla.h" #include -Tabla :: Tabla() : Instrmnt() +namespace stk { + +Tabla :: Tabla( void ) : Instrmnt() { // This counts the number of sounding voices. nSounding_ = 0; @@ -23,7 +25,7 @@ Tabla :: Tabla() : Instrmnt() soundNumber_ = std::vector (TABLA_POLYPHONY, -1); } -Tabla :: ~Tabla() +Tabla :: ~Tabla( void ) { } @@ -45,7 +47,7 @@ static char tablaWaves[TABLA_NUMWAVES][16] = "DrTak2.raw" }; -void Tabla :: noteOn(StkFloat instrument, StkFloat amplitude) +void Tabla :: noteOn( StkFloat instrument, StkFloat amplitude ) { #if defined(_STK_DEBUG_) errorString_ << "Tabla::noteOn: instrument = " << instrument << ", amplitude = " << amplitude << '.'; @@ -117,34 +119,11 @@ void Tabla :: noteOn(StkFloat instrument, StkFloat amplitude) #endif } -void Tabla :: noteOff(StkFloat amplitude) +void Tabla :: noteOff( StkFloat amplitude ) { // Set all sounding wave filter gains low. int i = 0; while ( i < nSounding_ ) filters_[i++].setGain( amplitude * 0.01 ); } -StkFloat Tabla :: computeSample() -{ - lastOutput_ = 0.0; - if ( nSounding_ == 0 ) return lastOutput_; - - for ( int i=0; i= 0 ) { - if ( waves_[i].isFinished() ) { - // Re-order the list. - for ( int j=0; j soundOrder_[i] ) - soundOrder_[j] -= 1; - } - soundOrder_[i] = -1; - nSounding_--; - } - else - lastOutput_ += filters_[i].tick( waves_[i].tick() ); - } - } - - return lastOutput_; -} - +} // stk namespace diff --git a/projects/ragamatic/Tabla.h b/projects/ragamatic/Tabla.h index b4c6e6e..ea8a910 100644 --- a/projects/ragamatic/Tabla.h +++ b/projects/ragamatic/Tabla.h @@ -1,3 +1,12 @@ +#ifndef STK_TABLA_H +#define STK_TABLA_H + +#include "Instrmnt.h" +#include "FileWvIn.h" +#include "OnePole.h" + +namespace stk { + /***************************************************/ /*! \class Tabla \brief STK tabla drum class. @@ -8,17 +17,10 @@ sample rates. You can specify the maximum polyphony (maximum number of simultaneous voices) in Tabla.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_TABLA_H -#define STK_TABLA_H - -#include "Instrmnt.h" -#include "FileWvIn.h" -#include "OnePole.h" - const int TABLA_NUMWAVES = 15; const int TABLA_POLYPHONY = 4; @@ -26,27 +28,54 @@ class Tabla : public Instrmnt { public: //! Class constructor. - Tabla(); + Tabla( void ); //! Class destructor. - ~Tabla(); + ~Tabla( void ); //! Start a note with the given drum type and amplitude. - void noteOn(StkFloat instrument, StkFloat amplitude); + void noteOn( StkFloat instrument, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - FileWvIn waves_[TABLA_POLYPHONY]; OnePole filters_[TABLA_POLYPHONY]; std::vector soundOrder_; std::vector soundNumber_; - int nSounding_; + int nSounding_; }; +inline StkFloat Tabla :: tick( unsigned int ) +{ + lastFrame_[0] = 0.0; + if ( nSounding_ == 0 ) return lastFrame_[0]; + + for ( int i=0; i= 0 ) { + if ( waves_[i].isFinished() ) { + // Re-order the list. + for ( int j=0; j soundOrder_[i] ) + soundOrder_[j] -= 1; + } + soundOrder_[i] = -1; + nSounding_--; + } + else + lastFrame_[0] += filters_[i].tick( waves_[i].tick() ); + } + } + + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/projects/ragamatic/VoicDrum.cpp b/projects/ragamatic/VoicDrum.cpp index b959308..ce8c4c7 100644 --- a/projects/ragamatic/VoicDrum.cpp +++ b/projects/ragamatic/VoicDrum.cpp @@ -8,14 +8,16 @@ sample rates. You can specify the maximum polyphony (maximum number of simultaneous voices) in VoicDrum.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "VoicDrum.h" #include -VoicDrum :: VoicDrum() : Instrmnt() +namespace stk { + +VoicDrum :: VoicDrum( void ) : Instrmnt() { // This counts the number of sounding voices. nSounding_ = 0; @@ -23,7 +25,7 @@ VoicDrum :: VoicDrum() : Instrmnt() soundNumber_ = std::vector (VOICE_POLYPHONY, -1); } -VoicDrum :: ~VoicDrum() +VoicDrum :: ~VoicDrum( void ) { } @@ -114,33 +116,11 @@ void VoicDrum :: noteOn( StkFloat instrument, StkFloat amplitude ) #endif } -void VoicDrum :: noteOff(StkFloat amplitude) +void VoicDrum :: noteOff( StkFloat amplitude ) { // Set all sounding wave filter gains low. int i = 0; while ( i < nSounding_ ) filters_[i++].setGain( amplitude * 0.01 ); } -StkFloat VoicDrum :: computeSample() -{ - lastOutput_ = 0.0; - if ( nSounding_ == 0 ) return lastOutput_; - - for ( int i=0; i= 0 ) { - if ( waves_[i].isFinished() ) { - // Re-order the list. - for ( int j=0; j soundOrder_[i] ) - soundOrder_[j] -= 1; - } - soundOrder_[i] = -1; - nSounding_--; - } - else - lastOutput_ += filters_[i].tick( waves_[i].tick() ); - } - } - - return lastOutput_; -} +} // stk namespace diff --git a/projects/ragamatic/VoicDrum.h b/projects/ragamatic/VoicDrum.h index 14168ba..7c1451e 100644 --- a/projects/ragamatic/VoicDrum.h +++ b/projects/ragamatic/VoicDrum.h @@ -1,3 +1,12 @@ +#ifndef STK_VOICDRUM_H +#define STK_VOICDRUM_H + +#include "Instrmnt.h" +#include "FileWvIn.h" +#include "OnePole.h" + +namespace stk { + /***************************************************/ /*! \class VoicDrum \brief STK vocal drum sample player class. @@ -8,17 +17,10 @@ sample rates. You can specify the maximum polyphony (maximum number of simultaneous voices) in VoicDrum.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#ifndef STK_VOICDRUM_H -#define STK_VOICDRUM_H - -#include "Instrmnt.h" -#include "FileWvIn.h" -#include "OnePole.h" - const int VOICE_NUMWAVES = 11; const int VOICE_POLYPHONY = 4; @@ -26,21 +28,22 @@ class VoicDrum : public Instrmnt { public: //! Class constructor. - VoicDrum(); + VoicDrum( void ); //! Class destructor. - ~VoicDrum(); + ~VoicDrum( void ); //! Start a note with the given drum type and amplitude. - void noteOn(StkFloat instrument, StkFloat amplitude); + void noteOn( StkFloat instrument, StkFloat amplitude ); //! Stop a note with the given amplitude (speed of decay). - void noteOff(StkFloat amplitude); + void noteOff( StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); protected: - StkFloat computeSample( void ); - FileWvIn waves_[VOICE_POLYPHONY]; OnePole filters_[VOICE_POLYPHONY]; std::vector soundOrder_; @@ -49,4 +52,30 @@ class VoicDrum : public Instrmnt }; +inline StkFloat VoicDrum :: tick( unsigned int ) +{ + lastFrame_[0] = 0.0; + if ( nSounding_ == 0 ) return lastFrame_[0]; + + for ( int i=0; i= 0 ) { + if ( waves_[i].isFinished() ) { + // Re-order the list. + for ( int j=0; j soundOrder_[i] ) + soundOrder_[j] -= 1; + } + soundOrder_[i] = -1; + nSounding_--; + } + else + lastFrame_[0] += filters_[i].tick( waves_[i].tick() ); + } + } + + return lastFrame_[0]; +} + +} // stk namespace + #endif diff --git a/projects/ragamatic/ragamat.cpp b/projects/ragamatic/ragamat.cpp index fbab62b..b0ac11d 100644 --- a/projects/ragamatic/ragamat.cpp +++ b/projects/ragamatic/ragamat.cpp @@ -11,9 +11,11 @@ #include "RtAudio.h" #include +#include #include #include using std::min; +using namespace stk; StkFloat float_random(StkFloat max) // Return random float between 0.0 and max { diff --git a/projects/ragamatic/ragamat.dsp b/projects/ragamatic/ragamat.dsp index 4d5868b..f0a7506 100644 --- a/projects/ragamatic/ragamat.dsp +++ b/projects/ragamatic/ragamat.dsp @@ -124,10 +124,6 @@ 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 @@ -156,26 +152,14 @@ SOURCE=..\..\include\FileWvIn.h # End Source File # Begin Source File -SOURCE=..\..\src\Filter.cpp -# End Source File -# Begin Source File - 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 - SOURCE=..\..\include\Instrmnt.h # End Source File # Begin Source File @@ -312,15 +296,11 @@ SOURCE=.\VoicDrum.h # End Source File # Begin Source File -SOURCE=..\..\src\WaveLoop.cpp +SOURCE=..\..\src\FileLoop.cpp # End Source File # Begin Source File -SOURCE=..\..\include\WaveLoop.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\WvIn.cpp +SOURCE=..\..\include\FileLoop.h # End Source File # Begin Source File @@ -328,10 +308,6 @@ SOURCE=..\..\include\WvIn.h # End Source File # Begin Source File -SOURCE=..\..\src\WvOut.cpp -# End Source File -# Begin Source File - SOURCE=..\..\include\WvOut.h # End Source File # End Target diff --git a/src/ADSR.cpp b/src/ADSR.cpp index 34178e9..e609b72 100644 --- a/src/ADSR.cpp +++ b/src/ADSR.cpp @@ -2,33 +2,32 @@ /*! \class ADSR \brief STK ADSR envelope class. - This Envelope subclass implements a - traditional ADSR (Attack, Decay, - Sustain, Release) envelope. It - responds to simple keyOn and keyOff - messages, keeping track of its state. - The \e state = ADSR::DONE after the - envelope value reaches 0.0 in the - ADSR::RELEASE state. + This class implements a traditional ADSR (Attack, Decay, Sustain, + Release) envelope. It responds to simple keyOn and keyOff + messages, keeping track of its state. The \e state = ADSR::DONE + after the envelope value reaches 0.0 in the ADSR::RELEASE state. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "ADSR.h" -ADSR :: ADSR() : Envelope() +namespace stk { + +ADSR :: ADSR( void ) { target_ = 0.0; value_ = 0.0; attackRate_ = 0.001; decayRate_ = 0.001; + releaseRate_ = 0.005; sustainLevel_ = 0.5; - releaseRate_ = 0.01; state_ = ATTACK; + Stk::addSampleRateAlert( this ); } -ADSR :: ~ADSR() +ADSR :: ~ADSR( void ) { } @@ -44,20 +43,18 @@ void ADSR :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) void ADSR :: keyOn() { target_ = 1.0; - rate_ = attackRate_; state_ = ATTACK; } void ADSR :: keyOff() { target_ = 0.0; - rate_ = releaseRate_; state_ = RELEASE; } -void ADSR :: setAttackRate(StkFloat rate) +void ADSR :: setAttackRate( StkFloat rate ) { - if (rate < 0.0) { + if ( rate < 0.0 ) { errorString_ << "ADSR::setAttackRate: negative rates not allowed ... correcting!"; handleError( StkError::WARNING ); attackRate_ = -rate; @@ -65,9 +62,9 @@ void ADSR :: setAttackRate(StkFloat rate) else attackRate_ = rate; } -void ADSR :: setDecayRate(StkFloat rate) +void ADSR :: setDecayRate( StkFloat rate ) { - if (rate < 0.0) { + if ( rate < 0.0 ) { errorString_ << "ADSR::setDecayRate: negative rates not allowed ... correcting!"; handleError( StkError::WARNING ); decayRate_ = -rate; @@ -75,9 +72,9 @@ void ADSR :: setDecayRate(StkFloat rate) else decayRate_ = rate; } -void ADSR :: setSustainLevel(StkFloat level) +void ADSR :: setSustainLevel( StkFloat level ) { - if (level < 0.0 ) { + if ( level < 0.0 ) { errorString_ << "ADSR::setSustainLevel: level out of range ... correcting!"; handleError( StkError::WARNING ); sustainLevel_ = 0.0; @@ -85,9 +82,9 @@ void ADSR :: setSustainLevel(StkFloat level) else sustainLevel_ = level; } -void ADSR :: setReleaseRate(StkFloat rate) +void ADSR :: setReleaseRate( StkFloat rate ) { - if (rate < 0.0) { + if ( rate < 0.0 ) { errorString_ << "ADSR::setReleaseRate: negative rates not allowed ... correcting!"; handleError( StkError::WARNING ); releaseRate_ = -rate; @@ -95,9 +92,9 @@ void ADSR :: setReleaseRate(StkFloat rate) else releaseRate_ = rate; } -void ADSR :: setAttackTime(StkFloat time) +void ADSR :: setAttackTime( StkFloat time ) { - if (time < 0.0) { + if ( time < 0.0 ) { errorString_ << "ADSR::setAttackTime: negative times not allowed ... correcting!"; handleError( StkError::WARNING ); attackRate_ = 1.0 / ( -time * Stk::sampleRate() ); @@ -105,9 +102,9 @@ void ADSR :: setAttackTime(StkFloat time) else attackRate_ = 1.0 / ( time * Stk::sampleRate() ); } -void ADSR :: setDecayTime(StkFloat time) +void ADSR :: setDecayTime( StkFloat time ) { - if (time < 0.0) { + if ( time < 0.0 ) { errorString_ << "ADSR::setDecayTime: negative times not allowed ... correcting!"; handleError( StkError::WARNING ); decayRate_ = 1.0 / ( -time * Stk::sampleRate() ); @@ -115,9 +112,9 @@ void ADSR :: setDecayTime(StkFloat time) else decayRate_ = 1.0 / ( time * Stk::sampleRate() ); } -void ADSR :: setReleaseTime(StkFloat time) +void ADSR :: setReleaseTime( StkFloat time ) { - if (time < 0.0) { + if ( time < 0.0 ) { errorString_ << "ADSR::setReleaseTime: negative times not allowed ... correcting!"; handleError( StkError::WARNING ); releaseRate_ = sustainLevel_ / ( -time * Stk::sampleRate() ); @@ -125,74 +122,34 @@ void ADSR :: setReleaseTime(StkFloat time) else releaseRate_ = sustainLevel_ / ( time * Stk::sampleRate() ); } -void ADSR :: setAllTimes(StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime) +void ADSR :: setAllTimes( StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime ) { - this->setAttackTime(aTime); - this->setDecayTime(dTime); - this->setSustainLevel(sLevel); - this->setReleaseTime(rTime); + this->setAttackTime( aTime ); + this->setDecayTime( dTime ); + this->setSustainLevel( sLevel ); + this->setReleaseTime( rTime ); } -void ADSR :: setTarget(StkFloat target) +void ADSR :: setTarget( StkFloat target ) { target_ = target; - if (value_ < target_) { + if ( value_ < target_ ) { state_ = ATTACK; - this->setSustainLevel(target_); - rate_ = attackRate_; + this->setSustainLevel( target_ ); } - if (value_ > target_) { - this->setSustainLevel(target_); + if ( value_ > target_ ) { + this->setSustainLevel( target_ ); state_ = DECAY; - rate_ = decayRate_; } } -void ADSR :: setValue(StkFloat value) +void ADSR :: setValue( StkFloat value ) { state_ = SUSTAIN; target_ = value; value_ = value; - this->setSustainLevel(value); - rate_ = (StkFloat) 0.0; + this->setSustainLevel( value ); + lastFrame_[0] = value; } -int ADSR :: getState(void) const -{ - return state_; -} - -StkFloat ADSR :: computeSample() -{ - switch (state_) { - - case ATTACK: - value_ += rate_; - if (value_ >= target_) { - value_ = target_; - rate_ = decayRate_; - target_ = sustainLevel_; - state_ = DECAY; - } - break; - - case DECAY: - value_ -= decayRate_; - if (value_ <= sustainLevel_) { - value_ = sustainLevel_; - rate_ = (StkFloat) 0.0; - state_ = SUSTAIN; - } - break; - - case RELEASE: - value_ -= releaseRate_; - if (value_ <= 0.0) { - value_ = (StkFloat) 0.0; - state_ = DONE; - } - } - - lastOutput_ = value_; - return value_; -} +} // stk namespace diff --git a/src/Asymp.cpp b/src/Asymp.cpp index 854c2c7..3a0c25f 100644 --- a/src/Asymp.cpp +++ b/src/Asymp.cpp @@ -6,7 +6,7 @@ which asymptotically approaches a target value. The algorithm used is of the form: - x[n] = a x[n-1] + (1-a) target, + y[n] = a y[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 @@ -19,20 +19,26 @@ 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Asymp.h" #include -Asymp :: Asymp(void) : Envelope() +namespace stk { + +Asymp :: Asymp( void ) { + value_ = 0.0; + target_ = 0.0; + state_ = 0; factor_ = exp( -1.0 / ( 0.3 * Stk::sampleRate() ) ); constant_ = 0.0; + Stk::addSampleRateAlert( this ); } -Asymp :: ~Asymp(void) +Asymp :: ~Asymp( void ) { } @@ -44,33 +50,31 @@ void Asymp :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) } } -void Asymp :: keyOn(void) +void Asymp :: keyOn( void ) { - Envelope::keyOn(); - constant_ = ( 1.0 - factor_ ) * target_; + this->setTarget( 1.0 ); } -void Asymp :: keyOff(void) +void Asymp :: keyOff( void ) { - Envelope::keyOff(); - constant_ = ( 1.0 - factor_ ) * target_; + this->setTarget( 0.0 ); } -void Asymp :: setTau(StkFloat tau) +void Asymp :: setTau( StkFloat tau ) { - if (tau <= 0.0) { + if ( tau <= 0.0 ) { errorString_ << "Asymp::setTau: negative or zero tau not allowed ... ignoring!"; handleError( StkError::WARNING ); return; } - factor_ = std::exp( -1.0 / ( tau * Stk::sampleRate() ) ); + factor_ = std::exp( -1.0 / ( tau * Stk::sampleRate() ) ); constant_ = ( 1.0 - factor_ ) * target_; } -void Asymp :: setTime(StkFloat time) +void Asymp :: setTime( StkFloat time ) { - if (time <= 0.0) { + if ( time <= 0.0 ) { errorString_ << "Asymp::setTime: negative or zero times not allowed ... ignoring!"; handleError( StkError::WARNING ); return; @@ -81,34 +85,19 @@ void Asymp :: setTime(StkFloat time) constant_ = ( 1.0 - factor_ ) * target_; } -void Asymp :: setTarget(StkFloat target) +void Asymp :: setTarget( StkFloat target ) { - Envelope::setTarget( target ); + target_ = target; + if ( value_ != target_ ) state_ = 1; constant_ = ( 1.0 - factor_ ) * target_; } -StkFloat Asymp :: computeSample(void) +void Asymp :: setValue( StkFloat value ) { - 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_; + state_ = 0; + target_ = value; + value_ = value; } +} // stk namespace + diff --git a/src/BandedWG.cpp b/src/BandedWG.cpp index f25f52c..84e9079 100644 --- a/src/BandedWG.cpp +++ b/src/BandedWG.cpp @@ -25,7 +25,7 @@ - Tibetan Bowl = 3 by Georg Essl, 1999 - 2004. - Modified for Stk 4.0 by Gary Scavone. + Modified for STK 4.0 by Gary Scavone. */ /***************************************************/ @@ -33,7 +33,9 @@ #include "SKINI.msg" #include -BandedWG :: BandedWG() +namespace stk { + +BandedWG :: BandedWG( void ) { doPluck_ = true; @@ -55,22 +57,22 @@ BandedWG :: BandedWG() strikeAmp_ = 0.0; } -BandedWG :: ~BandedWG() +BandedWG :: ~BandedWG( void ) { } -void BandedWG :: clear() +void BandedWG :: clear( void ) { - for (int i=0; isetFrequency(frequency); @@ -252,7 +254,7 @@ void BandedWG :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void BandedWG :: noteOff(StkFloat amplitude) +void BandedWG :: noteOff( StkFloat amplitude ) { if ( !doPluck_ ) this->stopBowing((1.0 - amplitude) * 0.005); @@ -263,7 +265,7 @@ void BandedWG :: noteOff(StkFloat amplitude) #endif } -StkFloat BandedWG :: computeSample() +StkFloat BandedWG :: tick( unsigned int ) { int k; @@ -274,12 +276,12 @@ StkFloat BandedWG :: computeSample() // strikeAmp_ = 0.0; } else { - if (integrationConstant_ == 0.0) + if ( integrationConstant_ == 0.0 ) velocityInput_ = 0.0; else velocityInput_ = integrationConstant_ * velocityInput_; - for (k=0; ksetRatio(0, 0.999); - this->setRatio(1, 1.997); - this->setRatio(2, 3.006); - this->setRatio(3, 6.009); + this->setRatio( 0, 0.999 ); + this->setRatio( 1, 1.997 ); + this->setRatio( 2, 3.006 ); + this->setRatio( 3, 6.009 ); 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 ); } -BeeThree :: ~BeeThree() +BeeThree :: ~BeeThree( void ) { } -void BeeThree :: noteOn(StkFloat frequency, StkFloat amplitude) +void BeeThree :: noteOn( StkFloat frequency, StkFloat amplitude ) { gains_[0] = amplitude * fmGains_[95]; gains_[1] = amplitude * fmGains_[95]; @@ -79,28 +81,4 @@ void BeeThree :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -StkFloat BeeThree :: computeSample() -{ - 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]); - } - - 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(); - - lastOutput_ = temp * 0.125; - return lastOutput_; -} - - +} // stk namespace diff --git a/src/BiQuad.cpp b/src/BiQuad.cpp index bb6de29..e091296 100644 --- a/src/BiQuad.cpp +++ b/src/BiQuad.cpp @@ -2,26 +2,28 @@ /*! \class BiQuad \brief STK biquad (two-pole, two-zero) filter class. - This protected Filter subclass implements a - two-pole, two-zero digital filter. A method - is provided for creating a resonance in the - frequency response while maintaining a constant - filter gain. + This class implements a two-pole, two-zero digital filter. + Methods are provided for creating a resonance or notch in the + frequency response while maintaining a constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "BiQuad.h" #include +namespace stk { + BiQuad :: BiQuad() : Filter() { - std::vector b(3, 0.0); - std::vector a(3, 0.0); - b[0] = 1.0; - a[0] = 1.0; - Filter::setCoefficients( b, a ); + b_.resize( 3, 0.0 ); + a_.resize( 3, 0.0 ); + b_[0] = 1.0; + a_[0] = 1.0; + inputs_.resize( 3, 1, 0.0 ); + outputs_.resize( 3, 1, 0.0 ); + Stk::addSampleRateAlert( this ); } @@ -30,6 +32,17 @@ BiQuad :: ~BiQuad() Stk::removeSampleRateAlert( this ); } +void BiQuad :: setCoefficients( StkFloat b0, StkFloat b1, StkFloat b2, StkFloat a1, StkFloat a2, bool clearState ) +{ + b_[0] = b0; + b_[1] = b1; + b_[2] = b2; + a_[1] = a1; + a_[2] = a2; + + if ( clearState ) this->clear(); +} + void BiQuad :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) { if ( !ignoreSampleRateChange_ ) { @@ -38,40 +51,10 @@ void BiQuad :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) } } -void BiQuad :: clear(void) -{ - Filter::clear(); -} - -void BiQuad :: setB0(StkFloat b0) -{ - b_[0] = b0; -} - -void BiQuad :: setB1(StkFloat b1) -{ - b_[1] = b1; -} - -void BiQuad :: setB2(StkFloat b2) -{ - b_[2] = b2; -} - -void BiQuad :: setA1(StkFloat a1) -{ - a_[1] = a1; -} - -void BiQuad :: setA2(StkFloat a2) -{ - a_[2] = a2; -} - 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_[1] = -2.0 * radius * cos( TWO_PI * frequency / Stk::sampleRate() ); if ( normalize ) { // Use zeros at +- 1 and normalize the filter peak gain. @@ -85,7 +68,7 @@ void BiQuad :: setNotch(StkFloat frequency, StkFloat radius) { // This method does not attempt to normalize the filter gain. b_[2] = radius * radius; - b_[1] = (StkFloat) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate()); + b_[1] = (StkFloat) -2.0 * radius * cos( TWO_PI * (double) frequency / Stk::sampleRate() ); } void BiQuad :: setEqualGainZeroes() @@ -95,17 +78,4 @@ void BiQuad :: setEqualGainZeroes() b_[2] = -1.0; } -void BiQuad :: setGain(StkFloat gain) -{ - Filter::setGain(gain); -} - -StkFloat BiQuad :: getGain(void) const -{ - return Filter::getGain(); -} - -StkFloat BiQuad :: lastOut(void) const -{ - return Filter::lastOut(); -} +} // stk namespace diff --git a/src/Blit.cpp b/src/Blit.cpp index 0755a04..f6a5cad 100644 --- a/src/Blit.cpp +++ b/src/Blit.cpp @@ -22,8 +22,8 @@ /***************************************************/ #include "Blit.h" -#include -#include + +namespace stk { Blit:: Blit( StkFloat frequency ) { @@ -39,7 +39,7 @@ Blit :: ~Blit() void Blit :: reset() { phase_ = 0.0; - lastOutput_ = 0; + lastFrame_[0] = 0.0; } void Blit :: setFrequency( StkFloat frequency ) @@ -75,31 +75,4 @@ void Blit :: updateHarmonics( void ) #endif } -StkFloat Blit :: computeSample( void ) -{ - // The code below implements the SincM algorithm of Stilson and - // Smith with an additional scale factor of P / M applied to - // normalize the output. - - // A fully optimized version of this code would replace the two sin - // calls with a pair of fast sin oscillators, for which stable fast - // two-multiply algorithms are well known. In the spirit of STK, - // which favors clarity over performance, the optimization has not - // been made here. - - // Avoid a divide by zero at the sinc peak, which has a limiting - // value of 1.0. - StkFloat denominator = sin( phase_ ); - if ( denominator <= std::numeric_limits::epsilon() ) { - lastOutput_ = 1.0; - } else { - lastOutput_ = sin( m_ * phase_ ); - lastOutput_ /= m_ * denominator; - } - - phase_ += rate_; - if ( phase_ >= PI ) phase_ -= PI; - - return lastOutput_; -} - +} // stk namespace diff --git a/src/BlitSaw.cpp b/src/BlitSaw.cpp index 42908e3..abde294 100644 --- a/src/BlitSaw.cpp +++ b/src/BlitSaw.cpp @@ -20,8 +20,8 @@ /***************************************************/ #include "BlitSaw.h" -#include -#include + +namespace stk { BlitSaw:: BlitSaw( StkFloat frequency ) { @@ -38,7 +38,7 @@ void BlitSaw :: reset() { phase_ = 0.0f; state_ = 0.0; - lastOutput_ = 0; + lastFrame_[0] = 0.0; } void BlitSaw :: setFrequency( StkFloat frequency ) @@ -88,38 +88,4 @@ void BlitSaw :: updateHarmonics( void ) #endif } -StkFloat BlitSaw :: computeSample( void ) -{ - // The code below implements the BLIT algorithm of Stilson and - // Smith, followed by a summation and filtering operation to produce - // a sawtooth waveform. After experimenting with various approaches - // to calculate the average value of the BLIT over one period, I - // found that an estimate of C2_ = 1.0 / period (in samples) worked - // most consistently. A "leaky integrator" is then applied to the - // difference of the BLIT output and C2_. (GPS - 1 October 2005) - - // A fully optimized version of this code would replace the two sin - // calls with a pair of fast sin oscillators, for which stable fast - // two-multiply algorithms are well known. In the spirit of STK, - // which favors clarity over performance, the optimization has - // not been made here. - - // Avoid a divide by zero, or use of a denormalized divisor - // at the sinc peak, which has a limiting value of m_ / p_. - StkFloat denominator = sin( phase_ ); - if ( fabs(denominator) <= std::numeric_limits::epsilon() ) - lastOutput_ = a_; - else { - lastOutput_ = sin( m_ * phase_ ); - lastOutput_ /= p_ * denominator; - } - - lastOutput_ += state_ - C2_; - state_ = lastOutput_ * 0.995; - - phase_ += rate_; - if ( phase_ >= PI ) phase_ -= PI; - - return lastOutput_; -} - +} // stk namespace diff --git a/src/BlitSquare.cpp b/src/BlitSquare.cpp index d26cc18..330756a 100644 --- a/src/BlitSquare.cpp +++ b/src/BlitSquare.cpp @@ -25,14 +25,14 @@ Blit waveforms. This class is not guaranteed to be well behaved in the presence of significant aliasing. - Based on initial code of Robin Davies, 2005. - Modified algorithm code by Gary Scavone, 2005 - 2006. + Based on initial code of Robin Davies, 2005 + Modified algorithm code by Gary Scavone, 2005 - 2009. */ /***************************************************/ #include "BlitSquare.h" -#include -#include + +namespace stk { BlitSquare:: BlitSquare( StkFloat frequency ) { @@ -48,7 +48,7 @@ BlitSquare :: ~BlitSquare() void BlitSquare :: reset() { phase_ = 0.0; - lastOutput_ = 0; + lastFrame_[0] = 0.0; dcbState_ = 0.0; lastBlitOutput_ = 0; } @@ -92,40 +92,4 @@ void BlitSquare :: updateHarmonics( void ) #endif } -StkFloat BlitSquare :: computeSample( void ) -{ - StkFloat temp = lastBlitOutput_; - - // A fully optimized version of this would replace the two sin calls - // with a pair of fast sin oscillators, for which stable fast - // two-multiply algorithms are well known. In the spirit of STK, - // which favors clarity over performance, the optimization has - // not been made here. - - // Avoid a divide by zero, or use of a denomralized divisor - // at the sinc peak, which has a limiting value of 1.0. - StkFloat denominator = sin( phase_ ); - if ( fabs( denominator ) < std::numeric_limits::epsilon() ) { - // Inexact comparison safely distinguishes betwen *close to zero*, and *close to PI*. - if ( phase_ < 0.1f || phase_ > TWO_PI - 0.1f ) - lastBlitOutput_ = a_; - else - lastBlitOutput_ = -a_; - } - else { - lastBlitOutput_ = sin( m_ * phase_ ); - lastBlitOutput_ /= p_ * denominator; - } - - lastBlitOutput_ += temp; - - // Now apply DC blocker. - lastOutput_ = lastBlitOutput_ - dcbState_ + 0.999 * lastOutput_; - dcbState_ = lastBlitOutput_; - - phase_ += rate_; - if ( phase_ >= TWO_PI ) phase_ -= TWO_PI; - - return lastOutput_; -} - +} // stk namespace diff --git a/src/BlowBotl.cpp b/src/BlowBotl.cpp index 63abf41..25ba8bb 100644 --- a/src/BlowBotl.cpp +++ b/src/BlowBotl.cpp @@ -12,16 +12,18 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "BlowBotl.h" #include "SKINI.msg" +namespace stk { + #define __BOTTLE_RADIUS_ 0.999 -BlowBotl :: BlowBotl() +BlowBotl :: BlowBotl( void ) { dcBlock_.setBlockZero(); @@ -35,16 +37,16 @@ BlowBotl :: BlowBotl() maxPressure_ = (StkFloat) 0.0; } -BlowBotl :: ~BlowBotl() +BlowBotl :: ~BlowBotl( void ) { } -void BlowBotl :: clear() +void BlowBotl :: clear( void ) { resonator_.clear(); } -void BlowBotl :: setFrequency(StkFloat frequency) +void BlowBotl :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -56,20 +58,20 @@ void BlowBotl :: setFrequency(StkFloat frequency) resonator_.setResonance( freakency, __BOTTLE_RADIUS_, true ); } -void BlowBotl :: startBlowing(StkFloat amplitude, StkFloat rate) +void BlowBotl :: startBlowing( StkFloat amplitude, StkFloat rate ) { adsr_.setAttackRate(rate); maxPressure_ = amplitude; adsr_.keyOn(); } -void BlowBotl :: stopBlowing(StkFloat rate) +void BlowBotl :: stopBlowing( StkFloat rate ) { adsr_.setReleaseRate(rate); adsr_.keyOff(); } -void BlowBotl :: noteOn(StkFloat frequency, StkFloat amplitude) +void BlowBotl :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency(frequency); startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02); @@ -81,7 +83,7 @@ void BlowBotl :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void BlowBotl :: noteOff(StkFloat amplitude) +void BlowBotl :: noteOff( StkFloat amplitude ) { this->stopBlowing(amplitude * 0.02); @@ -91,29 +93,7 @@ void BlowBotl :: noteOff(StkFloat amplitude) #endif } -StkFloat BlowBotl :: computeSample() -{ - StkFloat breathPressure; - StkFloat randPressure; - StkFloat pressureDiff; - - // Calculate the breath pressure (envelope + vibrato) - breathPressure = maxPressure_ * adsr_.tick(); - breathPressure += vibratoGain_ * vibrato_.tick(); - - pressureDiff = breathPressure - resonator_.lastOut(); - - 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 ); - - return lastOutput_; -} - -void BlowBotl :: controlChange(int number, StkFloat value) +void BlowBotl :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -145,3 +125,5 @@ void BlowBotl :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/BlowHole.cpp b/src/BlowHole.cpp index 8bf8116..ec271f1 100644 --- a/src/BlowHole.cpp +++ b/src/BlowHole.cpp @@ -29,17 +29,19 @@ - Register State = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "BlowHole.h" #include "SKINI.msg" -#include +#include -BlowHole :: BlowHole(StkFloat lowestFrequency) +namespace stk { + +BlowHole :: BlowHole( StkFloat lowestFrequency ) { - length_ = (unsigned 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].setDelay( 5.0 * Stk::sampleRate() / 22050.0 ); // delays[1] is the delay line between the register vent and the tonehole. @@ -83,11 +85,11 @@ BlowHole :: BlowHole(StkFloat lowestFrequency) vibratoGain_ = 0.01; } -BlowHole :: ~BlowHole() +BlowHole :: ~BlowHole( void ) { } -void BlowHole :: clear() +void BlowHole :: clear( void ) { delays_[0].clear(); delays_[1].clear(); @@ -97,7 +99,7 @@ void BlowHole :: clear() vent_.tick( 0.0 ); } -void BlowHole :: setFrequency(StkFloat frequency) +void BlowHole :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -114,7 +116,7 @@ void BlowHole :: setFrequency(StkFloat frequency) delays_[1].setDelay(delay); } -void BlowHole :: setVent(StkFloat 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" @@ -132,7 +134,7 @@ void BlowHole :: setVent(StkFloat newValue) vent_.setGain( gain ); } -void BlowHole :: setTonehole(StkFloat newValue) +void BlowHole :: setTonehole( StkFloat newValue ) { // This method allows setting of the tonehole "open-ness" at // any point between "Open" (newValue = 1) and "Closed" @@ -150,19 +152,19 @@ void BlowHole :: setTonehole(StkFloat newValue) tonehole_.setB0( new_coeff ); } -void BlowHole :: startBlowing(StkFloat amplitude, StkFloat rate) +void BlowHole :: startBlowing( StkFloat amplitude, StkFloat rate ) { envelope_.setRate( rate ); envelope_.setTarget( amplitude ); } -void BlowHole :: stopBlowing(StkFloat rate) +void BlowHole :: stopBlowing( StkFloat rate ) { envelope_.setRate( rate ); envelope_.setTarget( 0.0 ); } -void BlowHole :: noteOn(StkFloat frequency, StkFloat amplitude) +void BlowHole :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->startBlowing( 0.55 + (amplitude * 0.30), amplitude * 0.005 ); @@ -174,7 +176,7 @@ void BlowHole :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void BlowHole :: noteOff(StkFloat amplitude) +void BlowHole :: noteOff( StkFloat amplitude ) { this->stopBlowing( amplitude * 0.01 ); @@ -184,42 +186,7 @@ void BlowHole :: noteOff(StkFloat amplitude) #endif } -StkFloat BlowHole :: computeSample() -{ - 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(); - - // Calculate the differential pressure = reflected - mouthpiece pressures - pressureDiff = delays_[0].lastOut() - breathPressure; - - // Do two-port junction scattering for register vent - 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_; - - // Do three-port junction scattering (under tonehole) - 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 ); - - return lastOutput_; -} - -void BlowHole :: controlChange(int number, StkFloat value) +void BlowHole :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -253,3 +220,5 @@ void BlowHole :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/BowTable.cpp b/src/BowTable.cpp deleted file mode 100644 index f5b23e7..0000000 --- a/src/BowTable.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/***************************************************/ -/*! \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 - 2007. -*/ -/***************************************************/ - -#include "BowTable.h" -#include - -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 :: computeSample(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_; -} - diff --git a/src/Bowed.cpp b/src/Bowed.cpp index 586fec3..d314ed6 100644 --- a/src/Bowed.cpp +++ b/src/Bowed.cpp @@ -17,14 +17,16 @@ - Vibrato Gain = 1 - Volume = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Bowed.h" #include "SKINI.msg" -Bowed :: Bowed(StkFloat lowestFrequency) +namespace stk { + +Bowed :: Bowed( StkFloat lowestFrequency ) { unsigned long length; length = (long) ( Stk::sampleRate() / lowestFrequency + 1 ); @@ -54,17 +56,17 @@ Bowed :: Bowed(StkFloat lowestFrequency) this->setFrequency( 220.0 ); } -Bowed :: ~Bowed() +Bowed :: ~Bowed( void ) { } -void Bowed :: clear() +void Bowed :: clear( void ) { neckDelay_.clear(); bridgeDelay_.clear(); } -void Bowed :: setFrequency(StkFloat frequency) +void Bowed :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -80,20 +82,20 @@ void Bowed :: setFrequency(StkFloat frequency) neckDelay_.setDelay( baseDelay_ * (1.0 - betaRatio_) ); // bow to nut (finger) length } -void Bowed :: startBowing(StkFloat amplitude, StkFloat rate) +void Bowed :: startBowing( StkFloat amplitude, StkFloat rate ) { - adsr_.setRate( rate ); + adsr_.setAttackRate( rate ); adsr_.keyOn(); maxVelocity_ = 0.03 + ( 0.2 * amplitude ); } -void Bowed :: stopBowing(StkFloat rate) +void Bowed :: stopBowing( StkFloat rate ) { - adsr_.setRate( rate ); + adsr_.setReleaseRate( rate ); adsr_.keyOff(); } -void Bowed :: noteOn(StkFloat frequency, StkFloat amplitude) +void Bowed :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->startBowing( amplitude, amplitude * 0.001 ); this->setFrequency( frequency ); @@ -104,7 +106,7 @@ void Bowed :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Bowed :: noteOff(StkFloat amplitude) +void Bowed :: noteOff( StkFloat amplitude ) { this->stopBowing( (1.0 - amplitude) * 0.005 ); @@ -114,41 +116,12 @@ void Bowed :: noteOff(StkFloat amplitude) #endif } -void Bowed :: setVibrato(StkFloat gain) +void Bowed :: setVibrato( StkFloat gain ) { vibratoGain_ = gain; } -StkFloat Bowed :: computeSample() -{ - StkFloat bowVelocity; - StkFloat bridgeRefl; - StkFloat nutRefl; - StkFloat newVel; - StkFloat velDiff; - StkFloat stringVel; - - bowVelocity = maxVelocity_ * adsr_.tick(); - - 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); - - if ( vibratoGain_ > 0.0 ) { - neckDelay_.setDelay( (baseDelay_ * (1.0 - betaRatio_) ) + - (baseDelay_ * vibratoGain_ * vibrato_.tick()) ); - } - - lastOutput_ = bodyFilter_.tick( bridgeDelay_.lastOut() ); - - return lastOutput_; -} - -void Bowed :: controlChange(int number, StkFloat value) +void Bowed :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -185,3 +158,5 @@ void Bowed :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Brass.cpp b/src/Brass.cpp index edc9cf9..c346ead 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ @@ -24,16 +24,18 @@ #include "SKINI.msg" #include -Brass :: Brass(StkFloat lowestFrequency) +namespace stk { + +Brass :: Brass( StkFloat lowestFrequency ) { - length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + length_ = (unsigned long) ( Stk::sampleRate() / lowestFrequency + 1 ); delayLine_.setMaximumDelay( length_ ); delayLine_.setDelay( 0.5 * length_ ); lipFilter_.setGain( 0.03 ); dcBlock_.setBlockZero(); - adsr_.setAllTimes( 0.005, 0.001, 1.0, 0.010); + adsr_.setAllTimes( 0.005, 0.001, 1.0, 0.010 ); vibrato_.setFrequency( 6.137 ); vibratoGain_ = 0.0; @@ -46,18 +48,18 @@ Brass :: Brass(StkFloat lowestFrequency) this->setFrequency( 220.0 ); } -Brass :: ~Brass() +Brass :: ~Brass( void ) { } -void Brass :: clear() +void Brass :: clear( void ) { delayLine_.clear(); lipFilter_.clear(); dcBlock_.clear(); } -void Brass :: setFrequency(StkFloat frequency) +void Brass :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -74,7 +76,7 @@ void Brass :: setFrequency(StkFloat frequency) lipFilter_.setResonance( freakency, 0.997 ); } -void Brass :: setLip(StkFloat frequency) +void Brass :: setLip( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -86,20 +88,20 @@ void Brass :: setLip(StkFloat frequency) lipFilter_.setResonance( freakency, 0.997 ); } -void Brass :: startBlowing(StkFloat amplitude, StkFloat rate) +void Brass :: startBlowing( StkFloat amplitude, StkFloat rate ) { adsr_.setAttackRate( rate ); maxPressure_ = amplitude; adsr_.keyOn(); } -void Brass :: stopBlowing(StkFloat rate) +void Brass :: stopBlowing( StkFloat rate ) { adsr_.setReleaseRate( rate ); adsr_.keyOff(); } -void Brass :: noteOn(StkFloat frequency, StkFloat amplitude) +void Brass :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->startBlowing( amplitude, amplitude * 0.001 ); @@ -110,7 +112,7 @@ void Brass :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Brass :: noteOff(StkFloat amplitude) +void Brass :: noteOff( StkFloat amplitude ) { this->stopBlowing( amplitude * 0.005 ); @@ -120,26 +122,7 @@ void Brass :: noteOff(StkFloat amplitude) #endif } -StkFloat Brass :: computeSample() -{ - StkFloat breathPressure = maxPressure_ * adsr_.tick(); - breathPressure += vibratoGain_ * vibrato_.tick(); - - 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_ ) ); - - return lastOutput_; -} - -void Brass :: controlChange(int number, StkFloat value) +void Brass :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -175,3 +158,5 @@ void Brass :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Chorus.cpp b/src/Chorus.cpp index b3ed8a9..d0f58de 100644 --- a/src/Chorus.cpp +++ b/src/Chorus.cpp @@ -2,60 +2,45 @@ /*! \class Chorus \brief STK chorus effect class. - This class implements a chorus effect. + This class implements a chorus effect. It takes a monophonic + input signal and produces a stereo output signal. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Chorus.h" -#include -Chorus :: Chorus(StkFloat baseDelay) +namespace stk { + +Chorus :: Chorus( StkFloat baseDelay ) { + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output 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; - mods_[0].setFrequency(0.2); - mods_[1].setFrequency(0.222222); + mods_[0].setFrequency( 0.2 ); + mods_[1].setFrequency( 0.222222 ); modDepth_ = 0.05; effectMix_ = 0.5; this->clear(); } -Chorus :: ~Chorus() -{ -} - -void Chorus :: clear() +void Chorus :: clear( void ) { delayLine_[0].clear(); delayLine_[1].clear(); - lastOutput_[0] = 0.0; - lastOutput_[1] = 0.0; + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; } -void Chorus :: setModDepth(StkFloat depth) +void Chorus :: setModFrequency( StkFloat frequency ) { - modDepth_ = depth; + mods_[0].setFrequency( frequency ); + mods_[1].setFrequency( frequency * 1.1111 ); } -void Chorus :: setModFrequency(StkFloat frequency) -{ - mods_[0].setFrequency(frequency); - mods_[1].setFrequency(frequency * 1.1111); -} - -StkFloat Chorus :: computeSample(StkFloat input) -{ - delayLine_[0].setDelay( baseLength_ * 0.707 * (1.0 + modDepth_ * mods_[0].tick()) ); - delayLine_[1].setDelay( baseLength_ * 0.5 * (1.0 - modDepth_ * 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(); -} +} // stk namespace diff --git a/src/Clarinet.cpp b/src/Clarinet.cpp index bd19a52..2c5856d 100644 --- a/src/Clarinet.cpp +++ b/src/Clarinet.cpp @@ -18,14 +18,16 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Clarinet.h" #include "SKINI.msg" -Clarinet :: Clarinet(StkFloat lowestFrequency) +namespace stk { + +Clarinet :: Clarinet( StkFloat lowestFrequency ) { length_ = (long) (Stk::sampleRate() / lowestFrequency + 1); delayLine_.setMaximumDelay( length_ ); @@ -39,17 +41,17 @@ Clarinet :: Clarinet(StkFloat lowestFrequency) vibratoGain_ = (StkFloat) 0.1; } -Clarinet :: ~Clarinet() +Clarinet :: ~Clarinet( void ) { } -void Clarinet :: clear() +void Clarinet :: clear( void ) { delayLine_.clear(); filter_.tick( 0.0 ); } -void Clarinet :: setFrequency(StkFloat frequency) +void Clarinet :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -65,19 +67,19 @@ void Clarinet :: setFrequency(StkFloat frequency) delayLine_.setDelay(delay); } -void Clarinet :: startBlowing(StkFloat amplitude, StkFloat rate) +void Clarinet :: startBlowing( StkFloat amplitude, StkFloat rate ) { envelope_.setRate(rate); envelope_.setTarget(amplitude); } -void Clarinet :: stopBlowing(StkFloat rate) +void Clarinet :: stopBlowing( StkFloat rate ) { envelope_.setRate(rate); envelope_.setTarget((StkFloat) 0.0); } -void Clarinet :: noteOn(StkFloat frequency, StkFloat amplitude) +void Clarinet :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency(frequency); this->startBlowing((StkFloat) 0.55 + (amplitude * (StkFloat) 0.30), amplitude * (StkFloat) 0.005); @@ -89,7 +91,7 @@ void Clarinet :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Clarinet :: noteOff(StkFloat amplitude) +void Clarinet :: noteOff( StkFloat amplitude ) { this->stopBlowing( amplitude * 0.01 ); @@ -99,32 +101,7 @@ void Clarinet :: noteOff(StkFloat amplitude) #endif } -StkFloat Clarinet :: computeSample() -{ - StkFloat pressureDiff; - StkFloat breathPressure; - - // Calculate the breath pressure (envelope + noise + vibrato) - breathPressure = envelope_.tick(); - breathPressure += breathPressure * noiseGain_ * noise_.tick(); - breathPressure += breathPressure * vibratoGain_ * vibrato_.tick(); - - // Perform commuted loss filtering. - 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)); - - // Apply output gain. - lastOutput_ *= outputGain_; - - return lastOutput_; -} - -void Clarinet :: controlChange(int number, StkFloat value) +void Clarinet :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -158,3 +135,5 @@ void Clarinet :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Delay.cpp b/src/Delay.cpp index b14501c..aedec9a 100644 --- a/src/Delay.cpp +++ b/src/Delay.cpp @@ -2,36 +2,23 @@ /*! \class Delay \brief STK non-interpolating delay line class. - This protected Filter subclass implements - a non-interpolating digital delay-line. - A fixed maximum length of 4095 and a delay - of zero is set using the default constructor. - Alternatively, the delay and maximum length - can be set during instantiation with an - overloaded constructor. + This class implements a non-interpolating digital delay-line. If + the delay and maximum length are not specified during + instantiation, a fixed maximum length of 4095 and a delay of zero + is set. - A non-interpolating delay line is typically - used in fixed delay-length applications, such - as for reverberation. + A non-interpolating delay line is typically used in fixed + delay-length applications, such as for reverberation. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Delay.h" -Delay :: Delay() : Filter() -{ - // Default maximum delay length set to 4095. - inputs_.resize( 4096 ); - this->clear(); +namespace stk { - inPoint_ = 0; - outPoint_ = 0; - delay_ = 0; -} - -Delay :: Delay(unsigned long delay, unsigned 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 @@ -46,10 +33,8 @@ Delay :: Delay(unsigned long delay, unsigned long maxDelay) handleError( StkError::FUNCTION_ARGUMENT ); } - if ( maxDelay > inputs_.size()-1 ) { - inputs_.resize( maxDelay+1 ); - this->clear(); - } + if ( ( maxDelay + 1 ) > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); inPoint_ = 0; this->setDelay( delay ); @@ -59,14 +44,7 @@ Delay :: ~Delay() { } -void Delay :: clear(void) -{ - for (unsigned int i=0; i inputs_.size() - 1 ) { // The value is too big. errorString_ << "Delay::setDelay: argument (" << delay << ") too big ... setting to maximum!\n"; @@ -109,26 +87,21 @@ void Delay :: setDelay(unsigned long delay) } } -unsigned long Delay :: getDelay(void) const -{ - return (unsigned long) delay_; -} - -StkFloat Delay :: energy(void) const +StkFloat Delay :: energy( void ) const { unsigned long i; register StkFloat e = 0; - if (inPoint_ >= outPoint_) { - for (i=outPoint_; i= outPoint_ ) { + for ( i=outPoint_; i delay_) { - errorString_ << "Delay::contentsAt: argument (" << tapDelay << ") too big!"; - handleError( StkError::WARNING ); - return 0.0; - } - - long tap = inPoint_ - i; - if (tap < 0) // Check for wraparound. + long tap = inPoint_ - tapDelay - 1; + while ( tap < 0 ) // Check for wraparound. tap += inputs_.size(); return inputs_[tap]; } -StkFloat Delay :: lastOut(void) const -{ - return Filter::lastOut(); -} - -StkFloat Delay :: nextOut(void) -{ - return inputs_[outPoint_]; -} - -StkFloat Delay :: computeSample( StkFloat input ) -{ - inputs_[inPoint_++] = input; - - // Check for end condition - if (inPoint_ == inputs_.size()) - inPoint_ = 0; - - // Read out next value - outputs_[0] = inputs_[outPoint_++]; - - if (outPoint_ == inputs_.size()) - outPoint_ = 0; - - return outputs_[0]; -} - -StkFloat Delay :: tick( StkFloat input ) -{ - return computeSample( input ); -} - -StkFrames& Delay :: tick( StkFrames& frames, unsigned int channel ) -{ - return Filter::tick( frames, channel ); -} +} // stk namespace diff --git a/src/DelayA.cpp b/src/DelayA.cpp index 19df1e6..31454da 100644 --- a/src/DelayA.cpp +++ b/src/DelayA.cpp @@ -2,11 +2,10 @@ /*! \class DelayA \brief STK allpass interpolating delay line class. - This Delay subclass implements a fractional-length digital - delay-line using a first-order allpass filter. A fixed maximum - length of 4095 and a delay of 0.5 is set using the default - constructor. Alternatively, the delay and maximum length can be - set during instantiation with an overloaded constructor. + This class implements a fractional-length digital delay-line using + a first-order allpass filter. If the delay and maximum length are + not specified during instantiation, a fixed maximum length of 4095 + and a delay of zero is set. An allpass filter has unity magnitude gain but variable phase delay properties, making it useful in achieving fractional delays @@ -15,23 +14,18 @@ minimum delay possible in this implementation is limited to a value of 0.5. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "DelayA.h" -DelayA :: DelayA() : Delay() -{ - this->setDelay( 0.5 ); - apInput_ = 0.0; - doNextOut_ = true; -} +namespace stk { -DelayA :: DelayA(StkFloat delay, unsigned long maxDelay) +DelayA :: DelayA( StkFloat delay, unsigned long maxDelay ) { - if ( delay < 0.0 || maxDelay < 1 ) { - errorString_ << "DelayA::DelayA: delay must be >= 0.0, maxDelay must be > 0!"; + if ( delay < 0.5 || maxDelay < 1 ) { + errorString_ << "DelayA::DelayA: delay must be >= 0.5, maxDelay must be > 0!"; handleError( StkError::FUNCTION_ARGUMENT ); } @@ -41,13 +35,11 @@ DelayA :: DelayA(StkFloat delay, unsigned long maxDelay) } // Writing before reading allows delays from 0 to length-1. - if ( maxDelay > inputs_.size()-1 ) { - inputs_.resize( maxDelay+1 ); - this->clear(); - } + if ( maxDelay + 1 > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); inPoint_ = 0; - this->setDelay(delay); + this->setDelay( delay ); apInput_ = 0.0; doNextOut_ = true; } @@ -58,16 +50,36 @@ DelayA :: ~DelayA() void DelayA :: clear() { - Delay::clear(); + for ( unsigned int i=0; i inputs_.size() - 1 ) { // The value is too big. + if ( delay + 1 > length ) { // The value is too big. errorString_ << "DelayA::setDelay: argument (" << delay << ") too big ... setting to maximum!"; handleError( StkError::WARNING ); @@ -75,7 +87,7 @@ void DelayA :: setDelay(StkFloat delay) outPointer = inPoint_ + 1.0; delay_ = length - 1; } - else if (delay < 0.5) { + else if ( delay < 0.5 ) { errorString_ << "DelayA::setDelay: argument (" << delay << ") less than 0.5 not possible!"; handleError( StkError::WARNING ); @@ -87,14 +99,14 @@ void DelayA :: setDelay(StkFloat delay) delay_ = delay; } - if (outPointer < 0) + while ( outPointer < 0 ) outPointer += length; // modulo maximum length 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; @@ -106,38 +118,13 @@ void DelayA :: setDelay(StkFloat delay) ((StkFloat) 1.0 + alpha_); // coefficient for all pass } -StkFloat DelayA :: getDelay(void) const +StkFloat DelayA :: contentsAt( unsigned long tapDelay ) { - return delay_; + long tap = inPoint_ - tapDelay - 1; + while ( tap < 0 ) // Check for wraparound. + tap += inputs_.size(); + + return inputs_[tap]; } -StkFloat DelayA :: nextOut(void) -{ - if ( doNextOut_ ) { - // Do allpass interpolation delay. - nextOutput_ = -coeff_ * outputs_[0]; - nextOutput_ += apInput_ + (coeff_ * inputs_[outPoint_]); - doNextOut_ = false; - } - - return nextOutput_; -} - -StkFloat DelayA :: computeSample( StkFloat input ) -{ - inputs_[inPoint_++] = input; - - // Increment input pointer modulo length. - if (inPoint_ == inputs_.size()) - inPoint_ = 0; - - outputs_[0] = nextOut(); - doNextOut_ = true; - - // Save the allpass input and increment modulo length. - apInput_ = inputs_[outPoint_++]; - if (outPoint_ == inputs_.size()) - outPoint_ = 0; - - return outputs_[0]; -} +} // stk namespace diff --git a/src/DelayL.cpp b/src/DelayL.cpp index 1e08139..0003ed1 100644 --- a/src/DelayL.cpp +++ b/src/DelayL.cpp @@ -2,34 +2,26 @@ /*! \class DelayL \brief STK linear interpolating delay line class. - This Delay subclass implements a fractional- - length digital delay-line using first-order - linear interpolation. A fixed maximum length - of 4095 and a delay of zero is set using the - default constructor. Alternatively, the - delay and maximum length can be set during - instantiation with an overloaded constructor. + This class implements a fractional-length digital delay-line using + first-order linear interpolation. If the delay and maximum length + are not specified during instantiation, a fixed maximum length of + 4095 and a delay of zero is set. - Linear interpolation is an efficient technique - for achieving fractional delay lengths, though - it does introduce high-frequency signal - attenuation to varying degrees depending on the - fractional delay setting. The use of higher - order Lagrange interpolators can typically - improve (minimize) this attenuation characteristic. + Linear interpolation is an efficient technique for achieving + fractional delay lengths, though it does introduce high-frequency + signal attenuation to varying degrees depending on the fractional + delay setting. The use of higher order Lagrange interpolators can + typically improve (minimize) this attenuation characteristic. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "DelayL.h" -DelayL :: DelayL() : Delay() -{ - doNextOut_ = true; -} +namespace stk { -DelayL :: DelayL(StkFloat delay, unsigned long maxDelay) +DelayL :: DelayL( StkFloat delay, unsigned long maxDelay ) { if ( delay < 0.0 || maxDelay < 1 ) { errorString_ << "DelayL::DelayL: delay must be >= 0.0, maxDelay must be > 0!"; @@ -42,13 +34,11 @@ DelayL :: DelayL(StkFloat delay, unsigned long maxDelay) } // Writing before reading allows delays from 0 to length-1. - if ( maxDelay > inputs_.size()-1 ) { - inputs_.resize( maxDelay+1 ); - this->clear(); - } + if ( maxDelay + 1 > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); inPoint_ = 0; - this->setDelay(delay); + this->setDelay( delay ); doNextOut_ = true; } @@ -56,11 +46,29 @@ DelayL :: ~DelayL() { } -void DelayL :: setDelay(StkFloat delay) +void DelayL :: setMaximumDelay( unsigned long delay ) +{ + if ( delay < inputs_.size() ) return; + + if ( delay < 0 ) { + errorString_ << "DelayL::setMaximumDelay: argument (" << delay << ") less than zero!\n"; + handleError( StkError::WARNING ); + return; + } + else if ( delay < delay_ ) { + errorString_ << "DelayL::setMaximumDelay: argument (" << delay << ") less than current delay setting (" << delay_ << ")!\n"; + handleError( StkError::WARNING ); + return; + } + + inputs_.resize( delay + 1 ); +} + +void DelayL :: setDelay( StkFloat delay ) { StkFloat outPointer; - if ( delay > inputs_.size() - 1 ) { // The value is too big. + if ( delay + 1 > inputs_.size() ) { // The value is too big. errorString_ << "DelayL::setDelay: argument (" << delay << ") too big ... setting to maximum!"; handleError( StkError::WARNING ); @@ -89,42 +97,13 @@ void DelayL :: setDelay(StkFloat delay) omAlpha_ = (StkFloat) 1.0 - alpha_; } -StkFloat DelayL :: getDelay(void) const +StkFloat DelayL :: contentsAt( unsigned long tapDelay ) { - return delay_; -} - -StkFloat DelayL :: nextOut(void) -{ - if ( doNextOut_ ) { - // First 1/2 of interpolation - nextOutput_ = inputs_[outPoint_] * omAlpha_; - // Second 1/2 of interpolation - if (outPoint_+1 < inputs_.size()) - nextOutput_ += inputs_[outPoint_+1] * alpha_; - else - nextOutput_ += inputs_[0] * alpha_; - doNextOut_ = false; - } - - return nextOutput_; -} - -StkFloat DelayL :: computeSample( StkFloat input ) -{ - inputs_[inPoint_++] = input; - - // Increment input pointer modulo length. - if (inPoint_ == inputs_.size()) - inPoint_ = 0; - - outputs_[0] = nextOut(); - doNextOut_ = true; - - // Increment output pointer modulo length. - if (++outPoint_ == inputs_.size()) - outPoint_ = 0; - - return outputs_[0]; + long tap = inPoint_ - tapDelay - 1; + while ( tap < 0 ) // Check for wraparound. + tap += inputs_.size(); + + return inputs_[tap]; } +} // stk namespace diff --git a/src/Drummer.cpp b/src/Drummer.cpp index a258d44..37dddd5 100644 --- a/src/Drummer.cpp +++ b/src/Drummer.cpp @@ -11,13 +11,15 @@ of simultaneous voices) via a #define in the Drummer.h. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Drummer.h" #include +namespace stk { + // Not really General MIDI yet. unsigned char genMIDIMap[128] = { 0,0,0,0,0,0,0,0, // 0-7 @@ -53,7 +55,7 @@ char waveNames[DRUM_NUMWAVES][16] = "tambourn.raw" }; -Drummer :: Drummer() : Instrmnt() +Drummer :: Drummer( void ) : Instrmnt() { // This counts the number of sounding voices. nSounding_ = 0; @@ -61,11 +63,11 @@ Drummer :: Drummer() : Instrmnt() soundNumber_ = std::vector (DRUM_POLYPHONY, -1); } -Drummer :: ~Drummer() +Drummer :: ~Drummer( void ) { } -void Drummer :: noteOn(StkFloat instrument, StkFloat amplitude) +void Drummer :: noteOn( StkFloat instrument, StkFloat amplitude ) { #if defined(_STK_DEBUG_) errorString_ << "Drummer::NoteOn: instrument = " << instrument << ", amplitude = " << amplitude << '.'; @@ -110,7 +112,7 @@ void Drummer :: noteOn(StkFloat instrument, StkFloat amplitude) if ( soundOrder_[iWave] < 0 ) break; nSounding_ += 1; } - else { + else { // interrupt oldest voice for ( iWave=0; iWave= 0 ) { - if ( waves_[i].isFinished() ) { - // Re-order the list. - for ( int j=0; j soundOrder_[i] ) - soundOrder_[j] -= 1; - } - soundOrder_[i] = -1; - nSounding_--; - } - else - lastOutput_ += filters_[i].tick( waves_[i].tick() ); - } - } - - return lastOutput_; -} +} // stk namespace diff --git a/src/Echo.cpp b/src/Echo.cpp index a89c312..e911778 100644 --- a/src/Echo.cpp +++ b/src/Echo.cpp @@ -4,13 +4,15 @@ This class implements an echo effect. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Echo.h" #include +namespace stk { + Echo :: Echo( unsigned long maximumDelay ) : Effect() { this->setMaximumDelay( maximumDelay ); @@ -19,15 +21,10 @@ Echo :: Echo( unsigned long maximumDelay ) : Effect() this->clear(); } -Echo :: ~Echo() -{ -} - -void Echo :: clear() +void Echo :: clear( void ) { delayLine_.clear(); - lastOutput_[0] = 0.0; - lastOutput_[1] = 0.0; + lastFrame_[0] = 0.0; } void Echo :: setMaximumDelay( unsigned long delay ) @@ -54,10 +51,4 @@ void Echo :: setDelay( unsigned long delay ) delayLine_.setDelay( size ); } -StkFloat Echo :: computeSample(StkFloat input) -{ - lastOutput_[0] = effectMix_ * delayLine_.tick(input); - lastOutput_[0] += input * (1.0 - effectMix_); - lastOutput_[1] = lastOutput_[0]; - return lastOutput_[0]; -} +} // stk namespace diff --git a/src/Effect.cpp b/src/Effect.cpp deleted file mode 100644 index 494855e..0000000 --- a/src/Effect.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/***************************************************/ -/*! \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 - 2007. -*/ -/***************************************************/ - -#include "Effect.h" -#include - -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 input ) -{ - return computeSample( input ); -} - -StkFrames& Effect :: tick( StkFrames& frames, unsigned int channel ) -{ - if ( channel >= frames.channels() ) { - errorString_ << "Effect::tick(): channel and StkFrames arguments are incompatible!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( frames.channels() == 1 ) { - for ( unsigned int i=0; i value_) { - value_ += rate_; - if (value_ >= target_) { - value_ = target_; - state_ = 0; - } - } - else { - value_ -= rate_; - if (value_ <= target_) { - value_ = target_; - state_ = 0; - } - } - } - - lastOutput_ = value_; - return value_; -} +} // stk namespace diff --git a/src/FM.cpp b/src/FM.cpp index 9751140..c1ca204 100644 --- a/src/FM.cpp +++ b/src/FM.cpp @@ -19,13 +19,15 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "FM.h" #include "SKINI.msg" +namespace stk { + FM :: FM( unsigned int operators ) : nOperators_(operators) { @@ -73,7 +75,7 @@ FM :: FM( unsigned int operators ) } } -FM :: ~FM() +FM :: ~FM( void ) { for (unsigned int i=0; isetFrequency( baseFrequency_ * ratios_[i] ); } -void FM :: setRatio(unsigned int waveIndex, StkFloat ratio) +void FM :: setRatio( unsigned int waveIndex, StkFloat ratio ) { if ( waveIndex < 0 ) { errorString_ << "FM::setRatio: waveIndex parameter is less than zero!"; @@ -115,7 +117,7 @@ void FM :: setRatio(unsigned int waveIndex, StkFloat ratio) waves_[waveIndex]->setFrequency( ratio ); } -void FM :: setGain(unsigned int waveIndex, StkFloat gain) +void FM :: setGain( unsigned int waveIndex, StkFloat gain ) { if ( waveIndex < 0 ) { errorString_ << "FM::setGain: waveIndex parameter is less than zero!"; @@ -131,39 +133,19 @@ void FM :: setGain(unsigned int waveIndex, StkFloat gain) gains_[waveIndex] = gain; } -void FM :: setModulationSpeed(StkFloat mSpeed) +void FM :: keyOn( void ) { - vibrato_.setFrequency( mSpeed ); -} - -void FM :: setModulationDepth(StkFloat mDepth) -{ - modDepth_ = mDepth; -} - -void FM :: setControl1(StkFloat cVal) -{ - control1_ = cVal * 2.0; -} - -void FM :: setControl2(StkFloat cVal) -{ - control2_ = cVal * 2.0; -} - -void FM :: keyOn() -{ - for (unsigned int i=0; ikeyOn(); } -void FM :: keyOff() +void FM :: keyOff( void ) { - for (unsigned int i=0; ikeyOff(); } -void FM :: noteOff(StkFloat amplitude) +void FM :: noteOff( StkFloat amplitude ) { this->keyOff(); @@ -173,7 +155,7 @@ void FM :: noteOff(StkFloat amplitude) #endif } -void FM :: controlChange(int number, StkFloat value) +void FM :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -212,3 +194,4 @@ void FM :: controlChange(int number, StkFloat value) #endif } +} // stk namespace diff --git a/src/FMVoices.cpp b/src/FMVoices.cpp index c324ffd..b9fcea6 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ @@ -34,13 +34,15 @@ #include "SKINI.msg" #include "Phonemes.h" -FMVoices :: FMVoices() +namespace stk { + +FMVoices :: FMVoices( void ) : FM() { // Concatenate the STK rawwave path to the rawwave files 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 ); + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); this->setRatio(0, 2.00); this->setRatio(1, 4.00); @@ -67,11 +69,11 @@ FMVoices :: FMVoices() this->setFrequency( 110.0 ); } -FMVoices :: ~FMVoices() +FMVoices :: ~FMVoices( void ) { } -void FMVoices :: setFrequency(StkFloat frequency) +void FMVoices :: setFrequency( StkFloat frequency ) { StkFloat temp, temp2 = 0.0; int tempi = 0; @@ -109,7 +111,7 @@ void FMVoices :: setFrequency(StkFloat frequency) gains_[2] = 1.0; } -void FMVoices :: noteOn(StkFloat frequency, StkFloat amplitude) +void FMVoices :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); tilt_[0] = amplitude; @@ -123,32 +125,7 @@ void FMVoices :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -StkFloat FMVoices :: computeSample() -{ - register StkFloat temp, temp2; - - 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]->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, StkFloat value) +void FMVoices :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -188,3 +165,5 @@ void FMVoices :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/WaveLoop.cpp b/src/FileLoop.cpp similarity index 61% rename from src/WaveLoop.cpp rename to src/FileLoop.cpp index 41b7469..56cfdec 100644 --- a/src/WaveLoop.cpp +++ b/src/FileLoop.cpp @@ -1,31 +1,33 @@ /***************************************************/ -/*! \class WaveLoop - \brief STK waveform oscillator class. +/*! \class FileLoop + \brief STK file looping / oscillator class. - This class inherits from FileWvIn and provides audio file looping - functionality. Any audio file that can be loaded by FileRead can - be looped using this class. + This class provides audio file looping functionality. Any audio + file that can be loaded by FileRead can be looped using this + class. - WaveLoop supports multi-channel data. It is important to - distinguish the tick() methods, which return samples produced by - averaging across sample frames, from the tickFrame() methods, - which return references or pointers to multi-channel sample - frames. + FileLoop supports multi-channel data. It is important to + distinguish the tick() method that computes a single frame (and + returns only the specified sample of a multi-channel frame) from + the overloaded one that takes an StkFrames object for + multi-channel and/or multi-frame data. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ -#include "WaveLoop.h" +#include "FileLoop.h" #include -WaveLoop :: WaveLoop( unsigned long chunkThreshold, unsigned long chunkSize ) +namespace stk { + +FileLoop :: FileLoop( unsigned long chunkThreshold, unsigned long chunkSize ) : FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0) { Stk::addSampleRateAlert( this ); } -WaveLoop :: WaveLoop( std::string fileName, bool raw, bool doNormalize, +FileLoop :: FileLoop( std::string fileName, bool raw, bool doNormalize, unsigned long chunkThreshold, unsigned long chunkSize ) : FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0) { @@ -33,18 +35,12 @@ WaveLoop :: WaveLoop( std::string fileName, bool raw, bool doNormalize, Stk::addSampleRateAlert( this ); } -WaveLoop :: ~WaveLoop() +FileLoop :: ~FileLoop( void ) { Stk::removeSampleRateAlert( this ); } -void WaveLoop :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) -{ - if ( !ignoreSampleRateChange_ ) - this->setRate( oldRate * rate_ / newRate ); -} - -void WaveLoop :: openFile( std::string fileName, bool raw, bool doNormalize ) +void FileLoop :: openFile( std::string fileName, bool raw, bool doNormalize ) { // Call close() in case another file is already open. this->closeFile(); @@ -56,7 +52,7 @@ void WaveLoop :: openFile( std::string fileName, bool raw, bool doNormalize ) if ( file_.fileSize() > chunkThreshold_ ) { chunking_ = true; chunkPointer_ = 0; - data_.resize( chunkSize_, file_.channels() ); + data_.resize( chunkSize_ + 1, file_.channels() ); if ( doNormalize ) normalizing_ = true; else normalizing_ = false; } @@ -79,7 +75,7 @@ void WaveLoop :: openFile( std::string fileName, bool raw, bool doNormalize ) } // Resize our lastOutputs container. - lastOutputs_.resize( 1, file_.channels() ); + lastFrame_.resize( 1, file_.channels() ); // Set default rate based on file sampling rate. this->setRate( data_.dataRate() / Stk::sampleRate() ); @@ -89,7 +85,7 @@ void WaveLoop :: openFile( std::string fileName, bool raw, bool doNormalize ) this->reset(); } -void WaveLoop :: setRate( StkFloat rate ) +void FileLoop :: setRate( StkFloat rate ) { rate_ = rate; @@ -97,13 +93,7 @@ void WaveLoop :: setRate( StkFloat rate ) else interpolate_ = false; } -void WaveLoop :: setFrequency( StkFloat frequency ) -{ - // This is a looping frequency. - this->setRate( file_.fileSize() * frequency / Stk::sampleRate() ); -} - -void WaveLoop :: addTime( StkFloat time ) +void FileLoop :: addTime( StkFloat time ) { // Add an absolute time in samples. time_ += time; @@ -115,7 +105,7 @@ void WaveLoop :: addTime( StkFloat time ) time_ -= fileSize; } -void WaveLoop :: addPhase( StkFloat angle ) +void FileLoop :: addPhase( StkFloat angle ) { // Add a time in cycles (one cycle = fileSize). StkFloat fileSize = file_.fileSize(); @@ -127,35 +117,40 @@ void WaveLoop :: addPhase( StkFloat angle ) time_ -= fileSize; } -void WaveLoop :: addPhaseOffset( StkFloat angle ) +void FileLoop :: addPhaseOffset( StkFloat angle ) { // Add a phase offset in cycles, where 1.0 = fileSize. phaseOffset_ = file_.fileSize() * angle; } -void WaveLoop :: computeFrame( void ) +StkFloat FileLoop :: tick( unsigned int channel ) { +#if defined(_STK_DEBUG_) + if ( channel >= data_.channels() ) { + errorString_ << "FileLoop::tick(): channel argument and soundfile data are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + // Check limits of time address ... if necessary, recalculate modulo // fileSize. StkFloat fileSize = file_.fileSize(); + while ( time_ < 0.0 ) time_ += fileSize; while ( time_ >= fileSize ) time_ -= fileSize; - StkFloat tyme; + StkFloat tyme = time_; if ( phaseOffset_ ) { - tyme = time_ + phaseOffset_; + tyme += phaseOffset_; while ( tyme < 0.0 ) tyme += fileSize; while ( tyme >= fileSize ) tyme -= fileSize; } - else { - tyme = time_; - } - if (chunking_) { + if ( chunking_ ) { // Check the time address vs. our current buffer limits. if ( ( time_ < (StkFloat) chunkPointer_ ) || @@ -184,14 +179,46 @@ void WaveLoop :: computeFrame( void ) } if ( interpolate_ ) { - for ( unsigned int i=0; itick(); + for ( j=0; j #include +#include #include +namespace stk { + FileRead :: FileRead() : fd_(0) { @@ -730,3 +733,4 @@ void FileRead :: read( StkFrames& buffer, unsigned long startFrame, bool doNorma handleError( StkError::FILE_ERROR); } +} // stk namespace diff --git a/src/FileWrite.cpp b/src/FileWrite.cpp index 1db299a..e2e2e60 100644 --- a/src/FileWrite.cpp +++ b/src/FileWrite.cpp @@ -17,13 +17,16 @@ type, the data type will automatically be modified. Compressed data types are not supported. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "FileWrite.h" +#include #include +namespace stk { + const FileWrite::FILE_TYPE FileWrite :: FILE_RAW = 1; const FileWrite::FILE_TYPE FileWrite :: FILE_WAV = 2; const FileWrite::FILE_TYPE FileWrite :: FILE_SND = 3; @@ -699,3 +702,5 @@ void FileWrite :: write( StkFrames& buffer ) errorString_ << "FileWrite::write(): error writing data to file!"; handleError( StkError::FILE_ERROR ); } + +} // stk namespace diff --git a/src/FileWvIn.cpp b/src/FileWvIn.cpp index e1e3906..557308d 100644 --- a/src/FileWvIn.cpp +++ b/src/FileWvIn.cpp @@ -4,14 +4,15 @@ This class inherits from WvIn. It provides a "tick-level" interface to the FileRead class. It also provides variable-rate - "playback" functionality. Audio file support is provided by the - FileRead class. Linear interpolation is used for fractional "read - rates". + playback functionality. Audio file support is provided by the + FileRead class. Linear interpolation is used for fractional read + rates. - FileWvIn supports multi-channel data. It is important to distinguish - the tick() methods, which return samples produced by averaging - across sample frames, from the tickFrame() methods, which return - references to multi-channel sample frames. + FileWvIn supports multi-channel data. It is important to + distinguish the tick() method that computes a single frame (and + returns only the specified sample of a multi-channel frame) from + the overloaded one that takes an StkFrames object for + multi-channel and/or multi-frame data. FileWvIn will either load the entire content of an audio file into local memory or incrementally read file data from disk in chunks. @@ -20,19 +21,21 @@ chunkThreshold (in sample frames) will be read incrementally in chunks of \e chunkSize each (also in sample frames). - When the end of a file is reached, subsequent calls to the tick() - functions return zero-valued data. + When the file end is reached, subsequent calls to the tick() + functions return zeros and isFinished() returns \e true. See the FileRead class for a description of the supported audio file formats. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "FileWvIn.h" #include +namespace stk { + FileWvIn :: FileWvIn( unsigned long chunkThreshold, unsigned long chunkSize ) : finished_(true), interpolate_(false), time_(0.0), chunkThreshold_(chunkThreshold), chunkSize_(chunkSize) @@ -65,6 +68,7 @@ void FileWvIn :: closeFile( void ) { if ( file_.isOpen() ) file_.close(); finished_ = true; + lastFrame_.resize( 0, 0 ); } void FileWvIn :: openFile( std::string fileName, bool raw, bool doNormalize ) @@ -91,8 +95,8 @@ void FileWvIn :: openFile( std::string fileName, bool raw, bool doNormalize ) // Load all or part of the data. file_.read( data_, 0, doNormalize ); - // Resize our lastOutputs container. - lastOutputs_.resize( 1, file_.channels() ); + // Resize our lastFrame container. + lastFrame_.resize( 1, file_.channels() ); // Set default rate based on file sampling rate. this->setRate( data_.dataRate() / Stk::sampleRate() ); @@ -105,8 +109,7 @@ void FileWvIn :: openFile( std::string fileName, bool raw, bool doNormalize ) void FileWvIn :: reset(void) { time_ = (StkFloat) 0.0; - for ( unsigned int i=0; i 0.0) { + if ( max > 0.0 ) { max = 1.0 / max; max *= peak; for ( i=0; i file_.fileSize() - 1.0 ) { time_ = file_.fileSize() - 1.0; - for ( unsigned int i=0; i= data_.channels() ) { + errorString_ << "FileWvIn::tick(): channel argument and soundfile data are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif -void FileWvIn :: computeFrame( void ) -{ - if ( finished_ ) return; + if ( finished_ ) return 0.0; if ( time_ < 0.0 || time_ > (StkFloat) ( file_.fileSize() - 1.0 ) ) { - for ( unsigned int i=0; itick(); + for ( j=0; jincrementFrame(); } -void FileWvOut :: computeFrames( const StkFrames& frames ) +void FileWvOut :: tick( const StkFrames& frames ) { +#if defined(_STK_DEBUG_) if ( !file_.isOpen() ) { - errorString_ << "FileWvOut::computeFrames(): no file open!"; + errorString_ << "FileWvOut::tick(): no file open!"; handleError( StkError::WARNING ); return; } if ( data_.channels() != frames.channels() ) { - errorString_ << "FileWvOut::computeFrames(): incompatible channel value in StkFrames argument!"; + errorString_ << "FileWvOut::tick(): incompatible channel value in StkFrames argument!"; handleError( StkError::FUNCTION_ARGUMENT ); } +#endif + unsigned int iFrames = 0; unsigned int j, nChannels = data_.channels(); - if ( nChannels == 1 || frames.interleaved() ) { + for ( unsigned int i=0; iincrementFrame(); + for ( j=0; jincrementFrame(); - } + this->incrementFrame(); } } +} // stk namespace diff --git a/src/Filter.cpp b/src/Filter.cpp deleted file mode 100644 index 9f2572d..0000000 --- a/src/Filter.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/***************************************************/ -/*! \class Filter - \brief STK filter class. - - This class implements a generic structure that - can be used to create a wide range of filters. - It can function independently or be subclassed - to provide more specific controls based on a - particular filter type. - - In particular, this class implements the standard - difference equation: - - 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 coefficients - are normalized by a[0]. - - The \e gain parameter is applied at the filter - input and does not affect the coefficient values. - The default gain value is 1.0. This structure - 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 - 2007. -*/ -/***************************************************/ - -#include "Filter.h" -#include - -Filter :: Filter() -{ - // The default constructor should setup for pass-through. - gain_ = 1.0; - b_.push_back( 1.0 ); - a_.push_back( 1.0 ); - - inputs_.push_back( 0.0 ); - outputs_.push_back( 0.0 ); -} - -Filter :: Filter( std::vector &bCoefficients, std::vector &aCoefficients ) -{ - // Check the arguments. - 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 ) { - errorString_ << "Filter: a[0] coefficient cannot == 0!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - gain_ = 1.0; - b_ = bCoefficients; - a_ = aCoefficients; - - inputs_ = std::vector ( b_.size() ); - outputs_ = std::vector ( a_.size() ); - this->clear(); -} - -Filter :: ~Filter() -{ -} - -void Filter :: clear(void) -{ - unsigned int i; - for (i=0; i &bCoefficients, std::vector &aCoefficients, bool clearState ) -{ - // Check the arguments. - 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 ) { - errorString_ << "Filter::setCoefficients: a[0] coefficient cannot == 0!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( b_.size() != bCoefficients.size() ) { - b_ = bCoefficients; - inputs_.clear(); - inputs_ = std::vector ( 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 ) { - unsigned int i; - for ( i=0; i &bCoefficients, bool clearState ) -{ - // Check the argument. - if ( bCoefficients.size() == 0 ) { - errorString_ << "Filter::setNumerator: coefficient vector must have size > 0!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( b_.size() != bCoefficients.size() ) { - b_ = bCoefficients; - inputs_.clear(); - inputs_ = std::vector ( b_.size() ); - } - else { - for ( unsigned int i=0; iclear(); -} - -void Filter :: setDenominator( std::vector &aCoefficients, bool clearState ) -{ - // 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 ) { - errorString_ << "Filter::setDenominator: a[0] coefficient cannot == 0!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( a_.size() != aCoefficients.size() ) { - a_ = aCoefficients; - outputs_.clear(); - outputs_ = std::vector ( a_.size() ); - } - else { - for ( unsigned int i=0; iclear(); - - // Scale coefficients by a[0] if necessary - if ( a_[0] != 1.0 ) { - unsigned int i; - for ( i=0; i0; i--) { - outputs_[0] += b_[i] * inputs_[i]; - inputs_[i] = inputs_[i-1]; - } - outputs_[0] += b_[0] * inputs_[0]; - - for (i=a_.size()-1; i>0; i--) { - outputs_[0] += -a_[i] * outputs_[i]; - outputs_[i] = outputs_[i-1]; - } - - return outputs_[0]; -} - - -StkFrames& Filter :: tick( StkFrames& frames, unsigned int channel ) -{ - if ( channel >= frames.channels() ) { - errorString_ << "Filter::tick(): channel and StkFrames arguments are incompatible!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( frames.channels() == 1 ) { - for ( unsigned int i=0; i &coefficients ) +{ + // Check the arguments. + if ( coefficients.size() == 0 ) { + errorString_ << "Fir: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + gain_ = 1.0; + b_ = coefficients; + + inputs_.resize( b_.size(), 1, 0.0 ); + this->clear(); +} + +Fir :: ~Fir() +{ +} + +void Fir :: setCoefficients( std::vector &coefficients, bool clearState ) +{ + // Check the argument. + if ( coefficients.size() == 0 ) { + errorString_ << "Fir::setCoefficients: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( b_.size() != coefficients.size() ) { + b_ = coefficients; + inputs_.resize( b_.size(), 1, 0.0 ); + } + else { + for ( unsigned int i=0; iclear(); +} + +} // stk namespace diff --git a/src/Flute.cpp b/src/Flute.cpp index 08fee16..e8bb553 100644 --- a/src/Flute.cpp +++ b/src/Flute.cpp @@ -18,16 +18,18 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Flute.h" #include "SKINI.msg" -Flute :: Flute(StkFloat lowestFrequency) +namespace stk { + +Flute :: Flute( StkFloat lowestFrequency ) { - length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + length_ = (unsigned long) ( Stk::sampleRate() / lowestFrequency + 1 ); boreDelay_.setMaximumDelay( length_ ); boreDelay_.setDelay( 100.0 ); @@ -54,11 +56,11 @@ Flute :: Flute(StkFloat lowestFrequency) lastFrequency_ = 220.0; } -Flute :: ~Flute() +Flute :: ~Flute( void ) { } -void Flute :: clear() +void Flute :: clear( void ) { jetDelay_.clear(); boreDelay_.clear(); @@ -66,7 +68,7 @@ void Flute :: clear() dcBlock_.clear(); } -void Flute :: setFrequency(StkFloat frequency) +void Flute :: setFrequency( StkFloat frequency ) { lastFrequency_ = frequency; if ( frequency <= 0.0 ) { @@ -87,20 +89,20 @@ void Flute :: setFrequency(StkFloat frequency) jetDelay_.setDelay(delay * jetRatio_); } -void Flute :: startBlowing(StkFloat amplitude, StkFloat rate) +void Flute :: startBlowing( StkFloat amplitude, StkFloat rate ) { adsr_.setAttackRate( rate ); maxPressure_ = amplitude / (StkFloat) 0.8; adsr_.keyOn(); } -void Flute :: stopBlowing(StkFloat rate) +void Flute :: stopBlowing( StkFloat rate ) { adsr_.setReleaseRate( rate ); adsr_.keyOff(); } -void Flute :: noteOn(StkFloat frequency, StkFloat amplitude) +void Flute :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02 ); @@ -112,7 +114,7 @@ void Flute :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Flute :: noteOff(StkFloat amplitude) +void Flute :: noteOff( StkFloat amplitude ) { this->stopBlowing( amplitude * 0.02 ); @@ -122,12 +124,12 @@ void Flute :: noteOff(StkFloat amplitude) #endif } -void Flute :: setJetReflection(StkFloat coefficient) +void Flute :: setJetReflection( StkFloat coefficient ) { jetReflection_ = coefficient; } -void Flute :: setEndReflection(StkFloat coefficient) +void Flute :: setEndReflection( StkFloat coefficient ) { endReflection_ = coefficient; } @@ -140,28 +142,7 @@ void Flute :: setJetDelay( StkFloat aRatio ) jetDelay_.setDelay(temp * aRatio); // Scaled by ratio. } -StkFloat Flute :: computeSample() -{ - StkFloat pressureDiff; - StkFloat breathPressure; - - // Calculate the breath pressure (envelope + noise + vibrato) - breathPressure = maxPressure_ * adsr_.tick(); - breathPressure += breathPressure * ( noiseGain_ * noise_.tick() + vibratoGain_ * vibrato_.tick() ); - - 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_ = (StkFloat) 0.3 * boreDelay_.tick( pressureDiff ); - - lastOutput_ *= outputGain_; - return lastOutput_; -} - -void Flute :: controlChange(int number, StkFloat value) +void Flute :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -195,3 +176,5 @@ void Flute :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/FormSwep.cpp b/src/FormSwep.cpp index 4ee9302..32a73ec 100644 --- a/src/FormSwep.cpp +++ b/src/FormSwep.cpp @@ -2,63 +2,83 @@ /*! \class FormSwep \brief STK sweepable formant filter class. - This public BiQuad filter subclass implements - a formant (resonance) which can be "swept" - over time from one frequency setting to another. - It provides methods for controlling the sweep - rate and target frequency. + This class implements a formant (resonance) which can be "swept" + over time from one frequency setting to another. It provides + methods for controlling the sweep rate and target frequency. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "FormSwep.h" +#include -FormSwep :: FormSwep() : BiQuad() +namespace stk { + +FormSwep :: FormSwep( void ) { - 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; + frequency_ = 0.0; + radius_ = 0.0; + targetGain_ = 1.0; + targetFrequency_ = 0.0; + targetRadius_ = 0.0; + deltaGain_ = 0.0; + deltaFrequency_ = 0.0; + deltaRadius_ = 0.0; + sweepState_ = 0.0; + sweepRate_ = 0.002; dirty_ = false; - this->clear(); + + b_.resize( 3, 0.0 ); + a_.resize( 3, 0.0 ); + a_[0] = 1.0; + inputs_.resize( 3, 1, 0.0 ); + outputs_.resize( 3, 1, 0.0 ); + + Stk::addSampleRateAlert( this ); } FormSwep :: ~FormSwep() { + Stk::removeSampleRateAlert( this ); } -void FormSwep :: setResonance(StkFloat frequency, StkFloat radius) +void FormSwep :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) { + errorString_ << "FormSwep::sampleRateChanged: you may need to recompute filter coefficients!"; + handleError( StkError::WARNING ); + } +} + +void FormSwep :: setResonance( StkFloat frequency, StkFloat radius ) { - dirty_ = false; radius_ = radius; frequency_ = frequency; - BiQuad::setResonance( frequency_, radius_, true ); + a_[2] = radius * radius; + a_[1] = -2.0 * radius * cos( TWO_PI * frequency / Stk::sampleRate() ); + + // 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]; } -void FormSwep :: setStates(StkFloat frequency, StkFloat radius, StkFloat gain) +void FormSwep :: setStates( StkFloat frequency, StkFloat radius, StkFloat gain ) { dirty_ = false; if ( frequency_ != frequency || radius_ != radius ) - BiQuad::setResonance( frequency, radius, true ); + this->setResonance( frequency, radius ); - frequency_ = frequency; - radius_ = radius; gain_ = gain; targetFrequency_ = frequency; targetRadius_ = radius; targetGain_ = gain; } -void FormSwep :: setTargets(StkFloat frequency, StkFloat radius, StkFloat gain) +void FormSwep :: setTargets( StkFloat frequency, StkFloat radius, StkFloat gain ) { dirty_ = true; startFrequency_ = frequency_; @@ -70,42 +90,21 @@ void FormSwep :: setTargets(StkFloat frequency, StkFloat radius, StkFloat gain) deltaFrequency_ = frequency - frequency_; deltaRadius_ = radius - radius_; deltaGain_ = gain - gain_; - sweepState_ = (StkFloat) 0.0; + sweepState_ = 0.0; } -void FormSwep :: setSweepRate(StkFloat rate) +void FormSwep :: setSweepRate( StkFloat rate ) { sweepRate_ = rate; if ( sweepRate_ > 1.0 ) sweepRate_ = 1.0; if ( sweepRate_ < 0.0 ) sweepRate_ = 0.0; } -void FormSwep :: setSweepTime(StkFloat time) +void FormSwep :: setSweepTime( StkFloat time ) { sweepRate_ = 1.0 / ( time * Stk::sampleRate() ); if ( sweepRate_ > 1.0 ) sweepRate_ = 1.0; if ( sweepRate_ < 0.0 ) sweepRate_ = 0.0; } -StkFloat FormSwep :: computeSample( StkFloat input ) -{ - if (dirty_) { - sweepState_ += sweepRate_; - if ( sweepState_ >= 1.0 ) { - sweepState_ = 1.0; - dirty_ = false; - radius_ = targetRadius_; - frequency_ = targetFrequency_; - gain_ = targetGain_; - } - else { - radius_ = startRadius_ + (deltaRadius_ * sweepState_); - frequency_ = startFrequency_ + (deltaFrequency_ * sweepState_); - gain_ = startGain_ + (deltaGain_ * sweepState_); - } - BiQuad::setResonance( frequency_, radius_, true ); - } - - return BiQuad::computeSample( input ); -} - +} // stk namespace diff --git a/src/Function.cpp b/src/Function.cpp deleted file mode 100644 index 224d2b8..0000000 --- a/src/Function.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/***************************************************/ -/*! \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 - 2007. -*/ -/***************************************************/ - -#include "Function.h" - -Function :: Function() : Stk() -{ - lastOutput_ = (StkFloat) 0.0; -} - -Function :: ~Function() -{ -} - -StkFloat Function :: tick( StkFloat input ) -{ - return computeSample( input ); -} - -StkFrames& Function :: tick( StkFrames& frames, unsigned int channel ) -{ - if ( channel >= frames.channels() ) { - errorString_ << "Function::tick(): channel and StkFrames arguments are incompatible!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( frames.channels() == 1 ) { - for ( unsigned int i=0; i= frames.channels() ) { - errorString_ << "Generator::tick(): channel and StkFrames arguments are incompatible!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( frames.channels() == 1 ) { - for ( unsigned int i=0; i +namespace stk { + Granulate :: Granulate( void ) { this->setGrainParameters(); // use default values @@ -38,7 +40,7 @@ Granulate :: Granulate( unsigned int nVoices, std::string fileName, bool typeRaw this->setVoices( nVoices ); } -Granulate :: ~Granulate() +Granulate :: ~Granulate( void ) { } @@ -85,13 +87,9 @@ void Granulate :: openFile( std::string fileName, bool typeRaw ) { // Attempt to load the soundfile data. FileRead file( fileName, typeRaw ); - if ( file.channels() != 1 ) { - errorString_ << "Granulate::openFile: this class currently only supports monophonic soundfiles."; - handleError( StkError::FUNCTION_ARGUMENT ); - } - data_.resize( file.fileSize(), file.channels() ); file.read( data_ ); + lastFrame_.resize( 1, file.channels(), 0.0 ); this->reset(); @@ -102,7 +100,7 @@ void Granulate :: openFile( std::string fileName, bool typeRaw ) } -void Granulate :: reset() +void Granulate :: reset( void ) { gPointer_ = 0; @@ -115,7 +113,8 @@ void Granulate :: reset() grains_[i].state = GRAIN_STOPPED; } - lastOutput_ = 0.0; + for ( unsigned int i=0; i= data_.channels() ) { + errorString_ << "Granulate::tick(): channel argument and soundfile data are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + unsigned int i, j, nChannels = lastFrame_.channels(); + for ( j=0; j 0 ) { - sample = data_[ grains_[i].pointer++ ]; + for ( j=0; j= data_.frames() ) grains_[i].pointer = 0; } @@ -276,6 +288,7 @@ StkFloat Granulate :: computeSample( void ) stretchCounter_ = 0; } - return lastOutput_ * gain_; + return lastFrame_[channel]; } +} // stk namespace diff --git a/src/HevyMetl.cpp b/src/HevyMetl.cpp index 4688ff5..55ba230 100644 --- a/src/HevyMetl.cpp +++ b/src/HevyMetl.cpp @@ -22,19 +22,21 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "HevyMetl.h" -HevyMetl :: HevyMetl() +namespace stk { + +HevyMetl :: HevyMetl( void ) : FM() { // Concatenate the STK rawwave path to the rawwave files 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 ); + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); this->setRatio(0, 1.0 * 1.000); this->setRatio(1, 4.0 * 0.999); @@ -56,11 +58,11 @@ HevyMetl :: HevyMetl() modDepth_ = 0.0; } -HevyMetl :: ~HevyMetl() +HevyMetl :: ~HevyMetl( void ) { } -void HevyMetl :: noteOn(StkFloat frequency, StkFloat amplitude) +void HevyMetl :: noteOn( StkFloat frequency, StkFloat amplitude ) { gains_[0] = amplitude * fmGains_[92]; gains_[1] = amplitude * fmGains_[76]; @@ -75,29 +77,4 @@ void HevyMetl :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -StkFloat HevyMetl :: computeSample() -{ - 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 = 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); - - 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(); - - lastOutput_ = temp * 0.5; - return lastOutput_; -} +} // stk namespace diff --git a/src/Iir.cpp b/src/Iir.cpp new file mode 100644 index 0000000..1aba722 --- /dev/null +++ b/src/Iir.cpp @@ -0,0 +1,125 @@ +/***************************************************/ +/*! \class Iir + \brief STK general infinite impulse response filter class. + + This class provides a generic digital filter structure that can be + used to implement IIR filters. For filters containing only + feedforward terms, the Fir class is slightly more efficient. + + In particular, this class implements the standard difference + equation: + + 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 are normalized + by a[0]. + + The \e gain parameter is applied at the filter input and does not + affect the coefficient values. The default gain value is 1.0. + This structure 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 - 2009. +*/ +/***************************************************/ + +#include "Iir.h" + +namespace stk { + +Iir :: Iir() +{ + // The default constructor should setup for pass-through. + b_.push_back( 1.0 ); + a_.push_back( 1.0 ); + + inputs_.resize( 1, 1, 0.0 ); + outputs_.resize( 1, 1, 0.0 ); +} + +Iir :: Iir( std::vector &bCoefficients, std::vector &aCoefficients ) +{ + // Check the arguments. + if ( bCoefficients.size() == 0 || aCoefficients.size() == 0 ) { + errorString_ << "Iir: a and b coefficient vectors must both have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( aCoefficients[0] == 0.0 ) { + errorString_ << "Iir: a[0] coefficient cannot == 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + gain_ = 1.0; + b_ = bCoefficients; + a_ = aCoefficients; + + inputs_.resize( b_.size(), 1, 0.0 ); + outputs_.resize( a_.size(), 1, 0.0 ); + this->clear(); +} + +Iir :: ~Iir() +{ +} + +void Iir :: setCoefficients( std::vector &bCoefficients, std::vector &aCoefficients, bool clearState ) +{ + this->setNumerator( bCoefficients, false ); + this->setDenominator( aCoefficients, false ); + + if ( clearState ) this->clear(); +} + +void Iir :: setNumerator( std::vector &bCoefficients, bool clearState ) +{ + // Check the argument. + if ( bCoefficients.size() == 0 ) { + errorString_ << "Iir::setNumerator: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( b_.size() != bCoefficients.size() ) { + b_ = bCoefficients; + inputs_.resize( b_.size(), 1, 0.0 ); + } + else { + for ( unsigned int i=0; iclear(); +} + +void Iir :: setDenominator( std::vector &aCoefficients, bool clearState ) +{ + // Check the argument. + if ( aCoefficients.size() == 0 ) { + errorString_ << "Iir::setDenominator: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( aCoefficients[0] == 0.0 ) { + errorString_ << "Iir::setDenominator: a[0] coefficient cannot == 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( a_.size() != aCoefficients.size() ) { + a_ = aCoefficients; + outputs_.resize( a_.size(), 1, 0.0 ); + } + else { + for ( unsigned int i=0; iclear(); + + // Scale coefficients by a[0] if necessary + if ( a_[0] != 1.0 ) { + unsigned int i; + for ( i=0; i= data_.channels() ) { + errorString_ << "InetWvIn::tick(): channel argument is incompatible with data stream!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif if ( bufferCounter_ == 0 ) bufferCounter_ = readData(); - unsigned int nChannels = lastOutputs_.channels(); - long temp = (bufferFrames_ - bufferCounter_) * nChannels; + unsigned int nChannels = lastFrame_.channels(); + long index = ( bufferFrames_ - bufferCounter_ ) * nChannels; for ( unsigned int i=0; itick(); + for ( j=0; jisValid( soket_->id() ) ) return; + if ( !soket_ || !soket_->isValid( soket_->id() ) ) { +#if defined(_STK_DEBUG_) + errorString_ << "InetWvOut::tick(): a valid socket connection does not exist!"; + handleError( StkError::DEBUG_WARNING ); +#endif + return; + } unsigned int nChannels = data_.channels(); StkFloat input = sample; @@ -188,43 +197,34 @@ void InetWvOut :: computeSample( const StkFloat sample ) this->incrementFrame(); } -void InetWvOut :: computeFrames( const StkFrames& frames ) +void InetWvOut :: tick( const StkFrames& frames ) { - if ( !soket_ || !soket_->isValid( soket_->id() ) ) return; + if ( !soket_ || !soket_->isValid( soket_->id() ) ) { +#if defined(_STK_DEBUG_) + errorString_ << "InetWvOut::tick(): a valid socket connection does not exist!"; + handleError( StkError::DEBUG_WARNING ); +#endif + return; + } +#if defined(_STK_DEBUG_) if ( data_.channels() != frames.channels() ) { - errorString_ << "InetWvOut::computeFrames(): incompatible channel value in StkFrames argument!"; + errorString_ << "InetWvOut::tick(): incompatible channel value in StkFrames argument!"; handleError( StkError::FUNCTION_ARGUMENT ); } +#endif unsigned int j, nChannels = data_.channels(); - if ( nChannels == 1 || frames.interleaved() ) { + unsigned int iFrames = 0; + for ( unsigned int i=0; iincrementFrame(); + for ( j=0; jincrementFrame(); - } + this->incrementFrame(); } } + +} // stk namespace diff --git a/src/Instrmnt.cpp b/src/Instrmnt.cpp deleted file mode 100644 index 2a5b25d..0000000 --- a/src/Instrmnt.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/***************************************************/ -/*! \class Instrmnt - \brief STK instrument abstract base class. - - This class provides a common interface for - all STK instruments. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - -#include "Instrmnt.h" - -Instrmnt :: Instrmnt() -{ -} - -Instrmnt :: ~Instrmnt() -{ -} - -void Instrmnt :: setFrequency(StkFloat frequency) -{ - errorString_ << "Instrmnt::setFrequency: virtual setFrequency function call!"; - handleError( StkError::WARNING ); -} - -StkFloat Instrmnt :: lastOut() const -{ - return lastOutput_; -} - -// Support for stereo output: -StkFloat Instrmnt :: lastOutLeft(void) const -{ - return 0.5 * lastOutput_; -} - -StkFloat Instrmnt :: lastOutRight(void) const -{ - return 0.5 * lastOutput_; -} - -StkFloat Instrmnt :: tick( void ) -{ - return computeSample(); -} - -StkFrames& Instrmnt :: tick( StkFrames& frames, unsigned int channel ) -{ - if ( channel >= frames.channels() ) { - errorString_ << "Instrmnt::tick(): channel and StkFrames arguments are incompatible!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( frames.channels() == 1 ) { - for ( unsigned int i=0; i +#include -JCRev :: JCRev(StkFloat T60) +namespace stk { + +JCRev :: JCRev( StkFloat T60 ) { + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output + // Delay lengths for 44100 Hz sample rate. int lengths[9] = {1777, 1847, 1993, 2137, 389, 127, 43, 211, 179}; double scaler = Stk::sampleRate() / 44100.0; int delay, i; if ( scaler != 1.0 ) { - for (i=0; i<9; i++) { - delay = (int) floor(scaler * lengths[i]); + for ( i=0; i<9; i++ ) { + delay = (int) floor( scaler * lengths[i] ); if ( (delay & 1) == 0) delay++; while ( !this->isPrime(delay) ) delay += 2; lengths[i] = delay; } } - for (i=0; i<3; i++) { + for ( i=0; i<3; i++ ) { allpassDelays_[i].setMaximumDelay( lengths[i+4] ); allpassDelays_[i].setDelay( lengths[i+4] ); } @@ -53,10 +56,6 @@ JCRev :: JCRev(StkFloat T60) this->clear(); } -JCRev :: ~JCRev() -{ -} - void JCRev :: clear() { allpassDelays_[0].clear(); @@ -68,8 +67,8 @@ void JCRev :: clear() combDelays_[3].clear(); outRightDelay_.clear(); outLeftDelay_.clear(); - lastOutput_[0] = 0.0; - lastOutput_[1] = 0.0; + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; } void JCRev :: setT60( StkFloat T60 ) @@ -78,46 +77,44 @@ void JCRev :: setT60( StkFloat T60 ) combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate()))); } -StkFloat JCRev :: computeSample(StkFloat input) +StkFrames& JCRev :: tick( StkFrames& frames, unsigned int channel ) { - StkFloat temp, temp0, temp1, temp2, temp3, temp4, temp5, temp6; - StkFloat filtout; +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + errorString_ << "JCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif - temp = allpassDelays_[0].lastOut(); - temp0 = allpassCoefficient_ * temp; - temp0 += input; - allpassDelays_[0].tick(temp0); - temp0 = -(allpassCoefficient_ * temp0) + temp; - - temp = allpassDelays_[1].lastOut(); - temp1 = allpassCoefficient_ * temp; - temp1 += temp0; - allpassDelays_[1].tick(temp1); - temp1 = -(allpassCoefficient_ * temp1) + temp; - - temp = allpassDelays_[2].lastOut(); - temp2 = allpassCoefficient_ * temp; - temp2 += temp1; - 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()); + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + errorString_ << "JCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0) - lastOutput_ = (StkFloat) 1.0; - if (lastOutput_ < -1.0) - lastOutput_ = (StkFloat) -1.0; - return lastOutput_; -} diff --git a/src/Makefile.in b/src/Makefile.in index c320781..fe62150 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -7,11 +7,12 @@ RM = /bin/rm OBJECT_PATH = @object_path@ vpath %.o $(OBJECT_PATH) -OBJECTS = Stk.o Generator.o Noise.o SubNoise.o Blit.o BlitSaw.o BlitSquare.o \ - Envelope.o ADSR.o Asymp.o Modulate.o SingWave.o SineWave.o Granulate.o \ - FileRead.o FileWrite.o WvIn.o FileWvIn.o WaveLoop.o WvOut.o FileWvOut.o \ - Filter.o OneZero.o OnePole.o PoleZero.o TwoZero.o TwoPole.o \ +OBJECTS = Stk.o Generator.o Noise.o Blit.o BlitSaw.o BlitSquare.o Granulate.o \ + Envelope.o ADSR.o Asymp.o Modulate.o SineWave.o FileLoop.o SingWave.o \ + FileRead.o FileWrite.o WvIn.o FileWvIn.o WvOut.o FileWvOut.o \ + Filter.o Fir.o Iir.o OneZero.o OnePole.o PoleZero.o TwoZero.o TwoPole.o \ BiQuad.o FormSwep.o Delay.o DelayL.o DelayA.o \ + \ Effect.o PRCRev.o JCRev.o NRev.o \ Chorus.o Echo.o PitShift.o \ Function.o ReedTable.o JetTable.o BowTable.o \ @@ -25,7 +26,6 @@ OBJECTS = Stk.o Generator.o Noise.o SubNoise.o Blit.o BlitSaw.o BlitSquare.o \ \ Messager.o Skini.o MidiFileIn.o - INCLUDE = @include@ ifeq ($(strip $(INCLUDE)),) vpath %.h ../include @@ -36,20 +36,19 @@ else endif CC = @CXX@ -DEFS = @byte_order@ -DEFS += @debug@ -CFLAGS = @cflags@ -CFLAGS += @warn@ $(INCLUDE) -Iinclude +DEFS = @CPPFLAGS@ +DEFS += @byte_order@ +CFLAGS = @CXXFLAGS@ +CFLAGS += $(INCLUDE) -Iinclude REALTIME = @realtime@ ifeq ($(REALTIME),yes) OBJECTS += RtMidi.o RtAudio.o RtWvOut.o RtWvIn.o InetWvOut.o InetWvIn.o Thread.o Mutex.o Socket.o TcpClient.o TcpServer.o UdpSocket.o @objects@ - DEFS += @audio_apis@ endif RAWWAVES = @rawwaves@ ifeq ($(strip $(RAWWAVES)), ) - RAWWAVES = ../rawwaves/ + RAWWAVES = ../../rawwaves/ endif DEFS += -DRAWWAVE_PATH=\"$(RAWWAVES)\" @@ -70,3 +69,4 @@ $(OBJECTS) : Stk.h clean : -rm $(OBJECT_PATH)/*.o -rm $(LIBRARY) + -rm -fR *.dSYM diff --git a/src/Mandolin.cpp b/src/Mandolin.cpp index d11903b..1f3e1d2 100644 --- a/src/Mandolin.cpp +++ b/src/Mandolin.cpp @@ -23,15 +23,17 @@ - String Detuning = 1 - Microphone Position = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Mandolin.h" #include "SKINI.msg" -Mandolin :: Mandolin(StkFloat lowestFrequency) - : PluckTwo(lowestFrequency) +namespace stk { + +Mandolin :: Mandolin( StkFloat lowestFrequency ) + : PluckTwo( lowestFrequency ) { // Concatenate the STK rawwave path to the rawwave files soundfile_[0] = new FileWvIn( (Stk::rawwavePath() + "mand1.raw").c_str(), true ); @@ -52,7 +54,7 @@ Mandolin :: Mandolin(StkFloat lowestFrequency) waveDone_ = soundfile_[mic_]->isFinished(); } -Mandolin :: ~Mandolin() +Mandolin :: ~Mandolin( void ) { for ( int i=0; i<12; i++ ) delete soundfile_[i]; @@ -83,7 +85,7 @@ void Mandolin :: pluck( StkFloat amplitude ) dampTime_ = (long) lastLength_; // See tick method below. } -void Mandolin :: pluck(StkFloat amplitude, StkFloat position) +void Mandolin :: pluck( StkFloat amplitude, StkFloat position ) { // Pluck position puts zeroes at position * length. pluckPosition_ = position; @@ -101,7 +103,7 @@ void Mandolin :: pluck(StkFloat amplitude, StkFloat position) this->pluck( amplitude ); } -void Mandolin :: noteOn(StkFloat frequency, StkFloat amplitude) +void Mandolin :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->pluck( amplitude ); @@ -112,7 +114,7 @@ void Mandolin :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Mandolin :: setBodySize(StkFloat size) +void Mandolin :: setBodySize( StkFloat size ) { // Scale the commuted body response by its sample rate (22050). StkFloat rate = size * 22050.0 / Stk::sampleRate(); @@ -120,37 +122,7 @@ void Mandolin :: setBodySize(StkFloat size) soundfile_[i]->setRate( rate ); } -StkFloat Mandolin :: computeSample() -{ - 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(); - } - - // Damping hack to help avoid overflow on re-plucking. - if ( dampTime_ >=0 ) { - dampTime_ -= 1; - // Calculate 1st delay filtered reflection plus pluck excitation. - 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() * 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_) ) ); - // Calculate 2nd delay just like the 1st. - lastOutput_ += delayLine2_.tick( filter2_.tick( temp + (delayLine2_.lastOut() * loopGain_) ) ); - } - - lastOutput_ *= 0.3; - return lastOutput_; -} - -void Mandolin :: controlChange(int number, StkFloat value) +void Mandolin :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -184,3 +156,5 @@ void Mandolin :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Mesh2D.cpp b/src/Mesh2D.cpp index 43c04af..f1a67fb 100644 --- a/src/Mesh2D.cpp +++ b/src/Mesh2D.cpp @@ -27,7 +27,9 @@ #include "Mesh2D.h" #include "SKINI.msg" -Mesh2D :: Mesh2D(short nX, short nY) +namespace stk { + +Mesh2D :: Mesh2D( short nX, short nY ) { this->setNX(nX); this->setNY(nY); @@ -52,11 +54,11 @@ Mesh2D :: Mesh2D(short nX, short nY) yInput_ = 0; } -Mesh2D :: ~Mesh2D() +Mesh2D :: ~Mesh2D( void ) { } -void Mesh2D :: clear() +void Mesh2D :: clear( void ) { this->clearMesh(); @@ -70,7 +72,7 @@ void Mesh2D :: clear() counter_=0; } -void Mesh2D :: clearMesh() +void Mesh2D :: clearMesh( void ) { int x, y; for (x=0; xtick1() : this->tick0()); + lastFrame_[0] = ((counter_ & 1) ? this->tick1() : this->tick0()); counter_++; - return lastOutput_; + return lastFrame_[0]; } const StkFloat VSCALE = 0.5; -StkFloat Mesh2D :: tick0() +StkFloat Mesh2D :: tick0( void ) { int x, y; StkFloat outsamp = 0; @@ -315,7 +317,7 @@ StkFloat Mesh2D :: tick0() return outsamp; } -StkFloat Mesh2D :: tick1() +StkFloat Mesh2D :: tick1( void ) { int x, y; StkFloat outsamp = 0; @@ -362,7 +364,7 @@ StkFloat Mesh2D :: tick1() return outsamp; } -void Mesh2D :: controlChange(int number, StkFloat value) +void Mesh2D :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -394,3 +396,5 @@ void Mesh2D :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Messager.cpp b/src/Messager.cpp index f42687e..e93ee81 100644 --- a/src/Messager.cpp +++ b/src/Messager.cpp @@ -28,14 +28,24 @@ 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Messager.h" #include +#include #include "SKINI.msg" +namespace stk { + +#if defined(__STK_REALTIME__) + +extern "C" THREAD_RETURN THREAD_TYPE stdinHandler(void * ptr); +extern "C" THREAD_RETURN THREAD_TYPE socketHandler(void * ptr); + +#endif // __STK_REALTIME__ + static const int STK_FILE = 0x1; static const int STK_MIDI = 0x2; static const int STK_STDIN = 0x4; @@ -152,6 +162,8 @@ bool Messager :: startStdInput() return true; } +//} // stk namespace + THREAD_RETURN THREAD_TYPE stdinHandler(void *ptr) { Messager::MessagerData *data = (Messager::MessagerData *) ptr; @@ -208,6 +220,8 @@ void midiHandler( double timeStamp, std::vector *bytes, void *ptr data->mutex.unlock(); } +//namespace stk { + bool Messager :: startMidiInput( int port ) { if ( data_.sources == STK_FILE ) { @@ -290,6 +304,8 @@ bool Messager :: startSocketInput( int port ) return true; } +//} // stk namespace + #if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) #include #include @@ -412,4 +428,7 @@ THREAD_RETURN THREAD_TYPE socketHandler(void *ptr) return NULL; } +} // stk namespace + #endif + diff --git a/src/MidiFileIn.cpp b/src/MidiFileIn.cpp index f8bc377..8cb6abc 100644 --- a/src/MidiFileIn.cpp +++ b/src/MidiFileIn.cpp @@ -11,13 +11,16 @@ Tempo changes are internally tracked by the class and reflected in the values returned by the function getTickSeconds(). - by Gary P. Scavone, 2003. + by Gary P. Scavone, 2003 - 2009. */ /**********************************************************************/ #include "MidiFileIn.h" +#include #include +namespace stk { + MidiFileIn :: MidiFileIn( std::string fileName ) { // Attempt to open the file. @@ -360,3 +363,5 @@ bool MidiFileIn :: readVariableLength( unsigned long *value ) return true; } + +} // stk namespace diff --git a/src/Modal.cpp b/src/Modal.cpp index 30d627d..7f28175 100644 --- a/src/Modal.cpp +++ b/src/Modal.cpp @@ -1,19 +1,21 @@ /***************************************************/ /*! \class Modal - \brief STK resonance model instrument. + \brief STK resonance model abstract base class. This class contains an excitation wavetable, an envelope, an oscillator, and N resonances (non-sweeping BiQuad filters), where N is set during instantiation. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Modal.h" -Modal :: Modal(unsigned int modes) +namespace stk { + +Modal :: Modal( unsigned int modes ) : nModes_(modes) { if ( nModes_ == 0 ) { @@ -45,29 +47,29 @@ Modal :: Modal(unsigned int modes) strikePosition_ = 0.561; } -Modal :: ~Modal() +Modal :: ~Modal( void ) { - for (unsigned int i=0; iclear(); } -void Modal :: setFrequency(StkFloat frequency) +void Modal :: setFrequency( StkFloat frequency ) { baseFrequency_ = frequency; - for (unsigned int i=0; isetRatioAndRadius( i, ratios_[i], radii_[i] ); } -void Modal :: setRatioAndRadius(unsigned int modeIndex, StkFloat ratio, StkFloat radius) +void Modal :: setRatioAndRadius( unsigned int modeIndex, StkFloat ratio, StkFloat radius ) { if ( modeIndex >= nModes_ ) { errorString_ << "Modal::setRatioAndRadius: modeIndex parameter is greater than number of modes!"; @@ -99,17 +101,7 @@ void Modal :: setRatioAndRadius(unsigned int modeIndex, StkFloat ratio, StkFloat filters_[modeIndex]->setResonance(temp, radius); } -void Modal :: setMasterGain(StkFloat aGain) -{ - masterGain_ = aGain; -} - -void Modal :: setDirectGain(StkFloat aGain) -{ - directGain_ = aGain; -} - -void Modal :: setModeGain(unsigned int modeIndex, StkFloat gain) +void Modal :: setModeGain( unsigned int modeIndex, StkFloat gain ) { if ( modeIndex >= nModes_ ) { errorString_ << "Modal::setModeGain: modeIndex parameter is greater than number of modes!"; @@ -117,10 +109,10 @@ void Modal :: setModeGain(unsigned int modeIndex, StkFloat gain) return; } - filters_[modeIndex]->setGain(gain); + filters_[modeIndex]->setGain( gain ); } -void Modal :: strike(StkFloat amplitude) +void Modal :: strike( StkFloat amplitude ) { StkFloat gain = amplitude; if ( amplitude < 0.0 ) { @@ -141,7 +133,7 @@ void Modal :: strike(StkFloat amplitude) wave_->reset(); StkFloat temp; - for (unsigned int i=0; istrike(amplitude); this->setFrequency(frequency); @@ -161,7 +153,7 @@ void Modal :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Modal :: noteOff(StkFloat amplitude) +void Modal :: noteOff( StkFloat amplitude ) { // This calls damp, but inverts the meaning of amplitude (high // amplitude means fast damping). @@ -173,35 +165,16 @@ void Modal :: noteOff(StkFloat amplitude) #endif } -void Modal :: damp(StkFloat amplitude) +void Modal :: damp( StkFloat amplitude ) { StkFloat temp; - for (unsigned int i=0; isetResonance(temp, radii_[i]*amplitude); + filters_[i]->setResonance( temp, radii_[i]*amplitude ); } } -StkFloat Modal :: computeSample() -{ - StkFloat temp = masterGain_ * onepole_.tick( wave_->tick() * envelope_.tick() ); - - StkFloat temp2 = 0.0; - for (unsigned int i=0; itick(temp); - - temp2 -= temp2 * directGain_; - temp2 += directGain_ * temp; - - if (vibratoGain_ != 0.0) { - // Calculate AM and apply to master out - temp = 1.0 + (vibrato_.tick() * vibratoGain_); - temp2 = temp * temp2; - } - - lastOutput_ = temp2; - return lastOutput_; -} +} // stk namespace diff --git a/src/ModalBar.cpp b/src/ModalBar.cpp index 2aae6b0..58ed0d6 100644 --- a/src/ModalBar.cpp +++ b/src/ModalBar.cpp @@ -24,7 +24,7 @@ - Two Fixed = 7 - Clump = 8 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ @@ -32,7 +32,9 @@ #include "SKINI.msg" #include -ModalBar :: ModalBar() +namespace stk { + +ModalBar :: ModalBar( void ) : Modal() { // Concatenate the STK rawwave path to the rawwave file @@ -43,12 +45,12 @@ ModalBar :: ModalBar() this->setPreset( 0 ); } -ModalBar :: ~ModalBar() +ModalBar :: ~ModalBar( void ) { delete wave_; } -void ModalBar :: setStickHardness(StkFloat hardness) +void ModalBar :: setStickHardness( StkFloat hardness ) { stickHardness_ = hardness; if ( hardness < 0.0 ) { @@ -66,7 +68,7 @@ void ModalBar :: setStickHardness(StkFloat hardness) masterGain_ = 0.1 + (1.8 * stickHardness_); } -void ModalBar :: setStrikePosition(StkFloat position) +void ModalBar :: setStrikePosition( StkFloat position ) { strikePosition_ = position; if ( position < 0.0 ) { @@ -92,7 +94,7 @@ void ModalBar :: setStrikePosition(StkFloat position) this->setModeGain(2, 0.11 * temp); } -void ModalBar :: setPreset(int preset) +void ModalBar :: setPreset( int preset ) { // Presets: // First line: relative modal frequencies (negative number is @@ -156,7 +158,7 @@ void ModalBar :: setPreset(int preset) vibratoGain_ = 0.0; } -void ModalBar :: controlChange(int number, StkFloat value) +void ModalBar :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -194,3 +196,5 @@ void ModalBar :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Modulate.cpp b/src/Modulate.cpp index 128d961..d5e1b4d 100644 --- a/src/Modulate.cpp +++ b/src/Modulate.cpp @@ -6,54 +6,44 @@ modulations to give a nice, natural human modulation function. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Modulate.h" -Modulate :: Modulate() +namespace stk { + +Modulate :: Modulate( void ) { vibrato_.setFrequency( 6.0 ); vibratoGain_ = 0.04; - noise_.setRate( 330 ); - randomGain_ = 0.05; + noiseRate_ = (unsigned int) ( 330.0 * Stk::sampleRate() / 22050.0 ); + noiseCounter_ = noiseRate_; + randomGain_ = 0.05; filter_.setPole( 0.999 ); filter_.setGain( randomGain_ ); + + Stk::addSampleRateAlert( this ); } -Modulate :: ~Modulate() +Modulate :: ~Modulate( void ) { + Stk::removeSampleRateAlert( this ); } -void Modulate :: reset() +void Modulate :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) { - lastOutput_ = (StkFloat) 0.0; + if ( !ignoreSampleRateChange_ ) + noiseRate_ = (unsigned int ) ( newRate * noiseRate_ / oldRate ); } -void Modulate :: setVibratoRate(StkFloat rate) -{ - vibrato_.setFrequency( rate ); -} - -void Modulate :: setVibratoGain(StkFloat gain) -{ - vibratoGain_ = gain; -} - -void Modulate :: setRandomGain(StkFloat gain) +void Modulate :: setRandomGain( StkFloat gain ) { randomGain_ = gain; filter_.setGain( randomGain_ ); } -StkFloat Modulate :: computeSample() -{ - // Compute periodic and random modulations. - lastOutput_ = vibratoGain_ * vibrato_.tick(); - lastOutput_ += filter_.tick( noise_.tick() ); - return lastOutput_; -} - +} // stk namespace diff --git a/src/Moog.cpp b/src/Moog.cpp index 76324f0..632b3c4 100644 --- a/src/Moog.cpp +++ b/src/Moog.cpp @@ -14,19 +14,21 @@ - Vibrato Gain = 1 - Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Moog.h" #include "SKINI.msg" -Moog :: Moog() +namespace stk { + +Moog :: Moog( void ) { // Concatenate the STK rawwave path to the rawwave file attacks_.push_back( new FileWvIn( (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_.push_back ( new FileLoop( (Stk::rawwavePath() + "impuls20.raw").c_str(), true ) ); + loops_.push_back ( new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ) ); // vibrato loops_[1]->setFrequency( 6.122 ); filters_[0].setTargets( 0.0, 0.7 ); @@ -38,11 +40,11 @@ Moog :: Moog() modDepth_ = 0.0; } -Moog :: ~Moog() +Moog :: ~Moog( void ) { } -void Moog :: setFrequency(StkFloat frequency) +void Moog :: setFrequency( StkFloat frequency ) { baseFrequency_ = frequency; if ( frequency <= 0.0 ) { @@ -56,7 +58,7 @@ void Moog :: setFrequency(StkFloat frequency) loops_[0]->setFrequency( baseFrequency_ ); } -void Moog :: noteOn(StkFloat frequency, StkFloat amplitude) +void Moog :: noteOn( StkFloat frequency, StkFloat amplitude ) { StkFloat temp; @@ -82,35 +84,7 @@ void Moog :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Moog :: setModulationSpeed(StkFloat mSpeed) -{ - loops_[1]->setFrequency( mSpeed ); -} - -void Moog :: setModulationDepth(StkFloat mDepth) -{ - modDepth_ = mDepth * 0.5; -} - -StkFloat Moog :: computeSample() -{ - StkFloat temp; - - if ( modDepth_ != 0.0 ) { - temp = loops_[1]->tick() * modDepth_; - loops_[0]->setFrequency( baseFrequency_ * (1.0 + temp) ); - } - - 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, StkFloat value) +void Moog :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -144,3 +118,5 @@ void Moog :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Mutex.cpp b/src/Mutex.cpp index d3c7b9c..5730d98 100644 --- a/src/Mutex.cpp +++ b/src/Mutex.cpp @@ -7,12 +7,14 @@ systems, the pthread library is used. Under Windows, critical sections are used. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Mutex.h" +namespace stk { + Mutex :: Mutex() { @@ -98,3 +100,5 @@ void Mutex :: signal() #endif } + +} // stk namespace diff --git a/src/NRev.cpp b/src/NRev.cpp index 09e97ff..09e4de3 100644 --- a/src/NRev.cpp +++ b/src/NRev.cpp @@ -2,43 +2,45 @@ /*! \class NRev \brief CCRMA's NRev reverberator class. - This class is derived from the CLM NRev - function, which is based on the use of - networks of simple allpass and comb delay - filters. This particular arrangement consists - of 6 comb filters in parallel, followed by 3 - allpass filters, a lowpass filter, and another - allpass in series, followed by two allpass - filters in parallel with corresponding right - and left outputs. + This class takes a monophonic input signal and produces a stereo + output signal. It is derived from the CLM NRev function, which is + based on the use of networks of simple allpass and comb delay + filters. This particular arrangement consists of 6 comb filters + in parallel, followed by 3 allpass filters, a lowpass filter, and + another allpass in series, followed by two allpass filters in + parallel with corresponding right and left outputs. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "NRev.h" -#include +#include -NRev :: NRev(StkFloat T60) +namespace stk { + +NRev :: NRev( StkFloat T60 ) { + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output + int lengths[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19}; double scaler = Stk::sampleRate() / 25641.0; int delay, i; - for (i=0; i<15; i++) { + for ( i=0; i<15; i++ ) { delay = (int) floor(scaler * lengths[i]); if ( (delay & 1) == 0) delay++; while ( !this->isPrime(delay) ) delay += 2; lengths[i] = delay; } - for (i=0; i<6; i++) { + for ( i=0; i<6; i++ ) { 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++) { + for ( i=0; i<8; i++ ) { allpassDelays_[i].setMaximumDelay( lengths[i+6] ); allpassDelays_[i].setDelay( lengths[i+6] ); } @@ -49,17 +51,13 @@ NRev :: NRev(StkFloat T60) this->clear(); } -NRev :: ~NRev() -{ -} - 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; + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; lowpassState_ = 0.0; } @@ -69,47 +67,44 @@ void NRev :: setT60( StkFloat T60 ) combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate()))); } -StkFloat NRev :: computeSample(StkFloat input) +StkFrames& NRev :: tick( StkFrames& frames, unsigned int channel ) { - 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); +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + errorString_ << "NRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); } - for (i=0; i<3; i++) { - temp = allpassDelays_[i].lastOut(); - temp1 = allpassCoefficient_ * temp; - temp1 += temp0; - allpassDelays_[i].tick(temp1); - temp0 = -(allpassCoefficient_ * temp1) + temp; +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + errorString_ << "NRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i #include -Noise :: Noise() : Generator() -{ - // Seed the random number generator with system time. - this->setSeed( 0 ); - lastOutput_ = (StkFloat) 0.0; -} +namespace stk { -Noise :: Noise( unsigned int seed ) : Generator() +Noise :: Noise( unsigned int seed ) { // Seed the random number generator this->setSeed( seed ); - lastOutput_ = (StkFloat) 0.0; -} - -Noise :: ~Noise() -{ } void Noise :: setSeed( unsigned int seed ) { if ( seed == 0 ) - srand( (unsigned int) time(NULL) ); + srand( (unsigned int) time( NULL ) ); else srand( seed ); } -StkFloat Noise :: computeSample() -{ - lastOutput_ = (StkFloat) (2.0 * rand() / (RAND_MAX + 1.0) ); - lastOutput_ -= 1.0; - return lastOutput_; -} +} // stk namespace + diff --git a/src/OnePole.cpp b/src/OnePole.cpp index 256bf32..7f0994c 100644 --- a/src/OnePole.cpp +++ b/src/OnePole.cpp @@ -2,64 +2,36 @@ /*! \class OnePole \brief STK one-pole filter class. - This protected Filter subclass implements - a one-pole digital filter. A method is - provided for setting the pole position along - the real axis of the z-plane while maintaining - a constant peak filter gain. + This class implements a one-pole digital filter. A method is + provided for setting the pole position along the real axis of the + z-plane while maintaining a constant peak filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "OnePole.h" -OnePole :: OnePole() : Filter() +namespace stk { + +OnePole :: OnePole( StkFloat thePole ) { - std::vector b(1, 0.1); - std::vector a(2, 1.0); - a[1] = -0.9; - Filter::setCoefficients( b, a ); -} + b_.resize( 1 ); + a_.resize( 2 ); + inputs_.resize( 1, 1, 0.0 ); + outputs_.resize( 2, 1, 0.0 ); -OnePole :: OnePole(StkFloat thePole) : Filter() -{ - std::vector b(1); - std::vector a(2, 1.0); - a[1] = -thePole; - - // Normalize coefficients for peak unity gain. - if (thePole > 0.0) - b[0] = (StkFloat) (1.0 - thePole); - else - b[0] = (StkFloat) (1.0 + thePole); - - Filter::setCoefficients( b, a ); + this->setPole( thePole ); } OnePole :: ~OnePole() { } -void OnePole :: clear(void) -{ - Filter::clear(); -} - -void OnePole :: setB0(StkFloat b0) -{ - b_[0] = b0; -} - -void OnePole :: setA1(StkFloat a1) -{ - a_[1] = a1; -} - -void OnePole :: setPole(StkFloat thePole) +void OnePole :: setPole( StkFloat thePole ) { // Normalize coefficients for peak unity gain. - if (thePole > 0.0) + if ( thePole > 0.0 ) b_[0] = (StkFloat) (1.0 - thePole); else b_[0] = (StkFloat) (1.0 + thePole); @@ -67,31 +39,12 @@ void OnePole :: setPole(StkFloat thePole) a_[1] = -thePole; } -void OnePole :: setGain(StkFloat gain) +void OnePole :: setCoefficients( StkFloat b0, StkFloat a1, bool clearState ) { - Filter::setGain(gain); + b_[0] = b0; + a_[1] = a1; + + if ( clearState ) this->clear(); } -StkFloat OnePole :: getGain(void) const -{ - return Filter::getGain(); -} - -StkFloat OnePole :: lastOut(void) const -{ - return Filter::lastOut(); -} - -StkFloat OnePole :: tick( StkFloat input ) -{ - inputs_[0] = gain_ * input; - outputs_[0] = b_[0] * inputs_[0] - a_[1] * outputs_[1]; - outputs_[1] = outputs_[0]; - - return outputs_[0]; -} - -StkFrames& OnePole :: tick( StkFrames& frames, unsigned int channel ) -{ - return Filter::tick( frames, channel ); -} +} // stk namespace diff --git a/src/OneZero.cpp b/src/OneZero.cpp index 4823c69..61ff5cd 100644 --- a/src/OneZero.cpp +++ b/src/OneZero.cpp @@ -2,63 +2,34 @@ /*! \class OneZero \brief STK one-zero filter class. - This protected Filter subclass implements - a one-zero digital filter. A method is - provided for setting the zero position - along the real axis of the z-plane while - maintaining a constant filter gain. + This class implements a one-zero digital filter. A method is + provided for setting the zero position along the real axis of the + z-plane while maintaining a constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "OneZero.h" -OneZero :: OneZero() : Filter() +namespace stk { + +OneZero :: OneZero( StkFloat theZero ) { - std::vector b(2, 0.5); - std::vector a(1, 1.0); - Filter::setCoefficients( b, a ); + b_.resize( 2 ); + inputs_.resize( 2, 1, 0.0 ); + + this->setZero( theZero ); } -OneZero :: OneZero(StkFloat theZero) : Filter() -{ - std::vector b(2); - std::vector a(1, 1.0); - - // Normalize coefficients for unity gain. - if (theZero > 0.0) - b[0] = 1.0 / ((StkFloat) 1.0 + theZero); - else - b[0] = 1.0 / ((StkFloat) 1.0 - theZero); - - b[1] = -theZero * b[0]; - Filter::setCoefficients( b, a ); -} - -OneZero :: ~OneZero(void) +OneZero :: ~OneZero( void ) { } -void OneZero :: clear(void) -{ - Filter::clear(); -} - -void OneZero :: setB0(StkFloat b0) -{ - b_[0] = b0; -} - -void OneZero :: setB1(StkFloat b1) -{ - b_[1] = b1; -} - -void OneZero :: setZero(StkFloat theZero) +void OneZero :: setZero( StkFloat theZero ) { // Normalize coefficients for unity gain. - if (theZero > 0.0) + if ( theZero > 0.0 ) b_[0] = 1.0 / ((StkFloat) 1.0 + theZero); else b_[0] = 1.0 / ((StkFloat) 1.0 - theZero); @@ -66,31 +37,12 @@ void OneZero :: setZero(StkFloat theZero) b_[1] = -theZero * b_[0]; } -void OneZero :: setGain(StkFloat gain) +void OneZero :: setCoefficients( StkFloat b0, StkFloat b1, bool clearState ) { - Filter::setGain(gain); + b_[0] = b0; + b_[1] = b1; + + if ( clearState ) this->clear(); } -StkFloat OneZero :: getGain(void) const -{ - return Filter::getGain(); -} - -StkFloat OneZero :: lastOut(void) const -{ - return Filter::lastOut(); -} - -StkFloat OneZero :: tick( StkFloat input ) -{ - inputs_[0] = gain_ * input; - outputs_[0] = b_[1] * inputs_[1] + b_[0] * inputs_[0]; - inputs_[1] = inputs_[0]; - - return outputs_[0]; -} - -StkFrames& OneZero :: tick( StkFrames& frames, unsigned int channel ) -{ - return Filter::tick( frames, channel ); -} +} // stk namespace diff --git a/src/PRCRev.cpp b/src/PRCRev.cpp index 0e10c52..c5cf64a 100644 --- a/src/PRCRev.cpp +++ b/src/PRCRev.cpp @@ -10,15 +10,19 @@ two series allpass units and two parallel comb filters. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "PRCRev.h" -#include +#include -PRCRev :: PRCRev(StkFloat T60) +namespace stk { + +PRCRev :: PRCRev( StkFloat T60 ) { + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output + // Delay lengths for 44100 Hz sample rate. int lengths[4]= {353, 1097, 1777, 2137}; double scaler = Stk::sampleRate() / 44100.0; @@ -34,7 +38,7 @@ PRCRev :: PRCRev(StkFloat T60) } } - for (i=0; i<2; i++) { + for ( i=0; i<2; i++ ) { allpassDelays_[i].setMaximumDelay( lengths[i] ); allpassDelays_[i].setDelay( lengths[i] ); @@ -48,18 +52,14 @@ PRCRev :: PRCRev(StkFloat T60) this->clear(); } -PRCRev :: ~PRCRev() -{ -} - -void PRCRev :: clear() +void PRCRev :: clear( void ) { allpassDelays_[0].clear(); allpassDelays_[1].clear(); combDelays_[0].clear(); combDelays_[1].clear(); - lastOutput_[0] = 0.0; - lastOutput_[1] = 0.0; + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; } void PRCRev :: setT60( StkFloat T60 ) @@ -68,31 +68,44 @@ void PRCRev :: setT60( StkFloat T60 ) combCoefficient_[1] = pow(10.0, (-3.0 * combDelays_[1].getDelay() / (T60 * Stk::sampleRate()))); } -StkFloat PRCRev :: computeSample(StkFloat input) +StkFrames& PRCRev :: tick( StkFrames& frames, unsigned int channel ) { - StkFloat temp, temp0, temp1, temp2, temp3; +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + errorString_ << "PRCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif - temp = allpassDelays_[0].lastOut(); - temp0 = allpassCoefficient_ * temp; - temp0 += input; - allpassDelays_[0].tick(temp0); - temp0 = -(allpassCoefficient_ * temp0) + temp; - - temp = allpassDelays_[1].lastOut(); - temp1 = allpassCoefficient_ * temp; - temp1 += temp0; - allpassDelays_[1].tick(temp1); - temp1 = -(allpassCoefficient_ * temp1) + temp; - - temp2 = temp1 + (combCoefficient_[0] * combDelays_[0].lastOut()); - temp3 = temp1 + (combCoefficient_[1] * combDelays_[1].lastOut()); + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + errorString_ << "PRCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; isetRatio(0, 1.50 * 1.000); this->setRatio(1, 3.00 * 0.995); @@ -54,16 +56,16 @@ PercFlut :: PercFlut() modDepth_ = 0.005; } -PercFlut :: ~PercFlut() +PercFlut :: ~PercFlut( void ) { } -void PercFlut :: setFrequency(StkFloat frequency) +void PercFlut :: setFrequency( StkFloat frequency ) { baseFrequency_ = frequency; } -void PercFlut :: noteOn(StkFloat frequency, StkFloat amplitude) +void PercFlut :: noteOn( StkFloat frequency, StkFloat amplitude ) { gains_[0] = amplitude * fmGains_[99] * 0.5; gains_[1] = amplitude * fmGains_[71] * 0.5; @@ -78,29 +80,4 @@ void PercFlut :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -StkFloat PercFlut :: computeSample() -{ - 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]); - - 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(); - - 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(); - - lastOutput_ = temp * 0.5; - return lastOutput_; -} +} // stk namespace diff --git a/src/Phonemes.cpp b/src/Phonemes.cpp index e2c49b3..fa5c0e4 100644 --- a/src/Phonemes.cpp +++ b/src/Phonemes.cpp @@ -6,13 +6,15 @@ set of 32 static phoneme formant parameters and provide access to those values. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Phonemes.h" #include +namespace stk { + const char Phonemes :: phonemeNames[32][4] = {"eee", "ihh", "ehh", "aaa", "ahh", "aww", "ohh", "uhh", @@ -292,3 +294,5 @@ StkFloat Phonemes :: formantGain( unsigned int index, unsigned int partial ) } return phonemeParameters[index][partial][2]; } + +} // stk namespace diff --git a/src/PitShift.cpp b/src/PitShift.cpp index d2fc7c8..58facf3 100644 --- a/src/PitShift.cpp +++ b/src/PitShift.cpp @@ -5,19 +5,19 @@ This class implements a simple pitch shifter using delay lines. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "PitShift.h" #include -const int maxDelay = 5024; +namespace stk { -PitShift :: PitShift() +PitShift :: PitShift( void ) { - delayLength = maxDelay - 24; - halfLength = delayLength / 2; + delayLength_ = maxDelay - 24; + halfLength_ = delayLength_ / 2; delay_[0] = 12; delay_[1] = maxDelay / 2; @@ -29,60 +29,60 @@ PitShift :: PitShift() rate_ = 1.0; } -PitShift :: ~PitShift() -{ -} - void PitShift :: clear() { delayLine_[0].clear(); delayLine_[1].clear(); - lastOutput_[0] = 0.0; - lastOutput_[1] = 0.0; + lastFrame_[0] = 0.0; } -void PitShift :: setShift(StkFloat shift) +void PitShift :: setShift( StkFloat shift ) { - if (shift < 1.0) { + if ( shift < 1.0 ) { rate_ = 1.0 - shift; } - else if (shift > 1.0) { + else if ( shift > 1.0 ) { rate_ = 1.0 - shift; } else { rate_ = 0.0; - delay_[0] = halfLength+12; + delay_[0] = halfLength_ + 12; } } -StkFloat PitShift :: computeSample(StkFloat input) +StkFrames& PitShift :: tick( StkFrames& frames, unsigned int channel ) { - // 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; +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + errorString_ << "PitShift::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif - delay_[1] = delay_[0] + halfLength; - while (delay_[1] > maxDelay-12) delay_[1] -= delayLength; - while (delay_[1] < 12) delay_[1] += delayLength; + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + errorString_ << "PitShift::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0 ) loopGain_ = 0.99999; } -void PluckTwo :: setDetune(StkFloat detune) +void PluckTwo :: setDetune( StkFloat detune ) { detuning_ = detune; if ( detuning_ <= 0.0 ) { @@ -90,13 +91,13 @@ void PluckTwo :: setDetune(StkFloat detune) delayLine2_.setDelay( (lastLength_ * detuning_) - 0.5); } -void PluckTwo :: setFreqAndDetune(StkFloat frequency, StkFloat detune) +void PluckTwo :: setFreqAndDetune( StkFloat frequency, StkFloat detune ) { detuning_ = detune; this->setFrequency( frequency ); } -void PluckTwo :: setPluckPosition(StkFloat position) +void PluckTwo :: setPluckPosition( StkFloat position ) { pluckPosition_ = position; if ( position < 0.0 ) { @@ -111,14 +112,14 @@ void PluckTwo :: setPluckPosition(StkFloat position) } } -void PluckTwo :: setBaseLoopGain(StkFloat aGain) +void PluckTwo :: setBaseLoopGain( StkFloat aGain ) { baseLoopGain_ = aGain; loopGain_ = baseLoopGain_ + (lastFrequency_ * 0.000005); if ( loopGain_ > 0.99999 ) loopGain_ = 0.99999; } -void PluckTwo :: noteOff(StkFloat amplitude) +void PluckTwo :: noteOff( StkFloat amplitude ) { loopGain_ = (1.0 - amplitude) * 0.5; @@ -128,3 +129,4 @@ void PluckTwo :: noteOff(StkFloat amplitude) #endif } +} // stk namespace diff --git a/src/Plucked.cpp b/src/Plucked.cpp index 4c601fe..7b04351 100644 --- a/src/Plucked.cpp +++ b/src/Plucked.cpp @@ -13,26 +13,28 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Plucked.h" +namespace stk { + Plucked :: Plucked( StkFloat lowestFrequency ) { - length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + length_ = (unsigned long) ( Stk::sampleRate() / lowestFrequency + 1 ); loopGain_ = 0.999; delayLine_.setMaximumDelay( length_ ); delayLine_.setDelay( 0.5 * length_ ); this->clear(); } -Plucked :: ~Plucked() +Plucked :: ~Plucked( void ) { } -void Plucked :: clear() +void Plucked :: clear( void ) { delayLine_.clear(); loopFilter_.clear(); @@ -92,7 +94,7 @@ void Plucked :: noteOn( StkFloat frequency, StkFloat amplitude ) #endif } -void Plucked :: noteOff(StkFloat amplitude) +void Plucked :: noteOff( StkFloat amplitude ) { loopGain_ = 1.0 - amplitude; if ( loopGain_ < 0.0 ) { @@ -112,10 +114,4 @@ void Plucked :: noteOff(StkFloat amplitude) #endif } -StkFloat Plucked :: computeSample() -{ - // Here's the whole inner loop of the instrument!! - lastOutput_ = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) ); - lastOutput_ *= 3.0; - return lastOutput_; -} +} // stk namespace diff --git a/src/PoleZero.cpp b/src/PoleZero.cpp index e0ba403..76951ad 100644 --- a/src/PoleZero.cpp +++ b/src/PoleZero.cpp @@ -2,53 +2,44 @@ /*! \class PoleZero \brief STK one-pole, one-zero filter class. - This protected Filter subclass implements - a one-pole, one-zero digital filter. A - method is provided for creating an allpass - filter with a given coefficient. Another - method is provided to create a DC blocking filter. + This class implements a one-pole, one-zero digital filter. A + method is provided for creating an allpass filter with a given + coefficient. Another method is provided to create a DC blocking + filter. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "PoleZero.h" -PoleZero :: PoleZero() : Filter() +namespace stk { + +PoleZero :: PoleZero() { // Default setting for pass-through. - std::vector b(2, 0.0); - std::vector a(2, 0.0); - b[0] = 1.0; - a[0] = 1.0; - Filter::setCoefficients( b, a ); + b_.resize( 2, 0.0 ); + a_.resize( 2, 0.0 ); + b_[0] = 1.0; + a_[0] = 1.0; + inputs_.resize( 2, 1, 0.0 ); + outputs_.resize( 2, 1, 0.0 ); } PoleZero :: ~PoleZero() { } -void PoleZero :: clear(void) -{ - Filter::clear(); -} - -void PoleZero :: setB0(StkFloat b0) +void PoleZero :: setCoefficients( StkFloat b0, StkFloat b1, StkFloat a1, bool clearState ) { b_[0] = b0; -} - -void PoleZero :: setB1(StkFloat b1) -{ b_[1] = b1; -} - -void PoleZero :: setA1(StkFloat a1) -{ a_[1] = a1; + + if ( clearState ) this->clear(); } -void PoleZero :: setAllpass(StkFloat coefficient) +void PoleZero :: setAllpass( StkFloat coefficient ) { b_[0] = coefficient; b_[1] = 1.0; @@ -56,7 +47,7 @@ void PoleZero :: setAllpass(StkFloat coefficient) a_[1] = coefficient; } -void PoleZero :: setBlockZero(StkFloat thePole) +void PoleZero :: setBlockZero( StkFloat thePole ) { b_[0] = 1.0; b_[1] = -1.0; @@ -64,32 +55,4 @@ void PoleZero :: setBlockZero(StkFloat thePole) a_[1] = -thePole; } -void PoleZero :: setGain(StkFloat gain) -{ - Filter::setGain(gain); -} - -StkFloat PoleZero :: getGain(void) const -{ - return Filter::getGain(); -} - -StkFloat PoleZero :: lastOut(void) const -{ - return Filter::lastOut(); -} - -StkFloat PoleZero :: tick( StkFloat input ) -{ - inputs_[0] = gain_ * input; - 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]; -} - -StkFrames& PoleZero :: tick( StkFrames& frames, unsigned int channel ) -{ - return Filter::tick( frames, channel ); -} +} // stk namespace diff --git a/src/ReedTable.cpp b/src/ReedTable.cpp deleted file mode 100644 index afaa37b..0000000 --- a/src/ReedTable.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/***************************************************/ -/*! \class ReedTable - \brief STK reed table class. - - This class implements a simple one breakpoint, - non-linear reed function, as described by - Smith (1986). This function is based on a - memoryless non-linear spring model of the reed - (the reed mass is ignored) which saturates when - the reed collides with the mouthpiece facing. - - See McIntyre, Schumacher, & Woodhouse (1983), - Smith (1986), Hirschman, Cook, Scavone, and - others for more information. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - -#include "ReedTable.h" - -ReedTable :: ReedTable() -{ - offset_ = (StkFloat) 0.6; // Offset is a bias, related to reed rest position. - slope_ = (StkFloat) -0.8; // Slope corresponds loosely to reed stiffness. -} - -ReedTable :: ~ReedTable() -{ -} - -void ReedTable :: setOffset(StkFloat offset) -{ - offset_ = offset; -} - -void ReedTable :: setSlope(StkFloat slope) -{ - slope_ = slope; -} - -StkFloat ReedTable :: computeSample(StkFloat input) -{ - // The input is differential pressure across the reed. - lastOutput_ = offset_ + (slope_ * input); - - // If output is > 1, the reed has slammed shut and the - // reflection function value saturates at 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_ = (StkFloat) -1.0; - return lastOutput_; -} - diff --git a/src/Resonate.cpp b/src/Resonate.cpp index 9457d75..5e2d717 100644 --- a/src/Resonate.cpp +++ b/src/Resonate.cpp @@ -13,14 +13,16 @@ - Zero Radii = 1 - Envelope Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Resonate.h" #include "SKINI.msg" -Resonate :: Resonate() +namespace stk { + +Resonate :: Resonate( void ) { poleFrequency_ = 4000.0; poleRadius_ = 0.95; @@ -30,21 +32,11 @@ Resonate :: Resonate() zeroRadius_ = 0.0; } -Resonate :: ~Resonate() +Resonate :: ~Resonate( void ) { } -void Resonate :: keyOn() -{ - adsr_.keyOn(); -} - -void Resonate :: keyOff() -{ - adsr_.keyOff(); -} - -void Resonate :: noteOn(StkFloat frequency, StkFloat amplitude) +void Resonate :: noteOn( StkFloat frequency, StkFloat amplitude ) { adsr_.setTarget( amplitude ); this->keyOn(); @@ -55,7 +47,7 @@ void Resonate :: noteOn(StkFloat frequency, StkFloat amplitude) handleError( StkError::DEBUG_WARNING ); #endif } -void Resonate :: noteOff(StkFloat amplitude) +void Resonate :: noteOff( StkFloat amplitude ) { this->keyOff(); @@ -88,7 +80,7 @@ void Resonate :: setResonance( StkFloat frequency, StkFloat radius ) filter_.setResonance( poleFrequency_, poleRadius_, true ); } -void Resonate :: setNotch(StkFloat frequency, StkFloat radius) +void Resonate :: setNotch( StkFloat frequency, StkFloat radius ) { zeroFrequency_ = frequency; if ( frequency < 0.0 ) { @@ -107,19 +99,7 @@ void Resonate :: setNotch(StkFloat frequency, StkFloat radius) filter_.setNotch( zeroFrequency_, zeroRadius_ ); } -void Resonate :: setEqualGainZeroes() -{ - filter_.setEqualGainZeroes(); -} - -StkFloat Resonate :: computeSample() -{ - lastOutput_ = filter_.tick( noise_.tick() ); - lastOutput_ *= adsr_.tick(); - return lastOutput_; -} - -void Resonate :: controlChange(int number, StkFloat value) +void Resonate :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -153,3 +133,5 @@ void Resonate :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Rhodey.cpp b/src/Rhodey.cpp index 2175a73..a093cdc 100644 --- a/src/Rhodey.cpp +++ b/src/Rhodey.cpp @@ -26,19 +26,21 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Rhodey.h" -Rhodey :: Rhodey() +namespace stk { + +Rhodey :: Rhodey( void ) : FM() { // Concatenate the STK rawwave path to the rawwave files 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 ); + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); this->setRatio(0, 1.0); this->setRatio(1, 0.5); @@ -58,11 +60,11 @@ Rhodey :: Rhodey() twozero_.setGain( 1.0 ); } -Rhodey :: ~Rhodey() +Rhodey :: ~Rhodey( void ) { } -void Rhodey :: setFrequency(StkFloat frequency) +void Rhodey :: setFrequency( StkFloat frequency ) { baseFrequency_ = frequency * 2.0; @@ -70,7 +72,7 @@ void Rhodey :: setFrequency(StkFloat frequency) waves_[i]->setFrequency( baseFrequency_ * ratios_[i] ); } -void Rhodey :: noteOn(StkFloat frequency, StkFloat amplitude) +void Rhodey :: noteOn( StkFloat frequency, StkFloat amplitude ) { gains_[0] = amplitude * fmGains_[99]; gains_[1] = amplitude * fmGains_[90]; @@ -85,26 +87,4 @@ void Rhodey :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -StkFloat Rhodey :: computeSample() -{ - StkFloat temp, temp2; - - 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_[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_; - temp = temp * (1.0 + temp2); - - lastOutput_ = temp * 0.5; - return lastOutput_; -} +} // stk namespace diff --git a/src/RtAudio.cpp b/src/RtAudio.cpp index cdefda4..cd427f3 100644 --- a/src/RtAudio.cpp +++ b/src/RtAudio.cpp @@ -4,13 +4,13 @@ RtAudio provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, Jack, - and OSS), SGI, Macintosh OS X (CoreAudio and Jack), and Windows + and OSS), Macintosh OS X (CoreAudio and Jack), and Windows (DirectSound and ASIO) operating systems. RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes - Copyright (c) 2001-2007 Gary P. Scavone + Copyright (c) 2001-2009 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -38,10 +38,13 @@ */ /************************************************************************/ -// RtAudio: Version 4.0.3 +// RtAudio: Version 4.0.5 #include "RtAudio.h" #include +#include +#include +#include // Static variable definitions. const unsigned int RtApi::MAX_SAMPLE_RATES = 14; @@ -359,6 +362,13 @@ double RtApi :: getStreamTime( void ) #endif } +unsigned int RtApi :: getStreamSampleRate( void ) +{ + verifyStream(); + + return stream_.sampleRate; +} + // *************************************************** // // @@ -393,7 +403,9 @@ double RtApi :: getStreamTime( void ) // implementation. struct CoreHandle { AudioDeviceID id[2]; // device ids - UInt32 iStream[2]; // device stream index (first for mono mode) + AudioDeviceIOProcID procId[2]; + UInt32 iStream[2]; // device stream index (or first if using multiple) + UInt32 nStreams[2]; // number of streams to use bool xrun[2]; char *deviceBuffer; pthread_cond_t condition; @@ -401,7 +413,7 @@ struct CoreHandle { bool internalDrain; // Indicates if stop is initiated from callback or not. CoreHandle() - :deviceBuffer(0), drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } + :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } }; RtApiCore :: RtApiCore() @@ -805,69 +817,71 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne return FAILURE; } - // Search for a stream that contains the desired number of + // Search for one or more streams that contain the desired number of // channels. CoreAudio devices can have an arbitrary number of // streams and each stream can have an arbitrary number of channels. // For each stream, a single buffer of interleaved samples is - // provided. RtAudio currently only supports the use of one stream - // of interleaved data or multiple consecutive single-channel - // streams. Thus, our search below is limited to these two - // contexts. - unsigned int streamChannels = 0, nStreams = 0; - UInt32 iChannel = 0, iStream = 0; - unsigned int offsetCounter = firstChannel; - stream_.deviceInterleaved[mode] = true; - nStreams = bufferList->mNumberBuffers; + // provided. RtAudio prefers the use of one stream of interleaved + // data or multiple consecutive single-channel streams. However, we + // now support multiple consecutive multi-channel streams of + // interleaved data as well. + UInt32 iStream, offsetCounter = firstChannel; + UInt32 nStreams = bufferList->mNumberBuffers; + bool monoMode = false; bool foundStream = false; + // First check that the device supports the requested number of + // channels. + UInt32 deviceChannels = 0; + for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; + + if ( deviceChannels < ( channels + firstChannel ) ) { + free( bufferList ); + errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count."; + errorText_ = errorStream_.str(); + return FAILURE; + } + + // Look for a single stream meeting our needs. + UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; if ( streamChannels >= channels + offsetCounter ) { - iChannel += offsetCounter; + firstStream = iStream; + channelOffset = offsetCounter; foundStream = true; break; } if ( streamChannels > offsetCounter ) break; offsetCounter -= streamChannels; - iChannel += streamChannels; } - // If we didn't find a single stream above, see if we can meet - // the channel specification in mono mode (i.e. using separate - // non-interleaved buffers). This can only work if there are N - // consecutive one-channel streams, where N is the number of - // desired channels (+ channel offset). + // If we didn't find a single stream above, then we should be able + // to meet the channel specification with multiple streams. if ( foundStream == false ) { - unsigned int counter = 0; + monoMode = true; offsetCounter = firstChannel; - iChannel = 0; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; - if ( offsetCounter ) { - if ( streamChannels > offsetCounter ) break; - offsetCounter -= streamChannels; - } - else if ( streamChannels == 1 ) - counter++; - else - counter = 0; - if ( counter == channels ) { - iStream -= channels - 1; - iChannel -= channels - 1; - stream_.deviceInterleaved[mode] = false; - foundStream = true; - break; - } - iChannel += streamChannels; + if ( streamChannels > offsetCounter ) break; + offsetCounter -= streamChannels; + } + + firstStream = iStream; + channelOffset = offsetCounter; + Int32 channelCounter = channels + offsetCounter - streamChannels; + + if ( streamChannels > 1 ) monoMode = false; + while ( channelCounter > 0 ) { + streamChannels = bufferList->mBuffers[++iStream].mNumberChannels; + if ( streamChannels > 1 ) monoMode = false; + channelCounter -= streamChannels; + streamCount++; } } - free( bufferList ); - if ( foundStream == false ) { - errorStream_ << "RtApiCore::probeDeviceOpen: unable to find OS-X stream on device (" << device << ") for requested channels."; - errorText_ = errorStream_.str(); - return FAILURE; - } + free( bufferList ); // Determine the buffer size. AudioValueRange bufferRange; @@ -885,8 +899,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum; - // Set the buffer size. For mono mode, I'm assuming we only need to - // make this setting for the master channel. + // Set the buffer size. For multiple streams, I'm assuming we only + // need to make this setting for the master channel. UInt32 theSize = (UInt32) *bufferSize; dataSize = sizeof( UInt32 ); result = AudioDeviceSetProperty( id, NULL, 0, isInput, @@ -911,8 +925,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.bufferSize = *bufferSize; stream_.nBuffers = 1; - // Get the stream ID(s) so we can set the stream format. In mono - // mode, we'll have to do this for each stream (channel). + // Get the stream ID(s) so we can set the stream format. We'll have + // to do this for each stream. AudioStreamID streamIDs[ nStreams ]; dataSize = nStreams * sizeof( AudioStreamID ); result = AudioDeviceGetProperty( id, 0, isInput, @@ -928,13 +942,11 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // device and change that if necessary. AudioStreamBasicDescription description; dataSize = sizeof( AudioStreamBasicDescription ); - if ( stream_.deviceInterleaved[mode] ) nStreams = 1; - else nStreams = channels; bool updateFormat; - for ( unsigned int i=0; iflags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; + stream_.deviceInterleaved[mode] = true; + if ( monoMode == true ) stream_.deviceInterleaved[mode] = false; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; @@ -1078,8 +1090,12 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; - if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && - stream_.nUserChannels[mode] > 1 ) + if ( streamCount == 1 ) { + if ( stream_.nUserChannels[mode] > 1 && + stream_.userInterleaved != stream_.deviceInterleaved[mode] ) + stream_.doConvertBuffer[mode] = true; + } + else if ( monoMode && stream_.userInterleaved ) stream_.doConvertBuffer[mode] = true; // Allocate our CoreHandle structure for the stream. @@ -1101,11 +1117,13 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } else handle = (CoreHandle *) stream_.apiHandle; - handle->iStream[mode] = iStream; + handle->iStream[mode] = firstStream; + handle->nStreams[mode] = streamCount; handle->id[mode] = id; // Allocate necessary internal buffers. - unsigned long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); + unsigned long bufferBytes; + bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory."; @@ -1113,9 +1131,9 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } // If possible, we will make use of the CoreAudio stream buffers as - // "device buffers". However, we can't do this if the device - // buffers are non-interleaved ("mono" mode). - if ( !stream_.deviceInterleaved[mode] && stream_.doConvertBuffer[mode] ) { + // "device buffers". However, we can't do this if using multiple + // streams. + if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); @@ -1134,13 +1152,6 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory."; goto error; } - - // Save a pointer to our own device buffer in the CoreHandle - // structure because we may need to use the stream_.deviceBuffer - // variable to point to the CoreAudio buffer before buffer - // conversion (if we have a duplex stream with two different - // conversion schemes). - handle->deviceBuffer = stream_.deviceBuffer; } } @@ -1149,30 +1160,22 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.state = STREAM_STOPPED; stream_.callbackInfo.object = (void *) this; - // Setup the buffer conversion information structure. We override - // the channel offset value and perform our own setting for that - // here. + // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { - setConvertInfo( mode, 0 ); - - // Add channel offset for interleaved channels. - if ( firstChannel > 0 && stream_.deviceInterleaved[mode] ) { - if ( mode == OUTPUT ) { - for ( int k=0; k 1 ) setConvertInfo( mode, 0 ); + else setConvertInfo( mode, channelOffset ); } if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device ) // Only one callback procedure per device. stream_.mode = DUPLEX; else { +#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) + result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] ); +#else + // deprecated in favor of AudioDeviceCreateIOProcID() result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo ); +#endif if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ")."; errorText_ = errorStream_.str(); @@ -1225,13 +1228,23 @@ void RtApiCore :: closeStream( void ) if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[0], callbackHandler ); +#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) + AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] ); +#else + // deprecated in favor of AudioDeviceDestroyIOProcID() AudioDeviceRemoveIOProc( handle->id[0], callbackHandler ); +#endif } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[1], callbackHandler ); +#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) + AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] ); +#else + // deprecated in favor of AudioDeviceDestroyIOProcID() AudioDeviceRemoveIOProc( handle->id[1], callbackHandler ); +#endif } for ( int i=0; i<2; i++ ) { @@ -1241,8 +1254,8 @@ void RtApiCore :: closeStream( void ) } } - if ( handle->deviceBuffer ) { - free( handle->deviceBuffer ); + if ( stream_.deviceBuffer ) { + free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } @@ -1311,6 +1324,11 @@ void RtApiCore :: stopStream( void ) MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + OSStatus result = noErr; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { @@ -1338,10 +1356,11 @@ void RtApiCore :: stopStream( void ) } } + stream_.state = STREAM_STOPPED; + unlock: MUTEX_UNLOCK( &stream_.mutex ); - stream_.state = STREAM_STOPPED; if ( result == noErr ) return; error( RtError::SYSTEM_ERROR ); } @@ -1386,6 +1405,12 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, MUTEX_LOCK( &stream_.mutex ); + // The state might change while waiting on a mutex. + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return SUCCESS; + } + AudioDeviceID outputDevice = handle->id[0]; // Invoke user callback to get fresh output data UNLESS we are @@ -1418,48 +1443,96 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, if ( handle->drainCounter > 1 ) { // write zeros to the output stream - if ( stream_.deviceInterleaved[0] ) { + if ( handle->nStreams[0] == 1 ) { memset( outBufferList->mBuffers[handle->iStream[0]].mData, 0, outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); } - else { - for ( unsigned int i=0; inStreams[0]; i++ ) { memset( outBufferList->mBuffers[handle->iStream[0]+i].mData, 0, outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize ); } } } - else if ( stream_.doConvertBuffer[0] ) { - - if ( stream_.deviceInterleaved[0] ) - stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->iStream[0]].mData; - else - stream_.deviceBuffer = handle->deviceBuffer; - - convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); - - if ( !stream_.deviceInterleaved[0] ) { - UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; - for ( unsigned int i=0; imBuffers[handle->iStream[0]+i].mData, - &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); - } + else if ( handle->nStreams[0] == 1 ) { + if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer + convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData, + stream_.userBuffer[0], stream_.convertInfo[0] ); } - - } - else { - if ( stream_.deviceInterleaved[0] ) { + else { // copy from user buffer memcpy( outBufferList->mBuffers[handle->iStream[0]].mData, stream_.userBuffer[0], outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); } - else { + } + else { // fill multiple streams + Float32 *inBuffer = (Float32 *) stream_.userBuffer[0]; + if ( stream_.doConvertBuffer[0] ) { + convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); + inBuffer = (Float32 *) stream_.deviceBuffer; + } + + if ( stream_.deviceInterleaved[0] == false ) { // mono mode UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; - for ( unsigned int i=0; imBuffers[handle->iStream[0]+i].mData, - &stream_.userBuffer[0][i*bufferBytes], bufferBytes ); + (void *)&inBuffer[i*stream_.bufferSize], bufferBytes ); + } + } + else { // fill multiple multi-channel streams with interleaved data + UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset; + Float32 *out, *in; + + bool inInterleaved = ( stream_.userInterleaved ) ? true : false; + UInt32 inChannels = stream_.nUserChannels[0]; + if ( stream_.doConvertBuffer[0] ) { + inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode + inChannels = stream_.nDeviceChannels[0]; + } + + if ( inInterleaved ) inOffset = 1; + else inOffset = stream_.bufferSize; + + channelsLeft = inChannels; + for ( unsigned int i=0; inStreams[0]; i++ ) { + in = inBuffer; + out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData; + streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; + + outJump = 0; + // Account for possible channel offset in first stream + if ( i == 0 && stream_.channelOffset[0] > 0 ) { + streamChannels -= stream_.channelOffset[0]; + outJump = stream_.channelOffset[0]; + out += outJump; + } + + // Account for possible unfilled channels at end of the last stream + if ( streamChannels > channelsLeft ) { + outJump = streamChannels - channelsLeft; + streamChannels = channelsLeft; + } + + // Determine input buffer offsets and skips + if ( inInterleaved ) { + inJump = inChannels; + in += inChannels - channelsLeft; + } + else { + inJump = 1; + in += (inChannels - channelsLeft) * inOffset; + } + + for ( unsigned int i=0; iid[1]; + AudioDeviceID inputDevice; + inputDevice = handle->id[1]; if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) { - if ( stream_.doConvertBuffer[1] ) { + if ( handle->nStreams[1] == 1 ) { + if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer + convertBuffer( stream_.userBuffer[1], + (char *) inBufferList->mBuffers[handle->iStream[1]].mData, + stream_.convertInfo[1] ); + } + else { // copy to user buffer + memcpy( stream_.userBuffer[1], + inBufferList->mBuffers[handle->iStream[1]].mData, + inBufferList->mBuffers[handle->iStream[1]].mDataByteSize ); + } + } + else { // read from multiple streams + Float32 *outBuffer = (Float32 *) stream_.userBuffer[1]; + if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer; - if ( stream_.deviceInterleaved[1] ) - stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->iStream[1]].mData; - else { - stream_.deviceBuffer = (char *) handle->deviceBuffer; + if ( stream_.deviceInterleaved[1] == false ) { // mono mode UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; - for ( unsigned int i=0; imBuffers[handle->iStream[1]+i].mData, bufferBytes ); } } + else { // read from multiple multi-channel streams + UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset; + Float32 *out, *in; - convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); + bool outInterleaved = ( stream_.userInterleaved ) ? true : false; + UInt32 outChannels = stream_.nUserChannels[1]; + if ( stream_.doConvertBuffer[1] ) { + outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode + outChannels = stream_.nDeviceChannels[1]; + } - } - else { - memcpy( stream_.userBuffer[1], - inBufferList->mBuffers[handle->iStream[1]].mData, - inBufferList->mBuffers[handle->iStream[1]].mDataByteSize ); + if ( outInterleaved ) outOffset = 1; + else outOffset = stream_.bufferSize; + + channelsLeft = outChannels; + for ( unsigned int i=0; inStreams[1]; i++ ) { + out = outBuffer; + in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData; + streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; + + inJump = 0; + // Account for possible channel offset in first stream + if ( i == 0 && stream_.channelOffset[1] > 0 ) { + streamChannels -= stream_.channelOffset[1]; + inJump = stream_.channelOffset[1]; + in += inJump; + } + + // Account for possible unread channels at end of the last stream + if ( streamChannels > channelsLeft ) { + inJump = streamChannels - channelsLeft; + streamChannels = channelsLeft; + } + + // Determine output buffer offsets and skips + if ( outInterleaved ) { + outJump = outChannels; + out += outChannels - channelsLeft; + } + else { + outJump = 1; + out += (outChannels - channelsLeft) * outOffset; + } + + for ( unsigned int i=0; istreamName.empty() ) - client = jack_client_new( options->streamName.c_str() ); + client = jack_client_open( options->streamName.c_str(), jackoptions, status ); else - client = jack_client_new( "RtApiJack" ); + client = jack_client_open( "RtApiJack", jackoptions, status ); if ( client == 0 ) { errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!"; error( RtError::WARNING ); @@ -1792,7 +1941,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne ports = jack_get_ports( client, NULL, NULL, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). - unsigned int iColon = 0; + size_t iColon = 0; do { port = (char *) ports[ nPorts ]; iColon = port.find(":"); @@ -1816,8 +1965,8 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // Count the available ports containing the client name as device // channels. Jack "input ports" equal RtAudio output channels. unsigned int nChannels = 0; - unsigned long flag = JackPortIsOutput; - if ( mode == INPUT ) flag = JackPortIsInput; + unsigned long flag = JackPortIsInput; + if ( mode == INPUT ) flag = JackPortIsOutput; ports = jack_get_ports( client, deviceName.c_str(), NULL, flag ); if ( ports ) { while ( ports[ nChannels ] ) nChannels++; @@ -2131,6 +2280,11 @@ void RtApiJack :: stopStream( void ) MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { @@ -2161,6 +2315,21 @@ void RtApiJack :: abortStream( void ) stopStream(); } +// This function will be called by a spawned thread when the user +// callback function signals that the stream should be stopped or +// aborted. It is necessary to handle it this way because the +// callbackEvent() function must return before the jack_deactivate() +// function will return. +extern "C" void *jackStopStream( void *ptr ) +{ + CallbackInfo *info = (CallbackInfo *) ptr; + RtApiJack *object = (RtApiJack *) info->object; + + object->stopStream(); + + pthread_exit( NULL ); +} + bool RtApiJack :: callbackEvent( unsigned long nframes ) { if ( stream_.state == STREAM_STOPPED ) return SUCCESS; @@ -2180,15 +2349,23 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { - if ( handle->internalDrain == false ) - pthread_cond_signal( &handle->condition ); + if ( handle->internalDrain == true ) { + ThreadHandle id; + pthread_create( &id, NULL, jackStopStream, info ); + } else - stopStream(); + pthread_cond_signal( &handle->condition ); return SUCCESS; } MUTEX_LOCK( &stream_.mutex ); + // The state might change while waiting on a mutex. + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return SUCCESS; + } + // Invoke user callback first, to get fresh output data. if ( handle->drainCounter == 0 ) { RtAudioCallback callback = (RtAudioCallback) info->callback; @@ -2206,7 +2383,8 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) stream_.bufferSize, streamTime, status, info->userData ); if ( handle->drainCounter == 2 ) { MUTEX_UNLOCK( &stream_.mutex ); - abortStream(); + ThreadHandle id; + pthread_create( &id, NULL, jackStopStream, info ); return SUCCESS; } else if ( handle->drainCounter == 1 ) @@ -2270,7 +2448,7 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) RtApi::tickStreamTime(); return SUCCESS; } -//******************** End of __UNIX_JACK__ *********************// + //******************** End of __UNIX_JACK__ *********************// #endif #if defined(__WINDOWS_ASIO__) // ASIO API on Windows @@ -2625,11 +2803,28 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; else if ( granularity == -1 ) { // Make sure bufferSize is a power of two. - double power = std::log10( (double) *bufferSize ) / log10( 2.0 ); - *bufferSize = (int) pow( 2.0, floor(power+0.5) ); + int log2_of_min_size = 0; + int log2_of_max_size = 0; + + for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) { + if ( minSize & ((long)1 << i) ) log2_of_min_size = i; + if ( maxSize & ((long)1 << i) ) log2_of_max_size = i; + } + + long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) ); + int min_delta_num = log2_of_min_size; + + for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) { + long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) ); + if (current_delta < min_delta) { + min_delta = current_delta; + min_delta_num = i; + } + } + + *bufferSize = ( (unsigned int)1 << min_delta_num ); if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; - else *bufferSize = preferSize; } else if ( granularity != 0 ) { // Set to an even multiple of granularity, rounding up. @@ -2897,6 +3092,11 @@ void RtApiAsio :: stopStream() MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { @@ -2932,10 +3132,10 @@ void RtApiAsio :: abortStream() // The following lines were commented-out because some behavior was // noted where the device buffers need to be zeroed to avoid - // continuing sound, even when the device buffers are completed + // continuing sound, even when the device buffers are completely // disposed. So now, calling abort is the same as calling stop. - //AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - //handle->drainCounter = 1; + // AsioHandle *handle = (AsioHandle *) stream_.apiHandle; + // handle->drainCounter = 1; stopStream(); } @@ -3188,15 +3388,15 @@ static const char* getAsioErrorString( ASIOError result ) }; static Messages m[] = - { - { ASE_NotPresent, "Hardware input or output is not present or available." }, - { ASE_HWMalfunction, "Hardware is malfunctioning." }, - { ASE_InvalidParameter, "Invalid input parameter." }, - { ASE_InvalidMode, "Invalid mode." }, - { ASE_SPNotAdvancing, "Sample position not advancing." }, - { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." }, - { ASE_NoMemory, "Not enough memory to complete the request." } - }; + { + { ASE_NotPresent, "Hardware input or output is not present or available." }, + { ASE_HWMalfunction, "Hardware is malfunctioning." }, + { ASE_InvalidParameter, "Invalid input parameter." }, + { ASE_InvalidMode, "Invalid mode." }, + { ASE_SPNotAdvancing, "Sample position not advancing." }, + { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." }, + { ASE_NoMemory, "Not enough memory to complete the request." } + }; for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i ) if ( m[i].value == result ) return m[i].message; @@ -3220,7 +3420,7 @@ static const char* getAsioErrorString( ASIOError result ) #include #if defined(__MINGW32__) -// missing from latest mingw winapi + // missing from latest mingw winapi #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */ #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */ @@ -3235,7 +3435,7 @@ static const char* getAsioErrorString( ASIOError result ) static inline DWORD dsPointerDifference( DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize ) { - if (laterPointer > earlierPointer) + if ( laterPointer > earlierPointer ) return laterPointer - earlierPointer; else return laterPointer - earlierPointer + bufferSize; @@ -3683,8 +3883,10 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes ) bufferBytes *= 2; - // Set cooperative level to DSSCL_EXCLUSIVE - result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE ); + // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes. + // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE ); + // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes. + result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsinfo.name << ")!"; @@ -3725,6 +3927,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSBUFFERDESC ); bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | + DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCHARDWARE ); // Force hardware mixing bufferDescription.dwBufferBytes = bufferBytes; @@ -3735,6 +3938,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | + DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE ); // Force software mixing result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); @@ -3846,6 +4050,11 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned // Update wave format structure and buffer information. waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; + dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; + + // If the user wants an even bigger buffer, increase the device buffer size accordingly. + while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes ) + bufferBytes *= 2; // Setup the secondary DS buffer description. dsBufferSize = bufferBytes; @@ -3867,6 +4076,20 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned return FAILURE; } + // Get the buffer size ... might be different from what we specified. + DSCBCAPS dscbcaps; + dscbcaps.dwSize = sizeof( DSCBCAPS ); + result = buffer->GetCaps( &dscbcaps ); + if ( FAILED( result ) ) { + input->Release(); + buffer->Release(); + errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsinfo.name << ")!"; + errorText_ = errorStream_.str(); + return FAILURE; + } + + bufferBytes = dscbcaps.dwBufferBytes; + // Lock the capture buffer LPVOID audioPtr; DWORD dataLen; @@ -3995,6 +4218,8 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned goto error; } + // Boost DS thread priority + SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST ); return SUCCESS; error: @@ -4105,9 +4330,9 @@ void RtApiDs :: startStream() timeBeginPeriod( 1 ); /* - memset( &statistics, 0, sizeof( statistics ) ); - statistics.sampleRate = stream_.sampleRate; - statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0]; + memset( &statistics, 0, sizeof( statistics ) ); + statistics.sampleRate = stream_.sampleRate; + statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0]; */ buffersRolling = false; @@ -4164,6 +4389,11 @@ void RtApiDs :: stopStream() MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + HRESULT result = 0; LPVOID audioPtr; DWORD dataLen; @@ -4181,7 +4411,7 @@ void RtApiDs :: stopStream() LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = buffer->Stop(); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping output buffer!"; + errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!"; errorText_ = errorStream_.str(); goto unlock; } @@ -4190,7 +4420,7 @@ void RtApiDs :: stopStream() // we won't have old data playing. result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking output buffer!"; + errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!"; errorText_ = errorStream_.str(); goto unlock; } @@ -4201,7 +4431,7 @@ void RtApiDs :: stopStream() // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking output buffer!"; + errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!"; errorText_ = errorStream_.str(); goto unlock; } @@ -4217,7 +4447,7 @@ void RtApiDs :: stopStream() result = buffer->Stop(); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping input buffer!"; + errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!"; errorText_ = errorStream_.str(); goto unlock; } @@ -4226,7 +4456,7 @@ void RtApiDs :: stopStream() // we won't have old data playing. result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking input buffer!"; + errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!"; errorText_ = errorStream_.str(); goto unlock; } @@ -4237,7 +4467,7 @@ void RtApiDs :: stopStream() // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking input buffer!"; + errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!"; errorText_ = errorStream_.str(); goto unlock; } @@ -4250,6 +4480,7 @@ void RtApiDs :: stopStream() timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows. stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); + if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR ); } @@ -4295,6 +4526,12 @@ void RtApiDs :: callbackEvent() MUTEX_LOCK( &stream_.mutex ); + // The state might change while waiting on a mutex. + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + // Invoke user callback to get fresh output data UNLESS we are // draining stream. if ( handle->drainCounter == 0 ) { @@ -4339,7 +4576,7 @@ void RtApiDs :: callbackEvent() long bufferBytes; if ( stream_.mode == DUPLEX && !buffersRolling ) { - assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); + //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); // It takes a while for the devices to get rolling. As a result, // there's no guarantee that the capture and write device pointers @@ -4390,7 +4627,7 @@ void RtApiDs :: callbackEvent() Sleep( 1 ); } - assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); + //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); buffersRolling = true; handle->bufferPointer[0] = ( safeWritePos + handle->dsPointerLeadTime[0] ); @@ -4650,17 +4887,17 @@ void RtApiDs :: callbackEvent() } #ifdef GENERATE_DEBUG_LOG if ( currentDebugLogEntry < debugLog.size() ) - { - TTickRecord &r = debugLog[currentDebugLogEntry++]; - r.currentReadPointer = currentReadPos; - r.safeReadPointer = safeReadPos; - r.currentWritePointer = currentWritePos; - r.safeWritePointer = safeWritePos; - r.readTime = readTime; - r.writeTime = writeTime; - r.nextReadPointer = handles[1].bufferPointer; - r.nextWritePointer = handles[0].bufferPointer; - } + { + TTickRecord &r = debugLog[currentDebugLogEntry++]; + r.currentReadPointer = currentReadPos; + r.safeReadPointer = safeReadPos; + r.currentWritePointer = currentWritePos; + r.safeWritePointer = safeWritePos; + r.readTime = readTime; + r.writeTime = writeTime; + r.nextReadPointer = handles[1].bufferPointer; + r.nextWritePointer = handles[0].bufferPointer; + } #endif unlock: @@ -4756,7 +4993,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, static char* getErrorString( int code ) { - switch ( code ) { + switch ( code ) { case DSERR_ALLOCATED: return "Already allocated"; @@ -4805,7 +5042,7 @@ static char* getErrorString( int code ) default: return "DirectSound unknown error"; - } + } } //******************** End of __WINDOWS_DS__ *********************// #endif @@ -4816,12 +5053,13 @@ static char* getErrorString( int code ) #include #include -// A structure to hold various information related to the ALSA API -// implementation. + // A structure to hold various information related to the ALSA API + // implementation. struct AlsaHandle { snd_pcm_t *handles[2]; bool synchronized; bool xrun[2]; + pthread_cond_t runnable; AlsaHandle() :synchronized(false) { xrun[0] = false; xrun[1] = false; } @@ -4857,17 +5095,17 @@ unsigned int RtApiAlsa :: getDeviceCount( void ) errorText_ = errorStream_.str(); error( RtError::WARNING ); goto nextcard; - } - subdevice = -1; - while( 1 ) { + } + subdevice = -1; + while( 1 ) { result = snd_ctl_pcm_next_device( handle, &subdevice ); - if ( result < 0 ) { + if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtError::WARNING ); break; } - if ( subdevice < 0 ) + if ( subdevice < 0 ) break; nDevices++; } @@ -4900,17 +5138,17 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) errorText_ = errorStream_.str(); error( RtError::WARNING ); goto nextcard; - } - subdevice = -1; - while( 1 ) { + } + subdevice = -1; + while( 1 ) { result = snd_ctl_pcm_next_device( chandle, &subdevice ); - if ( result < 0 ) { + if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtError::WARNING ); break; } - if ( subdevice < 0 ) break; + if ( subdevice < 0 ) break; if ( nDevices == device ) { sprintf( name, "hw:%d,%d", card, subdevice ); goto foundDevice; @@ -4934,10 +5172,22 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) foundDevice: + // If a stream is already open, we cannot probe the stream devices. + // Thus, use the saved results. + if ( stream_.state != STREAM_CLOSED && + ( stream_.device[0] == device || stream_.device[1] == device ) ) { + if ( device >= devices_.size() ) { + errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened."; + error( RtError::WARNING ); + return info; + } + return devices_[ device ]; + } + int openMode = SND_PCM_ASYNC; snd_pcm_stream_t stream; - snd_pcm_info_t *pcminfo; - snd_pcm_info_alloca( &pcminfo ); + snd_pcm_info_t *pcminfo; + snd_pcm_info_alloca( &pcminfo ); snd_pcm_t *phandle; snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca( ¶ms ); @@ -5128,6 +5378,16 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) return info; } +void RtApiAlsa :: saveDeviceInfo( void ) +{ + devices_.clear(); + + unsigned int nDevices = getDeviceCount(); + devices_.resize( nDevices ); + for ( unsigned int i=0; isaveDeviceInfo(); + snd_pcm_stream_t stream; if ( mode == OUTPUT ) stream = SND_PCM_STREAM_PLAYBACK; @@ -5379,19 +5646,10 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } // Set the buffer number, which in ALSA is referred to as the "period". - int dir; + int totalSize, dir; unsigned int periods = 0; if ( options ) periods = options->numberOfBuffers; - if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2; - // Even though the hardware might allow 1 buffer, it won't work reliably. - if ( periods < 2 ) periods = 2; - result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } + totalSize = *bufferSize * periods; // Set the buffer (or period) size. snd_pcm_uframes_t periodSize = *bufferSize; @@ -5404,6 +5662,18 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne } *bufferSize = periodSize; + if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2; + else periods = totalSize / *bufferSize; + // Even though the hardware might allow 1 buffer, it won't work reliably. + if ( periods < 2 ) periods = 2; + result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir ); + if ( result < 0 ) { + snd_pcm_close( phandle ); + errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << "."; + errorText_ = errorStream_.str(); + return FAILURE; + } + // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { @@ -5433,9 +5703,19 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne snd_pcm_sw_params_alloca( &sw_params ); snd_pcm_sw_params_current( phandle, sw_params ); snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize ); - snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, 0x7fffffff ); + snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX ); snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 ); - snd_pcm_sw_params_set_silence_size( phandle, sw_params, INT_MAX ); + + // The following two settings were suggested by Theo Veenker + //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize ); + //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 ); + + // here are two options for a fix + //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX ); + snd_pcm_uframes_t val; + snd_pcm_sw_params_get_boundary( sw_params, &val ); + snd_pcm_sw_params_set_silence_size( phandle, sw_params, val ); + result = snd_pcm_sw_params( phandle, sw_params ); if ( result < 0 ) { snd_pcm_close( phandle ); @@ -5469,6 +5749,12 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory."; goto error; } + + if ( pthread_cond_init( &apiInfo->runnable, NULL ) ) { + errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable."; + goto error; + } + stream_.apiHandle = (void *) apiInfo; apiInfo->handles[0] = 0; apiInfo->handles[1] = 0; @@ -5537,13 +5823,28 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.callbackInfo.object = (void *) this; // Set the thread attributes for joinable and realtime scheduling - // priority. The higher priority will only take affect if the - // program is run as root or suid. + // priority (optional). The higher priority will only take affect + // if the program is run as root or suid. Note, under Linux + // processes with CAP_SYS_NICE privilege, a user can change + // scheduling policy and priority (thus need not be root). See + // POSIX "capabilities". pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) - pthread_attr_setschedpolicy( &attr, SCHED_RR ); + if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { + struct sched_param param; + int priority = options->priority; + int min = sched_get_priority_min( SCHED_RR ); + int max = sched_get_priority_max( SCHED_RR ); + if ( priority < min ) priority = min; + else if ( priority > max ) priority = max; + param.sched_priority = priority; + pthread_attr_setschedparam( &attr, ¶m ); + pthread_attr_setschedpolicy( &attr, SCHED_RR ); + } + else + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif @@ -5562,6 +5863,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne error: if ( apiInfo ) { + pthread_cond_destroy( &apiInfo->runnable ); if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); delete apiInfo; @@ -5591,10 +5893,14 @@ void RtApiAlsa :: closeStream() return; } + AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; stream_.callbackInfo.isRunning = false; + MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) + pthread_cond_signal( &apiInfo->runnable ); + MUTEX_UNLOCK( &stream_.mutex ); pthread_join( stream_.callbackInfo.thread, NULL ); - AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; if ( stream_.state == STREAM_RUNNING ) { stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) @@ -5604,6 +5910,7 @@ void RtApiAlsa :: closeStream() } if ( apiInfo ) { + pthread_cond_destroy( &apiInfo->runnable ); if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); delete apiInfo; @@ -5672,6 +5979,8 @@ void RtApiAlsa :: startStream() unlock: MUTEX_UNLOCK( &stream_.mutex ); + pthread_cond_signal( &apiInfo->runnable ); + if ( result >= 0 ) return; error( RtError::SYSTEM_ERROR ); } @@ -5685,11 +5994,13 @@ void RtApiAlsa :: stopStream() return; } - // Change the state before the lock to improve shutdown response - // when using a callback. - stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + int result = 0; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; @@ -5715,6 +6026,7 @@ void RtApiAlsa :: stopStream() } unlock: + stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; @@ -5730,11 +6042,13 @@ void RtApiAlsa :: abortStream() return; } - // Change the state before the lock to improve shutdown response - // when using a callback. - stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + int result = 0; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; @@ -5757,18 +6071,24 @@ void RtApiAlsa :: abortStream() } unlock: + stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); - stream_.state = STREAM_STOPPED; if ( result >= 0 ) return; error( RtError::SYSTEM_ERROR ); } void RtApiAlsa :: callbackEvent() { + AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; if ( stream_.state == STREAM_STOPPED ) { - if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds - return; + MUTEX_LOCK( &stream_.mutex ); + pthread_cond_wait( &apiInfo->runnable, &stream_.mutex ); + if ( stream_.state != STREAM_RUNNING ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { @@ -5778,7 +6098,6 @@ void RtApiAlsa :: callbackEvent() } int doStopStream = 0; - AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; @@ -5791,7 +6110,12 @@ void RtApiAlsa :: callbackEvent() apiInfo->xrun[1] = false; } doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], - stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); + stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); + + if ( doStopStream == 2 ) { + abortStream(); + return; + } MUTEX_LOCK( &stream_.mutex ); @@ -5832,7 +6156,7 @@ void RtApiAlsa :: callbackEvent() } if ( result < (int) stream_.bufferSize ) { - // Either an error or underrun occured. + // Either an error or overrun occured. if ( result == -EPIPE ) { snd_pcm_state_t state = snd_pcm_state( handle[1] ); if ( state == SND_PCM_STATE_XRUN ) { @@ -5853,7 +6177,7 @@ void RtApiAlsa :: callbackEvent() errorText_ = errorStream_.str(); } error( RtError::WARNING ); - goto unlock; + goto tryOutput; } // Do byte swapping if necessary. @@ -5869,6 +6193,8 @@ void RtApiAlsa :: callbackEvent() if ( result == 0 && frames > 0 ) stream_.latency[1] = frames; } + tryOutput: + if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { // Setup parameters and do buffer conversion if necessary. @@ -5934,7 +6260,6 @@ void RtApiAlsa :: callbackEvent() RtApi::tickStreamTime(); if ( doStopStream == 1 ) this->stopStream(); - else if ( doStopStream == 2 ) this->abortStream(); } extern "C" void *alsaCallbackHandler( void *ptr ) @@ -5943,15 +6268,6 @@ extern "C" void *alsaCallbackHandler( void *ptr ) RtApiAlsa *object = (RtApiAlsa *) info->object; bool *isRunning = &info->isRunning; -#ifdef SCHED_RR - // Set a higher scheduler priority (P.J. Leonard) - struct sched_param param; - int min = sched_get_priority_min( SCHED_RR ); - int max = sched_get_priority_max( SCHED_RR ); - param.sched_priority = min + ( max - min ) / 2; // Is this the best number? - sched_setscheduler( 0, SCHED_RR, ¶m ); -#endif - while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent(); @@ -5982,6 +6298,7 @@ struct OssHandle { int id[2]; // device ids bool xrun[2]; bool triggered; + pthread_cond_t runnable; OssHandle() :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } @@ -6014,6 +6331,7 @@ unsigned int RtApiOss :: getDeviceCount( void ) return 0; } + close( mixerfd ); return sysinfo.numaudios; } @@ -6126,7 +6444,7 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, + unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { @@ -6223,14 +6541,14 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned // For duplex operation, specifically set this mode (this doesn't seem to work). /* - if ( flags | O_RDWR ) { + if ( flags | O_RDWR ) { result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL ); if ( result == -1) { - errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; + errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ")."; + errorText_ = errorStream_.str(); + return FAILURE; + } } - } */ // Check the device channel support. @@ -6438,6 +6756,11 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned goto error; } + if ( pthread_cond_init( &handle->runnable, NULL ) ) { + errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable."; + goto error; + } + stream_.apiHandle = (void *) handle; } else { @@ -6501,7 +6824,19 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) - pthread_attr_setschedpolicy( &attr, SCHED_RR ); + if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { + struct sched_param param; + int priority = options->priority; + int min = sched_get_priority_min( SCHED_RR ); + int max = sched_get_priority_max( SCHED_RR ); + if ( priority < min ) priority = min; + else if ( priority > max ) priority = max; + param.sched_priority = priority; + pthread_attr_setschedparam( &attr, ¶m ); + pthread_attr_setschedpolicy( &attr, SCHED_RR ); + } + else + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif @@ -6520,6 +6855,7 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned error: if ( handle ) { + pthread_cond_destroy( &handle->runnable ); if ( handle->id[0] ) close( handle->id[0] ); if ( handle->id[1] ) close( handle->id[1] ); delete handle; @@ -6549,10 +6885,14 @@ void RtApiOss :: closeStream() return; } + OssHandle *handle = (OssHandle *) stream_.apiHandle; stream_.callbackInfo.isRunning = false; + MUTEX_LOCK( &stream_.mutex ); + if ( stream_.state == STREAM_STOPPED ) + pthread_cond_signal( &handle->runnable ); + MUTEX_UNLOCK( &stream_.mutex ); pthread_join( stream_.callbackInfo.thread, NULL ); - OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.state == STREAM_RUNNING ) { if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); @@ -6562,6 +6902,7 @@ void RtApiOss :: closeStream() } if ( handle ) { + pthread_cond_destroy( &handle->runnable ); if ( handle->id[0] ) close( handle->id[0] ); if ( handle->id[1] ) close( handle->id[1] ); delete handle; @@ -6601,6 +6942,9 @@ void RtApiOss :: startStream() // when fed samples. MUTEX_UNLOCK( &stream_.mutex ); + + OssHandle *handle = (OssHandle *) stream_.apiHandle; + pthread_cond_signal( &handle->runnable ); } void RtApiOss :: stopStream() @@ -6612,11 +6956,14 @@ void RtApiOss :: stopStream() return; } - // Change the state before the lock to improve shutdown response - // when using a callback. - stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); + // The state might change while waiting on a mutex. + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + int result = 0; OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { @@ -6665,9 +7012,9 @@ void RtApiOss :: stopStream() } unlock: + stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); - stream_.state = STREAM_STOPPED; if ( result != -1 ) return; error( RtError::SYSTEM_ERROR ); } @@ -6681,11 +7028,14 @@ void RtApiOss :: abortStream() return; } - // Change the state before the lock to improve shutdown response - // when using a callback. - stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); + // The state might change while waiting on a mutex. + if ( stream_.state == STREAM_STOPPED ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + int result = 0; OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { @@ -6708,18 +7058,24 @@ void RtApiOss :: abortStream() } unlock: + stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); - stream_.state = STREAM_STOPPED; if ( result != -1 ) return; error( RtError::SYSTEM_ERROR ); } void RtApiOss :: callbackEvent() { + OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.state == STREAM_STOPPED ) { - if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds - return; + MUTEX_LOCK( &stream_.mutex ); + pthread_cond_wait( &handle->runnable, &stream_.mutex ); + if ( stream_.state != STREAM_RUNNING ) { + MUTEX_UNLOCK( &stream_.mutex ); + return; + } + MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { @@ -6733,7 +7089,6 @@ void RtApiOss :: callbackEvent() RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; - OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; @@ -6744,6 +7099,10 @@ void RtApiOss :: callbackEvent() } doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); + if ( doStopStream == 2 ) { + this->abortStream(); + return; + } MUTEX_LOCK( &stream_.mutex ); @@ -6792,7 +7151,7 @@ void RtApiOss :: callbackEvent() handle->xrun[0] = true; errorText_ = "RtApiOss::callbackEvent: audio write error."; error( RtError::WARNING ); - goto unlock; + // Continue on to input section. } } @@ -6836,7 +7195,6 @@ void RtApiOss :: callbackEvent() RtApi::tickStreamTime(); if ( doStopStream == 1 ) this->stopStream(); - else if ( doStopStream == 2 ) this->abortStream(); } extern "C" void *ossCallbackHandler( void *ptr ) @@ -6845,13 +7203,6 @@ extern "C" void *ossCallbackHandler( void *ptr ) RtApiOss *object = (RtApiOss *) info->object; bool *isRunning = &info->isRunning; -#ifdef SCHED_RR - // Set a higher scheduler priority (P.J. Leonard) - struct sched_param param; - param.sched_priority = 39; // Is this the best number? - sched_setscheduler( 0, SCHED_RR, ¶m ); -#endif - while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent(); @@ -6874,11 +7225,11 @@ extern "C" void *ossCallbackHandler( void *ptr ) // message printing. void RtApi :: error( RtError::Type type ) { + errorStream_.str(""); // clear the ostringstream if ( type == RtError::WARNING && showWarnings_ == true ) std::cerr << '\n' << errorText_ << "\n\n"; else throw( RtError( errorText_, type ) ); - errorStream_.str(""); // clear the ostringstream } void RtApi :: verifyStream() @@ -6905,7 +7256,7 @@ void RtApi :: clearStreamInfo() stream_.callbackInfo.userData = 0; stream_.callbackInfo.isRunning = false; for ( int i=0; i<2; i++ ) { - stream_.device[i] = 0; + stream_.device[i] = 11111; stream_.doConvertBuffer[i] = false; stream_.deviceInterleaved[i] = true; stream_.doByteSwap[i] = false; @@ -7041,10 +7392,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info if (info.inFormat == RTAUDIO_SINT8) { signed char *in = (signed char *)inBuffer; - scale = 1.0 / 128.0; + scale = 1.0 / 127.5; for (unsigned int i=0; i>8) | (x<<8); } + //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); } + //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); } + void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ) { register char val; @@ -7480,8 +7843,8 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat *(ptr) = *(ptr+1); *(ptr+1) = val; - // Increment 4 bytes. - ptr += 4; + // Increment 3 more bytes. + ptr += 3; } } else if ( format == RTAUDIO_FLOAT64 ) { @@ -7509,18 +7872,18 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat *(ptr) = *(ptr+1); *(ptr+1) = val; - // Increment 8 bytes. - ptr += 8; + // Increment 5 more bytes. + ptr += 5; } } } -// Indentation settings for Vim and Emacs -// -// Local Variables: -// c-basic-offset: 2 -// indent-tabs-mode: nil -// End: -// -// vim: et sts=2 sw=2 + // Indentation settings for Vim and Emacs + // + // Local Variables: + // c-basic-offset: 2 + // indent-tabs-mode: nil + // End: + // + // vim: et sts=2 sw=2 diff --git a/src/RtMidi.cpp b/src/RtMidi.cpp index 837e9c6..8e05db9 100644 --- a/src/RtMidi.cpp +++ b/src/RtMidi.cpp @@ -8,7 +8,7 @@ RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2007 Gary P. Scavone + Copyright (c) 2003-2009 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -35,7 +35,7 @@ */ /**********************************************************************/ -// RtMidi: Version 1.0.7 +// RtMidi: Version 1.0.9 #include "RtMidi.h" #include @@ -69,9 +69,9 @@ void RtMidi :: error( RtError::Type type ) // Common RtMidiIn Definitions //*********************************************************************// -RtMidiIn :: RtMidiIn() : RtMidi() +RtMidiIn :: RtMidiIn( const std::string clientName ) : RtMidi() { - this->initialize(); + this->initialize( clientName ); } void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) @@ -144,9 +144,9 @@ double RtMidiIn :: getMessage( std::vector *message ) // Common RtMidiOut Definitions //*********************************************************************// -RtMidiOut :: RtMidiOut() : RtMidi() +RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi() { - this->initialize(); + this->initialize( clientName ); } @@ -155,7 +155,7 @@ RtMidiOut :: RtMidiOut() : RtMidi() //*********************************************************************// // API information found at: -// - http://developer. apple .com/audio/pdf/coreaudio.pdf +// - http://developer.apple.com/audio/pdf/coreaudio.pdf #if defined(__MACOSX_CORE__) @@ -187,20 +187,24 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef 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; + + bool& continueSysex = data->continueSysex; + RtMidiIn::MidiMessage& message = data->message; + const MIDIPacket *packet = &list->packet[0]; for ( unsigned int i=0; inumPackets; ++i ) { // 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. + // broken across multiple packets and PacketLists but are bundled + // alone within each packet (these packets do not contain other + // message types). If sysex messages are split across multiple + // MIDIPacketLists, they must be handled by multiple calls to this + // function. nBytes = packet->length; if ( nBytes == 0 ) continue; @@ -220,12 +224,13 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef iByte = 0; if ( continueSysex ) { // We have a continuing, segmented sysex message. - if ( !(data->ignoreFlags & 0x01) ) { + 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; + continueSysex = packet->data[nBytes-1] != 0xF7; + if ( !continueSysex ) { // If not a continuing sysex message, invoke the user callback function or queue the message. if ( data->usingCallback && message.bytes.size() > 0 ) { @@ -259,7 +264,7 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef iByte = nBytes; } else size = nBytes - iByte; - if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; + continueSysex = packet->data[nBytes-1] != 0xF7; } else if ( status < 0xF3 ) { if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { @@ -311,11 +316,11 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef } } -void RtMidiIn :: initialize( void ) +void RtMidiIn :: initialize( const std::string& clientName ) { // Set up our client. MIDIClientRef client; - OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client ); + OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); if ( result != noErr ) { errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object."; error( RtError::DRIVER_ERROR ); @@ -329,7 +334,7 @@ void RtMidiIn :: initialize( void ) inputData_.apiData = (void *) data; } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -352,7 +357,9 @@ void RtMidiIn :: openPort( unsigned int portNumber ) MIDIPortRef port; CoreMidiData *data = static_cast (apiData_); - OSStatus result = MIDIInputPortCreate( data->client, CFSTR("RtMidi MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port ); + OSStatus result = MIDIInputPortCreate( data->client, + CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + midiInputCallback, (void *)&inputData_, &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; @@ -427,6 +434,130 @@ unsigned int RtMidiIn :: getPortCount() return MIDIGetNumberOfSources(); } +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + + // Begin with the endpoint's name. + str = NULL; + MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + + MIDIEntityRef entity = NULL; + MIDIEndpointGetEntity( endpoint, &entity ); + if ( entity == NULL ) + // probably virtual + return result; + + if ( CFStringGetLength( result ) == 0 ) { + // endpoint name has zero length -- try the entity + str = NULL; + MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + } + // now consider the device's name + MIDIDeviceRef device = NULL; + MIDIEntityGetDevice( entity, &device ); + if ( device == NULL ) + return result; + + str = NULL; + MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); + if ( CFStringGetLength( result ) == 0 ) { + CFRelease( result ); + return str; + } + if ( str != NULL ) { + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { + CFRelease( result ); + return str; + } else { + if ( CFStringGetLength( str ) == 0 ) { + CFRelease( str ); + return result; + } + // does the entity name already start with the device name? + // (some drivers do this though they shouldn't) + // if so, do not prepend + if ( CFStringCompareWithOptions( result, /* endpoint name */ + str /* device name */, + CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { + // prepend the device name to the entity name + if ( CFStringGetLength( result ) > 0 ) + CFStringInsert( result, 0, CFSTR(" ") ); + CFStringInsert( result, 0, str ); + } + CFRelease( str ); + } + } + return result; +} + +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + OSStatus err; + int i; + + // Does the endpoint have connections? + CFDataRef connections = NULL; + int nConnected = 0; + bool anyStrings = false; + err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); + if ( connections != NULL ) { + // It has connections, follow them + // Concatenate the names of all connected devices + nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); + if ( nConnected ) { + const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); + for ( i=0; i (apiData_); - OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port ); + OSStatus result = MIDIOutputPortCreate( data->client, + CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; @@ -588,33 +725,59 @@ RtMidiOut :: ~RtMidiOut() void RtMidiOut :: sendMessage( std::vector *message ) { + // The CoreMidi documentation indicates a maximum PackList size of + // 64K, so we may need to break long sysex messages into pieces and + // send via separate lists. 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 ); - } + if ( nBytes == 0 ) { + errorString_ = "RtMidiOut::sendMessage: no data in message argument!"; + error( RtError::WARNING ); + return; } - // 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 ); + if ( nBytes > 3 && ( message->at(0) != 0xF0 ) ) { + errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?"; + error( RtError::WARNING ); + return; + } + + unsigned int packetBytes, bytesLeft = nBytes; + unsigned int messageIndex = 0; + MIDITimeStamp timeStamp = 0; + CoreMidiData *data = static_cast (apiData_); + + while ( bytesLeft > 0 ) { + + packetBytes = ( bytesLeft > 32736 ) ? 32736 : bytesLeft; + Byte buffer[packetBytes + 32]; // extra memory for other structure variables + MIDIPacketList *packetList = (MIDIPacketList *) buffer; + MIDIPacket *curPacket = MIDIPacketListInit( packetList ); + + curPacket = MIDIPacketListAdd( packetList, packetBytes+32, curPacket, timeStamp, packetBytes, (const Byte *) &message->at( messageIndex ) ); + if ( !curPacket ) { + errorString_ = "RtMidiOut::sendMessage: could not allocate packet list"; + error( RtError::DRIVER_ERROR ); + } + messageIndex += packetBytes; + bytesLeft -= packetBytes; + + // Send to any destinations that may have connected to us. + OSStatus result; + if ( data->endpoint ) { + result = MIDIReceived( data->endpoint, packetList ); + 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, packetList ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; + error( RtError::WARNING ); + } } } } @@ -637,6 +800,10 @@ void RtMidiOut :: sendMessage( std::vector *message ) // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer // time stamps and other assorted fixes!!! +// If you don't need timestamping for incoming MIDI events, define the +// preprocessor definition AVOID_TIMESTAMPING to save resources +// associated with the ALSA sequencer queues. + #include #include @@ -713,7 +880,9 @@ extern "C" void *alsaMidiHandler( void *ptr ) // 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(); + if ( !continueSysex ) + message.bytes.clear(); + switch ( ev->type ) { case SND_SEQ_EVENT_PORT_SUBSCRIBED: @@ -723,8 +892,14 @@ extern "C" void *alsaMidiHandler( void *ptr ) break; case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: +#if defined(__RTMIDI_DEBUG__) std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; - data->doInput = false; + std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" + << (int) ev->data.connect.sender.port + << ", dest = " << (int) ev->data.connect.dest.client << ":" + << (int) ev->data.connect.dest.port + << std::endl; +#endif break; case SND_SEQ_EVENT_QFRAME: // MIDI time code @@ -768,7 +943,7 @@ extern "C" void *alsaMidiHandler( void *ptr ) else message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); - continueSysex = ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() != 0xF7 ); + continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); if ( continueSysex ) break; @@ -794,7 +969,7 @@ extern "C" void *alsaMidiHandler( void *ptr ) snd_seq_free_event(ev); if ( message.bytes.size() == 0 ) continue; - if ( data->usingCallback ) { + if ( data->usingCallback && !continueSysex ) { RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; callback( message.timeStamp, &message.bytes, data->userData ); } @@ -813,10 +988,10 @@ extern "C" void *alsaMidiHandler( void *ptr ) return 0; } -void RtMidiIn :: initialize( void ) +void RtMidiIn :: initialize( const std::string& clientName ) { // Set up the ALSA sequencer client. - snd_seq_t *seq; + snd_seq_t *seq; int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); if ( result < 0 ) { errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; @@ -824,7 +999,7 @@ void RtMidiIn :: initialize( void ) } // Set client name. - snd_seq_set_client_name(seq, "RtMidi Input Client"); + snd_seq_set_client_name( seq, clientName.c_str() ); // Save our api-specific connection information. AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; @@ -834,6 +1009,7 @@ void RtMidiIn :: initialize( void ) inputData_.apiData = (void *) data; // Create the input queue +#ifndef AVOID_TIMESTAMPING data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); // Set arbitrary tempo (mm=100) and resolution (240) snd_seq_queue_tempo_t *qtempo; @@ -842,6 +1018,7 @@ void RtMidiIn :: initialize( void ) snd_seq_queue_tempo_set_ppq(qtempo, 240); snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); snd_seq_drain_output(data->seq); +#endif } // This function is used to count or get the pinfo structure for a given port number. @@ -860,7 +1037,10 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int 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; + unsigned int atyp = snd_seq_port_info_get_type( pinfo ); + if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue; + unsigned int caps = snd_seq_port_info_get_capability( pinfo ); + if ( ( caps & type ) != type ) continue; if ( count == portNumber ) return 1; count++; } @@ -871,7 +1051,7 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int return 0; } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -910,10 +1090,12 @@ void RtMidiIn :: openPort( unsigned int portNumber ) SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); - snd_seq_port_info_set_name(pinfo, "RtMidi Input"); +#endif + snd_seq_port_info_set_name(pinfo, portName.c_str() ); data->vport = snd_seq_create_port(data->seq, pinfo); if ( data->vport < 0 ) { @@ -935,8 +1117,10 @@ void RtMidiIn :: openPort( unsigned int portNumber ) if ( inputData_.doInput == false ) { // Start the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); @@ -971,9 +1155,11 @@ void RtMidiIn :: openVirtualPort( std::string portName ) SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); +#endif snd_seq_port_info_set_name(pinfo, portName.c_str()); data->vport = snd_seq_create_port(data->seq, pinfo); @@ -985,8 +1171,10 @@ void RtMidiIn :: openVirtualPort( std::string portName ) if ( inputData_.doInput == false ) { // Start the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); @@ -1013,8 +1201,10 @@ void RtMidiIn :: closePort( void ) snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); // Stop the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_stop_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif connected_ = false; } } @@ -1033,8 +1223,10 @@ RtMidiIn :: ~RtMidiIn() // Cleanup. if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); +#ifndef AVOID_TIMESTAMPING snd_seq_free_queue( data->seq, data->queue_id ); snd_seq_close( data->seq ); +#endif delete data; } @@ -1049,12 +1241,20 @@ unsigned int RtMidiIn :: getPortCount() std::string RtMidiIn :: getPortName( unsigned int portNumber ) { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_client_info_alloca( &cinfo ); + 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 ) ); + int cnum = snd_seq_port_info_get_client( pinfo ); + snd_seq_get_any_client_info( data->seq, cnum, cinfo ); + std::ostringstream os; + os << snd_seq_client_info_get_name( cinfo ); + os << ":"; + os << snd_seq_port_info_get_port( pinfo ); + std::string stringName = os.str(); return stringName; } @@ -1080,12 +1280,20 @@ unsigned int RtMidiOut :: getPortCount() std::string RtMidiOut :: getPortName( unsigned int portNumber ) { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_client_info_alloca( &cinfo ); + 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 ) ); + int cnum = snd_seq_port_info_get_client(pinfo); + snd_seq_get_any_client_info( data->seq, cnum, cinfo ); + std::ostringstream os; + os << snd_seq_client_info_get_name(cinfo); + os << ":"; + os << snd_seq_port_info_get_port(pinfo); + std::string stringName = os.str(); return stringName; } @@ -1095,18 +1303,18 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber ) return 0; } -void RtMidiOut :: initialize( void ) +void RtMidiOut :: initialize( const std::string& clientName ) { // Set up the ALSA sequencer client. - snd_seq_t *seq; - int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0); + snd_seq_t *seq; + int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); 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"); + snd_seq_set_client_name( seq, clientName.c_str() ); // Save our api-specific connection information. AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; @@ -1131,7 +1339,7 @@ void RtMidiOut :: initialize( void ) apiData_ = (void *) data; } -void RtMidiOut :: openPort( unsigned int portNumber ) +void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; @@ -1161,7 +1369,7 @@ void RtMidiOut :: openPort( unsigned int portNumber ) sender.client = snd_seq_client_id( data->seq ); if ( data->vport < 0 ) { - data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", + data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC ); if ( data->vport < 0 ) { @@ -1423,7 +1631,7 @@ extern "C" void *irixMidiHandler( void *ptr ) return 0; } -void RtMidiIn :: initialize( void ) +void RtMidiIn :: initialize( const std::string& /*clientName*/ ) { // Initialize the Irix MIDI system. At the moment, we will not // worry about a return value of zero (ports) because there is a @@ -1436,7 +1644,7 @@ void RtMidiIn :: initialize( void ) inputData_.apiData = (void *) data; } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -1564,7 +1772,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber ) return stringName; } -void RtMidiOut :: initialize( void ) +void RtMidiOut :: initialize( const std::string& /*clientName*/ ) { // Initialize the Irix MIDI system. At the moment, we will not // worry about a return value of zero (ports) because there is a @@ -1576,7 +1784,7 @@ void RtMidiOut :: initialize( void ) apiData_ = (void *) data; } -void RtMidiOut :: openPort( unsigned int portNumber ) +void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; @@ -1754,21 +1962,31 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin, unsigned char *ptr = (unsigned char *) &midiMessage; for ( int i=0; imessage.bytes.push_back( *ptr++ ); } - else if ( !(data->ignoreFlags & 0x01) ) { - // Sysex message and we're not ignoring it - MIDIHDR *sysex = ( MIDIHDR *) midiMessage; - for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) - apiData->message.bytes.push_back( sysex->lpData[i] ); + else { // Sysex message ( MIM_LONGDATA ) + MIDIHDR *sysex = ( MIDIHDR *) midiMessage; + if ( !( data->ignoreFlags & 0x01 ) ) { + // Sysex message and we're not ignoring it + for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) + apiData->message.bytes.push_back( sysex->lpData[i] ); + } - // When the callback has to be unaffected (application closes), - // it seems WinMM calls it with an empty sysex to de-queue the buffer - // If the buffer is requeued afer that message, the PC suddenly reboots - // after one or two minutes (JB). + // The WinMM API requires that the sysex buffer be requeued after + // input of each sysex message. Even if we are ignoring sysex + // messages, we still need to requeue the buffer in case the user + // decides to not ignore sysex messages in the future. However, + // it seems that WinMM calls this function with an empty sysex + // buffer when an application closes and in this case, we should + // avoid requeueing it, else the computer suddenly reboots after + // one or two minutes. if ( apiData->sysexBuffer->dwBytesRecorded > 0 ) { + //if ( sysex->dwBytesRecorded > 0 ) { MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; + + if ( data->ignoreFlags & 0x01 ) return; } + else return; } if ( data->usingCallback ) { @@ -1783,13 +2001,11 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin, std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; } - // 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. + // Clear the vector for the next input message. apiData->message.bytes.clear(); } -void RtMidiIn :: initialize( void ) +void RtMidiIn :: initialize( const std::string& /*clientName*/ ) { // We'll issue a warning here if no devices are available but not // throw an error since the user can plugin something later. @@ -1806,7 +2022,7 @@ void RtMidiIn :: initialize( void ) data->message.bytes.clear(); // needs to be empty for first input message } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -1840,8 +2056,8 @@ void RtMidiIn :: openPort( unsigned int portNumber ) // Allocate and init the sysex buffer. data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; - data->sysexBuffer->lpData = new char[1024]; - data->sysexBuffer->dwBufferLength = 1024; + data->sysexBuffer->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; + data->sysexBuffer->dwBufferLength = RT_SYSEX_BUFFER_SIZE; data->sysexBuffer->dwFlags = 0; result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) ); @@ -1970,7 +2186,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber ) return stringName; } -void RtMidiOut :: initialize( void ) +void RtMidiOut :: initialize( const std::string& /*clientName*/ ) { // We'll issue a warning here if no devices are available but not // throw an error since the user can plug something in later. @@ -1985,7 +2201,7 @@ void RtMidiOut :: initialize( void ) apiData_ = (void *) data; } -void RtMidiOut :: openPort( unsigned int portNumber ) +void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; diff --git a/src/RtWvIn.cpp b/src/RtWvIn.cpp index 6480f1e..837950b 100644 --- a/src/RtWvIn.cpp +++ b/src/RtWvIn.cpp @@ -10,15 +10,19 @@ RtWvIn supports multi-channel data in both interleaved and non-interleaved formats. It is important to distinguish the - tick() methods, which return samples produced by averaging across - sample frames, from the tickFrame() methods, which return - references or pointers to multi-channel sample frames. + tick() method that computes a single frame (and returns only the + specified sample of a multi-channel frame) from the overloaded one + that takes an StkFrames object for multi-channel and/or + multi-frame data. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "RtWvIn.h" +#include + +namespace stk { // This function is automatically called by RtAudio to supply input audio data. int read( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, @@ -56,7 +60,9 @@ void RtWvIn :: fillBuffer( void *buffer, unsigned int nFrames ) nSamples -= counter; } + mutex_.lock(); framesFilled_ += nFrames; + mutex_.unlock(); if ( framesFilled_ > data_.frames() ) { framesFilled_ = data_.frames(); errorString_ << "RtWvIn: audio buffer overrun!"; @@ -69,7 +75,10 @@ RtWvIn :: RtWvIn( unsigned int nChannels, StkFloat sampleRate, int device, int b { // We'll let RtAudio deal with channel and sample rate limitations. RtAudio::StreamParameters parameters; - parameters.deviceId = device; + if ( device == 0 ) + parameters.deviceId = adc_.getDefaultInputDevice(); + else + parameters.deviceId = device - 1; parameters.nChannels = nChannels; unsigned int size = bufferFrames; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; @@ -82,7 +91,7 @@ RtWvIn :: RtWvIn( unsigned int nChannels, StkFloat sampleRate, int device, int b } data_.resize( size * nBuffers, nChannels ); - lastOutputs_.resize( 1, nChannels ); + lastFrame_.resize( 1, nChannels ); } RtWvIn :: ~RtWvIn() @@ -104,22 +113,82 @@ void RtWvIn :: stop() if ( !stopped_ ) { adc_.stopStream(); stopped_ = true; + for ( unsigned int i=0; i= data_.channels() ) { + errorString_ << "RtWvIn::tick(): channel argument is incompatible with streamed channels!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + if ( stopped_ ) this->start(); // Block until at least one frame is available. - while ( framesFilled_ == 0 ) - Stk::sleep( 1 ); + while ( framesFilled_ == 0 ) Stk::sleep( 1 ); - for ( unsigned int i=0; i= data_.frames() ) readIndex_ = 0; + + return lastFrame_[channel]; } + +StkFrames& RtWvIn :: tick( StkFrames& frames ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( nChannels != frames.channels() ) { + errorString_ << "RtWvIn::tick(): StkFrames argument is incompatible with adc channels!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + if ( stopped_ ) this->start(); + + // See how much space we have and fill as much as we can ... if we + // still have space left in the frames object, then wait and repeat. + unsigned int nFrames, bytes, framesRead = 0; + while ( framesRead < frames.frames() ) { + + // Block until we have some input data. + while ( framesFilled_ == 0 ) Stk::sleep( 1 ); + + // Copy data in one chunk up to the end of the data buffer. + nFrames = framesFilled_; + if ( readIndex_ + nFrames > data_.frames() ) + nFrames = data_.frames() - readIndex_; + if ( nFrames > frames.frames() - framesRead ) + nFrames = frames.frames() - framesRead; + bytes = nFrames * nChannels * sizeof( StkFloat ); + StkFloat *samples = &data_[readIndex_ * nChannels]; + memcpy( &frames[framesRead * nChannels], samples, bytes ); + + readIndex_ += nFrames; + if ( readIndex_ == data_.frames() ) readIndex_ = 0; + + framesRead += nFrames; + mutex_.lock(); + framesFilled_ -= nFrames; + mutex_.unlock(); + } + + unsigned long index = (frames.frames() - 1) * nChannels; + for ( unsigned int i=0; i + +namespace stk { // Streaming status states. enum { RUNNING, EMPTYING, FINISHED }; @@ -73,7 +76,9 @@ int RtWvOut :: readBuffer( void *buffer, unsigned int frameCount ) nFrames -= counter; } + mutex_.lock(); framesFilled_ -= frameCount; + mutex_.unlock(); if ( framesFilled_ < 0 ) { framesFilled_ = 0; // writeIndex_ = readIndex_; @@ -90,7 +95,10 @@ RtWvOut :: RtWvOut( unsigned int nChannels, StkFloat sampleRate, int device, int { // We'll let RtAudio deal with channel and sample rate limitations. RtAudio::StreamParameters parameters; - parameters.deviceId = device; + if ( device == 0 ) + parameters.deviceId = dac_.getDefaultOutputDevice(); + else + parameters.deviceId = device - 1; parameters.nChannels = nChannels; unsigned int size = bufferFrames; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; @@ -104,12 +112,13 @@ RtWvOut :: RtWvOut( unsigned int nChannels, StkFloat sampleRate, int device, int } data_.resize( size * nBuffers, nChannels ); - unsigned int offset = (unsigned int ) (data_.frames() / 2.0); - writeIndex_ = offset; // start writing half-way into buffer - framesFilled_ = offset; + + // Start writing half-way into buffer. + writeIndex_ = (unsigned int ) (data_.frames() / 2.0); + framesFilled_ = writeIndex_; } -RtWvOut :: ~RtWvOut() +RtWvOut :: ~RtWvOut( void ) { // Change status flag to signal callback to clear the buffer and close. status_ = EMPTYING; @@ -117,7 +126,7 @@ RtWvOut :: ~RtWvOut() dac_.closeStream(); } -void RtWvOut :: start() +void RtWvOut :: start( void ) { if ( stopped_ ) { dac_.startStream(); @@ -125,7 +134,7 @@ void RtWvOut :: start() } } -void RtWvOut :: stop() +void RtWvOut :: stop( void ) { if ( !stopped_ ) { dac_.stopStream(); @@ -133,7 +142,7 @@ void RtWvOut :: stop() } } -void RtWvOut :: computeSample( const StkFloat sample ) +void RtWvOut :: tick( const StkFloat sample ) { if ( stopped_ ) this->start(); @@ -147,65 +156,57 @@ void RtWvOut :: computeSample( const StkFloat sample ) for ( unsigned int j=0; jstart(); - // Block until we have room for the frames of output data. - while ( data_.frames() - framesFilled_ < frames.frames() ) Stk::sleep( 1 ); + // See how much space we have and fill as much as we can ... if we + // still have samples left in the frames object, then wait and + // repeat. + unsigned int framesEmpty, nFrames, bytes, framesWritten = 0; + unsigned int nChannels = data_.channels(); + while ( framesWritten < frames.frames() ) { - unsigned int j, nChannels = data_.channels(); - if ( nChannels == 1 || frames.interleaved() ) { + // Block until we have some room for output data. + while ( framesFilled_ == (long) data_.frames() ) Stk::sleep( 1 ); + framesEmpty = data_.frames() - framesFilled_; - unsigned int index, iFrames = 0; - for ( unsigned int i=0; i data_.frames() ) + nFrames = data_.frames() - writeIndex_; + if ( nFrames > frames.frames() - framesWritten ) + nFrames = frames.frames() - framesWritten; + bytes = nFrames * nChannels * sizeof( StkFloat ); + StkFloat *samples = &data_[writeIndex_ * nChannels]; + memcpy( samples, &frames[framesWritten * nChannels], bytes ); + for ( unsigned int i=0; ikeyOff(); @@ -52,3 +54,5 @@ void Sampler :: noteOff(StkFloat amplitude) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Saxofony.cpp b/src/Saxofony.cpp index 70ef03a..548102b 100644 --- a/src/Saxofony.cpp +++ b/src/Saxofony.cpp @@ -31,14 +31,16 @@ - Vibrato Gain = 1 - Breath Pressure = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Saxofony.h" #include "SKINI.msg" -Saxofony :: Saxofony(StkFloat lowestFrequency) +namespace stk { + +Saxofony :: Saxofony( StkFloat lowestFrequency ) { length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); // Initialize blowing position to 0.2 of length / 2. @@ -58,18 +60,18 @@ Saxofony :: Saxofony(StkFloat lowestFrequency) vibratoGain_ = 0.1; } -Saxofony :: ~Saxofony() +Saxofony :: ~Saxofony( void ) { } -void Saxofony :: clear() +void Saxofony :: clear( void ) { delays_[0].clear(); delays_[1].clear(); filter_.clear(); } -void Saxofony :: setFrequency(StkFloat frequency) +void Saxofony :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -86,7 +88,7 @@ void Saxofony :: setFrequency(StkFloat frequency) delays_[1].setDelay( position_ * delay ); } -void Saxofony :: setBlowPosition(StkFloat position) +void Saxofony :: setBlowPosition( StkFloat position ) { if ( position_ == position ) return; @@ -101,19 +103,19 @@ void Saxofony :: setBlowPosition(StkFloat position) delays_[1].setDelay( position_ * totalDelay ); } -void Saxofony :: startBlowing(StkFloat amplitude, StkFloat rate) +void Saxofony :: startBlowing( StkFloat amplitude, StkFloat rate ) { envelope_.setRate( rate ); envelope_.setTarget( amplitude ); } -void Saxofony :: stopBlowing(StkFloat rate) +void Saxofony :: stopBlowing( StkFloat rate ) { envelope_.setRate( rate ); envelope_.setTarget( 0.0 ); } -void Saxofony :: noteOn(StkFloat frequency, StkFloat amplitude) +void Saxofony :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->startBlowing( 0.55 + (amplitude * 0.30), amplitude * 0.005 ); @@ -125,7 +127,7 @@ void Saxofony :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Saxofony :: noteOff(StkFloat amplitude) +void Saxofony :: noteOff( StkFloat amplitude ) { this->stopBlowing( amplitude * 0.01 ); @@ -135,28 +137,7 @@ void Saxofony :: noteOff(StkFloat amplitude) #endif } -StkFloat Saxofony :: computeSample() -{ - 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(); - - 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_; -} - -void Saxofony :: controlChange(int number, StkFloat value) +void Saxofony :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -194,3 +175,5 @@ void Saxofony :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Shakers.cpp b/src/Shakers.cpp index 929b71d..c2e4f6f 100644 --- a/src/Shakers.cpp +++ b/src/Shakers.cpp @@ -48,26 +48,30 @@ - Little Rocks = 21 - Tuned Bamboo Chimes = 22 - by Perry R. Cook, 1996 - 2004. + by Perry R. Cook, 1996 - 2009. */ /***************************************************/ -#include "Stk.h" +#include "Shakers.h" +#include "SKINI.msg" +#include #include -int my_random(int max) // Return Random Int Between 0 and max +namespace stk { + +int my_random( int max ) // Return Random Int Between 0 and max { int temp = (int) ((float)max * rand() / (RAND_MAX + 1.0) ); return temp; } -StkFloat float_random(StkFloat max) // Return random float between 0.0 and max +StkFloat float_random( StkFloat max ) // Return random float between 0.0 and max { StkFloat temp = (StkFloat) (max * rand() / (RAND_MAX + 1.0) ); return temp; } -StkFloat noise_tick() // Return random StkFloat float between -1.0 and 1.0 +StkFloat noise_tick( void ) // Return random StkFloat float between -1.0 and 1.0 { StkFloat temp = (StkFloat) (2.0 * rand() / (RAND_MAX + 1.0) ); temp -= 1.0; @@ -274,10 +278,7 @@ const StkFloat LITLROCKS_RES = 0.843; // Finally ... the class code! -#include "Shakers.h" -#include "SKINI.msg" - -Shakers :: Shakers() +Shakers :: Shakers( void ) { int i; @@ -316,7 +317,7 @@ Shakers :: Shakers() this->setupNum(instType_); } -Shakers :: ~Shakers() +Shakers :: ~Shakers( void ) { } @@ -331,7 +332,7 @@ char instrs[NUM_INSTR][10] = { "BigRocks", "LitlRoks", "TBamboo" }; -int Shakers :: setupName(char* instr) +int Shakers :: setupName( char* instr ) { int which = 0; @@ -348,22 +349,22 @@ int Shakers :: setupName(char* instr) return this->setupNum(which); } -void Shakers :: setFinalZs(StkFloat z0, StkFloat z1, StkFloat z2) +void Shakers :: setFinalZs( StkFloat z0, StkFloat z1, StkFloat z2 ) { finalZCoeffs_[0] = z0; finalZCoeffs_[1] = z1; finalZCoeffs_[2] = z2; } -void Shakers :: setDecays(StkFloat sndDecay_, StkFloat sysDecay_) +void Shakers :: setDecays( StkFloat sndDecay, StkFloat sysDecay ) { - soundDecay_ = sndDecay_; - systemDecay_ = sysDecay_; + soundDecay_ = sndDecay; + systemDecay_ = sysDecay; } -int Shakers :: setFreqAndReson(int which, StkFloat freq, StkFloat reson) +int Shakers :: setFreqAndReson( int which, StkFloat freq, StkFloat reson ) { - if (which < MAX_FREQS) { + if ( which < MAX_FREQS ) { resons_[which] = reson; center_freqs_[which] = freq; t_center_freqs_[which] = freq; @@ -374,7 +375,7 @@ int Shakers :: setFreqAndReson(int which, StkFloat freq, StkFloat reson) else return 0; } -int Shakers :: setupNum(int inst) +int Shakers :: setupNum( int inst ) { int i, rv = 0; StkFloat temp; @@ -793,7 +794,7 @@ int Shakers :: setupNum(int inst) return rv; } -void Shakers :: noteOn(StkFloat frequency, StkFloat 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; @@ -808,7 +809,7 @@ void Shakers :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Shakers :: noteOff(StkFloat amplitude) +void Shakers :: noteOff( StkFloat amplitude ) { shakeEnergy_ = 0.0; if (instType_==10 || instType_==3) ratchetPos_ = 0; @@ -816,7 +817,7 @@ void Shakers :: noteOff(StkFloat amplitude) const StkFloat MIN_ENERGY = 0.3; -StkFloat Shakers :: computeSample() +StkFloat Shakers :: tick( unsigned int ) { StkFloat data; StkFloat temp_rand; @@ -824,15 +825,15 @@ StkFloat Shakers :: computeSample() if (instType_ == 4) { if (shakeEnergy_ > MIN_ENERGY) { - lastOutput_ = wuter_tick(); - lastOutput_ *= 0.0001; + lastFrame_[0] = wuter_tick(); + lastFrame_[0] *= 0.0001; } else { - lastOutput_ = 0.0; + lastFrame_[0] = 0.0; } } else if (instType_ == 22) { - lastOutput_ = tbamb_tick(); + lastFrame_[0] = tbamb_tick(); } else if (instType_ == 10 || instType_ == 3) { if (ratchetPos_ > 0) { @@ -842,10 +843,10 @@ StkFloat Shakers :: computeSample() ratchetPos_ -= 1; } totalEnergy_ = ratchet_; - lastOutput_ = ratchet_tick(); - lastOutput_ *= 0.0001; + lastFrame_[0] = ratchet_tick(); + lastFrame_[0] *= 0.0001; } - else lastOutput_ = 0.0; + else lastFrame_[0] = 0.0; } else { // generic_tick() if (shakeEnergy_ > MIN_ENERGY) { @@ -879,15 +880,15 @@ StkFloat Shakers :: computeSample() data += finalZCoeffs_[2] * finalZ_[2]; // Extra zero(s) for shape if (data > 10000.0) data = 10000.0; if (data < -10000.0) data = -10000.0; - lastOutput_ = data * 0.0001; + lastFrame_[0] = data * 0.0001; } - else lastOutput_ = 0.0; + else lastFrame_[0] = 0.0; } - return lastOutput_; + return lastFrame_[0]; } -void Shakers :: controlChange(int number, StkFloat value) +void Shakers :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -997,7 +998,7 @@ void Shakers :: controlChange(int number, StkFloat value) // KLUDGE-O-MATIC-O-RAMA -StkFloat Shakers :: wuter_tick() { +StkFloat Shakers :: wuter_tick( void ) { StkFloat data; int j; shakeEnergy_ *= systemDecay_; // Exponential system decay @@ -1068,7 +1069,7 @@ StkFloat Shakers :: wuter_tick() { return data; } -StkFloat Shakers :: ratchet_tick() { +StkFloat Shakers :: ratchet_tick( void ) { StkFloat data; if (my_random(1024) < nObjects_) { sndLevel_ += 512 * ratchet_ * totalEnergy_; @@ -1094,7 +1095,7 @@ StkFloat Shakers :: ratchet_tick() { return data; } -StkFloat Shakers :: tbamb_tick() { +StkFloat Shakers :: tbamb_tick( void ) { StkFloat data, temp; static int which = 0; int i; @@ -1129,3 +1130,5 @@ StkFloat Shakers :: tbamb_tick() { else data = 0.0; return data; } + +} // stk namespace diff --git a/src/Simple.cpp b/src/Simple.cpp index 84fef6e..ebf3a58 100644 --- a/src/Simple.cpp +++ b/src/Simple.cpp @@ -13,17 +13,19 @@ - Envelope Rate = 11 - Gain = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Simple.h" #include "SKINI.msg" -Simple :: Simple() +namespace stk { + +Simple :: Simple( void ) { // Concatenate the STK rawwave path to the rawwave file - loop_ = new WaveLoop( (Stk::rawwavePath() + "impuls10.raw").c_str(), true ); + loop_ = new FileLoop( (Stk::rawwavePath() + "impuls10.raw").c_str(), true ); filter_.setPole( 0.5 ); baseFrequency_ = 440.0; @@ -31,22 +33,22 @@ Simple :: Simple() loopGain_ = 0.5; } -Simple :: ~Simple() +Simple :: ~Simple( void ) { delete loop_; } -void Simple :: keyOn() +void Simple :: keyOn( void ) { adsr_.keyOn(); } -void Simple :: keyOff() +void Simple :: keyOff( void ) { adsr_.keyOff(); } -void Simple :: noteOn(StkFloat frequency, StkFloat amplitude) +void Simple :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->keyOn(); this->setFrequency( frequency ); @@ -57,7 +59,7 @@ void Simple :: noteOn(StkFloat frequency, StkFloat amplitude) handleError( StkError::DEBUG_WARNING ); #endif } -void Simple :: noteOff(StkFloat amplitude) +void Simple :: noteOff( StkFloat amplitude ) { this->keyOff(); @@ -67,23 +69,13 @@ void Simple :: noteOff(StkFloat amplitude) #endif } -void Simple :: setFrequency(StkFloat frequency) +void Simple :: setFrequency( StkFloat frequency ) { biquad_.setResonance( frequency, 0.98, true ); loop_->setFrequency( frequency ); } -StkFloat Simple :: computeSample() -{ - 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, StkFloat value) +void Simple :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -119,3 +111,5 @@ void Simple :: controlChange(int number, StkFloat value) handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/SineWave.cpp b/src/SineWave.cpp index 338f749..2b1bb67 100644 --- a/src/SineWave.cpp +++ b/src/SineWave.cpp @@ -9,13 +9,15 @@ The "table" length, set in SineWave.h, is 2048 samples by default. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "SineWave.h" #include +namespace stk { + StkFrames SineWave :: table_; SineWave :: SineWave( void ) @@ -42,10 +44,10 @@ void SineWave :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) this->setRate( oldRate * rate_ / newRate ); } -void SineWave :: reset(void) +void SineWave :: reset( void ) { time_ = 0.0; - lastOutput_ = 0; + lastFrame_[0] = 0; } void SineWave :: setFrequency( StkFloat frequency ) @@ -58,55 +60,19 @@ void SineWave :: addTime( StkFloat time ) { // Add an absolute time in samples. time_ += time; - - while ( time_ < 0.0 ) - time_ += TABLE_SIZE; - while ( time_ >= TABLE_SIZE ) - time_ -= TABLE_SIZE; } -void SineWave :: addPhase( StkFloat angle ) +void SineWave :: addPhase( StkFloat phase ) { // Add a time in cycles (one cycle = TABLE_SIZE). - time_ += TABLE_SIZE * angle; - - while ( time_ < 0.0 ) - time_ += TABLE_SIZE; - while ( time_ >= TABLE_SIZE ) - time_ -= TABLE_SIZE; + time_ += TABLE_SIZE * phase; } -void SineWave :: addPhaseOffset( StkFloat angle ) +void SineWave :: addPhaseOffset( StkFloat phaseOffset ) { - // Add a phase offset in cycles, where 1.0 = TABLE_SIZE. - phaseOffset_ = TABLE_SIZE * angle; + // Add a phase offset relative to any previous offset value. + time_ += ( phaseOffset - phaseOffset_ ) * TABLE_SIZE; + phaseOffset_ = phaseOffset; } -StkFloat SineWave :: computeSample( void ) -{ - // Check limits of time address ... if necessary, recalculate modulo - // TABLE_SIZE. - while ( time_ < 0.0 ) - time_ += TABLE_SIZE; - while ( time_ >= TABLE_SIZE ) - time_ -= TABLE_SIZE; - - StkFloat tyme; - if ( phaseOffset_ ) { - tyme = time_ + phaseOffset_; - while ( tyme < 0.0 ) - tyme += TABLE_SIZE; - while ( tyme >= TABLE_SIZE ) - tyme -= TABLE_SIZE; - } - else { - tyme = time_; - } - - lastOutput_ = table_.interpolate( tyme ); - - // Increment time, which can be negative. - time_ += rate_; - - return lastOutput_; -} +} // stk namespace diff --git a/src/SingWave.cpp b/src/SingWave.cpp index 0668893..3a76c27 100644 --- a/src/SingWave.cpp +++ b/src/SingWave.cpp @@ -2,116 +2,52 @@ /*! \class SingWave \brief STK "singing" looped soundfile class. - This class contains all that is needed to make - a pitched musical sound, like a simple voice - or violin. In general, it will not be used - alone because of munchkinification effects - from pitch shifting. It will be used as an - excitation source for other instruments. + This class loops a specified soundfile and modulates it both + periodically and randomly to produce a pitched musical sound, like + a simple voice or violin. In general, it is not be used alone + because of "munchkinification" effects from pitch shifting. + Within STK, it is used as an excitation source for other + instruments. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "SingWave.h" + +namespace stk { -SingWave :: SingWave( std::string fileName, bool raw) +SingWave :: SingWave( std::string fileName, bool raw ) { // An exception could be thrown here. - wave_ = new WaveLoop( fileName, raw ); + wave_.openFile( fileName, raw ); rate_ = 1.0; sweepRate_ = 0.001; + modulator_.setVibratoRate( 6.0 ); modulator_.setVibratoGain( 0.04 ); modulator_.setRandomGain( 0.005 ); + this->setFrequency( 75.0 ); pitchEnvelope_.setRate( 1.0 ); - this->computeSample(); - this->computeSample(); + this->tick(); + this->tick(); pitchEnvelope_.setRate( sweepRate_ * rate_ ); } SingWave :: ~SingWave() { - delete wave_; } -void SingWave :: reset() -{ - wave_->reset(); - lastOutput_ = 0.0; -} - -void SingWave :: normalize() -{ - wave_->normalize(); -} - -void SingWave :: normalize(StkFloat peak) -{ - wave_->normalize( peak ); -} - -void SingWave :: setFrequency(StkFloat frequency) +void SingWave :: setFrequency( StkFloat frequency ) { StkFloat temp = rate_; - rate_ = wave_->getSize() * frequency / Stk::sampleRate(); + rate_ = wave_.getSize() * frequency / Stk::sampleRate(); temp -= rate_; if ( temp < 0) temp = -temp; pitchEnvelope_.setTarget( rate_ ); pitchEnvelope_.setRate( sweepRate_ * temp ); } -void SingWave :: setVibratoRate(StkFloat rate) -{ - modulator_.setVibratoRate( rate ); -} - -void SingWave :: setVibratoGain(StkFloat gain) -{ - modulator_.setVibratoGain(gain); -} - -void SingWave :: setRandomGain(StkFloat gain) -{ - modulator_.setRandomGain(gain); -} - -void SingWave :: setSweepRate(StkFloat rate) -{ - sweepRate_ = rate; -} - -void SingWave :: setGainRate(StkFloat rate) -{ - envelope_.setRate(rate); -} - -void SingWave :: setGainTarget(StkFloat target) -{ - envelope_.setTarget(target); -} - -void SingWave :: noteOn() -{ - envelope_.keyOn(); -} - -void SingWave :: noteOff() -{ - envelope_.keyOff(); -} - -StkFloat SingWave :: computeSample() -{ - // Set the wave rate. - StkFloat newRate = pitchEnvelope_.tick(); - newRate += newRate * modulator_.tick(); - wave_->setRate( newRate ); - - lastOutput_ = wave_->tick(); - lastOutput_ *= envelope_.tick(); - - return lastOutput_; -} +} // stk namespace diff --git a/src/Sitar.cpp b/src/Sitar.cpp index 1d70209..9591be5 100644 --- a/src/Sitar.cpp +++ b/src/Sitar.cpp @@ -13,16 +13,17 @@ Stanford, bearing the names of Karplus and/or Strong. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Sitar.h" -#include -Sitar :: Sitar(StkFloat lowestFrequency) +namespace stk { + +Sitar :: Sitar( StkFloat lowestFrequency ) { - unsigned long length = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + unsigned long length = (unsigned long) ( Stk::sampleRate() / lowestFrequency + 1 ); delayLine_.setMaximumDelay( length ); delay_ = 0.5 * length; delayLine_.setDelay( delay_ ); @@ -35,17 +36,17 @@ Sitar :: Sitar(StkFloat lowestFrequency) this->clear(); } -Sitar :: ~Sitar() +Sitar :: ~Sitar( void ) { } -void Sitar :: clear() +void Sitar :: clear( void ) { delayLine_.clear(); loopFilter_.clear(); } -void Sitar :: setFrequency(StkFloat frequency) +void Sitar :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency; if ( frequency <= 0.0 ) { @@ -61,12 +62,12 @@ void Sitar :: setFrequency(StkFloat frequency) if ( loopGain_ > 0.9995 ) loopGain_ = 0.9995; } -void Sitar :: pluck(StkFloat amplitude) +void Sitar :: pluck( StkFloat amplitude ) { envelope_.keyOn(); } -void Sitar :: noteOn(StkFloat frequency, StkFloat amplitude) +void Sitar :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->pluck( amplitude ); @@ -78,7 +79,7 @@ void Sitar :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Sitar :: noteOff(StkFloat amplitude) +void Sitar :: noteOff( StkFloat amplitude ) { loopGain_ = (StkFloat) 1.0 - amplitude; if ( loopGain_ < 0.0 ) { @@ -98,18 +99,4 @@ void Sitar :: noteOff(StkFloat amplitude) #endif } -StkFloat Sitar :: computeSample() -{ - if ( fabs(targetDelay_ - delay_) > 0.001 ) { - if ( targetDelay_ < delay_ ) - delay_ *= 0.99999; - else - delay_ *= 1.00001; - delayLine_.setDelay( delay_ ); - } - - lastOutput_ = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) + - (amGain_ * envelope_.tick() * noise_.tick())); - - return lastOutput_; -} +} // stk namespace diff --git a/src/Skini.cpp b/src/Skini.cpp index 807a0d1..4563f12 100644 --- a/src/Skini.cpp +++ b/src/Skini.cpp @@ -19,13 +19,15 @@ See also SKINI.txt. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Skini.h" #include "SKINI.tbl" +namespace stk { + Skini :: Skini() { } @@ -215,3 +217,5 @@ std::string Skini :: whatsThisController( long number ) } return controller; } + +} // stk namespace diff --git a/src/Socket.cpp b/src/Socket.cpp index 572585a..f7c21fc 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -5,12 +5,14 @@ This class provides common functionality for TCP and UDP internet socket server and client subclasses. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Socket.h" +namespace stk { + Socket :: Socket() { soket_ = -1; @@ -43,28 +45,13 @@ void Socket :: close( int socket ) #endif } -int Socket :: id( void ) const -{ - return soket_; -} - -int Socket :: port( void ) const -{ - return port_; -} - -bool Socket :: isValid( int socket ) -{ - return socket != -1; -} - void Socket :: setBlocking( int socket, bool enable ) { if ( !isValid( socket ) ) return; #if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) - int tmp = ::fcntl(socket, F_GETFL, 0); + int tmp = ::fcntl( socket, F_GETFL, 0 ); if ( tmp >= 0 ) tmp = ::fcntl( socket, F_SETFL, enable ? (tmp &~ O_NONBLOCK) : (tmp | O_NONBLOCK) ); @@ -88,3 +75,4 @@ int Socket :: readBuffer(int socket, void *buffer, long bufferSize, int flags ) return recv( socket, (char *)buffer, bufferSize, flags ); } +} // stk namespace diff --git a/src/Sphere.cpp b/src/Sphere.cpp index 1d249e5..b5d6390 100644 --- a/src/Sphere.cpp +++ b/src/Sphere.cpp @@ -5,49 +5,16 @@ This class implements a spherical ball with radius, mass, position, and velocity parameters. - by Perry R. Cook, 1995 - 2004. + by Perry R. Cook, 1995 - 2009. */ /***************************************************/ #include "Sphere.h" -#include +#include -Sphere::Sphere(StkFloat radius) -{ - radius_ = radius; - mass_ = 1.0; -}; +namespace stk { -Sphere::~Sphere() -{ -} - -void Sphere::setPosition(StkFloat x, StkFloat y, StkFloat z) -{ - position_.setXYZ(x, y, z); -}; - -void Sphere::setVelocity(StkFloat x, StkFloat y, StkFloat z) -{ - velocity_.setXYZ(x, y, z); -}; - -void Sphere::setRadius(StkFloat radius) -{ - radius_ = radius; -}; - -void Sphere::setMass(StkFloat mass) -{ - mass_ = mass; -}; - -Vector3D* Sphere::getPosition() -{ - return &position_; -}; - -Vector3D* Sphere::getRelativePosition(Vector3D* position) +Vector3D* Sphere::getRelativePosition( Vector3D* position ) { workingVector_.setXYZ(position->getX() - position_.getX(), position->getY() - position_.getY(), @@ -55,13 +22,13 @@ Vector3D* Sphere::getRelativePosition(Vector3D* position) return &workingVector_; }; -StkFloat Sphere::getVelocity(Vector3D* velocity) +StkFloat Sphere::getVelocity( Vector3D* velocity ) { velocity->setXYZ( velocity_.getX(), velocity_.getY(), velocity_.getZ() ); return velocity_.getLength(); }; -StkFloat Sphere::isInside(Vector3D *position) +StkFloat Sphere::isInside( Vector3D *position ) { // Return directed distance from aPosition to spherical boundary ( < // 0 if inside). @@ -73,16 +40,6 @@ StkFloat Sphere::isInside(Vector3D *position) return distance - radius_; }; -StkFloat Sphere::getRadius() -{ - return radius_; -}; - -StkFloat Sphere::getMass() -{ - return mass_; -}; - void Sphere::addVelocity(StkFloat x, StkFloat y, StkFloat z) { velocity_.setX(velocity_.getX() + x); @@ -90,10 +47,4 @@ void Sphere::addVelocity(StkFloat x, StkFloat y, StkFloat z) velocity_.setZ(velocity_.getZ() + z); } -void Sphere::tick(StkFloat timeIncrement) -{ - position_.setX(position_.getX() + (timeIncrement * velocity_.getX())); - position_.setY(position_.getY() + (timeIncrement * velocity_.getY())); - position_.setZ(position_.getZ() + (timeIncrement * velocity_.getZ())); -}; - +} // stk namespace diff --git a/src/StifKarp.cpp b/src/StifKarp.cpp index f5b004c..98aeaf8 100644 --- a/src/StifKarp.cpp +++ b/src/StifKarp.cpp @@ -17,7 +17,7 @@ - String Sustain = 11 - String Stretch = 1 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ @@ -25,9 +25,11 @@ #include "SKINI.msg" #include -StifKarp :: StifKarp(StkFloat lowestFrequency) +namespace stk { + +StifKarp :: StifKarp( StkFloat lowestFrequency ) { - length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1); + length_ = (unsigned long) ( Stk::sampleRate() / lowestFrequency + 1 ); delayLine_.setMaximumDelay( length_ ); delayLine_.setDelay( 0.5 * length_ ); combDelay_.setMaximumDelay( length_ ); @@ -44,18 +46,18 @@ StifKarp :: StifKarp(StkFloat lowestFrequency) this->clear(); } -StifKarp :: ~StifKarp() +StifKarp :: ~StifKarp( void ) { } -void StifKarp :: clear() +void StifKarp :: clear( void ) { delayLine_.clear(); combDelay_.clear(); filter_.clear(); } -void StifKarp :: setFrequency(StkFloat frequency) +void StifKarp :: setFrequency( StkFloat frequency ) { lastFrequency_ = frequency; if ( frequency <= 0.0 ) { @@ -80,7 +82,7 @@ void StifKarp :: setFrequency(StkFloat frequency) combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); } -void StifKarp :: setStretch(StkFloat stretch) +void StifKarp :: setStretch( StkFloat stretch ) { stretching_ = stretch; StkFloat coefficient; @@ -102,7 +104,7 @@ void StifKarp :: setStretch(StkFloat stretch) } } -void StifKarp :: setPickupPosition(StkFloat position) { +void StifKarp :: setPickupPosition( StkFloat position ) { pickupPosition_ = position; if ( position < 0.0 ) { errorString_ << "StifKarp::setPickupPosition: parameter is less than zero ... setting to 0.0!"; @@ -119,14 +121,14 @@ void StifKarp :: setPickupPosition(StkFloat position) { combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); } -void StifKarp :: setBaseLoopGain(StkFloat aGain) +void StifKarp :: setBaseLoopGain( StkFloat aGain ) { baseLoopGain_ = aGain; loopGain_ = baseLoopGain_ + (lastFrequency_ * 0.000005); if ( loopGain_ > 0.99999 ) loopGain_ = (StkFloat) 0.99999; } -void StifKarp :: pluck(StkFloat amplitude) +void StifKarp :: pluck( StkFloat amplitude ) { StkFloat gain = amplitude; if ( gain > 1.0 ) { @@ -148,7 +150,7 @@ void StifKarp :: pluck(StkFloat amplitude) } } -void StifKarp :: noteOn(StkFloat frequency, StkFloat amplitude) +void StifKarp :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->pluck( amplitude ); @@ -159,7 +161,7 @@ void StifKarp :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void StifKarp :: noteOff(StkFloat amplitude) +void StifKarp :: noteOff( StkFloat amplitude ) { StkFloat gain = amplitude; if ( gain > 1.0 ) { @@ -180,23 +182,7 @@ void StifKarp :: noteOff(StkFloat amplitude) #endif } -StkFloat StifKarp :: computeSample() -{ - StkFloat temp = delayLine_.lastOut() * loopGain_; - - // Calculate allpass stretching. - for (int i=0; i<4; i++) - temp = biquad_[i].tick(temp); - - // Moving average filter. - temp = filter_.tick(temp); - - lastOutput_ = delayLine_.tick(temp); - lastOutput_ = lastOutput_ - combDelay_.tick( lastOutput_ ); - return lastOutput_; -} - -void StifKarp :: controlChange(int number, StkFloat value) +void StifKarp :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -227,3 +213,4 @@ void StifKarp :: controlChange(int number, StkFloat value) #endif } +} // stk namespace diff --git a/src/Stk.cpp b/src/Stk.cpp index 2f9ad59..b26a826 100644 --- a/src/Stk.cpp +++ b/src/Stk.cpp @@ -22,7 +22,7 @@ STK WWW site: http://ccrma.stanford.edu/software/stk/ The Synthesis ToolKit in C++ (STK) - Copyright (c) 1995-2007 Perry R. Cook and Gary P. Scavone + Copyright (c) 1995-2009 Perry R. Cook and Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -52,6 +52,8 @@ #include "Stk.h" +namespace stk { + StkFloat Stk :: srate_ = (StkFloat) SRATE; std::string Stk :: rawwavepath_ = RAWWAVE_PATH; const Stk::StkFormat Stk :: STK_SINT8 = 0x1; @@ -224,8 +226,8 @@ void Stk :: handleError( std::string message, StkError::Type type ) // StkFrames definitions // -StkFrames :: StkFrames( unsigned int nFrames, unsigned int nChannels, bool interleaved ) - : nFrames_( nFrames ), nChannels_( nChannels ), interleaved_( interleaved ) +StkFrames :: StkFrames( unsigned int nFrames, unsigned int nChannels ) + : nFrames_( nFrames ), nChannels_( nChannels ) { size_ = nFrames_ * nChannels_; bufferSize_ = size_; @@ -244,8 +246,8 @@ StkFrames :: StkFrames( unsigned int nFrames, unsigned int nChannels, bool inter dataRate_ = Stk::sampleRate(); } -StkFrames :: StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels, bool interleaved ) - : nFrames_( nFrames ), nChannels_( nChannels ), interleaved_( interleaved ) +StkFrames :: StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels ) + : nFrames_( nFrames ), nChannels_( nChannels ) { size_ = nFrames_ * nChannels_; bufferSize_ = size_; @@ -269,10 +271,22 @@ StkFrames :: ~StkFrames() if ( data_ ) free( data_ ); } -bool StkFrames :: empty() const +StkFrames :: StkFrames( const StkFrames& f ) + : size_(0), bufferSize_(0) { - if ( size_ > 0 ) return false; - else return true; + resize( f.frames(), f.channels() ); + dataRate_ = Stk::sampleRate(); + for ( unsigned int i=0; i= size_ ) { - std::ostringstream error; - error << "StkFrames::operator[]: invalid index (" << n << ") value!"; - Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); - } -#endif - - return data_[n]; -} - -StkFloat StkFrames :: operator[] ( size_t n ) const -{ -#if defined(_STK_DEBUG_) - if ( n >= size_ ) { - std::ostringstream error; - error << "StkFrames::operator[]: invalid index (" << n << ") value!"; - Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); - } -#endif - - return data_[n]; -} - -StkFloat& StkFrames :: operator() ( size_t frame, unsigned int channel ) -{ -#if defined(_STK_DEBUG_) - if ( frame >= nFrames_ || channel >= nChannels_ ) { - std::ostringstream error; - error << "StkFrames::operator(): invalid frame (" << frame << ") or channel (" << channel << ") value!"; - Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); - } -#endif - - if ( interleaved_ ) - return data_[ frame * nChannels_ + channel ]; - else - return data_[ channel * nFrames_ + frame ]; -} - -StkFloat StkFrames :: operator() ( size_t frame, unsigned int channel ) const -{ -#if defined(_STK_DEBUG_) - if ( frame >= nFrames_ || channel >= nChannels_ ) { - std::ostringstream error; - error << "StkFrames::operator(): invalid frame (" << frame << ") or channel (" << channel << ") value!"; - Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); - } -#endif - - if ( interleaved_ ) - return data_[ frame * nChannels_ + channel ]; - else - return data_[ channel * nFrames_ + frame ]; -} - StkFloat StkFrames :: interpolate( StkFloat frame, unsigned int channel ) const { #if defined(_STK_DEBUG_) - if ( frame >= (StkFloat) nFrames_ || channel >= nChannels_ ) { - std::ostringstream error; - error << "StkFrames::interpolate: invalid frame (" << frame << ") or channel (" << channel << ") value!"; - Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); - } + if ( frame < 0.0 || frame > (StkFloat) ( nFrames_ - 1 ) || channel >= nChannels_ ) { + std::ostringstream error; + error << "StkFrames::interpolate: invalid frame (" << frame << ") or channel (" << channel << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } #endif size_t iIndex = ( size_t ) frame; // integer part of index StkFloat output, alpha = frame - (StkFloat) iIndex; // fractional part of index - if ( interleaved_ ) { - iIndex = iIndex * nChannels_ + channel; - output = data_[ iIndex ]; + iIndex = iIndex * nChannels_ + channel; + output = data_[ iIndex ]; + if ( alpha > 0.0 ) output += ( alpha * ( data_[ iIndex + nChannels_ ] - output ) ); - } - else { - iIndex += channel * nFrames_; - output = data_[ iIndex ]; - output += ( alpha * ( data_[ ++iIndex ] - output ) ); - } return output; } + +} // stk namespace diff --git a/src/SubNoise.cpp b/src/SubNoise.cpp deleted file mode 100644 index eba5a53..0000000 --- a/src/SubNoise.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/***************************************************/ -/*! \class SubNoise - \brief STK sub-sampled noise generator. - - Generates a new random number every "rate" ticks - 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 - 2007. -*/ -/***************************************************/ - -#include "SubNoise.h" - -SubNoise :: SubNoise( int subRate ) : Noise() -{ - if ( subRate <= 0 ) { - errorString_ << "SubNoise: subRate argument is negative ... setting to 16!"; - handleError( StkError::WARNING ); - rate_ = 16; - } - - // Precompute an output. - Noise::computeSample(); - counter_ = rate_; -} - -SubNoise :: ~SubNoise() -{ -} - -void SubNoise :: setRate( int subRate ) -{ - if ( subRate > 0 ) - rate_ = subRate; -} - -StkFloat SubNoise :: computeSample() -{ - if ( counter_-- == 0 ) { - Noise::computeSample(); - counter_ = rate_; - } - - return lastOutput_; -} - diff --git a/src/TapDelay.cpp b/src/TapDelay.cpp new file mode 100644 index 0000000..ee7371a --- /dev/null +++ b/src/TapDelay.cpp @@ -0,0 +1,104 @@ +/***************************************************/ +/*! \class TapDelay + \brief STK non-interpolating tapped delay line class. + + This class implements a non-interpolating digital delay-line with + an arbitrary number of output "taps". If the maximum length and + tap delays are not specified during instantiation, a fixed maximum + length of 4095 and a single tap delay of zero is set. + + A non-interpolating delay line is typically used in fixed + delay-length applications, such as for reverberation. + + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. +*/ +/***************************************************/ + +#include "TapDelay.h" + +namespace stk { + +TapDelay :: TapDelay( std::vector taps, 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 + // delayline of length = maxDelay+1. + if ( maxDelay < 1 ) { + errorString_ << "TapDelay::TapDelay: maxDelay must be > 0!\n"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + for ( unsigned int i=0; i maxDelay ) { + errorString_ << "TapDelay::TapDelay: maxDelay must be > than all tap delay values!\n"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + } + + if ( ( maxDelay + 1 ) > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); + + inPoint_ = 0; + this->setTapDelays( taps ); +} + +TapDelay :: ~TapDelay() +{ +} + +void TapDelay :: setMaximumDelay( unsigned long delay ) +{ + if ( delay < inputs_.size() ) return; + + if ( delay < 0 ) { + errorString_ << "TapDelay::setMaximumDelay: argument (" << delay << ") less than zero!\n"; + handleError( StkError::WARNING ); + return; + } + + for ( unsigned int i=0; i taps ) +{ + if ( taps.size() != outPoint_.size() ) { + outPoint_.resize( taps.size() ); + delays_.resize( taps.size() ); + lastFrame_.resize( 1, taps.size(), 0.0 ); + } + + for ( unsigned int i=0; i inputs_.size() - 1 ) { // The value is too big. + errorString_ << "TapDelay::setTapDelay: argument (" << taps[i] << ") too big ... setting to maximum!\n"; + handleError( StkError::WARNING ); + + // Force delay to maximum length. + outPoint_[i] = inPoint_ + 1; + if ( outPoint_[i] == inputs_.size() ) outPoint_[i] = 0; + delays_[i] = inputs_.size() - 1; + } + else if ( taps[i] < 0 ) { + errorString_ << "TapDelay::setDelay: argument (" << taps[i] << ") less than zero ... setting to zero!\n"; + handleError( StkError::WARNING ); + + outPoint_[i] = inPoint_; + delays_[i] = 0; + } + else { // read chases write + if ( inPoint_ >= taps[i] ) outPoint_[i] = inPoint_ - taps[i]; + else outPoint_[i] = inputs_.size() + inPoint_ - taps[i]; + delays_[i] = taps[i]; + } + } +} + +} // stk namespace + diff --git a/src/TcpClient.cpp b/src/TcpClient.cpp index 1df92cd..167f085 100644 --- a/src/TcpClient.cpp +++ b/src/TcpClient.cpp @@ -19,20 +19,23 @@ 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "TcpClient.h" +#include -TcpClient :: TcpClient(int port, std::string hostname ) +namespace stk { + +TcpClient :: TcpClient( int port, std::string hostname ) { #if defined(__OS_WINDOWS__) // windoze-only stuff WSADATA wsaData; WORD wVersionRequested = MAKEWORD(1,1); - WSAStartup(wVersionRequested, &wsaData); - if (wsaData.wVersion != wVersionRequested) { + WSAStartup( wVersionRequested, &wsaData ); + if ( wsaData.wVersion != wVersionRequested ) { errorString_ << "TcpClient: Incompatible Windows socket library version!"; handleError( StkError::PROCESS_SOCKET ); } @@ -42,7 +45,7 @@ TcpClient :: TcpClient(int port, std::string hostname ) connect( port, hostname ); } -TcpClient :: ~TcpClient() +TcpClient :: ~TcpClient( void ) { } @@ -52,21 +55,21 @@ int TcpClient :: connect( int port, std::string hostname ) this->close( soket_ ); // Create the client-side socket - soket_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (soket_ < 0) { + soket_ = ::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if ( soket_ < 0 ) { errorString_ << "TcpClient: 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) { + if ( result < 0 ) { errorString_ << "TcpClient: Error setting socket options!"; handleError( StkError::PROCESS_SOCKET ); } struct hostent *hostp; - if ( (hostp = gethostbyname( hostname.c_str() )) == 0 ) { + if ( ( hostp = gethostbyname( hostname.c_str() ) ) == 0 ) { errorString_ << "TcpClient: unknown host (" << hostname << ")!"; handleError( StkError::PROCESS_SOCKET_IPADDR ); } @@ -74,12 +77,11 @@ int TcpClient :: connect( int port, std::string hostname ) // Fill in the address structure struct sockaddr_in server_address; server_address.sin_family = AF_INET; - memcpy((void *)&server_address.sin_addr, hostp->h_addr, hostp->h_length); + memcpy( (void *)&server_address.sin_addr, hostp->h_addr, hostp->h_length ); server_address.sin_port = htons(port); // Connect to the server - if ( ::connect(soket_, (struct sockaddr *)&server_address, - sizeof(server_address) ) < 0) { + if ( ::connect( soket_, (struct sockaddr *)&server_address, sizeof(server_address) ) < 0 ) { errorString_ << "TcpClient: Couldn't connect to socket server!"; handleError( StkError::PROCESS_SOCKET ); } @@ -87,14 +89,16 @@ int TcpClient :: connect( int port, std::string hostname ) return soket_; } -int TcpClient :: writeBuffer(const void *buffer, long bufferSize, int flags ) +int TcpClient :: writeBuffer( const void *buffer, long bufferSize, int flags ) { if ( !isValid( soket_ ) ) return -1; return send( soket_, (const char *)buffer, bufferSize, flags ); } -int TcpClient :: readBuffer(void *buffer, long bufferSize, int flags ) +int TcpClient :: readBuffer( void *buffer, long bufferSize, int flags ) { if ( !isValid( soket_ ) ) return -1; return recv( soket_, (char *)buffer, bufferSize, flags ); } + +} // stk namespace diff --git a/src/TcpServer.cpp b/src/TcpServer.cpp index d2207de..7f413f4 100644 --- a/src/TcpServer.cpp +++ b/src/TcpServer.cpp @@ -19,12 +19,14 @@ 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 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "TcpServer.h" +namespace stk { + TcpServer :: TcpServer( int port ) { // Create a socket server. @@ -93,3 +95,5 @@ int TcpServer :: readBuffer(void *buffer, long bufferSize, int flags ) if ( !isValid( soket_ ) ) return -1; return recv( soket_, (char *)buffer, bufferSize, flags ); } + +} // stk namespace diff --git a/src/Thread.cpp b/src/Thread.cpp index e44d6eb..2d49171 100644 --- a/src/Thread.cpp +++ b/src/Thread.cpp @@ -2,17 +2,28 @@ /*! \class Thread \brief STK thread class. - This class provides a uniform interface for - cross-platform thread use. On Linux and IRIX - systems, the pthread library is used. Under Windows, - the Windows thread library is 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 - 2007. + 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 - 2009. */ /***************************************************/ #include "Thread.h" +namespace stk { + Thread :: Thread() { thread_ = 0; @@ -91,3 +102,5 @@ void Thread :: testCancel(void) #endif } + +} // stk namespace diff --git a/src/TubeBell.cpp b/src/TubeBell.cpp index 13b1bf6..43cd416 100644 --- a/src/TubeBell.cpp +++ b/src/TubeBell.cpp @@ -26,19 +26,21 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "TubeBell.h" -TubeBell :: TubeBell() +namespace stk { + +TubeBell :: TubeBell( void ) : FM() { // Concatenate the STK rawwave path to the rawwave files 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 ); + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); this->setRatio(0, 1.0 * 0.995); this->setRatio(1, 1.414 * 0.995); @@ -59,11 +61,11 @@ TubeBell :: TubeBell() vibrato_.setFrequency( 2.0 ); } -TubeBell :: ~TubeBell() +TubeBell :: ~TubeBell( void ) { } -void TubeBell :: noteOn(StkFloat frequency, StkFloat amplitude) +void TubeBell :: noteOn( StkFloat frequency, StkFloat amplitude ) { gains_[0] = amplitude * fmGains_[94]; gains_[1] = amplitude * fmGains_[76]; @@ -78,26 +80,4 @@ void TubeBell :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -StkFloat TubeBell :: computeSample() -{ - StkFloat temp, temp2; - - 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_[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_; - temp = temp * (1.0 + temp2); - - lastOutput_ = temp * 0.5; - return lastOutput_; -} +} // stk namespace diff --git a/src/TwoPole.cpp b/src/TwoPole.cpp index 27c82b9..9ffcf60 100644 --- a/src/TwoPole.cpp +++ b/src/TwoPole.cpp @@ -2,25 +2,28 @@ /*! \class TwoPole \brief STK two-pole filter class. - This protected Filter subclass implements - a two-pole digital filter. A method is - provided for creating a resonance in the - frequency response while maintaining a nearly - constant filter gain. + This class implements a two-pole digital filter. A method is + provided for creating a resonance in the frequency response while + maintaining a nearly constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "TwoPole.h" -#include +#include -TwoPole :: TwoPole() : Filter() +namespace stk { + +TwoPole :: TwoPole( void ) { - std::vector b(1, 1.0); - std::vector a(3, 0.0); - a[0] = 1.0; - Filter::setCoefficients( b, a ); + b_.resize( 1 ); + a_.resize( 3 ); + inputs_.resize( 1, 1, 0.0 ); + outputs_.resize( 3, 1, 0.0 ); + b_[0] = 1.0; + a_[0] = 1.0; + Stk::addSampleRateAlert( this ); } @@ -37,27 +40,7 @@ void TwoPole :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) } } -void TwoPole :: clear(void) -{ - Filter::clear(); -} - -void TwoPole :: setB0(StkFloat b0) -{ - b_[0] = b0; -} - -void TwoPole :: setA1(StkFloat a1) -{ - a_[1] = a1; -} - -void TwoPole :: setA2(StkFloat a2) -{ - a_[2] = a2; -} - -void TwoPole :: setResonance(StkFloat frequency, StkFloat radius, bool normalize) +void TwoPole :: setResonance( StkFloat frequency, StkFloat radius, bool normalize ) { a_[2] = radius * radius; a_[1] = (StkFloat) -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate()); @@ -70,32 +53,13 @@ void TwoPole :: setResonance(StkFloat frequency, StkFloat radius, bool normalize } } -void TwoPole :: setGain(StkFloat gain) +void TwoPole :: setCoefficients( StkFloat b0, StkFloat a1, StkFloat a2, bool clearState ) { - Filter::setGain(gain); + b_[0] = b0; + a_[1] = a1; + a_[2] = a2; + + if ( clearState ) this->clear(); } -StkFloat TwoPole :: getGain(void) const -{ - return Filter::getGain(); -} - -StkFloat TwoPole :: lastOut(void) const -{ - return Filter::lastOut(); -} - -StkFloat TwoPole :: tick( StkFloat input ) -{ - inputs_[0] = gain_ * input; - 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]; -} - -StkFrames& TwoPole :: tick( StkFrames& frames, unsigned int channel ) -{ - return Filter::tick( frames, channel ); -} +} // stk namespace diff --git a/src/TwoZero.cpp b/src/TwoZero.cpp index 9efb942..2ced9e6 100644 --- a/src/TwoZero.cpp +++ b/src/TwoZero.cpp @@ -2,25 +2,25 @@ /*! \class TwoZero \brief STK two-zero filter class. - This protected Filter subclass implements - a two-zero digital filter. A method is - provided for creating a "notch" in the - frequency response while maintaining a - constant filter gain. + This class implements a two-zero digital filter. A method is + provided for creating a "notch" in the frequency response while + maintaining a constant filter gain. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "TwoZero.h" -#include +#include -TwoZero :: TwoZero() : Filter() +namespace stk { + +TwoZero :: TwoZero( void ) { - std::vector b(3, 0.0); - b[0] = 1.0; - std::vector a(1, 1.0); - Filter::setCoefficients( b, a ); + b_.resize( 3, 0.0 ); + inputs_.resize( 3, 1, 0.0 ); + b_[0] = 1.0; + Stk::addSampleRateAlert( this ); } @@ -37,66 +37,27 @@ void TwoZero :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) } } -void TwoZero :: clear(void) -{ - Filter::clear(); -} - -void TwoZero :: setB0(StkFloat b0) +void TwoZero :: setCoefficients( StkFloat b0, StkFloat b1, StkFloat b2, bool clearState ) { b_[0] = b0; -} - -void TwoZero :: setB1(StkFloat b1) -{ b_[1] = b1; -} - -void TwoZero :: setB2(StkFloat b2) -{ b_[2] = b2; + + if ( clearState ) this->clear(); } -void TwoZero :: setNotch(StkFloat frequency, StkFloat radius) +void TwoZero :: setNotch( StkFloat frequency, StkFloat radius ) { 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_[0] = 1.0 / ( 1.0 - b_[1] + b_[2] ); b_[1] *= b_[0]; b_[2] *= b_[0]; } -void TwoZero :: setGain(StkFloat gain) -{ - Filter::setGain(gain); -} - -StkFloat TwoZero :: getGain(void) const -{ - return Filter::getGain(); -} - -StkFloat TwoZero :: lastOut(void) const -{ - return Filter::lastOut(); -} - -StkFloat TwoZero :: tick( StkFloat input ) -{ - inputs_[0] = gain_ * input; - 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]; -} - -StkFrames& TwoZero :: tick( StkFrames& frames, unsigned int channel ) -{ - return Filter::tick( frames, channel ); -} +} // stk namespace diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp index 18e9e42..7a7605b 100644 --- a/src/UdpSocket.cpp +++ b/src/UdpSocket.cpp @@ -17,11 +17,14 @@ read/write methods. Values less than or equal to zero indicate the occurence of an error. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "UdpSocket.h" +#include + +namespace stk { UdpSocket :: UdpSocket(int port ) { @@ -102,3 +105,5 @@ int UdpSocket :: writeBufferTo( const void *buffer, long bufferSize, int port, s this->setAddress( &address, port, hostname ); return sendto( soket_, (const char *)buffer, bufferSize, flags, (struct sockaddr *)&address, sizeof(address) ); } + +} // stk namespace diff --git a/src/Vector3D.cpp b/src/Vector3D.cpp deleted file mode 100644 index 8377432..0000000 --- a/src/Vector3D.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/***************************************************/ -/*! \class Vector3D - \brief STK 3D vector class. - - This class implements a three-dimensional vector. - - by Perry R. Cook, 1995 - 2004. -*/ -/***************************************************/ - -#include "Vector3D.h" -#include - -Vector3D :: Vector3D(StkFloat initX, StkFloat initY, StkFloat initZ) -{ - myX_ = initX; - myY_ = initY; - myZ_ = initZ; -} - -Vector3D :: ~Vector3D() -{ -} - -StkFloat Vector3D :: getX() -{ - return myX_; -} - -StkFloat Vector3D :: getY() -{ - return myY_; -} - -StkFloat Vector3D :: getZ() -{ - return myZ_; -} - -StkFloat Vector3D :: getLength() -{ - StkFloat temp; - temp = myX_ * myX_; - temp += myY_ * myY_; - temp += myZ_ * myZ_; - temp = sqrt(temp); - return temp; -} - -void Vector3D :: setXYZ(StkFloat x, StkFloat y, StkFloat z) -{ - myX_ = x; - myY_ = y; - myZ_ = z; -}; - -void Vector3D :: setX(StkFloat x) -{ - myX_ = x; -} - -void Vector3D :: setY(StkFloat y) -{ - myY_ = y; -} - -void Vector3D :: setZ(StkFloat z) -{ - myZ_ = z; -} - - diff --git a/src/VoicForm.cpp b/src/VoicForm.cpp index af12cd8..75e6e4e 100644 --- a/src/VoicForm.cpp +++ b/src/VoicForm.cpp @@ -21,45 +21,47 @@ - Vibrato Gain = 1 - Loudness (Spectral Tilt) = 128 - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "VoicForm.h" #include "Phonemes.h" #include "SKINI.msg" - +#include #include -VoicForm :: VoicForm() : Instrmnt() +namespace stk { + +VoicForm :: VoicForm( void ) : 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 ); for ( int i=0; i<4; i++ ) filters_[i].setSweepRate( 0.001 ); - onezero_.setZero( -0.9 ); - onepole_.setPole( 0.9 ); + onezero_.setZero( -0.9 ); + onepole_.setPole( 0.9 ); - noiseEnv_.setRate( 0.001 ); - noiseEnv_.setTarget( 0.0 ); + noiseEnv_.setRate( 0.001 ); + noiseEnv_.setTarget( 0.0 ); - this->setPhoneme( "eee" ); - this->clear(); + this->setPhoneme( "eee" ); + this->clear(); } -VoicForm :: ~VoicForm() +VoicForm :: ~VoicForm( void ) { - delete voiced_; + delete voiced_; } -void VoicForm :: clear() +void VoicForm :: clear( void ) { - onezero_.clear(); - onepole_.clear(); + onezero_.clear(); + onepole_.clear(); for ( int i=0; i<4; i++ ) { filters_[i].clear(); } @@ -74,16 +76,16 @@ void VoicForm :: setFrequency( StkFloat frequency ) freakency = 220.0; } - voiced_->setFrequency( freakency ); + voiced_->setFrequency( freakency ); } bool VoicForm :: setPhoneme( const char *phoneme ) { - bool found = false; + bool found = false; unsigned int i = 0; - while( i < 32 && !found ) { - if ( !strcmp( Phonemes::name(i), phoneme ) ) { - found = true; + 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) ); @@ -94,29 +96,19 @@ bool VoicForm :: setPhoneme( const char *phoneme ) errorString_ << "VoicForm::setPhoneme: found formant " << phoneme << " (number " << i << ")."; handleError( StkError::DEBUG_WARNING ); #endif - } - i++; - } + } + i++; + } - if ( !found ) { + if ( !found ) { errorString_ << "VoicForm::setPhoneme: phoneme " << phoneme << " not found!"; handleError( StkError::WARNING ); } - return found; + return found; } -void VoicForm :: setVoiced(StkFloat vGain) -{ - voiced_->setGainTarget(vGain); -} - -void VoicForm :: setUnVoiced(StkFloat nGain) -{ - noiseEnv_.setTarget(nGain); -} - -void VoicForm :: setFilterSweepRate(unsigned int whichOne, StkFloat rate) +void VoicForm :: setFilterSweepRate( unsigned int whichOne, StkFloat rate ) { if ( whichOne < 0 || whichOne > 3 ) { errorString_ << "VoicForm::setFilterSweepRate: filter select argument outside range 0-3!"; @@ -124,57 +116,23 @@ void VoicForm :: setFilterSweepRate(unsigned int whichOne, StkFloat rate) return; } - filters_[whichOne].setSweepRate(rate); + filters_[whichOne].setSweepRate(rate); } -void VoicForm :: setPitchSweepRate(StkFloat rate) +void VoicForm :: quiet( void ) { - voiced_->setSweepRate(rate); + voiced_->noteOff(); + noiseEnv_.setTarget( 0.0 ); } -void VoicForm :: speak() +void VoicForm :: noteOn( StkFloat frequency, StkFloat amplitude ) { - voiced_->noteOn(); + this->setFrequency( frequency ); + voiced_->setGainTarget( amplitude ); + onepole_.setPole( 0.97 - (amplitude * 0.2) ); } -void VoicForm :: quiet() -{ - voiced_->noteOff(); - noiseEnv_.setTarget( 0.0 ); -} - -void VoicForm :: noteOn(StkFloat frequency, StkFloat amplitude) -{ - this->setFrequency( frequency ); - voiced_->setGainTarget( amplitude ); - onepole_.setPole( 0.97 - (amplitude * 0.2) ); -} - -void VoicForm :: noteOff(StkFloat amplitude) -{ - this->quiet(); -} - -StkFloat VoicForm :: computeSample() -{ - 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_); - */ - return lastOutput_; -} - -void VoicForm :: controlChange(int number, StkFloat value) +void VoicForm :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -188,54 +146,56 @@ void VoicForm :: controlChange(int number, StkFloat value) handleError( StkError::WARNING ); } - if (number == __SK_Breath_) { // 2 - this->setVoiced( 1.0 - norm ); - this->setUnVoiced( 0.01 * norm ); - } - else if (number == __SK_FootControl_) { // 4 + if (number == __SK_Breath_) { // 2 + this->setVoiced( 1.0 - norm ); + this->setUnVoiced( 0.01 * norm ); + } + else if (number == __SK_FootControl_) { // 4 StkFloat temp = 0.0; - unsigned int i = (int) value; - if (i < 32) { + unsigned int i = (int) value; + if (i < 32) { temp = 0.9; - } - else if (i < 64) { + } + else if (i < 64) { i -= 32; temp = 1.0; - } - else if (i < 96) { + } + else if (i < 96) { i -= 64; temp = 1.1; - } - else if (i < 128) { + } + else if (i < 128) { i -= 96; temp = 1.2; - } - else if (i == 128) { + } + else if (i == 128) { 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) ); 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 - else if (number == __SK_ModWheel_) // 1 - voiced_->setVibratoGain( norm * 0.2); - else if (number == __SK_AfterTouch_Cont_) { // 128 - this->setVoiced( norm ); - onepole_.setPole( 0.97 - ( norm * 0.2) ); - } + } + else if (number == __SK_ModFrequency_) // 11 + voiced_->setVibratoRate( norm * 12.0); // 0 to 12 Hz + else if (number == __SK_ModWheel_) // 1 + voiced_->setVibratoGain( norm * 0.2); + else if (number == __SK_AfterTouch_Cont_) { // 128 + this->setVoiced( norm ); + onepole_.setPole( 0.97 - ( norm * 0.2) ); + } else { errorString_ << "VoicForm::controlChange: undefined control number (" << number << ")!"; handleError( StkError::WARNING ); } #if defined(_STK_DEBUG_) - errorString_ << "VoicForm::controlChange: number = " << number << ", value = " << value << '.'; - handleError( StkError::DEBUG_WARNING ); + errorString_ << "VoicForm::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } + +} // stk namespace diff --git a/src/Voicer.cpp b/src/Voicer.cpp index 396b65c..5814fbf 100644 --- a/src/Voicer.cpp +++ b/src/Voicer.cpp @@ -2,54 +2,54 @@ /*! \class Voicer \brief STK voice manager class. - This class can be used to manage a group of - STK instrument classes. Individual voices can - be controlled via unique note tags. - Instrument groups can be controlled by channel - number. + This class can be used to manage a group of STK instrument + classes. Individual voices can be controlled via unique note + tags. Instrument groups can be controlled by group number. - A previously constructed STK instrument class - is linked with a voice manager using the - addInstrument() function. An optional channel - number argument can be specified to the - addInstrument() function as well (default - channel = 0). The voice manager does not - delete any instrument instances ... it is the - responsibility of the user to allocate and - deallocate all instruments. + A previously constructed STK instrument class is linked with a + voice manager using the addInstrument() function. An optional + group number argument can be specified to the addInstrument() + function as well (default group = 0). The voice manager does not + delete any instrument instances ... it is the responsibility of + the user to allocate and deallocate all instruments. - The tick() function returns the mix of all - sounding voices. Each noteOn returns a unique - tag (credits to the NeXT MusicKit), so you can - send control changes to specific voices within - an ensemble. Alternately, control changes can - be sent to all voices on a given channel. + The tick() function returns the mix of all sounding voices. Each + noteOn returns a unique tag (credits to the NeXT MusicKit), so you + can send control changes to specific voices within an ensemble. + Alternately, control changes can be sent to all voices in a given + group. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Voicer.h" -#include -#include +#include + +namespace stk { Voicer :: Voicer( StkFloat decayTime ) { tags_ = 23456; muteTime_ = (int) ( decayTime * Stk::sampleRate() ); + lastFrame_.resize( 1, 1, 0.0 ); } -Voicer :: ~Voicer() -{ -} - -void Voicer :: addInstrument( Instrmnt *instrument, int channel ) +void Voicer :: addInstrument( Instrmnt *instrument, int group ) { Voicer::Voice voice; voice.instrument = instrument; - voice.channel = channel; + voice.group = group; voice.noteNumber = -1; voices_.push_back( voice ); + + // Check output channels and resize lastFrame_ if necessary. + if ( instrument->channelsOut() > lastFrame_.channels() ) { + unsigned int startChannel = lastFrame_.channels(); + lastFrame_.resize( 1, instrument->channelsOut() ); + for ( unsigned int i=startChannel; ichannelsOut() > maxChannels ) maxChannels = (*i).instrument->channelsOut(); + } + if ( maxChannels < lastFrame_.channels() ) + lastFrame_.resize( 1, maxChannels ); + } + else { errorString_ << "Voicer::removeInstrument: instrument pointer not found in current voices!"; handleError( StkError::WARNING ); } } -long Voicer :: noteOn(StkFloat noteNumber, StkFloat amplitude, int channel ) +long Voicer :: noteOn(StkFloat noteNumber, StkFloat amplitude, int group ) { 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].instrument->noteOn( frequency, amplitude * ONE_OVER_128 ); voices_[i].sounding = 1; return voices_[i].tag; } @@ -88,7 +97,7 @@ long Voicer :: noteOn(StkFloat noteNumber, StkFloat amplitude, int channel ) // 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].group = group; voices_[voice].noteNumber = noteNumber; voices_[voice].frequency = frequency; voices_[voice].instrument->noteOn( frequency, amplitude * ONE_OVER_128 ); @@ -107,10 +116,10 @@ long Voicer :: noteOn(StkFloat noteNumber, StkFloat amplitude, int channel ) return -1; } -void Voicer :: noteOff( StkFloat noteNumber, StkFloat amplitude, int channel ) +void Voicer :: noteOff( StkFloat noteNumber, StkFloat amplitude, int group ) { for ( unsigned int i=0; inoteOff( amplitude * ONE_OVER_128 ); voices_[i].sounding = -muteTime_; } @@ -128,11 +137,11 @@ void Voicer :: noteOff( long tag, StkFloat amplitude ) } } -void Voicer :: setFrequency( StkFloat noteNumber, int channel ) +void Voicer :: setFrequency( StkFloat noteNumber, int group ) { StkFloat frequency = (StkFloat) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); for ( unsigned int i=0; isetFrequency( frequency ); @@ -153,7 +162,7 @@ void Voicer :: setFrequency( long tag, StkFloat noteNumber ) } } -void Voicer :: pitchBend( StkFloat value, int channel ) +void Voicer :: pitchBend( StkFloat value, int group ) { StkFloat pitchScaler; if ( value < 64.0 ) @@ -161,7 +170,7 @@ void Voicer :: pitchBend( StkFloat value, int channel ) else pitchScaler = pow(2.0, (value-64.0)/64.0); for ( unsigned int i=0; isetFrequency( (StkFloat) (voices_[i].frequency * pitchScaler) ); } } @@ -181,10 +190,10 @@ void Voicer :: pitchBend( long tag, StkFloat value ) } } -void Voicer :: controlChange( int number, StkFloat value, int channel ) +void Voicer :: controlChange( int number, StkFloat value, int group ) { for ( unsigned int i=0; icontrolChange( number, value ); } } @@ -207,72 +216,4 @@ void Voicer :: silence( void ) } } -StkFloat Voicer :: tick() -{ - 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; - } - } - return lastOutput_ / voices_.size(); -} - -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 +namespace stk { + const int CAN_RADIUS = 100; const int PEA_RADIUS = 30; const int BUMP_RADIUS = 5; @@ -33,7 +35,7 @@ const StkFloat SLOW_TICK_SIZE = 0.0001; const StkFloat ENV_RATE = 0.001; -Whistle :: Whistle() +Whistle :: Whistle( void ) { sine_.setFrequency( 2800.0 ); @@ -54,31 +56,31 @@ Whistle :: Whistle() envelope_.setRate( ENV_RATE ); envelope_.keyOn(); - fippleFreqMod_ = 0.5; - fippleGainMod_ = 0.5; - blowFreqMod_ = 0.25; - noiseGain_ = 0.125; - baseFrequency_ = 2000; + fippleFreqMod_ = 0.5; + fippleGainMod_ = 0.5; + blowFreqMod_ = 0.25; + noiseGain_ = 0.125; + baseFrequency_ = 2000; - tickSize_ = NORM_TICK_SIZE; - canLoss_ = NORM_CAN_LOSS; + tickSize_ = NORM_TICK_SIZE; + canLoss_ = NORM_CAN_LOSS; - subSample_ = 1; - subSampCount_ = subSample_; + subSample_ = 1; + subSampCount_ = subSample_; } -Whistle :: ~Whistle() +Whistle :: ~Whistle( void ) { #ifdef WHISTLE_ANIMATION printf("Exit, Whistle bye bye!!\n"); #endif } -void Whistle :: clear() +void Whistle :: clear( void ) { } -void Whistle :: setFrequency(StkFloat frequency) +void Whistle :: setFrequency( StkFloat frequency ) { StkFloat freakency = frequency * 4; // the whistle is a transposing instrument if ( frequency <= 0.0 ) { @@ -90,19 +92,19 @@ void Whistle :: setFrequency(StkFloat frequency) baseFrequency_ = freakency; } -void Whistle :: startBlowing(StkFloat amplitude, StkFloat 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(StkFloat rate) +void Whistle :: stopBlowing( StkFloat rate ) { envelope_.setRate( rate ); envelope_.keyOff(); } -void Whistle :: noteOn(StkFloat frequency, StkFloat amplitude) +void Whistle :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->startBlowing( amplitude*2.0 ,amplitude * 0.2 ); @@ -112,7 +114,7 @@ void Whistle :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -void Whistle :: noteOff(StkFloat amplitude) +void Whistle :: noteOff( StkFloat amplitude ) { this->stopBlowing( amplitude * 0.02 ); @@ -124,17 +126,17 @@ void Whistle :: noteOff(StkFloat amplitude) int frameCount = 0; -StkFloat Whistle :: computeSample() +StkFloat Whistle :: tick( unsigned int ) { 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_) ) { @@ -151,7 +153,7 @@ StkFloat Whistle :: computeSample() 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); @@ -164,7 +166,7 @@ StkFloat Whistle :: computeSample() tempFreq *= baseFrequency_; sine_.setFrequency(tempFreq); - + tempVectorP_ = pea_.getPosition(); temp = can_.isInside(tempVectorP_); temp = -temp; // We know (hope) it's inside, just how much?? @@ -202,22 +204,22 @@ StkFloat Whistle :: computeSample() tempX = 0.0; 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_ ); // 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 + temp = envOut * envOut * gain / 2; + soundMix = temp * ( sine_.tick() + ( noiseGain_*noise_.tick() ) ); + lastFrame_[0] = 0.20 * soundMix; // should probably do one-zero filter here - return lastOutput_; + return lastFrame_[0]; } -void Whistle :: controlChange(int number, StkFloat value) +void Whistle :: controlChange( int number, StkFloat value ) { StkFloat norm = value * ONE_OVER_128; if ( norm < 0 ) { @@ -252,8 +254,9 @@ void Whistle :: controlChange(int number, StkFloat value) } #if defined(_STK_DEBUG_) - errorString_ << "Whistle::controlChange: number = " << number << ", value = " << value << '.'; - handleError( StkError::DEBUG_WARNING ); + errorString_ << "Whistle::controlChange: number = " << number << ", value = " << value << '.'; + handleError( StkError::DEBUG_WARNING ); #endif } +} // stk namespace diff --git a/src/Wurley.cpp b/src/Wurley.cpp index d560e5d..1f1412e 100644 --- a/src/Wurley.cpp +++ b/src/Wurley.cpp @@ -26,19 +26,21 @@ type who should worry about this (making money) worry away. - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. + by Perry R. Cook and Gary P. Scavone, 1995 - 2009. */ /***************************************************/ #include "Wurley.h" -Wurley :: Wurley() +namespace stk { + +Wurley :: Wurley( void ) : FM() { // Concatenate the STK rawwave path to the rawwave files 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 ); + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); this->setRatio(0, 1.0); this->setRatio(1, 4.0); @@ -59,11 +61,11 @@ Wurley :: Wurley() vibrato_.setFrequency( 8.0 ); } -Wurley :: ~Wurley() +Wurley :: ~Wurley( void ) { } -void Wurley :: setFrequency(StkFloat frequency) +void Wurley :: setFrequency( StkFloat frequency ) { baseFrequency_ = frequency; waves_[0]->setFrequency( baseFrequency_ * ratios_[0]); @@ -72,7 +74,7 @@ void Wurley :: setFrequency(StkFloat frequency) waves_[3]->setFrequency( ratios_[3] ); } -void Wurley :: noteOn(StkFloat frequency, StkFloat amplitude) +void Wurley :: noteOn( StkFloat frequency, StkFloat amplitude ) { gains_[0] = amplitude * fmGains_[99]; gains_[1] = amplitude * fmGains_[82]; @@ -87,27 +89,4 @@ void Wurley :: noteOn(StkFloat frequency, StkFloat amplitude) #endif } -StkFloat Wurley :: computeSample() -{ - StkFloat temp, temp2; - - 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_[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_; - temp = temp * (1.0 + temp2); - - lastOutput_ = temp * 0.5; - return lastOutput_; -} - +} // stk namespace diff --git a/src/WvIn.cpp b/src/WvIn.cpp deleted file mode 100644 index 10f5324..0000000 --- a/src/WvIn.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/***************************************************/ -/*! \class WvIn - \brief STK audio input abstract base class. - - This class provides common functionality for a variety of audio - data input subclasses. - - WvIn supports multi-channel data. It is important to distinguish - the tick() methods, which return samples produced by averaging - across sample frames, from the tickFrame() methods, which return - references or pointers to multi-channel sample frames. - - Both interleaved and non-interleaved data is supported via the use - of StkFrames objects. - - by Perry R. Cook and Gary P. Scavone, 1995 - 2007. -*/ -/***************************************************/ - -#include "WvIn.h" -#include - -WvIn :: WvIn() -{ -} - -WvIn :: ~WvIn() -{ -} - -StkFloat WvIn :: lastOut( void ) const -{ - if ( lastOutputs_.empty() ) return 0.0; - - if ( lastOutputs_.size() == 1 ) - return lastOutputs_[0]; - - StkFloat output = 0.0; - for ( unsigned int i=0; i= frames.channels() ) { - errorString_ << "WvIn::tick(): channel and StkFrames arguments are incompatible!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( frames.channels() == 1 ) { - for ( unsigned int i=0; icomputeFrame(); - for ( j=0; jcomputeFrame(); - index = i; - for ( j=0; j 1.0 ) { - sample = 1.0; - clip = true; - } - else if ( sample < -1.0 ) { - sample = -1.0; - clip = true; - } - - if ( clip == true && clipping_ == false ) { - // First occurrence of clipping since instantiation or reset. - clipping_ = true; - errorString_ << "WvOut: data value(s) outside +-1.0 detected ... clamping at outer bound!"; - handleError( StkError::WARNING ); - } - - return sample; -} - -void WvOut :: tick( const StkFloat sample ) -{ - this->computeSample( sample ); -} - -void WvOut :: tick( const StkFrames& frames, unsigned int channel ) -{ - if ( channel >= frames.channels() ) { - errorString_ << "WvOut::tick(): channel argument (" << channel << ") is incompatible with StkFrames argument!"; - handleError( StkError::FUNCTION_ARGUMENT ); - } - - if ( frames.channels() == 1 ) { - for ( unsigned int i=0; icomputeFrames( frames ); -}