From: IOhannes m zmölnig Date: Thu, 25 Nov 2010 14:09:54 +0000 (+0100) Subject: Imported Upstream version 1.0.5.patch2 X-Git-Tag: archive/raspbian/2.5.1+ds-1+rpi1~1^2~378 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=2b0b65a13090069d8a71c9d2cabf10471857404c;p=jacktrip.git Imported Upstream version 1.0.5.patch2 --- 2b0b65a13090069d8a71c9d2cabf10471857404c diff --git a/CHANGESLOG.txt b/CHANGESLOG.txt new file mode 100644 index 0000000..1849861 --- /dev/null +++ b/CHANGESLOG.txt @@ -0,0 +1,40 @@ +--- +1.0.5 +- (added) Compatibility with JamLink boxes (restricted at the moment to 48KHz, 64 buffer size and 1 channel) +- (added) New port structure that allows the communication between a public server and a local client +- (added) Option for packets without header +- (added) Option to change default client name +- (fixed) General optimizations and code cleanup +- (added) Improved, now cross-platform build script + +--- +1.0.4 +- (fixed) Buss error caused when no physical inputs or outputs ports are available + +--- +1.0.3 +- (added) Redundancy Algorithm for UDP Packets to to avoid glitches with packet losses +- (fixed) Now compiles on 64bits machines +- (fixed) Improved exceptions handling +- (added) Basic Karplus-Strong model added as Plug-in +- (added) Some functionality reimplemented using signals and slots for + more flexibility +- (added) Multiple-Client-Server in alpha testing, expect it working in the next release + +--- +1.0.2 Alpha +- (added) Port offset mode, to use a different UDP port than the default one. +- (fixed) Improved thread behavior + +--- +1.0.1 Alpha +- (added) jamlink mode to connect with jamlink boxes +- (fixed) thread priority in both Linux and Mac OS X (still need some work on the Mac OS X version) +- (fixed) Bug that was causing plug-ins not to behave correctly +- (added) Loopback mode +- (added) Underrun Modes: Wavetable (default) and set to zeros +- (added) Check for peer audio settings, program exists if they don't match +- (added) Automatically connect ports to available physical audio interface. + +--- +1.0 Alpha - initial release diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 0000000..cc95c0f --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,69 @@ +Jacktrip : Build Instructions for Linux and MacOS X + +JackTrip: A System for High-Quality Audio Network Performance over the Internet. + +--- +MacOS X (10.3.9 or higher) Installation: + +If you are installing on MacOS X, a universal binary is provided. You just need to have JackOSX installed on your machine +http://www.jackosx.com/ + +To install (using Terminal): go to bin/ directory and type: + + sudo cp jacktrip /usr/bin/ + (enter your password when prompted) + + sudo chmod 755 /usr/bin/jacktrip + (now you can run jacktrip from any directory using Terminal) + + +--- +Dependencies: + +You need to have installed the libraries in your system: +qt4-devel +jack-audio-connection-kit-devel + +If you are using yum (in Fedora 8 or later) you can just install them (as root) with: + yum install qt4-devel jack-audio-connection-kit-devel + +If you want to build on MacOS X, you need JackOSX +http://www.jackosx.com/ +and Qt 4.5 or higher. +http://trolltech.com/products/qt/ + + +--- +Build: + +If you're on Mac OS X or Fedora Linux and have all the dependencies installed, +you can build by simply going to the /src directory and typing the following: + ./build + + +If the previous script doesn't work on a different Linux flavor, try building +the Makfiles yourself. You'd need qmake (e.g., on Fedora, this command is called +qmake-qt4). Then you can build by: + qmake jacktrip.pro + make release + + +If you want to install install (using Terminal): on the /src directory type: + + sudo cp jacktrip /usr/bin/ + (enter your password when prompted) + + sudo chmod 755 /usr/bin/jacktrip + (now you can run jacktrip from any directory using Terminal) + + +--- +Post Configuration +Detailed instructions at +http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ + + +--- +Using JackTrip +Detailed instructions at +http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..9b5e4d7 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,19 @@ +High Priority TODOS: +-------------------- + +- Add redundancy to UDP (DONE) +- Finish header implementation, add run-time check + + +Plug-ins: +--------- +- Extend Plugin structure to include more than 1 plug-in and add the mode for local effect (not loopback) +- add the offset option to process starting from a different channel +- Set the faust compiler to automatically generate plugins +- Add low latency compression www.celt-codec.org + +Protocol: +--------- +- Add TCP clacc +- Add SCTP class +- Maybe add a layer of OSC communication for control messages diff --git a/documentation/documentation.cpp b/documentation/documentation.cpp new file mode 100644 index 0000000..54908c2 --- /dev/null +++ b/documentation/documentation.cpp @@ -0,0 +1,124 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file documentation.cpp + * \author Juan-Pablo Caceres + * \date July 2008 + */ + + +// Main Page Documentation +//----------------------------------------------------------------- +/** +\mainpage JackTrip Documentation + +\section intro_sec About JackTrip + +JackTrip: A System for High-Quality Audio Network Performance +over the Internet. + +JackTrip is a Linux and Mac OS X-based system used for multi-machine network +performance over the Internet. It supports any number of channels +(as many as the computer/network can handle) of bidirectional, high quality, +uncompressed audio signal steaming. + +You can use it between any combination of Linux and Mac OS X +(i.e., one end using Linux can connect to the other using Mac OS X). + +It is currently being developed and actively tested at CCRMA by the SoundWIRE group. + + + +\section install_sec Installation + +Download the latest release: +Download + +Please read the documentation inside the packet to install. + +\subsection install_subsec_osx Mac OS X Requirements + +You'll need: Jack OS X. +The documentation explains how to install it and set it up, and it's highly recommended. + +Jack OS X comes with JackPilot to do the audio routing. +An alternative is qjackctl, +which I find easier to use. You can find the binary here: +qjackctl mac binary + +If you use Leopard, you won't need to configure the UDP ports manually. + +\subsubsection install_linux Linux Requirements + +Please read the documentation inside the packet to compile and install in linux.\n +The older version of JackTrip is documented +here. +Please follow those instructions to configure the firewall under + + + +\section using Using JackTrip + +Type jacktrip in a terminal window to display a help list with all the options. +JackTrip uses Jack as its audio server. You have to make sure that the settings in +Jack are the same in the local and remote machine. + +There are two parameters that you want to tweak: Frames/Period and Sample Rate. +The Lower the Frames/Period, the lower the latency. +The higher the Sampling Rate, the higher the bandwidth requirements. +You have to make sure these settings match in both machines. + +\image html jack_main_settings.jpg + +You also may want to look at the internal buffering +-q, --queue parameter in JackTrip. If your connection is very unstable, with a lot of jitter, +you should increase this number at the expense of a higher latency. + +The audio bit resolution parameter, -b, --bitres, can be use to decrease (or increase) +the bandwidth requirements, at the expense of a lower audio quality. + +A basic connection will have one of the nodes as a server: + +jacktrip -s + +And the other as a client + +jacktrip -c [SERVER-IP-NUMBER] + +You'll see a JackTrip client in Jack. Everything you connect into the send ports +will be transmitted to your peer. You'll receive what your peer sends you on the receive ports. + +\image html jack_routing.png + +*/ + + diff --git a/documentation/html_footer.html b/documentation/html_footer.html new file mode 100644 index 0000000..979f8b8 --- /dev/null +++ b/documentation/html_footer.html @@ -0,0 +1,19 @@ + + +
+
+

 

+ +

Documentation generated by Doxygen $doxygenversion on $datetime

+

+© 2008 by Juan-Pablo Caceres (jcaceres at ccrma dot stanford dot edu) and +Chris Chafe
+SoundWIRE Group at +CCRMA - +Stanford University +
+

+SourceForge.net Logo +

+
+ diff --git a/documentation/img/jack_main_settings.jpg b/documentation/img/jack_main_settings.jpg new file mode 100644 index 0000000..822fde7 Binary files /dev/null and b/documentation/img/jack_main_settings.jpg differ diff --git a/documentation/img/jack_routing.png b/documentation/img/jack_routing.png new file mode 100644 index 0000000..77e42e2 Binary files /dev/null and b/documentation/img/jack_routing.png differ diff --git a/documentation/img/qjackctl.png b/documentation/img/qjackctl.png new file mode 100644 index 0000000..afde566 Binary files /dev/null and b/documentation/img/qjackctl.png differ diff --git a/faust-src/Makefile b/faust-src/Makefile new file mode 100644 index 0000000..14ffe57 --- /dev/null +++ b/faust-src/Makefile @@ -0,0 +1,99 @@ +all : jackgtk + +test: jackgtk alsagtk jackqt alsaqt ladspa ossgtk bench plot sndfile jackconsole + +svg: + $(MAKE) -f Makefile.svg + +puredata : + install -d puredatadir + $(MAKE) DEST='puredatadir/' ARCH='puredata.cpp' LIB='' -f Makefile.pdcompile + +alsagtk : + install -d alsagtkdir + $(MAKE) DEST='alsagtkdir/' ARCH='alsa-gtk.cpp' LIB='-lpthread -lasound `pkg-config --cflags --libs gtk+-2.0`' -f Makefile.compile + +jackgtk : + install -d jackgtkdir + $(MAKE) DEST='jackgtkdir/' ARCH='jack-gtk.cpp' LIB='`pkg-config --cflags --libs jack gtk+-2.0`' -f Makefile.compile + +jackqt : + install -d jackqtdir + $(MAKE) DEST='jackqtdir/' ARCH='jack-qt.cpp' LIB='-ljack' -f Makefile.qtcompile + +alsaqt : + install -d alsaqtdir + $(MAKE) DEST='alsaqtdir/' ARCH='alsa-qt.cpp' LIB='-lpthread -lasound' -f Makefile.qtcompile + +ladspa : + install -d ladspadir + $(MAKE) DEST='ladspadir/' ARCH='ladspa.cpp' LIB='-fPIC -shared' EXT='.so' -f Makefile.ladspacompile + +jackwx : + install -d jackwxdir + $(MAKE) DEST='jackwxdir/' ARCH='jack-wx.cpp' LIB='`pkg-config jack --cflags --libs` `wx-config --cflags --libs`' -f Makefile.compile + +ossgtk : + install -d ossgtkdir + $(MAKE) DEST='ossgtkdir/' ARCH='oss-gtk.cpp' LIB='-lpthread `pkg-config gtk+-2.0 --cflags --libs`' -f Makefile.compile + +osswx : + install -d osswxdir + $(MAKE) DEST='osswxdir/' ARCH='oss-wx.cpp' LIB='-lpthread `wx-config --cflags --libs`' -f Makefile.compile + +pagtk : + install -d pagtkdir + $(MAKE) DEST='pagtkdir/' ARCH='pa-gtk.cpp' LIB='-lpthread -lportaudio `pkg-config gtk+-2.0 --cflags --libs`' -f Makefile.compile + +pawx : + install -d pawxdir + $(MAKE) DEST='pawxdir/' ARCH='pa-wx.cpp' LIB='-lpthread -lportaudio `wx-config --cflags --libs`' -f Makefile.compile + +module : + install -d moduledir + $(MAKE) DEST='moduledir/' ARCH='module.cpp' LIB='-fPIC -shared' EXT='.so' -f Makefile.compile + +bundle : + install -d bundledir + $(MAKE) DEST='bundledir/' ARCH='module.cpp' LIB='-fPIC -bundle' EXT='.so' -f Makefile.compile + +msp : + install -d mspdir + $(MAKE) DEST='mspdir/' ARCH='max-msp.cpp' LIB='' -f Makefile.mspcompile + +vst : + install -d vstdir + $(MAKE) DEST='vstdir/' ARCH='vst.cpp' LIB='' -f Makefile.vstcompile + +bench : + install -d benchdir + $(MAKE) DEST='benchdir/' ARCH='bench.cpp' LIB='' -f Makefile.compile + +sndfile : + install -d sndfiledir + $(MAKE) DEST='sndfiledir/' ARCH='sndfile.cpp' LIB='-lsndfile' -f Makefile.compile + +plot : + install -d plotdir + $(MAKE) DEST='plotdir/' ARCH='plot.cpp' LIB='' -f Makefile.compile + +matlabplot : + install -d matlabplotdir + $(MAKE) DEST='matlabplotdir/' ARCH='matlabplot.cpp' LIB='' -f Makefile.compile + +q : + install -d qdir + $(MAKE) DEST='qdir/' ARCH='q.cpp' LIB='' -f Makefile.qcompile + +supercollider : + install -d supercolliderdir + $(MAKE) DEST='supercolliderdir/' ARCH='../architecture/supercollider.cpp' CXXFLAGS='`pkg-config --cflags libscsynth`' LIB='-fPIC -shared' EXT='.so' -f Makefile.sccompile + +jackconsole : + install -d jackconsoledir + $(MAKE) DEST='jackconsoledir/' ARCH='jack-console.cpp' LIB='`pkg-config --cflags --libs jack `' -f Makefile.compile + +clean : + rm -rf alsagtkdir jackgtkdir alsaqtdir jackqtdir vecalsagtkdir vecjackgtkdir ladspadir jackwxdir ossgtkdir osswxdir pagtkdir pawxdir moduledir bundledir mspdir vstdir benchdir sndfiledir plotdir benchdir supercolliderdir puredatadir qdir plotdir jackconsoledir matlabplotdir *-ps *-svg + + diff --git a/faust-src/Makefile.compile b/faust-src/Makefile.compile new file mode 100644 index 0000000..0681b10 --- /dev/null +++ b/faust-src/Makefile.compile @@ -0,0 +1,15 @@ +dspsrc := $(wildcard *.dsp) +cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) +appl := $(addprefix $(DEST), $(dspsrc:.dsp=$(EXT))) + + +all : $(appl) + + +$(DEST)%$(EXT) : %.dsp + faust $(VEC) -a $(ARCH) $< -o $@.cpp + $(CXX) -O3 $(CXXFLAGS) $(LIB) $@.cpp -o $@ + + +clean : + rm -f $(DEST) diff --git a/faust-src/Makefile.ladspacompile b/faust-src/Makefile.ladspacompile new file mode 100644 index 0000000..f9d7ec5 --- /dev/null +++ b/faust-src/Makefile.ladspacompile @@ -0,0 +1,22 @@ +DEST := ladspadir/ +dspsrc := $(wildcard *.dsp) +cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) +modules := $(addprefix $(DEST), $(dspsrc:%.dsp=%.so)) +os := $(shell uname) + +###allcpp: $(cppsrc) + +allmodules: $(modules) + +$(DEST)%.so: $(DEST)%.cpp +ifeq ($(os), Darwin) + $(CXX) -fPIC -bundle -O3 $(CXXFLAGS) -Dmydsp=$(patsubst %.so,%,$(notdir $@)) $< -o $@ +else + $(CXX) -fPIC -shared -O3 $(CXXFLAGS) -Dmydsp=$(patsubst %.so,%,$(notdir $@)) $< -o $@ +endif + +$(DEST)%.cpp: %.dsp + faust $(VEC) -a ladspa.cpp $< -o $@ + +clean: + rm -rf $(DEST) diff --git a/faust-src/Makefile.mspcompile b/faust-src/Makefile.mspcompile new file mode 100644 index 0000000..6d1572d --- /dev/null +++ b/faust-src/Makefile.mspcompile @@ -0,0 +1,44 @@ +dspsrc := $(wildcard *.dsp) +cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) +appl := $(addprefix $(DEST), $(dspsrc:.dsp=~.mxo)) +processor := $(shell uname -p) + +INC := -I/usr/local/include/c74support/max-includes -I/usr/local/include/c74support/msp-includes + +all : $(appl) + +$(DEST)%~.mxo : %.dsp Info.plist.template + install -d $@/Contents/MacOS + faust $(VEC) -a $(ARCH) $< -o $@/$(<:.dsp=.cpp) +ifeq ($(processor), i386) + g++ -arch i386 -fpascal-strings -fasm-blocks -g -O3 $(INC) -c $@/$(<:.dsp=.cpp) -o $@/$(<:.dsp=.i386.o) + g++ -framework MaxAPI -framework Carbon -framework MaxAudioAPI -arch i386 -Wl,-Y,1455 -bundle $@/$(<:.dsp=.i386.o) -o $@/$(<:.dsp=.i386~) + g++ -arch ppc -fpascal-strings -fasm-blocks -g -O3 $(INC) -c $@/$(<:.dsp=.cpp) -o $@/$(<:.dsp=.ppc.o) + g++ -framework Carbon -framework MaxAPI -framework MaxAudioAPI -arch ppc -Wl,-Y,1455 -bundle $@/$(<:.dsp=.ppc.o) -o $@/$(<:.dsp=.ppc~) + sed s/FOO/$(<:.dsp=~)/ $@/Contents/Info.plist + lipo -create $@/$(<:.dsp=.i386~) $@/$(<:.dsp=.ppc~) -output $@/Contents/MacOS/$(<:.dsp=~) + rm -f $@/$(<:.dsp=.ppc~) $@/$(<:.dsp=.ppc.o) $@/$(<:.dsp=.i386.o) $@/$(<:.dsp=.i386~) +else + g++ -arch ppc -fpascal-strings -fasm-blocks -g -O3 $(INC) -c $@/$(<:.dsp=.cpp) -o $@/$(<:.dsp=.ppc.o) + g++ -framework Carbon -framework MaxAPI -framework MaxAudioAPI -arch ppc -Wl,-Y,1455 -bundle $@/$(<:.dsp=.ppc.o) -o $@/$(<:.dsp=.ppc~) + sed s/FOO/$(<:.dsp=~)/ $@/Contents/Info.plist + lipo -create $@/$(<:.dsp=.ppc~) -output $@/Contents/MacOS/$(<:.dsp=~) + rm -f $@/$(<:.dsp=.ppc~) $@/$(<:.dsp=.ppc.o) +endif + +Info.plist.template : + echo '' > Info.plist.template + echo '' >> Info.plist.template + echo '' >> Info.plist.template + echo '' >> Info.plist.template + echo ' CFBundleExecutable' >> Info.plist.template + echo ' FOO' >> Info.plist.template + echo ' CFBundleName' >> Info.plist.template + echo ' FOO' >> Info.plist.template + echo ' CFBundlePackageType' >> Info.plist.template + echo ' iLaX' >> Info.plist.template + echo '' >> Info.plist.template + echo '' >> Info.plist.template + +clean : + rm -f $(DEST) diff --git a/faust-src/Makefile.pdcompile b/faust-src/Makefile.pdcompile new file mode 100644 index 0000000..c591c66 --- /dev/null +++ b/faust-src/Makefile.pdcompile @@ -0,0 +1,45 @@ +DEST := pddir/ +dspsrc := $(wildcard *.dsp) +cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) +modules := $(addprefix $(DEST), $(dspsrc:.dsp=~.pd_linux)) +patches := $(addprefix $(DEST), $(dspsrc:.dsp=.pd)) +FAUST2PD := faust2pd +F2PDFLAGS := -r 10 -s + +LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -fPIC \ + -Wall -W -Wshadow -Wno-unused -Wno-parentheses -Wno-switch $(CFLAGS) + +LINUXINCLUDE = + + +###-------------------------------------------- +### Will use faust2pd to create the GUI patches +### only if it is installed + +helper:=$(shell whereis faust2pd) + +ifeq ($(helper),faust2pd:) + todo:=$(modules) +else + todo:=$(modules) $(patches) +endif + +###-------------------------------------------- + + +allmodules: $(todo) + +$(DEST)%~.pd_linux: $(DEST)%.cpp + $(CXX) $(LINUXCFLAGS) $(LINUXINCLUDE) -shared -Dmydsp=$(patsubst %~.pd_linux,%,$(notdir $@)) $< -o $@ + +$(DEST)%.cpp: %.dsp + faust -a $(ARCH) $< -o $@ + +$(DEST)%.pd: %.dsp + faust -xml $< -o /dev/null + $(FAUST2PD) $(F2PDFLAGS) $<.xml + mv $(<:.dsp=.pd) $(DEST) + rm -f $<.xml + +clean: + rm -rf $(DEST) diff --git a/faust-src/Makefile.qcompile b/faust-src/Makefile.qcompile new file mode 100644 index 0000000..9a12af2 --- /dev/null +++ b/faust-src/Makefile.qcompile @@ -0,0 +1,17 @@ +DEST := qdir/ +dspsrc := $(wildcard *.dsp) +cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) +modules := $(addprefix $(DEST), $(dspsrc:%.dsp=%.so)) + +###allcpp: $(cppsrc) + +allmodules: $(modules) + +$(DEST)%.so: $(DEST)%.cpp + $(CXX) -shared -O3 $(CXXFLAGS) -Dmydsp=$(patsubst %.so,%,$(notdir $@)) $< -o $@ + +$(DEST)%.cpp: %.dsp + faust $(VEC) -a q.cpp $< -o $@ + +clean: + rm -rf $(DEST) diff --git a/faust-src/Makefile.qtcompile b/faust-src/Makefile.qtcompile new file mode 100644 index 0000000..70d1018 --- /dev/null +++ b/faust-src/Makefile.qtcompile @@ -0,0 +1,49 @@ +###-------------------------------------------- +### DEST : directory where to put binaries +### ARCH : faust architecture file + +system := $(shell uname -s) +dspsrc := $(wildcard *.dsp) +cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) + + +### check what type of applications to build (MacOSX Darwin or Linux) +ifeq ($(system), Darwin) +appls := $(addprefix $(DEST), $(dspsrc:.dsp=.app)) +else +appls := $(addprefix $(DEST), $(dspsrc:.dsp=)) +endif + + +TMP = /var/tmp/$(<:.dsp=) +###-------------------------------------------- + + +all : $(appls) + + +### Darwin +$(DEST)%.app : %.dsp + rm -rf $(TMP) + install -d $(TMP) + faust -a $(ARCH) $< -o $(TMP)/$<.cpp + cd $(TMP); qmake -project "INCLUDEPATH+=/usr/local/lib/faust/" "LIBS+=$(LIB)" "HEADERS+=/usr/local/lib/faust/faustqt.h" + cd $(TMP); qmake + cd $(TMP); xcodebuild -project $(<:.dsp=).xcodeproj + mv $(TMP)/build/Default/$(<:.dsp=.app) $@ + rm -rf $(TMP) + + +### Linux +$(DEST)% : %.dsp + rm -rf $(TMP) + install -d $(TMP) + faust -a $(ARCH) $< -o $(TMP)/$<.cpp + cd $(TMP); qmake -project "INCLUDEPATH+=/usr/local/lib/faust/" "LIBS+=$(LIB)" "HEADERS+=/usr/local/lib/faust/faustqt.h" + cd $(TMP); qmake + make -C $(TMP) + mv $(TMP)/$(<:.dsp=) $@ + rm -rf $(TMP) + +clean: + rm -rf $(DEST) diff --git a/faust-src/Makefile.sccompile b/faust-src/Makefile.sccompile new file mode 100644 index 0000000..628b2c8 --- /dev/null +++ b/faust-src/Makefile.sccompile @@ -0,0 +1,41 @@ +# Makefile to produce supercollider plugins with Faust +# 'foo.dsp' -> 'foo.so' and 'foo.sc' +# + +dspsrc := $(wildcard *.dsp) +scfiles := $(addprefix $(DEST), $(dspsrc:.dsp=.sc)) +sofiles := $(addprefix $(DEST), $(dspsrc:.dsp=.so)) +CXXFLAGS := `pkg-config --cflags libscsynth` $(CXXFLAGS) +LIB := -shared + + +###-------------------------------------------- +### Will use faust2sc to create the class file +### only if it is installed + +helper:=$(shell whereis faust2sc) + +ifeq ($(helper),faust2sc:) + todo:=$(sofiles) +else + todo:=$(sofiles) $(scfiles) +endif + + +###-------------------------------------------- + + +all : $(todo) + +$(DEST)%.cpp: %.dsp + faust -a $(ARCH) $< -o $@ + +$(DEST)%.so: $(DEST)%.cpp + $(CXX) $(CXXFLAGS) $(OPTFLAGS) $(LIB) $< -o $@ + +$(DEST)%.sc : %.dsp.xml + faust2sc --prefix=Faust $< --output=$@ + +%.dsp.xml: %.dsp + faust --xml -o /dev/null $< + diff --git a/faust-src/Makefile.svg b/faust-src/Makefile.svg new file mode 100644 index 0000000..fd271fe --- /dev/null +++ b/faust-src/Makefile.svg @@ -0,0 +1,12 @@ +src := $(wildcard *.dsp) +target := $(src:.dsp=.dsp-svg) + +all : $(target) + + +%.dsp-svg : %.dsp + faust -svg $< > /dev/null + + +clean : + rm -rf $(target) diff --git a/faust-src/Makefile.vstcompile b/faust-src/Makefile.vstcompile new file mode 100644 index 0000000..658a69b --- /dev/null +++ b/faust-src/Makefile.vstcompile @@ -0,0 +1,31 @@ +dspsrc := $(wildcard *.dsp) +cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) +appl := $(addprefix $(DEST), $(dspsrc:.dsp=$(EXT))) + +# Setup this variable to access the VST SDK files +vst_sdk := "/Volumes/Document1/Developpement/ProjectsCVS/JackCVS/JackOSX/jackosx/jackplugins/JACK-ASinsert/VST/VSTSDK" + +# Setup this variable with the location for the compiled VST plug-ins +install_plug_ins := "/Library/Audio/Plug-Ins/VST" + +all : $(appl) + + +$(DEST)% : %.dsp + install -d $@ + cp -r $(vst_sdk) $@ + cp -r /usr/local/lib/faust/VST/* $@ + faust $(VEC) -a $(ARCH) $< -o $@/vst-output.cpp + mv $@/vst-output.cpp $@/$(<:.dsp=.cpp) + sed -e 's/vst-output.cpp/$(<:.dsp=.cpp)/' $@/VST.xcode/project.pbxproj > $@/VST.xcode/new_project.pbxproj && mv $@/VST.xcode/new_project.pbxproj $@/VST.xcode/project.pbxproj + sed -e 's/XXXX/$(<:.dsp=)/' $@/Info.plist > $@/new_Info.plist && mv $@/new_Info.plist $@/Info.plist + xcodebuild -project $@/VST.xcode clean + xcodebuild -project $@/VST.xcode + mv $@/build/FaustVST.vst $@/build/$(<:.dsp=.vst) + rm -r $@/build/VST.build + install -d $(install_plug_ins) + cp -r $@/build/$(<:.dsp=.vst) $(install_plug_ins) + + +clean : + rm -f $(DEST) diff --git a/faust-src/minimal.cpp b/faust-src/minimal.cpp new file mode 100644 index 0000000..2d1222a --- /dev/null +++ b/faust-src/minimal.cpp @@ -0,0 +1,97 @@ +#ifdef __GNUC__ + +#define max(x,y) (((x)>(y)) ? (x) : (y)) +#define min(x,y) (((x)<(y)) ? (x) : (y)) + +// abs is now predefined +//template T abs (T a) { return (a> n); } + +/****************************************************************************** +******************************************************************************* + + VECTOR INTRINSICS + +******************************************************************************* +*******************************************************************************/ + +//inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); } +inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); } + +<> + +/****************************************************************************** +******************************************************************************* + + ABSTRACT USER INTERFACE + +******************************************************************************* +*******************************************************************************/ + +class UI +{ + bool fStopped; +public: + + UI() : fStopped(false) {} + virtual ~UI() {} + + virtual void addButton(char* label, float* zone) = 0; + virtual void addToggleButton(char* label, float* zone) = 0; + virtual void addCheckButton(char* label, float* zone) = 0; + virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) = 0; + virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) = 0; + virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) = 0; + + virtual void openFrameBox(char* label) = 0; + virtual void openTabBox(char* label) = 0; + virtual void openHorizontalBox(char* label) = 0; + virtual void openVerticalBox(char* label) = 0; + virtual void closeBox() = 0; + + virtual void run() = 0; + + void stop() { fStopped = true; } + bool stopped() { return fStopped; } +}; + + + + +/****************************************************************************** +******************************************************************************* + + FAUST DSP + +******************************************************************************* +*******************************************************************************/ + + + +//---------------------------------------------------------------- +// abstract definition of a signal processor +//---------------------------------------------------------------- + +class dsp { + protected: + int fSamplingFreq; + public: + dsp() {} + virtual ~dsp() {} + + virtual int getNumInputs() = 0; + virtual int getNumOutputs() = 0; + virtual void buildUserInterface(UI* interface) = 0; + virtual void init(int samplingRate) = 0; + virtual void compute(int len, float** inputs, float** outputs) = 0; +}; + + +//---------------------------------------------------------------------------- +// FAUST generated signal processor +//---------------------------------------------------------------------------- + + +<> diff --git a/faust-src/net-ks.dsp b/faust-src/net-ks.dsp new file mode 100644 index 0000000..858749a --- /dev/null +++ b/faust-src/net-ks.dsp @@ -0,0 +1,29 @@ +declare name "net-ks"; +declare version "1.0"; +declare author "Juan-Pablo Caceres"; +declare license "MIT"; +declare copyright "(c) Juan-Pablo Caceres 2008"; + +//----------------------------------------------------- +// Network-karplus-strong, +// Based on 'karplus' from Faust Examples +//----------------------------------------------------- + +import("music.lib"); + +// Excitation +//----------- +upfront(x) = (x-x') > 0.0; +decay(n,x) = x - (x>0.0)/n; +release(n) = + ~ decay(n); +trigger(n) = upfront : release(n) : >(0.0); + +// Filters +//-------- +// Average LowPass Filter +av_lowpass(x) = (x+x')/2; + +process = _ , + ( noise * 0.9 : + vgroup("excitator", *(button("play") : trigger(300))) ) + :> av_lowpass; diff --git a/jacktrip_doxygen b/jacktrip_doxygen new file mode 100644 index 0000000..61e6740 --- /dev/null +++ b/jacktrip_doxygen @@ -0,0 +1,1418 @@ +# Doxyfile 1.5.6 + +# 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 = JackTrip + +# 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 = + +# 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 = ./WWW/ + +# 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, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# 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 = YES + +# 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 + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# 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 = YES + +# 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 = 8 + +# 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 + +# 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 + +#--------------------------------------------------------------------------- +# 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 = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# 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 = YES + +# 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 = NO + +# 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 = NO + +# 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 = NO + +# 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 = NO + +# 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 = NO + +# 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 = YES + +# 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 = NO + +# 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 = + +#--------------------------------------------------------------------------- +# 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 = ./src ./documentation + +# 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 = + +# 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 = YES + +# 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 = + +# 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 = + +# 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 = ./documentation/img + +# 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 documentstion. + +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 = NO + +# 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 = + +# 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 = ./documentation/html_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 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_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. + +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 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_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 + +# 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 = NO + +# 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 Hiererachy 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 = NONE + +# 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 = a4wide + +# 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 = + +# 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 = YES + +# 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 = YES + +# 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 = Sans + +# 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 = + +# 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 = + +# 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 enabled by default, which results in a transparent +# background. 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 = YES + +# 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 +#--------------------------------------------------------------------------- + +# 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/src/DataProtocol.cpp b/src/DataProtocol.cpp new file mode 100644 index 0000000..3168a5f --- /dev/null +++ b/src/DataProtocol.cpp @@ -0,0 +1,61 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file DataProtocol.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#include "DataProtocol.h" +#include "jacktrip_globals.h" +#include "JackTrip.h" + +#include +#include + +#include +#include + +using std::cout; using std::endl; + + +//******************************************************************************* +DataProtocol::DataProtocol(JackTrip* jacktrip, + const runModeT runmode, + int /*bind_port*/, int /*peer_port*/) : + mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip) +{} + + +//******************************************************************************* +DataProtocol::~DataProtocol() +{} diff --git a/src/DataProtocol.h b/src/DataProtocol.h new file mode 100644 index 0000000..9f9d2bc --- /dev/null +++ b/src/DataProtocol.h @@ -0,0 +1,191 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file DataProtocol.h + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#ifndef __DATAPROTOCOL_H__ +#define __DATAPROTOCOL_H__ + +//#include //basic socket definitions +#include //sockaddr_in{} and other Internet defns +#include //inet(3) functions +#include +#include //for shared_ptr + +#include +#include + +class JackTrip; // forward declaration + + +/** \brief Base class that defines the transmission protocol. + * + * This base class defines most of the common method to setup and connect + * sockets using the individual protocols (UDP, TCP, SCTP, etc). + * + * The class has to be constructed using one of two modes (runModeT):\n + * - SENDER + * - RECEIVER + * + * This has to be specified as a constructor argument. When using, create two instances + * of the class, one to receive and one to send packets. Each instance will run on a + * separate thread. + * + * Redundancy and forward error correction should be implemented on each + * Transport protocol, cause they depend on the protocol itself + * + * \todo This Class should contain definition of jacktrip header and basic funcionality to obtain + * local machine IPs and maybe functions to manipulate IPs. + * Redundancy and forward error correction should be implemented on each + * Transport protocol, cause they depend on the protocol itself + * + * \todo The transport protocol itself has to be implemented subclassing this class, i.e., + * using a TCP or UDP protocol. + * + * Even if the underlined transmission protocol is stream oriented (as in TCP), + * we send packets that are the size of the audio processing buffer. + * Use AudioInterface::getBufferSize to obtain this value. + * + * Each transmission (i.e., inputs and outputs) run on its own thread. + */ +class DataProtocol : public QThread +{ +public: + + //----------ENUMS------------------------------------------ + /// \brief Enum to define packet header types + enum packetHeaderTypeT { + DEFAULT, ///< Default application header + JAMLINK, ///< Header to use with Jamlinks + EMPTY ///< Empty Header + }; + + /// \brief Enum to define class modes, SENDER or RECEIVER + enum runModeT { + SENDER, ///< Set class as a Sender (send packets) + RECEIVER ///< Set class as a Receiver (receives packets) + }; + //--------------------------------------------------------- + + + /** \brief The class constructor + * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) + * \param runmode Sets the run mode, use either DataProtocol::SENDER or + * DataProtocol::RECEIVER + * \param headertype packetHeaderTypeT header type to use for packets + * \param bind_port Port number to bind for this socket (this is the receive or send port depending on the runmode) + * \param peer_port Peer port number (this is the receive or send port depending on the runmode) + */ + DataProtocol(JackTrip* jacktrip, + const runModeT runmode, + int bind_port, int peer_port); + + /// \brief The class destructor + virtual ~DataProtocol(); + + /** \brief Implements the thread loop + * + * Depending on the runmode, with will run a DataProtocol::SENDER thread or + * DataProtocol::RECEIVER thread + */ + virtual void run() = 0; + + /// \brief Stops the execution of the Thread + virtual void stop() { mStopped = true; }; + + /** \brief Sets the size of the audio part of the packets + * \param size_bytes Size in bytes + */ + void setAudioPacketSize(const size_t size_bytes){ mAudioPacketSize = size_bytes; }; + + /** \brief Get the size of the audio part of the packets + * \return size_bytes Size in bytes + */ + size_t getAudioPacketSizeInBites() { return(mAudioPacketSize); }; + + /** \brief Set the peer address + * \param peerHostOrIP IPv4 number or host name + * \todo implement here instead of in the subclass UDP + */ + virtual void setPeerAddress(const char* peerHostOrIP) = 0; + + /** \brief Set the peer incomming (receiving) port number + * \param port Port number + * \todo implement here instead of in the subclass UDP + */ + virtual void setPeerPort(int port) = 0; + + //virtual void getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress, + // uint16_t& port) = 0; + +protected: + + /** \brief Get the Run Mode of the object + * \return SENDER or RECEIVER + */ + runModeT getRunMode() const { return mRunMode; }; + + /// Boolean stop the execution of the thread + volatile bool mStopped; + /// Boolean to indicate if the RECEIVER is waiting to obtain peer address + volatile bool mHasPeerAddress; + /// Boolean that indicates if a packet was received + volatile bool mHasPacketsToReceive; + + +private: + + int mLocalPort; ///< Local Port number to Bind + int mPeerPort; ///< Peer Port number to Bind + const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER + + struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct + struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct + + /// Number of clients running to check for ports already used + /// \note Unimplemented, try to find another way to check for used ports + static int sClientsRunning; + + size_t mAudioPacketSize; ///< Packet audio part size + + + /// \todo check a better way to access the header from the subclasses +protected: + //PacketHeader* mHeader; ///< Packet Header + JackTrip* mJackTrip; ///< JackTrip mediator class + +}; + +#endif diff --git a/src/DataProtocolPOSIX.cpp.tmp b/src/DataProtocolPOSIX.cpp.tmp new file mode 100644 index 0000000..b92459c --- /dev/null +++ b/src/DataProtocolPOSIX.cpp.tmp @@ -0,0 +1,222 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file DataProtocol.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#include "DataProtocol.h" +#include "jacktrip_globals.h" +#include "JackAudioInterface.h" +#include "PacketHeader.h" + +#include +#include + +#include +#include + +using std::cout; using std::endl; + +//******************************************************************************* +DataProtocol::DataProtocol(const runModeT runmode, + const packetHeaderTypeT headertype) : + mRunMode(runmode), mStopped(false), mHasPacketsToReceive(false), mHeader(NULL) +{ + //--------PROTOTYPE------------------------- + if ( headertype == DEFAULT ) { + mHeader = new DefaultHeader; + } + else if ( headertype == JAMLINK ) { + mHeader = new JamLinkHeader; + } + //------------------------------------------ + + // Base ports gInputPort_0 and gOutputPort_0defined at globals.h + if (mRunMode == RECEIVER) { + mLocalPort = gInputPort_0; + mPeerPort = gOutputPort_0; + } + else if (mRunMode == SENDER) { + mLocalPort = gOutputPort_0; + mPeerPort = gInputPort_0; + } + + this->setLocalIPv4Address(); +} + + +//******************************************************************************* +DataProtocol::~DataProtocol() +{ + delete mHeader; +} + + +//******************************************************************************* +void DataProtocol::stop() +{ + mStopped = true; +} + + +//******************************************************************************* +void DataProtocol::setLocalIPv4Address() +{ + bzero(&mLocalIPv4Addr, sizeof(mLocalIPv4Addr)); + mLocalIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol + mLocalIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address + mLocalIPv4Addr.sin_port = htons(mLocalPort);//set local port + //std::cout << "mLocalPort = " << mLocalPort << std::endl; +} + + +//******************************************************************************* +void DataProtocol::setPeerIPv4Address(const char* peerHostOrIP) +{ + const char* peerAddress; // dotted decimal address to use in the struct below + + /// \todo Improve this to make it work also with local ip numbers, in a LAN, + /// that don't have an assigned host name + /* + // Resolve Peer IPv4 with either doted integer IP or hostname + //---------------------------------------------------------- + std::cout << "Resolving Peer IPv4 address..." << std::endl; + QHostInfo info = QHostInfo::fromName(peerHostOrIP); + if ( !info.addresses().isEmpty() ) { + std::cout << "Peer Address Found" << std::endl; + QHostAddress address = info.addresses().first(); // use the first address in list + peerAddress = address.toString().toLatin1(); + } + else { + std::cerr << "ERROR: Could not set Peer IP Address" << std::endl; + std::cerr << "Check that it's public or that the hostname exists" << std::endl; + std::exit(1); + } + */ + + // temporary implementation to make this work + /// \todo change this + peerAddress = peerHostOrIP; + + // Set the Peer IPv4 Address struct + //--------------------------------- + bzero(&mPeerIPv4Addr, sizeof(mPeerIPv4Addr)); + mPeerIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol + mPeerIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address + mPeerIPv4Addr.sin_port = htons(mPeerPort);//set Peer port + //std::cout << "mPeerPort = " << mPeerPort << std::endl; + int nPeer = inet_pton(AF_INET, peerAddress, &mPeerIPv4Addr.sin_addr); + if ( nPeer == 1 ) { + std::cout << "Successful Set Peer Address" << std::endl; + } + else if ( nPeer == 0 ) { + std::cout << "Error: Incorrect presentation format for address" << std::endl; + std::exit(1); + } + else { + std::cout << "Error: Could not set Peer Address" << std::endl; + std::exit(1); + } + +} + + +//******************************************************************************* +void DataProtocol::setRingBuffer(std::tr1::shared_ptr RingBuffer) +{ + mRingBuffer = RingBuffer; +} + + +//******************************************************************************* +void DataProtocol::run() +{ + std::cout << "Running DataProtocol Thread" << std::endl; + std::cout << gPrintSeparator << std::endl; + size_t packet_size = getAudioPacketSize(); + int8_t packet[packet_size]; + + switch ( mRunMode ) + { + case RECEIVER : + //----------------------------------------------------------------------------------- + // Wait for the first packet to be ready and obtain address + // from that packet + /// \todo here is the place to read the datagram and check if the settings match + /// the local ones. Extract this information from the header + std::cout << "Waiting for Peer..." << std::endl; + this->receivePacket( (char*) packet, packet_size); // This blocks waiting for the first packet + std::cout << "Received Connection for Peer!" << std::endl; + + while ( !mStopped ) + { + //std::cout << "RECEIVING PACKETS" << std::endl; + /// \todo Set a timer to report packats arriving too late + //std::cout << "RECIEVING THREAD" << std::endl; + + this->receivePacket( (char*) packet, packet_size); + /// \todo Change this to match buffer size + //std::cout << "PACKET RECIEVED" << std::endl; + mRingBuffer->insertSlotBlocking(packet); + //std::cout << buf << std::endl; + } + break; + + + case SENDER : + //----------------------------------------------------------------------------------- + while ( !mStopped ) + { + //std::cout << "SENDING PACKETS --------------------------" << std::endl; + /// \todo This should be blocking, since we don't want to send trash + mRingBuffer->readSlotBlocking(packet); + //std::cout << "SENDING PACKETS" << std::endl; + this->sendPacket( (char*) packet, packet_size); + //std::cout << "SENDING PACKETS DONE!!!" << std::endl; + //this->sendPacket( sendtest, 64); + } + break; + } +} + +void DataProtocol::setAudioPacketSize(size_t size_bytes) +{ + mAudioPacketSize = size_bytes; +} + + +size_t DataProtocol::getAudioPacketSize() +{ + return(mAudioPacketSize); +} diff --git a/src/DataProtocolPOSIX.h.tmp b/src/DataProtocolPOSIX.h.tmp new file mode 100644 index 0000000..c812d02 --- /dev/null +++ b/src/DataProtocolPOSIX.h.tmp @@ -0,0 +1,213 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file DataProtocol.h + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#ifndef __DATAPROTOCOL_H__ +#define __DATAPROTOCOL_H__ + +//#include //basic socket definitions +#include //sockaddr_in{} and other Internet defns +#include //inet(3) functions +#include +#include //for shared_ptr + +#include + +#include "RingBuffer.h" +//#include "PacketHeader.h" +class PacketHeader; // forward declaration + + +/** \brief Base class that defines the transmission protocol. + * + * This base class defines most of the common method to setup and connect + * sockets using the individual protocols (UDP, TCP, SCTP, etc). + * + * The class has to be constructed using one of two modes (runModeT):\n + * - SENDER + * - RECEIVER + * + * This has to be specified as a constructor argument. When using, create two instances + * of the class, one to receive and one to send packets. Each instance will run on a + * separate thread. + * + * Redundancy and forward error correction should be implemented on each + * Transport protocol, cause they depend on the protocol itself + * + * \todo This Class should contain definition of jacktrip header and basic funcionality to obtain + * local machine IPs and maybe functions to manipulate IPs. + * Redundancy and forward error correction should be implemented on each + * Transport protocol, cause they depend on the protocol itself + * + * \todo The transport protocol itself has to be implemented subclassing this class, i.e., + * using a TCP or UDP protocol. + * + * Even if the underlined transmission protocol is stream oriented (as in TCP), + * we send packets that are the size of the audio processing buffer. + * Use AudioInterface::getBufferSize to obtain this value. + * + * Each transmission (i.e., inputs and outputs) run on its own thread. + */ +class DataProtocol : public QThread +{ +public: + + /// \brief Enum to define packet header types + enum packetHeaderTypeT { + DEFAULT, ///< Default application header + JAMLINK ///< Header to use with Jamlinks + }; + + /// \brief Enum to define class modes, SENDER or RECEIVER + enum runModeT { + SENDER, ///< Set class as a Sender (send packets) + RECEIVER ///< Set class as a Receiver (receives packets) + }; + + + /** \brief The class constructor + * \param runmode Sets the run mode, use either SENDER or RECEIVER + */ + DataProtocol(const runModeT runmode, + const packetHeaderTypeT headertype = DEFAULT); + + /// \brief The class destructor + virtual ~DataProtocol(); + + /** \brief Sets the peer (remote) IPv4 address struct + * \param peerHostOrIP Either an IPv4 dotted integer number or a hostname + */ + virtual void setPeerIPv4Address(const char* peerHostOrIP); + + /** \brief Receive a packet from the UDPSocket + * + * This method has to be implemented in the sub-classes + * \param buf Location at which to store the buffer + * \param n size of packet to receive in bytes + * \return number of bytes read, -1 on error + */ + virtual size_t receivePacket(char* buf, size_t n) = 0; + + /** \brief Sends a packet + * + * This method has to be implemented in the sub-classes + * \param buff Buffer to send + * \param n size of packet to receive in bytes + * \return number of bytes read, -1 on error + */ + virtual size_t sendPacket(const char* buff, size_t n) = 0; + + /** \brief Implements the thread loop + * + * Depending on the runmode, with will run a RECEIVE thread or + * SEND thread + */ + virtual void run(); + + /** \brief Set the pointer to the RingBuffer that'll be use to read + * or write + */ + void setRingBuffer(std::tr1::shared_ptr RingBuffer); + + /// \brief Stops the execution of the Thread + void stop(); + + /** \brief Sets the size of the audio part of the packets + * \param size_bytes Size in bytes + */ + void setAudioPacketSize(size_t size_bytes); + + /** \brief Get the size of the audio part of the packets + * \return size_bytes Size in bytes + */ + size_t getAudioPacketSize(); + + //virtual void getIPAddressFromFirstPacket() = 0; + + +protected: + + /** \brief Sets the local IPv4 address struct + * + * It uses the default active device. + */ + virtual void setLocalIPv4Address(); + + /** \brief Get the Run Mode of the object + * \return SENDER or RECEIVER + */ + runModeT getRunMode() const { return mRunMode; }; + + /** \brief Returns the Local machine IPv4 socket address stuct + * \return Socket address stuct + */ + const sockaddr_in& getLocalIPv4AddressStruct() const { return mLocalIPv4Addr; }; + + /** \brief Returns the Peer IPv4 socket address stuct + * \return Socket address stuct + */ + const sockaddr_in& getPeerIPv4AddressStruct() const { return mPeerIPv4Addr; }; + + +private: + + int mLocalPort; ///< Local Port number to Bind + int mPeerPort; ///< Peer Port number to Bind + const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER + + struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct + struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct + + /// Smart Pointer to RingBuffer to read (for SENDER) or write (for RECEIVER) + std::tr1::shared_ptr mRingBuffer; + + /// Boolean stop the execution of the thread + volatile bool mStopped; + /// Boolean to indicate if the RECEIVER is waiting to obtain peer address + volatile bool mHasPeerAddress; + /// Boolean that indicates if a packet was received + volatile bool mHasPacketsToReceive; + + /// Number of clients running to check for ports already used + /// \note Unimplemented, try to find another way to check for used ports + static int sClientsRunning; + + size_t mAudioPacketSize; ///< Packet audio part size + + PacketHeader* mHeader; ///< Packet Header +}; + +#endif diff --git a/src/JackAudioInterface.cpp b/src/JackAudioInterface.cpp new file mode 100644 index 0000000..5ca3ba3 --- /dev/null +++ b/src/JackAudioInterface.cpp @@ -0,0 +1,675 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackAudioInterface.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#include "JackAudioInterface.h" +#include "jacktrip_globals.h" +#include "JackTrip.h" + +#include +#include +#include +#include + +///************PROTORYPE FOR CELT************************** +//#include +//#include +//#include +///******************************************************** + +#include +#include + +using std::cout; using std::endl; + + +// sJackMutex definition +QMutex JackAudioInterface::sJackMutex; + + +//******************************************************************************* +JackAudioInterface::JackAudioInterface(JackTrip* jacktrip, + int NumInChans, int NumOutChans, + audioBitResolutionT AudioBitResolution, + const char* ClienName) : + mNumInChans(NumInChans), mNumOutChans(NumOutChans), + mAudioBitResolution(AudioBitResolution*8), mBitResolutionMode(AudioBitResolution), + mClient(NULL), + mClientName(ClienName), + mJackTrip(jacktrip) +{ + //setupClient(); + //setProcessCallback(); +} + + +//******************************************************************************* +JackAudioInterface::~JackAudioInterface() +{ + delete[] mInputPacket; + delete[] mOutputPacket; + + for (int i = 0; i < mNumInChans; i++) { + delete[] mInProcessBuffer[i]; + } + + for (int i = 0; i < mNumOutChans; i++) { + delete[] mOutProcessBuffer[i]; + } +} + + +//******************************************************************************* +void JackAudioInterface::setup() +{ + setupClient(); + setProcessCallback(); +} + + +//******************************************************************************* +void JackAudioInterface::setupClient() +{ + const char* client_name = mClientName; + const char* server_name = NULL; + jack_options_t options = JackNoStartServer; + jack_status_t status; + + // Try to connect to the server + /// \todo Write better warning messages. This following line displays very + /// verbose message, check how to desable them. + { + QMutexLocker locker(&sJackMutex); + mClient = jack_client_open (client_name, options, &status, server_name); + } + + if (mClient == NULL) { + //fprintf (stderr, "jack_client_open() failed, " + // "status = 0x%2.0x\n", status); + if (status & JackServerFailed) { + fprintf (stderr, "Unable to connect to JACK server\n"); + //std::cerr << "ERROR: Maybe the JACK server is not running?" << std::endl; + //std::cerr << gPrintSeparator << std::endl; + } + //std::exit(1); + throw std::runtime_error("Maybe the JACK server is not running?"); + } + if (status & JackServerStarted) { + fprintf (stderr, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + client_name = jack_get_client_name(mClient); + fprintf (stderr, "unique name `%s' assigned\n", client_name); + } + + // Set function to call if Jack shuts down + jack_on_shutdown (mClient, this->jackShutdown, 0); + + // Create input and output channels + createChannels(); + + // Allocate buffer memory to read and write + mSizeInBytesPerChannel = getSizeInBytesPerChannel(); + int size_input = mSizeInBytesPerChannel * getNumInputChannels(); + int size_output = mSizeInBytesPerChannel * getNumOutputChannels(); + mInputPacket = new int8_t[size_input]; + mOutputPacket = new int8_t[size_output]; + + // Buffer size member + mNumFrames = getBufferSizeInSamples(); + + // Initialize Buffer array to read and write audio + mInBuffer.resize(mNumInChans); + mOutBuffer.resize(mNumOutChans); + + // Initialize and asign memory for ProcessPlugins Buffers + mInProcessBuffer.resize(mNumInChans); + mOutProcessBuffer.resize(mNumOutChans); + + int nframes = getBufferSizeInSamples(); + for (int i = 0; i < mNumInChans; i++) { + mInProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } + for (int i = 0; i < mNumOutChans; i++) { + mOutProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } +} + + +//******************************************************************************* +void JackAudioInterface::createChannels() +{ + //Create Input Ports + mInPorts.resize(mNumInChans); + for (int i = 0; i < mNumInChans; i++) + { + QString inName; + QTextStream (&inName) << "send_" << i+1; + mInPorts[i] = jack_port_register (mClient, inName.toLatin1(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + } + + //Create Output Ports + mOutPorts.resize(mNumOutChans); + for (int i = 0; i < mNumInChans; i++) + { + QString outName; + QTextStream (&outName) << "receive_" << i+1; + mOutPorts[i] = jack_port_register (mClient, outName.toLatin1(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + } +} + + +//******************************************************************************* +uint32_t JackAudioInterface::getSampleRate() const +{ + return jack_get_sample_rate(mClient); +} + + +//******************************************************************************* +JackAudioInterface::samplingRateT JackAudioInterface::getSampleRateType() const +{ + uint32_t rate = jack_get_sample_rate(mClient); + + if ( rate == 22050 ) { + return JackAudioInterface::SR22; } + else if ( rate == 32000 ) { + return JackAudioInterface::SR32; } + else if ( rate == 44100 ) { + return JackAudioInterface::SR44; } + else if ( rate == 48000 ) { + return JackAudioInterface::SR48; } + else if ( rate == 88200 ) { + return JackAudioInterface::SR88; } + else if ( rate == 96000 ) { + return JackAudioInterface::SR96; } + else if ( rate == 19200 ) { + return JackAudioInterface::SR192; } + + return JackAudioInterface::UNDEF; +} + + +//******************************************************************************* +int JackAudioInterface::getSampleRateFromType(samplingRateT rate_type) +{ + int sample_rate = 0; + switch (rate_type) + { + case SR22 : + sample_rate = 22050; + return sample_rate; + break; + case SR32 : + sample_rate = 32000; + return sample_rate; + break; + case SR44 : + sample_rate = 44100; + return sample_rate; + break; + case SR48 : + sample_rate = 48000; + return sample_rate; + break; + case SR88 : + sample_rate = 88200; + return sample_rate; + break; + case SR96 : + sample_rate = 96000; + return sample_rate; + break; + case SR192 : + sample_rate = 192000; + return sample_rate; + break; + default: + return sample_rate; + break; + } + + return sample_rate; +} + +//******************************************************************************* +uint32_t JackAudioInterface::getBufferSizeInSamples() const +{ + return jack_get_buffer_size(mClient); +} + + +//******************************************************************************* +int JackAudioInterface::getAudioBitResolution() const +{ + return mAudioBitResolution; +} + + +//******************************************************************************* +int JackAudioInterface::getNumInputChannels() const +{ + return mNumInChans; +} + + +//******************************************************************************* +int JackAudioInterface::getNumOutputChannels() const +{ + return mNumOutChans; +} + + +//******************************************************************************* +size_t JackAudioInterface::getSizeInBytesPerChannel() const +{ + return (getBufferSizeInSamples() * getAudioBitResolution()/8); +} + +//******************************************************************************* +void JackAudioInterface::setProcessCallback() +{ + std::cout << "Setting JACK Process Callback..." << std::endl; + if ( int code = + jack_set_process_callback(mClient, JackAudioInterface::wrapperProcessCallback, this) + ) + { + //std::cerr << "Could not set the process callback" << std::endl; + //return(code); + (void) code; // to avoid compiler warnings + throw std::runtime_error("Could not set the Jack process callback"); + //std::exit(1); + } + std::cout << "SUCCESS" << std::endl; + std::cout << gPrintSeparator << std::endl; + //return(0); +} + + +//******************************************************************************* +int JackAudioInterface::startProcess() const +{ + //Tell the JACK server that we are ready to roll. Our + //process() callback will start running now. + if ( int code = (jack_activate(mClient)) ) + { + std::cerr << "Cannot activate client" << std::endl; + return(code); + } + return(0); +} + + +//******************************************************************************* +int JackAudioInterface::stopProcess() const +{ + QMutexLocker locker(&sJackMutex); + if ( int code = (jack_client_close(mClient)) ) + { + std::cerr << "Cannot disconnect client" << std::endl; + return(code); + } + return(0); +} + + +//******************************************************************************* +void JackAudioInterface::jackShutdown (void*) +{ + //std::cout << "The Jack Server was shut down!" << std::endl; + throw std::runtime_error("The Jack Server was shut down!"); + //std::cout << "Exiting program..." << std::endl; + //std::exit(1); +} + + +//******************************************************************************* +/* +void JackAudioInterface::setRingBuffers +(const std::tr1::shared_ptr InRingBuffer, + const std::tr1::shared_ptr OutRingBuffer) +{ + mInRingBuffer = InRingBuffer; + mOutRingBuffer = OutRingBuffer; +} +*/ + + +//******************************************************************************* +// Before sending and reading to Jack, we have to round to the sample resolution +// that the program is using. Jack uses 32 bits (gJackBitResolution in globals.h) +// by default +void JackAudioInterface::computeNetworkProcessFromNetwork() +{ + /// \todo cast *mInBuffer[i] to the bit resolution + //cout << mNumFrames << endl; + // Output Process (from NETWORK to JACK) + // ---------------------------------------------------------------- + // Read Audio buffer from RingBuffer (read from incoming packets) + //mOutRingBuffer->readSlotNonBlocking( mOutputPacket ); + mJackTrip->receiveNetworkPacket( mOutputPacket ); + + // Extract separate channels to send to Jack + for (int i = 0; i < mNumOutChans; i++) { + //-------- + // This should be faster for 32 bits + //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], + // mSizeInBytesPerChannel); + //-------- + sample_t* tmp_sample = mOutBuffer[i]; //sample buffer for channel i + for (int j = 0; j < mNumFrames; j++) { + //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); + // Change the bit resolution on each sample + //cout << tmp_sample[j] << endl; + fromBitToSampleConversion(&mOutputPacket[(i*mSizeInBytesPerChannel) + + (j*mBitResolutionMode)], + &tmp_sample[j], + mBitResolutionMode); + } + } +} + + +//******************************************************************************* +void JackAudioInterface::computeNetworkProcessToNetwork() +{ + // Input Process (from JACK to NETWORK) + // ---------------------------------------------------------------- + // Concatenate all the channels from jack to form packet + for (int i = 0; i < mNumInChans; i++) { + //-------- + // This should be faster for 32 bits + //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], + // mSizeInBytesPerChannel); + //-------- + sample_t* tmp_sample = mInBuffer[i]; //sample buffer for channel i + sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process + sample_t tmp_result; + for (int j = 0; j < mNumFrames; j++) { + //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); + // Change the bit resolution on each sample + + // Add the input jack buffer to the buffer resulting from the output process + tmp_result = tmp_sample[j] + tmp_process_sample[j]; + fromSampleToBitConversion(&tmp_result, + &mInputPacket[(i*mSizeInBytesPerChannel) + + (j*mBitResolutionMode)], + mBitResolutionMode); + } + } + // Send Audio buffer to RingBuffer (these goes out as outgoing packets) + //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); + mJackTrip->sendNetworkPacket( mInputPacket ); +} + + +//******************************************************************************* +int JackAudioInterface::processCallback(jack_nframes_t nframes) +{ + // Get input and output buffers from JACK + //------------------------------------------------------------------- + for (int i = 0; i < mNumInChans; i++) { + // Input Ports are READ ONLY + mInBuffer[i] = (sample_t*) jack_port_get_buffer(mInPorts[i], nframes); + } + for (int i = 0; i < mNumOutChans; i++) { + // Output Ports are WRITABLE + mOutBuffer[i] = (sample_t*) jack_port_get_buffer(mOutPorts[i], nframes); + } + //------------------------------------------------------------------- + // TEST: Loopback + // To test, uncomment and send audio to client input. The same audio + // should come out as output in the first channel + //memcpy (mOutBuffer[0], mInBuffer[0], sizeof(sample_t) * nframes); + //memcpy (mOutBuffer[1], mInBuffer[1], sizeof(sample_t) * nframes); + //------------------------------------------------------------------- + + // Allocate the Process Callback + //------------------------------------------------------------------- + // 1) First, process incoming packets + // ---------------------------------- + computeNetworkProcessFromNetwork(); + + + // 2) Dynamically allocate ProcessPlugin processes + // ----------------------------------------------- + // The processing will be done in order of allocation + + ///\todo Implement for more than one process plugin, now it just works propertely with one. + /// do it chaining outputs to inputs in the buffers. May need a tempo buffer + for (int i = 0; i < mNumInChans; i++) { + std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * nframes); + std::memcpy(mInProcessBuffer[i], mOutBuffer[i], sizeof(sample_t) * nframes); + } + for (int i = 0; i < mNumOutChans; i++) { + std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } + + for (int i = 0; i < mProcessPlugins.size(); i++) { + //mProcessPlugins[i]->compute(nframes, mOutBuffer.data(), mInBuffer.data()); + mProcessPlugins[i]->compute(nframes, mInProcessBuffer.data(), mOutProcessBuffer.data()); + } + + + // 3) Finally, send packets to peer + // -------------------------------- + computeNetworkProcessToNetwork(); + + + ///************PROTORYPE FOR CELT************************** + ///******************************************************** + /* + CELTMode* mode; + int* error; + mode = celt_mode_create(48000, 2, 64, error); + */ + //celt_mode_create(48000, 2, 64, NULL); + //unsigned char* compressed; + //CELTEncoder* celtEncoder; + //celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); + + ///******************************************************** + ///******************************************************** + + + + return 0; +} + + +//******************************************************************************* +int JackAudioInterface::wrapperProcessCallback(jack_nframes_t nframes, void *arg) +{ + return static_cast(arg)->processCallback(nframes); +} + + +//******************************************************************************* +// This function quantize from 32 bit to a lower bit resolution +// 24 bit is not working yet +void JackAudioInterface::fromSampleToBitConversion(const sample_t* const input, + int8_t* output, + const audioBitResolutionT targetBitResolution) +{ + int8_t tmp_8; + uint8_t tmp_u8; // unsigned to quantize the remainder in 24bits + int16_t tmp_16; + sample_t tmp_sample; + sample_t tmp_sample16; + sample_t tmp_sample8; + switch (targetBitResolution) + { + case BIT8 : + // 8bit integer between -128 to 127 + tmp_sample = floor( (*input) * 128.0 ); // 2^7 = 128.0 + tmp_8 = static_cast(tmp_sample); + std::memcpy(output, &tmp_8, 1); // 8bits = 1 bytes + break; + case BIT16 : + // 16bit integer between -32768 to 32767 + tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 + tmp_16 = static_cast(tmp_sample); + std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes + break; + case BIT24 : + // To convert to 24 bits, we first quantize the number to 16bit + tmp_sample = (*input) * 32768.0; // 2^15 = 32768.0 + tmp_sample16 = floor(tmp_sample); + tmp_16 = static_cast(tmp_sample16); + + // Then we compute the remainder error, and quantize that part into an 8bit number + // Note that this remainder is always positive, so we use an unsigned integer + tmp_sample8 = floor ((tmp_sample - tmp_sample16) //this is a positive number, between 0.0-1.0 + * 256.0); + tmp_u8 = static_cast(tmp_sample8); + + // Finally, we copy the 16bit number in the first 2 bytes, + // and the 8bit number in the third bite + std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes + std::memcpy(output+2, &tmp_u8, 1); // 8bits = 1 bytes + break; + case BIT32 : + std::memcpy(output, input, 4); // 32bit = 4 bytes + break; + } +} + + +//******************************************************************************* +void JackAudioInterface::fromBitToSampleConversion(const int8_t* const input, + sample_t* output, + const audioBitResolutionT sourceBitResolution) +{ + int8_t tmp_8; + uint8_t tmp_u8; + int16_t tmp_16; + sample_t tmp_sample; + sample_t tmp_sample16; + sample_t tmp_sample8; + switch (sourceBitResolution) + { + case BIT8 : + tmp_8 = *input; + tmp_sample = static_cast(tmp_8) / 128.0; + std::memcpy(output, &tmp_sample, 4); // 4 bytes + break; + case BIT16 : + tmp_16 = *( reinterpret_cast(input) ); // *((int16_t*) input); + tmp_sample = static_cast(tmp_16) / 32768.0; + std::memcpy(output, &tmp_sample, 4); // 4 bytes + break; + case BIT24 : + // We first extract the 16bit and 8bit number from the 3 bytes + tmp_16 = *( reinterpret_cast(input) ); + tmp_u8 = *( reinterpret_cast(input+2) ); + + // Then we recover the number + tmp_sample16 = static_cast(tmp_16); + tmp_sample8 = static_cast(tmp_u8) / 256.0; + tmp_sample = (tmp_sample16 + tmp_sample8) / 32768.0; + std::memcpy(output, &tmp_sample, 4); // 4 bytes + break; + case BIT32 : + std::memcpy(output, input, 4); // 4 bytes + break; + } +} + + +//******************************************************************************* +//void JackAudioInterface::appendProcessPlugin(const std::tr1::shared_ptr plugin) +void JackAudioInterface::appendProcessPlugin(ProcessPlugin* plugin) +{ + /// \todo check that channels in ProcessPlugins are less or same that jack channels + if ( plugin->getNumInputs() ) {} + mProcessPlugins.append(plugin); +} + + + +//******************************************************************************* +void JackAudioInterface::connectDefaultPorts() +{ + const char** ports; + + // Get physical output (capture) ports + if ( (ports = + jack_get_ports (mClient, NULL, NULL, + JackPortIsPhysical | JackPortIsOutput)) == NULL) + { + cout << "WARING: Cannot find any physical capture ports" << endl; + } + else + { + // Connect capure ports to jacktrip send + for (int i = 0; i < mNumInChans; i++) + { + // Check that we don't run out of capture ports + if ( ports[i] != NULL ) { + jack_connect(mClient, ports[i], jack_port_name(mInPorts[i])); + } + } + std::free(ports); + } + + // Get physical input (playback) ports + if ( (ports = + jack_get_ports (mClient, NULL, NULL, + JackPortIsPhysical | JackPortIsInput)) == NULL) + { + cout << "WARING: Cannot find any physical playback ports" << endl; + } + else + { + // Connect playback ports to jacktrip receive + for (int i = 0; i < mNumOutChans; i++) + { + // Check that we don't run out of capture ports + if ( ports[i] != NULL ) { + jack_connect(mClient, jack_port_name(mOutPorts[i]), ports[i]); + } + } + std::free(ports); + } +} diff --git a/src/JackAudioInterface.h b/src/JackAudioInterface.h new file mode 100644 index 0000000..2b01ff2 --- /dev/null +++ b/src/JackAudioInterface.h @@ -0,0 +1,305 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackAudioInterface.h + * \author Juan-Pablo Caceres + * \date June 2008 + */ + + +#ifndef __JACKAUDIOINTERFACE_H__ +#define __JACKAUDIOINTERFACE_H__ + +#include +#include //for shared_ptr +#include //for mem_fun_ref +#include + +#include +#include +#include + + +#include "jacktrip_types.h" +#include "ProcessPlugin.h" + +class JackTrip; //forward declaration + + +/** \brief Class that provides an interface with the Jack Audio Server + * + * \todo implement srate_callback + * \todo automatically starts jack with buffer and sample rate settings specified by the user + */ +class JackAudioInterface +{ +public: + + /// \brief Enum for Audio Resolution in bits + /// \todo implement this into the class, now it's using jack default of 32 bits + enum audioBitResolutionT { + BIT8 = 1, ///< 8 bits + BIT16 = 2, ///< 16 bits (default) + BIT24 = 3, ///< 24 bits + BIT32 = 4 ///< 32 bits + }; + + /// \brief Sampling Rates supported by JACK + enum samplingRateT { + SR22, ///< 22050 Hz + SR32, ///< 32000 Hz + SR44, ///< 44100 Hz + SR48, ///< 48000 Hz + SR88, ///< 88200 Hz + SR96, ///< 96000 Hz + SR192, ///< 192000 Hz + UNDEF ///< Undefined + }; + + /** \brief The class constructor + * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) + * \param NumInChans Number of Input Channels + * \param NumOutChans Number of Output Channels + * \param AudioBitResolution Audio Sample Resolutions in bits + * \param ClientName Client name in Jack + */ + JackAudioInterface(JackTrip* jacktrip, + int NumInChans, int NumOutChans, + audioBitResolutionT AudioBitResolution = BIT16, + const char* ClientName = "JackTrip"); + + /** \brief The class destructor + */ + virtual ~JackAudioInterface(); + + /** \brief Setup the client + */ + void setup(); + + /** \brief Get the Jack Server Sampling Rate, in samples/second + */ + uint32_t getSampleRate() const; + + /** \brief Get the Jack Server Sampling Rate Enum Type samplingRateT + * \return JackAudioInterface::samplingRateT enum type + */ + samplingRateT getSampleRateType() const; + + /** \brief Helper function to get the sample rate (in Hz) for a + * JackAudioInterface::samplingRateT + * \param rate_type JackAudioInterface::samplingRateT enum type + * \return Sample Rate in Hz + */ + static int getSampleRateFromType(samplingRateT rate_type); + + /** \brief Get the Jack Server Buffer Size, in samples + */ + uint32_t getBufferSizeInSamples() const; + + /** \brief Get the Jack Server Buffer Size, in bytes + */ + uint32_t getBufferSizeInBytes() const + { + return (getBufferSizeInSamples() * getAudioBitResolution()/8); + } + + /** \brief Get the Audio Bit Resolution, in bits + * + * This is one of the audioBitResolutionT set in construction + */ + int getAudioBitResolution() const; + + /// \brief Get Number of Input Channels + int getNumInputChannels() const; + + /// \brief Get Number of Output Channels + int getNumOutputChannels() const; + + /// \brief Get size of each audio per channel, in bytes + size_t getSizeInBytesPerChannel() const; + + /** \brief Tell the JACK server that we are ready to roll. The + * process-callback will start running. This runs on its own thread. + * \return 0 on success, otherwise a non-zero error code + */ + int startProcess() const; + + /** \brief Stops the process-callback thread + * \return 0 on success, otherwise a non-zero error code + */ + int stopProcess() const; + + /** \brief Set the pointer to the Input and Output RingBuffer + * that'll be use to read and write audio + * + * These RingBuffers are used to read and write audio samples on + * each JACK callback. + * \todo If the RingBuffer is blocked, the callback should stay + * on the last buffer, as in JackTrip (wavetable synth) + * \param InRingBuffer RingBuffer to read samples from + * \param OutRingBuffer RingBuffer to write samples to + */ + /* + void setRingBuffers(const std::tr1::shared_ptr InRingBuffer, + const std::tr1::shared_ptr OutRingBuffer); + */ + + /** \brief Append a ProcessPlugin. The order of processing is determined by + * the order by which appending is done. + * \param plugin a ProcesPlugin smart pointer. Create the object instance + * using something like:\n + * std::tr1::shared_ptr loopback(new ProcessPluginName); + */ + //void appendProcessPlugin(const std::tr1::shared_ptr plugin); + void appendProcessPlugin(ProcessPlugin* plugin); + + /** \brief Convert a 32bit number (sample_t) into one of the bit resolution + * supported (audioBitResolutionT). + * + * The result is stored in an int_8 array of the + * appropriate size to hold the value. The caller is responsible to allocate + * enough space to store the result. + */ + static void fromSampleToBitConversion(const sample_t* const input, + int8_t* output, + const audioBitResolutionT targetBitResolution); + + /** \brief Convert a audioBitResolutionT bit resolution number into a + * 32bit number (sample_t) + * + * The result is stored in an sample_t array of the + * appropriate size to hold the value. The caller is responsible to allocate + * enough space to store the result. + */ + static void fromBitToSampleConversion(const int8_t* const input, + sample_t* output, + const audioBitResolutionT sourceBitResolution); + + /// \brief Connect the default ports, capture to sends, and receives to playback + void connectDefaultPorts(); + + /// \brief Set Client Name to something different that the default (JackTrip) + void setClientName(const char* ClientName) + { mClientName = ClientName; } + +private: + + /** \brief Private method to setup a client of the Jack server. + * \exception std::runtime_error Can't start Jack + * + * This method is called by the class constructors. It does the following:\n + * - Connects to the JACK server + * - Sets the shutdown process callback + * - Creates the appropriate number of input and output channels + */ + void setupClient(); + + /** \brief Creates input and output channels in the Jack client + */ + void createChannels(); + + /** \brief JACK calls this shutdown_callback if the server ever shuts down or + * decides to disconnect the client. + */ + static void jackShutdown(void*); + + /// \brief Sets the part of the process callback that sends and receive packets + //void computeNetworkProcess(); + + /// \brief Compute the process to receive packets to JACK + void computeNetworkProcessFromNetwork(); + + /// \brief Compute the process from JACK to send packets + void computeNetworkProcessToNetwork(); + + /** \brief Set the process callback of the member function processCallback. + * This process will be called by the JACK server whenever there is work to be done. + */ + void setProcessCallback(); + + /** \brief JACK process callback + * + * This is the function to be called to process audio. This function is + * of the type JackProcessCallback, which is defined as:\n + * typedef int(* JackProcessCallback)(jack_nframes_t nframes, void *arg) + * \n + * See + * http://jackaudio.org/files/docs/html/types_8h.html#4923142208a8e7dacf00ca7a10681d2b + * for more details + */ + int processCallback(jack_nframes_t nframes); + + /** \brief Wrapper to cast the member processCallback to a static function pointer + * that can be used with jack_set_process_callback + * + * jack_set_process_callback needs a static member function pointer. A normal + * member function won't work because a this pointer is passed under the scenes. + * That's why we + * need to cast the member funcion processCallback to the static function + * wrapperProcessCallback. The callback is then set as:\n + * jack_set_process_callback(mClient, JackAudioInterface::wrapperProcessCallback, + * this) + */ + // reference : http://article.gmane.org/gmane.comp.audio.jackit/12873 + static int wrapperProcessCallback(jack_nframes_t nframes, void *arg) ; + + + int mNumInChans;///< Number of Input Channels + int mNumOutChans; ///< Number of Output Channels + int mNumFrames; ///< Buffer block size, in samples + int mAudioBitResolution; ///< Bit resolution in audio samples + audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode + + jack_client_t* mClient; ///< Jack Client + const char* mClientName; ///< Jack Client Name + QVarLengthArray mInPorts; ///< Vector of Input Ports (Channels) + QVarLengthArray mOutPorts; ///< Vector of Output Ports (Channels) + //jack_port_t** mInPorts; ///< Vector of Input Ports (Channels) + //jack_port_t** mOutPorts; ///< Vector of Output Ports (Channels) + QVarLengthArray mInBuffer; ///< Vector of Input buffers/channel read from JACK + QVarLengthArray mOutBuffer; ///< Vector of Output buffer/channel to write to JACK + + QVarLengthArray mInProcessBuffer;///< Vector of Input buffers/channel for ProcessPlugin + QVarLengthArray mOutProcessBuffer;///< Vector of Output buffers/channel for ProcessPlugin + + int8_t* mInputPacket; ///< Packet containing all the channels to read from the RingBuffer + int8_t* mOutputPacket; ///< Packet containing all the channels to send to the RingBuffer + size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel + + QVector mProcessPlugins; ///< Vector of ProcesPlugins + JackTrip* mJackTrip; ///< JackTrip mediator class + + static QMutex sJackMutex; ///< Mutex to make thread safe jack functions that are not +}; + + +#endif diff --git a/src/JackTrip.cpp b/src/JackTrip.cpp new file mode 100644 index 0000000..6384504 --- /dev/null +++ b/src/JackTrip.cpp @@ -0,0 +1,466 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTrip.cpp + * \author Juan-Pablo Caceres + * \date July 2008 + */ + +#include "JackTrip.h" +#include "UdpDataProtocol.h" +#include "RingBufferWavetable.h" +#include "jacktrip_globals.h" + +#include +//#include // for usleep, sleep +#include +#include + +#include +#include + +using std::cout; using std::endl; + + + +//******************************************************************************* +JackTrip::JackTrip(jacktripModeT JacktripMode, + dataProtocolT DataProtocolType, + int NumChans, + int BufferQueueLength, + unsigned int redundancy, + JackAudioInterface::audioBitResolutionT AudioBitResolution, + DataProtocol::packetHeaderTypeT PacketHeaderType, + underrunModeT UnderRunMode, + int receiver_bind_port, int sender_bind_port, + int receiver_peer_port, int sender_peer_port) : + mJackTripMode(JacktripMode), + mDataProtocol(DataProtocolType), + mPacketHeaderType(PacketHeaderType), + mNumChans(NumChans), + mBufferQueueLength(BufferQueueLength), + mSampleRate(0), + mAudioBufferSize(0), + mAudioBitResolution(AudioBitResolution), + mDataProtocolSender(NULL), + mDataProtocolReceiver(NULL), + mJackAudio(NULL), + mPacketHeader(NULL), + mUnderRunMode(UnderRunMode), + mSendRingBuffer(NULL), + mReceiveRingBuffer(NULL), + mReceiverBindPort(receiver_bind_port), + mSenderPeerPort(sender_peer_port), + mSenderBindPort(sender_bind_port), + mReceiverPeerPort(receiver_peer_port), + mRedundancy(redundancy), + mJackClientName("JackTrip") +{} + + +//******************************************************************************* +JackTrip::~JackTrip() +{ + delete mDataProtocolSender; + delete mDataProtocolReceiver; + delete mJackAudio; + delete mPacketHeader; + delete mSendRingBuffer; + delete mReceiveRingBuffer; +} + + +//******************************************************************************* +void JackTrip::setupJackAudio() +{ + // Create JackAudioInterface Client Object + mJackAudio = new JackAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); + mJackAudio->setClientName(mJackClientName); + mJackAudio->setup(); + mSampleRate = mJackAudio->getSampleRate(); + std::cout << "The Sampling Rate is: " << mSampleRate << std::endl; + std::cout << gPrintSeparator << std::endl; + mAudioBufferSize = mJackAudio->getBufferSizeInSamples(); + int AudioBufferSizeInBytes = mAudioBufferSize*sizeof(sample_t); + std::cout << "The Audio Buffer Size is: " << mAudioBufferSize << " samples" << std::endl; + std::cout << " or: " << AudioBufferSizeInBytes + << " bytes" << std::endl; + std::cout << gPrintSeparator << std::endl; + cout << "The Number of Channels is: " << mJackAudio->getNumInputChannels() << endl; + std::cout << gPrintSeparator << std::endl; + QThread::usleep(100); +} + + +//******************************************************************************* +void JackTrip::setupDataProtocol() +{ + // Create DataProtocol Objects + switch (mDataProtocol) { + case UDP: + std::cout << "Using UDP Protocol" << std::endl; + std::cout << gPrintSeparator << std::endl; + QThread::usleep(100); + mDataProtocolSender = new UdpDataProtocol(this, DataProtocol::SENDER, + //mSenderPeerPort, mSenderBindPort, + mSenderBindPort, mSenderPeerPort, + mRedundancy); + mDataProtocolReceiver = new UdpDataProtocol(this, DataProtocol::RECEIVER, + mReceiverBindPort, mReceiverPeerPort, + mRedundancy); + break; + case TCP: + throw std::invalid_argument("TCP Protocol is not implemented"); + break; + case SCTP: + throw std::invalid_argument("SCTP Protocol is not implemented"); + break; + default: + throw std::invalid_argument("Protocol not defined or unimplemented"); + break; + } + + // Set Audio Packet Size + mDataProtocolSender->setAudioPacketSize + (mJackAudio->getSizeInBytesPerChannel() * mNumChans); + mDataProtocolReceiver->setAudioPacketSize + (mJackAudio->getSizeInBytesPerChannel() * mNumChans); +} + + +//******************************************************************************* +void JackTrip::setupRingBuffers() +{ + // Create RingBuffers with the apprioprate size + /// \todo Make all this operations cleaner + switch (mUnderRunMode) { + case WAVETABLE: + mSendRingBuffer = new RingBufferWavetable(mJackAudio->getSizeInBytesPerChannel() * mNumChans, + gDefaultOutputQueueLength); + mReceiveRingBuffer = new RingBufferWavetable(mJackAudio->getSizeInBytesPerChannel() * mNumChans, + mBufferQueueLength); + + break; + case ZEROS: + mSendRingBuffer = new RingBuffer(mJackAudio->getSizeInBytesPerChannel() * mNumChans, + gDefaultOutputQueueLength); + mReceiveRingBuffer = new RingBuffer(mJackAudio->getSizeInBytesPerChannel() * mNumChans, + mBufferQueueLength); + break; + default: + throw std::invalid_argument("Underrun Mode undefined"); + break; + } +} + + +//******************************************************************************* +void JackTrip::setPeerAddress(const char* PeerHostOrIP) +{ + mPeerAddress = PeerHostOrIP; +} + + +//******************************************************************************* +void JackTrip::appendProcessPlugin(ProcessPlugin* plugin) +{ + mProcessPlugins.append(plugin); + //mJackAudio->appendProcessPlugin(plugin); +} + + +//******************************************************************************* +void JackTrip::start() +{ + // Check if ports are already binded by another process on this machine + checkIfPortIsBinded(mReceiverBindPort); + checkIfPortIsBinded(mSenderBindPort); + + // Set all classes and parameters + setupJackAudio(); + createHeader(mPacketHeaderType); + setupDataProtocol(); + setupRingBuffers(); + + // Start the threads for the specific mode + switch ( mJackTripMode ) + { + case CLIENT : + clientStart(); + break; + case SERVER : + serverStart(); + break; + case CLIENTTOPINGSERVER : + clientPingToServerStart(); + break; + default: + throw std::invalid_argument("Jacktrip Mode undefined"); + break; + } + + // Start Threads + mJackAudio->startProcess(); + for (int i = 0; i < mProcessPlugins.size(); ++i) { + mJackAudio->appendProcessPlugin(mProcessPlugins[i]); + } + mJackAudio->connectDefaultPorts(); + mDataProtocolSender->start(); + mDataProtocolReceiver->start(); +} + + +//******************************************************************************* +void JackTrip::stop() +{ + // Stop The Sender + mDataProtocolSender->stop(); + mDataProtocolSender->wait(); + + // Stop The Receiver + mDataProtocolReceiver->stop(); + mDataProtocolReceiver->wait(); + + // Stop the jack process callback + mJackAudio->stopProcess(); + + cout << "JackTrip Processes STOPPED!" << endl; + cout << gPrintSeparator << endl; + + // Emit the jack stopped signal + emit signalProcessesStopped(); +} + +//******************************************************************************* +void JackTrip::wait() +{ + mDataProtocolSender->wait(); + mDataProtocolReceiver->wait(); +} + + +//******************************************************************************* +void JackTrip::clientStart() +{ + // For the Client mode, the peer (or server) address has to be specified by the user + if ( mPeerAddress.isEmpty() ) { + throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode"); + } + else { + // Set the peer address + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); + mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); + cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl; + cout << gPrintSeparator << endl; + } +} + + +//******************************************************************************* +void JackTrip::serverStart() +{ + // Set the peer address + if ( !mPeerAddress.isEmpty() ) { + std::cout << "WARNING: SERVER mode: Peer Address was set but will be deleted." << endl; + mPeerAddress.clear(); + } + + // Get the client address when it connects + cout << "Waiting for Connection From Client..." << endl; + QHostAddress peerHostAddress; + uint16_t peer_port; + QUdpSocket UdpSockTemp;// Create socket to wait for client + + // Bind the socket + if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort, + QUdpSocket::DefaultForPlatform) ) + { + throw std::runtime_error("Could not bind UDP socket. It may be already binded."); + } + // Listen to client + while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::usleep(100000); } + char buf[1]; + // set client address + UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port); + UdpSockTemp.close(); // close the socket + + mPeerAddress = peerHostAddress.toString(); + cout << "Client Connection Received from IP : " + << qPrintable(mPeerAddress) << endl; + cout << gPrintSeparator << endl; + + // Set the peer address to send packets (in the protocol sender) + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() ); + mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() ); + // We reply to the same port the peer sent the packets + // This way we can go through NAT + // Because of the NAT traversal scheme, the portn need to be + // "symetric", e.g.: + // from Client to Server : src = 4474, dest = 4464 + // from Server to Client : src = 4464, dest = 4474 + mDataProtocolSender->setPeerPort(peer_port); + mDataProtocolReceiver->setPeerPort(peer_port); + setPeerPorts(peer_port); +} + +//******************************************************************************* +void JackTrip::clientPingToServerStart() +{ + // For the Client mode, the peer (or server) address has to be specified by the user + if ( mPeerAddress.isEmpty() ) { + throw std::invalid_argument("Peer Address has to be set if you run in CLIENTTOPINGSERVER mode"); + } + else { + // Set the peer address + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); + } + + // Start Threads + mJackAudio->startProcess(); + //mJackAudio->connectDefaultPorts(); + mDataProtocolSender->start(); + //cout << "STARTED DATA PROTOCOL SENDER-----------------------------" << endl; + //mDataProtocolReceiver->start(); + + QHostAddress serverHostAddress; + QUdpSocket UdpSockTemp;// Create socket to wait for server answer + uint16_t server_port; + + // Bind the socket + if ( !UdpSockTemp.bind(QHostAddress::Any, + mReceiverBindPort, + QUdpSocket::DefaultForPlatform) ) { + throw std::runtime_error("Could not bind UDP socket. It may be already binded."); + } + // Listen to server response + cout << "Waiting for server response..." << endl; + while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::usleep(100000); } + cout << "Received response from server!" << endl; + char buf[1]; + // set client address + UdpSockTemp.readDatagram(buf, 1, &serverHostAddress, &server_port); + UdpSockTemp.close(); // close the socket + + // Stop the sender thread to change server port + mDataProtocolSender->stop(); + mDataProtocolSender->wait(); // Wait for the thread to terminate + /* + while ( mDataProtocolSender->isRunning() ) + { + cout << "IS RUNNING!" << endl; + QThread::usleep(100000); + } + */ + cout << "Server port now set to: " << server_port-1 << endl; + cout << gPrintSeparator << endl; + mDataProtocolSender->setPeerPort(server_port-1); + + // Start Threads + //mJackAudio->connectDefaultPorts(); + mDataProtocolSender->start(); + mDataProtocolReceiver->start(); +} + + +//******************************************************************************* +void JackTrip::createHeader(const DataProtocol::packetHeaderTypeT headertype) +{ + switch (headertype) { + case DataProtocol::DEFAULT : + mPacketHeader = new DefaultHeader(this); + break; + case DataProtocol::JAMLINK : + mPacketHeader = new JamLinkHeader(this); + break; + case DataProtocol::EMPTY : + mPacketHeader = new EmptyHeader(this); + break; + default : + throw std::invalid_argument("Undefined Header Type"); + break; + } +} + + +//******************************************************************************* +void JackTrip::putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet) +{ + mPacketHeader->fillHeaderCommonFromAudio(); + mPacketHeader->putHeaderInPacket(full_packet); + + int8_t* audio_part; + audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); + //std::memcpy(audio_part, audio_packet, mJackAudio->getBufferSizeInBytes()); + std::memcpy(audio_part, audio_packet, mJackAudio->getSizeInBytesPerChannel() * mNumChans); +} + + +//******************************************************************************* +int JackTrip::getPacketSizeInBytes() const +{ + //return (mJackAudio->getBufferSizeInBytes() + mPacketHeader->getHeaderSizeInBytes()); + return (mJackAudio->getSizeInBytesPerChannel() * mNumChans + + mPacketHeader->getHeaderSizeInBytes()); +} + + +//******************************************************************************* +void JackTrip::parseAudioPacket(int8_t* full_packet, int8_t* audio_packet) +{ + int8_t* audio_part; + audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); + //std::memcpy(audio_packet, audio_part, mJackAudio->getBufferSizeInBytes()); + std::memcpy(audio_packet, audio_part, mJackAudio->getSizeInBytesPerChannel() * mNumChans); +} + + +//******************************************************************************* +void JackTrip::checkPeerSettings(int8_t* full_packet) +{ + mPacketHeader->checkPeerSettings(full_packet); +} + + +//******************************************************************************* +void JackTrip::checkIfPortIsBinded(int port) +{ + QUdpSocket UdpSockTemp;// Create socket to wait for client + + // Bind the socket + if ( !UdpSockTemp.bind(QHostAddress::Any, port, QUdpSocket::DontShareAddress) ) + { + UdpSockTemp.close(); // close the socket + throw std::runtime_error( + "Could not bind UDP socket. It may already be binded by another process on your machine. Try using a different port number"); + } + UdpSockTemp.close(); // close the socket +} diff --git a/src/JackTrip.h b/src/JackTrip.h new file mode 100644 index 0000000..69576f2 --- /dev/null +++ b/src/JackTrip.h @@ -0,0 +1,313 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTrip.h + * \author Juan-Pablo Caceres + * \date July 2008 + */ + +#ifndef __JACKTRIP_H__ +#define __JACKTRIP_H__ + +#include //for shared_ptr + +#include +#include + +#include "DataProtocol.h" +#include "JackAudioInterface.h" +#include "PacketHeader.h" +#include "RingBuffer.h" + + +/** \brief Main class to creates a SERVER (to listen) or a CLIENT (to connect + * to a listening server) to send audio streams in the network. + * + * All audio and network settings can be set in this class. + * This class also acts as a Mediator between all the other class. + * Classes that uses JackTrip methods need to register with it. + */ +class JackTrip : public QThread +{ + Q_OBJECT; + +public: + + //----------ENUMS------------------------------------------ + /// \brief Enum for the data Protocol. At this time only UDP is implemented + enum dataProtocolT { + UDP, ///< Use UDP (User Datagram Protocol) + TCP, ///< NOT IMPLEMENTED: Use TCP (Transmission Control Protocol) + SCTP ///< NOT IMPLEMENTED: Use SCTP (Stream Control Transmission Protocol) + }; + + /// \brief Enum for the JackTrip mode + enum jacktripModeT { + SERVER, ///< Run in Server Mode + CLIENT, ///< Run in Client Mode + CLIENTTOPINGSERVER ///< Client of the Ping Server Mode + }; + + /// \brief Enum for the JackTrip Underrun Mode, when packets + enum underrunModeT { + WAVETABLE, ///< Loops on the last received packet + ZEROS ///< Set new buffers to zero if there are no new ones + }; + //--------------------------------------------------------- + + + /** \brief The class Constructor with Default Parameters + * \param JacktripMode JackTrip::CLIENT or JackTrip::SERVER + * \param DataProtocolType JackTrip::dataProtocolT + * \param NumChans Number of Audio Channels (same for inputs and outputs) + * \param BufferQueueLength Audio Buffer for receiving packets + * \param AudioBitResolution Audio Sample Resolutions in bits + * \param redundancy redundancy factor for network data + */ + JackTrip(jacktripModeT JacktripMode = CLIENT, + dataProtocolT DataProtocolType = UDP, + int NumChans = 2, + int BufferQueueLength = 8, + unsigned int redundancy = 1, + JackAudioInterface::audioBitResolutionT AudioBitResolution = + JackAudioInterface::BIT16, + DataProtocol::packetHeaderTypeT PacketHeaderType = + DataProtocol::DEFAULT, + underrunModeT UnderRunMode = WAVETABLE, + int receiver_bind_port = gDefaultPort, + int sender_bind_port = gDefaultPort, + int receiver_peer_port = gDefaultPort, + int sender_peer_port = gDefaultPort); + + /// \brief The class destructor + virtual ~JackTrip(); + + /// \brief Set the Peer Address for jacktripModeT::CLIENT mode only + void setPeerAddress(const char* PeerHostOrIP); + + /** \brief Append a process plugin. Processes will be appended in order + * \param plugin Pointer to ProcessPlugin Class + */ + //void appendProcessPlugin(const std::tr1::shared_ptr plugin); + void appendProcessPlugin(ProcessPlugin* plugin); + + /// \brief Start the processing threads + void start(); + + /// \brief Stop the processing threads + void stop(); + + /// \brief Wait for all the threads to finish. This functions is used when JackTrip is + /// run as a thread + void wait(); + + /// \brief Check if UDP port is already binded + /// \param port Port number + void checkIfPortIsBinded(int port); + + //------------------------------------------------------------------------------------ + /// \name Methods to change parameters after construction + //@{ + // + /// \brief Sets (override) JackTrip Mode after construction + void setJackTripMode(jacktripModeT JacktripMode) + { mJackTripMode = JacktripMode; } + /// \brief Sets (override) DataProtocol Type after construction + void setDataProtocoType(dataProtocolT DataProtocolType) + { mDataProtocol = DataProtocolType; } + /// \brief Sets the Packet header type + void setPacketHeaderType(DataProtocol::packetHeaderTypeT PacketHeaderType) + { + mPacketHeaderType = PacketHeaderType; + delete mPacketHeader; + mPacketHeader = NULL; + createHeader(mPacketHeaderType); + } + /// \brief Sets (override) Number of Channels after construction + /// \todo implement this, not working right now because channels cannot be changed after construction + //void setNumChannels(int NumChans) + //{ mNumChans=NumChans; } + /// \brief Sets (override) Buffer Queue Length Mode after construction + void setBufferQueueLength(int BufferQueueLength) + { mBufferQueueLength = BufferQueueLength; } + /// \brief Sets (override) Audio Bit Resolution after construction + void setAudioBitResolution(JackAudioInterface::audioBitResolutionT AudioBitResolution) + { mAudioBitResolution = AudioBitResolution; } + /// \brief Sets (override) Underrun Mode + void setUnderRunMode(underrunModeT UnderRunMode) + { mUnderRunMode = UnderRunMode; } + /// \brief Sets port numbers for the local and peer machine. + /// Receive port is port + void setAllPorts(int port) + { + mReceiverBindPort = port; + mSenderPeerPort = port; + mSenderBindPort = port; + mReceiverPeerPort = port; + } + /// \brief Sets port numbers to bind in RECEIVER and SENDER sockets. + void setBindPorts(int port) + { + mReceiverBindPort = port; + mSenderBindPort = port; + } + /// \brief Sets port numbers for the peer (remote) machine. + void setPeerPorts(int port) + { + mSenderPeerPort = port; + mReceiverPeerPort = port; + } + /// \brief Set Client Name to something different that the default (JackTrip) + void setClientName(char* ClientName) + { mJackClientName = ClientName; } + //@} + //------------------------------------------------------------------------------------ + + + //------------------------------------------------------------------------------------ + /// \name Mediator Functions + //@{ + /// \todo Document all these functions + void createHeader(const DataProtocol::packetHeaderTypeT headertype); + void putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet); + int getPacketSizeInBytes() const; + void parseAudioPacket(int8_t* full_packet, int8_t* audio_packet); + void sendNetworkPacket(const int8_t* ptrToSlot) + { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot); } + void receiveNetworkPacket(int8_t* ptrToReadSlot) + { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); } + void readAudioBuffer(int8_t* ptrToReadSlot) + { mSendRingBuffer->readSlotBlocking(ptrToReadSlot); } + void writeAudioBuffer(const int8_t* ptrToSlot) + { mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot); } + uint32_t getBufferSizeInSamples() const + { return mJackAudio->getBufferSizeInSamples(); } + JackAudioInterface::samplingRateT getSampleRateType() const + { return mJackAudio->getSampleRateType(); } + uint8_t getAudioBitResolution() const + { return mJackAudio->getAudioBitResolution(); } + int getNumInputChannels() const + { return mJackAudio->getNumInputChannels(); } + int getNumOutputChannels() const + {return mJackAudio->getNumOutputChannels(); } + void checkPeerSettings(int8_t* full_packet); + void increaseSequenceNumber() + { mPacketHeader->increaseSequenceNumber(); } + int getSequenceNumber() const + { return mPacketHeader->getSequenceNumber(); } + int getPeerSequenceNumber(int8_t* full_packet) const + { return mPacketHeader->getPeerSequenceNumber(full_packet); } + //@} + //------------------------------------------------------------------------------------ + + +public slots: + /// \brief Slot to stop all the processes and threads + void slotStopProcesses() + { + std::cout << "Stopping JackTrip..." << std::endl; + stop(); + }; + + /** \brief This slot emits in turn the signal signalNoUdpPacketsForSeconds + * when UDP is waited for more than 30 seconds. + * + * It is used to remove the thread from the server. + */ + void slotUdpWatingTooLong(int wait_msec) + { + int wait_time = 30000; // msec + if ( !(wait_msec%wait_time) ) { + std::cerr << "UDP WAITED MORE THAN 30 seconds." << std::endl; + emit signalNoUdpPacketsForSeconds(); + } + } + + +signals: + /// \brieg Signal emitted when all the processes and threads are stopped + void signalProcessesStopped(); + /// \brieg Signal emitted when no UDP Packets have been received for a while + void signalNoUdpPacketsForSeconds(); + + +private: + + /// \brief Set the JackAudioInteface object + void setupJackAudio(); + /// \brief Set the DataProtocol objects + void setupDataProtocol(); + /// \brief Set the RingBuffer objects + void setupRingBuffers(); + /// \brief Starts for the CLIENT mode + void clientStart(); + /// \brief Starts for the SERVER mode + void serverStart(); + /// \brief Stats for the Client to Ping Server + void clientPingToServerStart(); + + jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT + dataProtocolT mDataProtocol; ///< Data Protocol Tipe + DataProtocol::packetHeaderTypeT mPacketHeaderType; ///< Packet Header Type + + int mNumChans; ///< Number of Channels (inputs = outputs) + int mBufferQueueLength; ///< Audio Buffer from network queue length + uint32_t mSampleRate; ///< Sample Rate + uint32_t mAudioBufferSize; ///< Audio buffer size to process on each callback + JackAudioInterface::audioBitResolutionT mAudioBitResolution; ///< Audio Bit Resolutions + QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode + + /// Pointer to Abstract Type DataProtocol that sends packets + DataProtocol* mDataProtocolSender; + ///< Pointer to Abstract Type DataProtocol that receives packets + DataProtocol* mDataProtocolReceiver; + JackAudioInterface* mJackAudio; ///< Interface to Jack Client + PacketHeader* mPacketHeader; ///< Pointer to Packet Header + underrunModeT mUnderRunMode; ///< underrunModeT Mode + + /// Pointer for the Send RingBuffer + RingBuffer* mSendRingBuffer; + /// Pointer for the Receive RingBuffer + RingBuffer* mReceiveRingBuffer; + + int mReceiverBindPort; ///< Incoming (receiving) port for local machine + int mSenderPeerPort; ///< Incoming (receiving) port for peer machine + int mSenderBindPort; ///< Outgoing (sending) port for local machine + int mReceiverPeerPort; ///< Outgoing (sending) port for peer machine + + unsigned int mRedundancy; ///< Redundancy factor in network data + const char* mJackClientName; ///< JackAudio Client Name + + QVector mProcessPlugins; ///< Vector of ProcesPlugins +}; + +#endif diff --git a/src/JackTripThread.cpp b/src/JackTripThread.cpp new file mode 100644 index 0000000..5b3f52d --- /dev/null +++ b/src/JackTripThread.cpp @@ -0,0 +1,102 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTripThread.cpp + * \author Juan-Pablo Caceres + * \date September 2008 + */ + + +#include "JackTripThread.h" +#include "NetKS.h" + +#include +#include + +using std::cout; using std::endl; + + +//******************************************************************************* +void JackTripThread::run() +{ + JackTrip jacktrip(mJackTripMode); + jacktrip.setAllPorts(mPortNum); + + if ( mJackTripMode == JackTrip::CLIENT ) + { + jacktrip.setPeerAddress(mPeerAddress); + } + + NetKS netks; + jacktrip.appendProcessPlugin(&netks); + //netks.play(); + + + //QThread::sleep(1); + jacktrip.start(); + //netks.play(); + jacktrip.wait(); + + + cout << "******** AFTER JACKTRIPTHREAD START **************" << endl; + //QThread::sleep(9999999); + + + + /* + jack_client_t* mClient; + const char* client_name = "JackThread"; + const char* server_name = NULL; + jack_options_t options = JackNoStartServer; + jack_status_t status; + + mClient = jack_client_open (client_name, options, &status, server_name); + + if (mClient == NULL) { + fprintf (stderr, "jack_client_open() failed, " + "status = 0x%2.0x\n", status); + if (status & JackServerFailed) { + fprintf (stderr, "Unable to connect to JACK server\n"); + } + std::exit(1); + } + if (status & JackServerStarted) { + fprintf (stderr, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + client_name = jack_get_client_name(mClient); + fprintf (stderr, "unique name `%s' assigned\n", client_name); + } + */ + + +} diff --git a/src/JackTripThread.h b/src/JackTripThread.h new file mode 100644 index 0000000..6825386 --- /dev/null +++ b/src/JackTripThread.h @@ -0,0 +1,64 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTripThread.h + * \author Juan-Pablo Caceres + * \date September 2008 + */ + +#ifndef __JACKTRIPTHREAD_H__ +#define __JACKTRIPTHREAD_H__ + +#include + +#include "JackTrip.h" + +/** \brief Test class that runs JackTrip inside a thread + */ +class JackTripThread : public QThread +{ +public: + JackTripThread(JackTrip::jacktripModeT JacktripMode) : mJackTripMode(JacktripMode) {}; + virtual ~JackTripThread(){}; + void run(); + + void setPort(int port_num) { mPortNum = port_num; } ; + void setPeerAddress(const char* PeerHostOrIP) { mPeerAddress = PeerHostOrIP; } + +private: + JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT + int mPortNum; + const char* mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode +}; + + +#endif //__JACKTRIPTHREAD_H__ diff --git a/src/JackTripWorker.cpp b/src/JackTripWorker.cpp new file mode 100644 index 0000000..e20c4ba --- /dev/null +++ b/src/JackTripWorker.cpp @@ -0,0 +1,178 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTripWorker.cpp + * \author Juan-Pablo Caceres + * \date September 2008 + */ + +#include +#include + +#include +#include + +#include "JackTripWorker.h" +#include "JackTrip.h" +#include "UdpMasterListener.h" +#include "NetKS.h" +#include "LoopBack.h" + +using std::cout; using std::endl; + +//******************************************************************************* +JackTripWorker::JackTripWorker(UdpMasterListener* udpmasterlistener) : + mUdpMasterListener(NULL), + mSpawning(false), + mID(0), + mNumChans(1) +{ + /* From the QT Documentation: + QThreadPool supports executing the same QRunnable more than once + by calling tryStart(this) from within QRunnable::run(). If autoDelete is + enabled the QRunnable will be deleted when the last thread exits the + run function. Calling start() multiple times with the same QRunnable + when autoDelete is enabled creates a race condition and is not recommended. + */ + mUdpMasterListener = udpmasterlistener; + setAutoDelete(false); // stick around after calling run() + //mNetks = new NetKS; + //mNetks->play(); +} + + +//******************************************************************************* +JackTripWorker::~JackTripWorker() +{ + delete mUdpMasterListener; + +} + + +//******************************************************************************* +void JackTripWorker::setJackTrip(int id, uint32_t client_address, + uint16_t server_port, uint16_t client_port, + int num_channels) +{ + { //Start Spawning, so lock mSpawning + QMutexLocker locker(&mMutex); + mSpawning = true; + } + mID = id; + // Set the jacktrip address and ports + mClientAddress.setAddress(client_address); + mServerPort = server_port; + mClientPort = client_port; + mNumChans = num_channels; +} + + +//******************************************************************************* +void JackTripWorker::run() +{ + /* + NOTE: This is the message that qt prints when an exception is thrown: + 'Qt Concurrent has caught an exception thrown from a worker thread. + This is not supported, exceptions thrown in worker threads must be + caught before control returns to Qt Concurrent.' + */ + + // Try catching any exceptions that come from JackTrip + try + { + // Local event loop. this is necesary because QRunnables don't have their own as QThreads + QEventLoop event_loop; + + // Create and setup JackTrip Object + JackTrip jacktrip(JackTrip::CLIENT, JackTrip::UDP, mNumChans, 2); + jacktrip.setPeerAddress( mClientAddress.toString().toLatin1().data() ); + jacktrip.setBindPorts(mServerPort); + jacktrip.setPeerPorts(mClientPort-1); + + // Connect signals and slots + // ------------------------- + QObject::connect(&jacktrip, SIGNAL(signalNoUdpPacketsForSeconds()), + &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); + + // Connection to terminate the local eventloop when jacktrip is done + QObject::connect(&jacktrip, SIGNAL(signalProcessesStopped()), + &event_loop, SLOT(quit()), Qt::QueuedConnection); + + // Karplus Strong String + NetKS netks; + jacktrip.appendProcessPlugin(&netks); + // Play the String + QTimer timer; + QObject::connect(&timer, SIGNAL(timeout()), &netks, SLOT(exciteString()), + Qt::QueuedConnection); + timer.start(300); + + // Start Threads and event loop + jacktrip.start(); + + { // Thread is already spawning, so release the lock + QMutexLocker locker(&mMutex); + mSpawning = false; + } + + event_loop.exec(); // Excecution will block here until exit() the QEventLoop + //-------------------------------------------------------------------------- + + // wait for jacktrip to be done before exiting the Worker Thread + jacktrip.wait(); + + } + catch ( const std::exception & e ) + { + std::cerr << "Couldn't send thread to the Pool" << endl; + std::cerr << e.what() << endl; + std::cerr << gPrintSeparator << endl; + } + + mUdpMasterListener->releasePort(mID); + { + // Thread is already spawning, so release the lock + QMutexLocker locker(&mMutex); + mSpawning = false; + } + + cout << "JackTrip ID = " << mID << " released from the THREAD POOL" << endl; + cout << gPrintSeparator << endl; +} + + +//******************************************************************************* +bool JackTripWorker::isSpawning() +{ + QMutexLocker locker(&mMutex); + return mSpawning; +} diff --git a/src/JackTripWorker.h b/src/JackTripWorker.h new file mode 100644 index 0000000..8df371e --- /dev/null +++ b/src/JackTripWorker.h @@ -0,0 +1,122 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTripWorker.h + * \author Juan-Pablo Caceres + * \date September 2008 + */ + +#ifndef __JACKTRIPWORKER_H__ +#define __JACKTRIPWORKER_H__ + +#include + +#include +#include +#include +#include +#include + +#include "jacktrip_types.h" + +class JackTrip; // forward declaration +class UdpMasterListener; // forward declaration + + +/** \brief Prototype of the worker class that will be cloned through sending threads to the + * Thread Pool + * + * This class can be send to the ThreadPool using the start() method. Each time + * it is sent, it'll became "independent" of the prototype, which means + * that the prototype state can be changed, and used to send and start + * another thread into the pool. setAutoDelete must be set to false + * in order for this to work. + */ +// Note that it is not possible to start run() as an event loop. That has to be implemented +// inside a QThread +class JackTripWorker : public QObject, public QRunnable +{ + Q_OBJECT; // QRunnable is not a QObject, so I have to inherit from QObject as well + +public: + /// \brief The class constructor + JackTripWorker(UdpMasterListener* udpmasterlistener); + + /// \brief The class destructor + virtual ~JackTripWorker(); + + /** \brief Implements the Thread Loop. + * To start the thread, call start() ( DO NOT CALL run() ). + */ + void run(); + + /** \brief Check if the Thread is Spawning + * \return true is it is spawning, false if it's already running + */ + bool isSpawning(); + + /** \brief Sets the JackTripWorker properties + * \param id ID number + * \param address + */ + void setJackTrip(int id, uint32_t client_address, + uint16_t server_port, uint16_t client_port, + int num_channels); + + +private slots: + void slotTest() + { + std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; + } + + +private: + + UdpMasterListener* mUdpMasterListener; ///< Master Listener Socket + QHostAddress mClientAddress; ///< Client Address + uint16_t mServerPort; ///< Server Ephemeral Incomming Port to use with Client + + /// Client Outgoing Port. By convention, the receving port will be mClientPort -1 + uint16_t mClientPort; + + /// Thread spawning internal lock. + /// If true, the prototype is working on creating (spawning) a new thread + volatile bool mSpawning; + QMutex mMutex; ///< Mutex to protect mSpawning + + int mID; ///< ID thread number + int mNumChans; ///< Number of Channels +}; + + +#endif //__JACKTRIPWORKER_H__ diff --git a/src/JackTripWorkerMessages.h b/src/JackTripWorkerMessages.h new file mode 100644 index 0000000..e3bef9a --- /dev/null +++ b/src/JackTripWorkerMessages.h @@ -0,0 +1,75 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTripWorkerMessages.h + * \author Juan-Pablo Caceres + * \date October 2008 + */ + +#ifndef __JACKTRIPWORKERMESSAGES_H__ +#define __JACKTRIPWORKERMESSAGES_H__ + +#include +#include + +#include + +class JackTripWorkerMessages : public QObject +{ + Q_OBJECT; + +public: + JackTripWorkerMessages() {}; + virtual ~JackTripWorkerMessages() {}; + + void play() + { + std::cout << "********** PALYING ***********************************" << std::endl; + QTimer *timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(slotTest()), Qt::QueuedConnection); + timer->start(300); + } + +public slots: + void slotTest() + { + std::cout << "---JackTripWorkerMessages slotTest()---" << std::endl; + } + +signals: + void signalTest(); + /// Signal to stop the event loop inside the JackTripWorker Thread + void signalStopEventLoop(); + +}; + +#endif //__JACKTRIPWORKERMESSAGES_H__ diff --git a/src/LoopBack.cpp b/src/LoopBack.cpp new file mode 100644 index 0000000..f22197b --- /dev/null +++ b/src/LoopBack.cpp @@ -0,0 +1,54 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file LoopBack.cpp + * \author Juan-Pablo Caceres + * \date July 2008 + */ + + +#include "LoopBack.h" +#include "jacktrip_types.h" + +#include // for memcpy + + +//******************************************************************************* +void LoopBack::compute(int nframes, float** inputs, float** outputs) +{ + for ( int i = 0; i < getNumInputs(); i++ ) { + // Everything that comes out, copy back to inputs + //memcpy(inputs[i], outputs[i], sizeof(sample_t) * nframes); + memcpy(outputs[i], inputs[i], sizeof(sample_t) * nframes); + } +} + diff --git a/src/LoopBack.h b/src/LoopBack.h new file mode 100644 index 0000000..c25de27 --- /dev/null +++ b/src/LoopBack.h @@ -0,0 +1,70 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file LoopBack.h + * \author Juan-Pablo Caceres + * \date July 2008 + */ + + +/** \brief Connect Inputs to Outputs + * + */ +#ifndef __LOOPBACK_H__ +#define __LOOPBACK_H__ + +#include "ProcessPlugin.h" + + +/** \brief This Class just copy audio from its inputs to its outputs. + * + * It can be use to do loopback without the need to externally connect channels + * in JACK. Note that if you do connect the channels in jack, you'll + * be effectively multiplying the signal by 2. + */ +class LoopBack : public ProcessPlugin +{ +public: + /// \brief The class constructor sets the number of channels to connect as loopback + LoopBack(int numchans) { mNumChannels = numchans; }; + /// \brief The class destructor + virtual ~LoopBack() {}; + + virtual int getNumInputs() { return(mNumChannels); }; + virtual int getNumOutputs() { return(mNumChannels); }; + virtual void compute(int nframes, float** inputs, float** outputs); + +private: + int mNumChannels; +}; + +#endif diff --git a/src/NetKS.h b/src/NetKS.h new file mode 100644 index 0000000..2d09c4b --- /dev/null +++ b/src/NetKS.h @@ -0,0 +1,137 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JackTrip.h + * \author Juan-Pablo Caceres + * \date October 2008 + */ + +#ifndef __NETKS_H__ +#define __NETKS_H__ + +#include +#include + +#include + +#include "ProcessPlugin.h" + +/** \brief A simple (basic) network Karplus Strong. + * + * This plugin creates a one channel network karplus strong. + */ +class NetKS : public ProcessPlugin +{ + Q_OBJECT; + + +public: + /* + void play() + { + std::cout << "********** PALYING ***********************************" << std::endl; + QTimer *timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(exciteString())); + timer->start(300); + } + */ + +private slots: + + /// \brief Stlot to excite (play) the string + void exciteString() + { + std::cout << "========= EXTICING STRING ===========" << std::endl; + fbutton0 = 1.0; + //std::cout << fbutton0 << std::endl; + usleep(280000); /// \todo Define this number based on the sampling rate and buffer size + fbutton0 = 0.0; + //std::cout << fbutton0 << std::endl; + } + + //=========== FROM FAUST =================================================== + private: + float fbutton0; + float fVec0[2]; + float fRec0[2]; + int iRec1[2]; + float fVec1[2]; + public: + virtual int getNumInputs() { return 1; } + virtual int getNumOutputs() { return 1; } + static void classInit(int /*samplingFreq*/) {} + virtual void instanceInit(int samplingFreq) { + fSamplingFreq = samplingFreq; + fbutton0 = 0.0; + for (int i=0; i<2; i++) fVec0[i] = 0; + for (int i=0; i<2; i++) fRec0[i] = 0; + for (int i=0; i<2; i++) iRec1[i] = 0; + for (int i=0; i<2; i++) fVec1[i] = 0; + } + virtual void init(int samplingFreq) { + classInit(samplingFreq); + instanceInit(samplingFreq); + } + /* + virtual void buildUserInterface(UI* interface) { + interface->openVerticalBox("excitator"); + interface->addButton("play", &fbutton0); + interface->closeBox(); + } + */ + virtual void compute (int count, float** input, float** output) { + float* input0 = input[0]; + float* output0 = output[0]; + float fSlow0 = fbutton0; + for (int i=0; i 0.000000f) + fRec0[1]) - (3.333333e-03f * (fRec0[1] > 0.000000f))); + iRec1[0] = (12345 + (1103515245 * iRec1[1])); + float fTemp0 = ((4.190951e-10f * iRec1[0]) * (fRec0[0] > 0.000000f)); + float fTemp1 = input0[i]; + fVec1[0] = (fTemp1 + fTemp0); + output0[i] = (0.500000f * ((fTemp0 + fTemp1) + fVec1[1])); + // post processing + fVec1[1] = fVec1[0]; + iRec1[1] = iRec1[0]; + fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; + } + } + + //============================================================================ + +}; + + +#endif // __NETKS_H__ + diff --git a/src/PacketHeader.cpp b/src/PacketHeader.cpp new file mode 100644 index 0000000..1a37556 --- /dev/null +++ b/src/PacketHeader.cpp @@ -0,0 +1,265 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file PacketHeader.cpp + * \author Juan-Pablo Caceres + * \date July 2008 + */ + +#include "PacketHeader.h" +#include "JackTrip.h" + +#include +#include +#include +#include + +using std::cout; using std::endl; + + +//####################################################################### +//####################### PacketHeader ################################## +//####################################################################### +//*********************************************************************** +PacketHeader::PacketHeader(JackTrip* jacktrip) : + mSeqNumber(0), mJackTrip(jacktrip) +{} + + +//*********************************************************************** +uint64_t PacketHeader::usecTime() +{ + struct timeval tv; + gettimeofday (&tv, NULL); + return ( (tv.tv_sec * 1000000) + // seconds + (tv.tv_usec) ); // plus the microseconds. Type suseconds_t, range [-1, 1000000] +} + + + + +//####################################################################### +//####################### DefaultHeader ################################# +//####################################################################### +//*********************************************************************** +DefaultHeader::DefaultHeader(JackTrip* jacktrip) : + PacketHeader(jacktrip), mJackTrip(jacktrip) +{ + mHeader.TimeStamp = 0; + mHeader.SeqNumber = 0; + mHeader.BufferSize = 0; + mHeader.SamplingRate = 0; + mHeader.BitResolution = 0; + mHeader.NumInChannels = 0; + mHeader.NumOutChannels = 0; + //mHeader.Dummy = 0; +} + + +//*********************************************************************** +void DefaultHeader::fillHeaderCommonFromAudio() +{ + mHeader.BufferSize = mJackTrip->getBufferSizeInSamples(); + mHeader.SamplingRate = mJackTrip->getSampleRateType (); + mHeader.NumInChannels = mJackTrip->getNumInputChannels(); + mHeader.BitResolution = mJackTrip->getAudioBitResolution(); + mHeader.NumOutChannels = mJackTrip->getNumOutputChannels(); + //mHeader.SeqNumber = 0; + mHeader.TimeStamp = PacketHeader::usecTime(); + //cout << mHeader.TimeStamp << endl; + //printHeader(); +} + + +//*********************************************************************** +void DefaultHeader::checkPeerSettings(int8_t* full_packet) +{ + bool error = false; + + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + + // Check Buffer Size + if ( peer_header->BufferSize != mHeader.BufferSize ) + { + std::cerr << "ERROR: Peer Buffer Size is : " << peer_header->BufferSize << endl; + std::cerr << " Local Buffer Size is : " << mHeader.BufferSize << endl; + std::cerr << "Make sure both machines use same buffer size" << endl; + std::cerr << gPrintSeparator << endl; + error = true; + } + + // Check Sampling Rate + if ( peer_header->SamplingRate != mHeader.SamplingRate ) + { + std::cerr << "ERROR: Peer Sampling Rate is : " << + JackAudioInterface::getSampleRateFromType + ( static_cast(peer_header->SamplingRate) ) << endl; + std::cerr << " Local Sampling Rate is : " << + JackAudioInterface::getSampleRateFromType + ( static_cast(mHeader.SamplingRate) ) << endl; + std::cerr << "Make sure both machines use the same Sampling Rate" << endl; + std::cerr << gPrintSeparator << endl; + error = true; + } + + // Check Audio Bit Resolution + if ( peer_header->BitResolution != mHeader.BitResolution ) + { + std::cerr << "ERROR: Peer Audio Bit Resolution is : " + << static_cast(peer_header->BitResolution) << endl; + std::cerr << " Local Audio Bit Resolution is : " + << static_cast(mHeader.BitResolution) << endl; + std::cerr << "Make sure both machines use the same Bit Resolution" << endl; + std::cerr << gPrintSeparator << endl; + error = true; + } + + // Exit program if error + if (error) + { + //std::cerr << "Exiting program..." << endl; + //std::exit(1); + throw std::logic_error("Local and Peer Settings don't match"); + } + /// \todo Check number of channels and other parameters +} + + +//*********************************************************************** +void DefaultHeader::printHeader() const +{ + cout << "Default Packet Header:" << endl; + cout << "Buffer Size = " << static_cast(mHeader.BufferSize) << endl; + // Get the sample rate in Hz form the JackAudioInterface::samplingRateT + int sample_rate = + JackAudioInterface::getSampleRateFromType + ( static_cast(mHeader.SamplingRate) ); + cout << "Sampling Rate = " << sample_rate << endl; + cout << "Audio Bit Resolutions = " << static_cast(mHeader.BitResolution) << endl; + cout << "Number of Input Channels = " << static_cast(mHeader.NumInChannels) << endl; + cout << "Number of Output Channels = " << static_cast(mHeader.NumOutChannels) << endl; + cout << "Sequence Number = " << static_cast(mHeader.SeqNumber) << endl; + cout << "Time Stamp = " << mHeader.TimeStamp << endl; + cout << gPrintSeparator << endl; + cout << sizeof(mHeader) << endl; +} + + +//*********************************************************************** +uint16_t DefaultHeader::getPeerSequenceNumber(int8_t* full_packet) const +{ + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->SeqNumber; +} + + + + + + + + +//####################################################################### +//####################### JamLinkHeader ################################# +//####################################################################### +//*********************************************************************** +JamLinkHeader::JamLinkHeader(JackTrip* jacktrip) : + PacketHeader(jacktrip), mJackTrip(jacktrip) +{ + mHeader.Common = 0; + mHeader.SeqNumber = 0; + mHeader.TimeStamp = 0; +} + + +//*********************************************************************** +void JamLinkHeader::fillHeaderCommonFromAudio() +{ + // Check number of channels + int num_inchannels = mJackTrip->getNumInputChannels(); + if ( num_inchannels != 1 ) { + //std::cerr << "ERROR: JamLink only support ONE channel. Run JackTrip using only one channel" + // << endl; + //std::exit(1); + throw std::logic_error("JamLink only support ONE channel. Run JackTrip using only one channel"); + } + + // Sampling Rate + int rate_type = mJackTrip->getSampleRateType(); + if ( rate_type != JackAudioInterface::SR48 ) { + throw std::logic_error("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); + } + + // Check Buffer Size + int buf_size = mJackTrip->getBufferSizeInSamples(); + if ( buf_size != 64 ) + { + throw std::logic_error("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); + } + + mHeader.Common = (ETX_MONO | ETX_16BIT | ETX_XTND) + 64; + switch (rate_type) + { + case JackAudioInterface::SR48 : + mHeader.Common = (mHeader.Common | ETX_48KHZ); + break; + case JackAudioInterface::SR44 : + mHeader.Common = (mHeader.Common | ETX_44KHZ); + break; + case JackAudioInterface::SR32 : + mHeader.Common = (mHeader.Common | ETX_32KHZ); + break; + case JackAudioInterface::SR22 : + mHeader.Common = (mHeader.Common | ETX_22KHZ); + break; + default: + //std::cerr << "ERROR: Sample rate not supported by JamLink" << endl; + //std::exit(1); + throw std::out_of_range("Sample rate not supported by JamLink"); + break; + } +} + + + + + + +//####################################################################### +//####################### EmptyHeader ################################# +//####################################################################### +//*********************************************************************** +EmptyHeader::EmptyHeader(JackTrip* jacktrip) : + PacketHeader(jacktrip), mJackTrip(jacktrip) +{} diff --git a/src/PacketHeader.h b/src/PacketHeader.h new file mode 100644 index 0000000..73cf979 --- /dev/null +++ b/src/PacketHeader.h @@ -0,0 +1,292 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file PacketHeader.h + * \author Juan-Pablo Caceres + * \date July 2008 + */ + +#ifndef __PACKETHEADER_H__ +#define __PACKETHEADER_H__ + +#include +#include // for shared_ptr +#include + +#include "jacktrip_types.h" +#include "jacktrip_globals.h" +class JackTrip; // Forward Declaration + + +/// \brief Abstract Header Struct, Header Stucts should subclass it +struct HeaderStruct{}; + +/// \brief Default Header Struct +struct DefaultHeaderStruct : public HeaderStruct +{ +public: + // watch out for alignment... + uint64_t TimeStamp; ///< Time Stamp + uint16_t SeqNumber; ///< Sequence Number + uint16_t BufferSize; ///< Buffer Size in Samples + uint8_t SamplingRate; ///< Sampling Rate in JackAudioInterface::samplingRateT + uint8_t BitResolution; ///< Audio Bit Resolution + uint8_t NumInChannels; ///< Number of Input Channels + uint8_t NumOutChannels; ///< Number of Output Channels + //uint8_t Dummy; ///< Dummy value to byte padding alignment +}; + +//--------------------------------------------------------- +//JamLink UDP Header: +/************************************************************************/ +/* values for the UDP stream type */ +/* streamType is a 16-bit value at the head of each UDP stream */ +/* Its bit map is as follows: (b15-msb) */ +/* B15:reserved, B14:extended header, B13 Stereo, B12 not 16-bit */ +/* B11-B9: 0-48 Khz, 1-44 Khz, 2-32 Khz, 3-24 Khz, */ +/* 4-22 Khz, 5-16 Khz, 6-11 Khz, 7-8 Khz */ +/* B8-0: Samples in packet */ +/************************************************************************/ +const unsigned short ETX_RSVD = (0<<15); +const unsigned short ETX_XTND = (1<<14); +const unsigned short ETX_STEREO = (1<<13); +const unsigned short ETX_MONO = (0<<13); +const unsigned short ETX_16BIT = (0<<12); +//inline unsigned short ETX_RATE_MASK(const unsigned short a) { a&(0x7<<9); } +const unsigned short ETX_48KHZ = (0<<9); +const unsigned short ETX_44KHZ = (1<<9); +const unsigned short ETX_32KHZ = (2<<9); +const unsigned short ETX_24KHZ = (3<<9); +const unsigned short ETX_22KHZ = (4<<9); +const unsigned short ETX_16KHZ = (5<<9); +const unsigned short ETX_11KHZ = (6<<9); +const unsigned short ETX_8KHZ = (7<<9); +// able to express up to 512 SPP +//inline unsigned short ETX_SPP(const unsigned short a) { (a&0x01FF); } + +/// \brief JamLink Header Struct +struct JamLinkHeaderStuct : public HeaderStruct +{ + // watch out for alignment -- need to be on 4 byte chunks + uint16_t Common; ///< Common part of the header, 16 bit + uint16_t SeqNumber; ///< Sequence Number + uint32_t TimeStamp; ///< Time Stamp +}; + + +//####################################################################### +//####################### PacketHeader ################################## +//####################################################################### +/** \brief Base class for header type. Subclass this struct to + * create a new header. + */ +class PacketHeader +{ +public: + /// \brief The class Constructor + PacketHeader(JackTrip* jacktrip); + /// \brief The class Destructor + virtual ~PacketHeader() {}; + + /** \brief Return a time stamp in microseconds + * \return Time stamp: microseconds since midnight (0 hour), January 1, 1970 + */ + static uint64_t usecTime(); + + /// \todo Implement this using a JackTrip Method (Mediator) member instead of the + /// reference to JackAudio + virtual void fillHeaderCommonFromAudio() = 0; + + /* \brief Parse the packet header and take appropriate measures (like change settings, or + * quit the program if peer settings don't match) + */ + virtual void parseHeader() = 0; + + virtual void checkPeerSettings(int8_t* full_packet) = 0; + virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const = 0; + + /* \brief Increase sequence number for counter, a 16bit number + */ + virtual void increaseSequenceNumber() + { + mSeqNumber++; + }; + + /* \brief Returns the current sequence number + * \return 16bit Sequence number + */ + virtual uint16_t getSequenceNumber() const + { + return mSeqNumber; + } + + /* \brief Get the header size in bytes + */ + virtual int getHeaderSizeInBytes() const = 0; + + + virtual void putHeaderInPacketBaseClass(int8_t* full_packet, + const HeaderStruct& header_struct) + { + std::memcpy(full_packet, reinterpret_cast(&header_struct), + getHeaderSizeInBytes() ); + }; + + /* \brief Put the header in buffer pointed by full_packet + * \param full_packet Pointer to full packet (audio+header). Size must be + * sizeof(header part) + sizeof(audio part) + */ + virtual void putHeaderInPacket(int8_t* full_packet) = 0; + +private: + uint16_t mSeqNumber; + JackTrip* mJackTrip; ///< JackTrip mediator class +}; + + + + +//####################################################################### +//####################### DefaultHeader ################################# +//####################################################################### +/** \brief Default Header + */ +class DefaultHeader : public PacketHeader +{ +public: + /* + //----------STRUCT----------------------------------------- + /// \brief Default Header Struct + struct DefaultHeaderStruct + { + // watch out for alignment... + uint64_t TimeStamp; ///< Time Stamp + uint16_t SeqNumber; ///< Sequence Number + uint16_t BufferSize; ///< Buffer Size in Samples + uint8_t SamplingRate; ///< Sampling Rate in JackAudioInterface::samplingRateT + uint8_t NumInChannels; ///< Number of Input Channels + uint8_t NumOutChannels; ///< Number of Output Channels + // uint8_t BitResolution; ///< \todo implement this part + }; + //--------------------------------------------------------- + */ + DefaultHeader(JackTrip* jacktrip); + virtual ~DefaultHeader() {}; + virtual void fillHeaderCommonFromAudio(); + virtual void parseHeader() {}; + virtual void checkPeerSettings(int8_t* full_packet); + virtual void increaseSequenceNumber() + { + mHeader.SeqNumber++; + //std::cout << "Sequence Number = " << static_cast(mHeader.SeqNumber) << std::endl; + }; + virtual uint16_t getSequenceNumber() const + { + return mHeader.SeqNumber; + } + virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const; + virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); }; + virtual void putHeaderInPacket(int8_t* full_packet) + { + putHeaderInPacketBaseClass(full_packet, mHeader); + //std::memcpy(full_packet, reinterpret_cast(&mHeader), + // getHeaderSizeInBytes() ); + }; + void printHeader() const; + +private: + //DefaultHeaderStruct mHeader; ///< Header Struct + DefaultHeaderStruct mHeader;///< Default Header Struct + JackTrip* mJackTrip; ///< JackTrip mediator class +}; + + + + +//####################################################################### +//####################### JamLinkHeader ################################# +//####################################################################### + +/** \brief JamLink Header + */ +class JamLinkHeader : public PacketHeader +{ +public: + + JamLinkHeader(JackTrip* jacktrip); + virtual ~JamLinkHeader() {}; + + virtual void fillHeaderCommonFromAudio(); + virtual void parseHeader() {}; + virtual void checkPeerSettings(int8_t* /*full_packet*/) {} + virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { /*\todo IMPLEMENT*/ return 0; } + virtual void increaseSequenceNumber() {}; + virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); }; + virtual void putHeaderInPacket(int8_t* full_packet) + { + putHeaderInPacketBaseClass(full_packet, mHeader); + }; + +private: + JamLinkHeaderStuct mHeader; ///< JamLink Header Struct + JackTrip* mJackTrip; ///< JackTrip mediator class +}; + + + +//####################################################################### +//####################### EmptyHeader ################################# +//####################################################################### + +/** \brief Empty Header to use with systems that don't include a header. + */ +class EmptyHeader : public PacketHeader +{ +public: + + EmptyHeader(JackTrip* jacktrip); + virtual ~EmptyHeader() {}; + + virtual void fillHeaderCommonFromAudio() {}; + virtual void parseHeader() {}; + virtual void checkPeerSettings(int8_t* /*full_packet*/) {} + virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; /*\todo IMPLEMENT*/} + virtual void increaseSequenceNumber() {}; + virtual int getHeaderSizeInBytes() const { return 0; }; + virtual void putHeaderInPacket(int8_t* /*full_packet*/) {}; + +private: + JackTrip* mJackTrip; ///< JackTrip mediator class +}; + + +#endif //__PACKETHEADER_H__ diff --git a/src/ProcessPlugin.cpp b/src/ProcessPlugin.cpp new file mode 100644 index 0000000..be26a31 --- /dev/null +++ b/src/ProcessPlugin.cpp @@ -0,0 +1,31 @@ +//#include "ProcessPlugin.h" + +/* +//---------------------------------------------------------------------------- +// Jack Callbacks +//---------------------------------------------------------------------------- + +int srate(jack_nframes_t nframes, void *arg) +{ + printf("the sample rate is now %u/sec\n", nframes); + return 0; +} + +void jack_shutdown(void *arg) +{ + std::cout << "" << std::endl; + std::exit(1); +} + +int process (jack_nframes_t nframes, void *arg) +{ + for (int i = 0; i < gNumInChans; i++) { + gInChannel[i] = (float *)jack_port_get_buffer(input_ports[i], nframes); + } + for (int i = 0; i < gNumOutChans; i++) { + gOutChannel[i] = (float *)jack_port_get_buffer(output_ports[i], nframes); + } + DSP.compute(nframes, gInChannel, gOutChannel); + return 0; +} +*/ diff --git a/src/ProcessPlugin.h b/src/ProcessPlugin.h new file mode 100644 index 0000000..70533c8 --- /dev/null +++ b/src/ProcessPlugin.h @@ -0,0 +1,81 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file ProcessPlugin.h + * \author Juan-Pablo Caceres + * \date July 2008 + */ + +#ifndef __PROCESSPLUGIN_H__ +#define __PROCESSPLUGIN_H__ + +#include +#include + +/** \brief Interface for the process plugins to add to the JACK callback process in + * JackAudioInterface + * + * This class contains the same methods of the FAUST dsp class. A mydsp class can inherit from + * this class the same way it inherits from dsp. Subclass should implement all methods + * except init, which is optional for processing that are sampling rate dependent or + * that need specific initialization. + */ +class ProcessPlugin : public QObject +{ +public: + + /// \brief The Class Constructor + ProcessPlugin() {}; + /// \brief The Class Destructor + virtual ~ProcessPlugin() {}; + + /// \brief Return Number of Input Channels + virtual int getNumInputs() = 0; + /// \brief Return Number of Output Channels + virtual int getNumOutputs() = 0; + + //virtual void buildUserInterface(UI* interface) = 0; + + /** \brief Do proper Initialization of members and class instances. By default this + * initializes the Sampling Frequency. If a class instance depends on the + * sampling frequency, it should be initialize here. + */ + virtual void init(int samplingRate) { fSamplingFreq = samplingRate; }; + + /// \brief Compute process + virtual void compute(int nframes, float** inputs, float** outputs) = 0; + +protected: + int fSamplingFreq; ///< Faust Data member, Sampling Rate +}; + +#endif diff --git a/src/RingBuffer.cpp b/src/RingBuffer.cpp new file mode 100644 index 0000000..a9e746c --- /dev/null +++ b/src/RingBuffer.cpp @@ -0,0 +1,250 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file RingBuffer.cpp + * \author Juan-Pablo Caceres + * \date July 2008 + */ + + +#include "RingBuffer.h" + +#include +#include +#include +#include + +using std::cout; using std::endl; + + +//******************************************************************************* +RingBuffer::RingBuffer(int SlotSize, int NumSlots) : + mSlotSize(SlotSize), + mNumSlots(NumSlots), + mTotalSize(mSlotSize*mNumSlots), + mReadPosition(0), + mWritePosition(0), + mFullSlots(0), + mRingBuffer(new int8_t[mTotalSize]), + mLastReadSlot(new int8_t[mSlotSize]) +{ + //QMutexLocker locker(&mMutex); // lock the mutex + + // Verify if there's enough space to for the buffers + if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) { + //std::cerr << "ERROR: RingBuffer out of memory!" << endl; + //std::cerr << "Exiting program..." << endl; + //std::exit(1); + throw std::length_error("RingBuffer out of memory!"); + } + + // Set the buffers to zeros + /* + for (int i=0; i +#include +#include + +#include "jacktrip_types.h" + + +/** \brief Provides a ring-buffer (or circular-buffer) that can be written to and read from + * asynchronously (blocking) or synchronously (non-blocking). + * + * The RingBuffer is an array of \b NumSlots slots of memory + * each of which is of size \b SlotSize bytes (8-bits). Slots can be read and + * written asynchronously/synchronously by multiple threads. + */ +class RingBuffer +{ +public: + + /** \brief The class constructor + * \param SlotSize Size of one slot in bytes + * \param NumSlots Number of slots + */ + RingBuffer(int SlotSize, int NumSlots); + + /** \brief The class destructor + */ + virtual ~RingBuffer(); + + /** \brief Insert a slot into the RingBuffer from ptrToSlot. This method will block until + * there's space in the buffer. + * + * The caller is responsible to make sure sizeof(WriteSlot) = SlotSize. This + * method should be use when the caller can block against its output, like + * sending/receiving UDP packets. It shouldn't be used by audio. For that, use the + * insertSlotNonBlocking. + * \param ptrToSlot Pointer to slot to insert into the RingBuffer + */ + void insertSlotBlocking(const int8_t* ptrToSlot); + + /** \brief Read a slot from the RingBuffer into ptrToReadSlot. This method will block until + * there's space in the buffer. + * + * The caller is responsible to make sure sizeof(ptrToReadSlot) = SlotSize. This + * method should be use when the caller can block against its input, like + * sending/receiving UDP packets. It shouldn't be used by audio. For that, use the + * readSlotNonBlocking. + * \param ptrToReadSlot Pointer to read slot from the RingBuffer + */ + void readSlotBlocking(int8_t* ptrToReadSlot); + + /** \brief Same as insertSlotBlocking but non-blocking (asynchronous) + * \param ptrToSlot Pointer to slot to insert into the RingBuffer + */ + void insertSlotNonBlocking(const int8_t* ptrToSlot); + + /** \brief Same as readSlotBlocking but non-blocking (asynchronous) + * \param ptrToReadSlot Pointer to read slot from the RingBuffer + */ + void readSlotNonBlocking(int8_t* ptrToReadSlot); + + +protected: + + /** \brief Sets the memory in the Read Slot when uderrun occurs. By default, + * this sets it to 0. Override this method in a subclass for a different behavior. + * \param ptrToReadSlot Pointer to read slot from the RingBuffer + */ + virtual void setUnderrunReadSlot(int8_t* ptrToReadSlot); + + /** \brief Uses the last read slot to set the memory in the Read Slot. + * + * The last read slot is the last packet that arrived, so if no new packets are received, + * it keeps looping the same packet. + * \param ptrToReadSlot Pointer to read slot from the RingBuffer + */ + virtual void setMemoryInReadSlotWithLastReadSlot(int8_t* ptrToReadSlot); + +private: + + /// \brief Resets the ring buffer for reads under-runs non-blocking + void underrunReset(); + /// \brief Resets the ring buffer for writes over-flows non-blocking + void overflowReset(); + /// \brief Helper method to debug, prints member variables to terminal + void debugDump() const; + + const int mSlotSize; ///< The size of one slot in byes + const int mNumSlots; ///< Number of Slots + const int mTotalSize; ///< Total size of the mRingBuffer = mSlotSize*mNumSlotss + int mReadPosition; ///< Read Positions in the RingBuffer (Tail) + int mWritePosition; ///< Write Position in the RingBuffer (Head) + int mFullSlots; ///< Number of used (full) slots, in slot-size + int8_t* mRingBuffer; ///< 8-bit array of data (1-byte) + int8_t* mLastReadSlot; ///< Last slot read + + // Thread Synchronization Private Members + QMutex mMutex; ///< Mutex to protect read and write operations + QWaitCondition mBufferIsNotFull; ///< Buffer not full condition to monitor threads + QWaitCondition mBufferIsNotEmpty; ///< Buffer not empty condition to monitor threads +}; + +#endif diff --git a/src/RingBufferWavetable.h b/src/RingBufferWavetable.h new file mode 100644 index 0000000..e63dedf --- /dev/null +++ b/src/RingBufferWavetable.h @@ -0,0 +1,71 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file RingBufferWavetable.h + * \author Juan-Pablo Caceres + * \date September 2008 + */ + +#ifndef __RINGBUFFERWAVETABLE_H__ +#define __RINGBUFFERWAVETABLE_H__ + + +/** \brief Same as RingBuffer, except that it uses the Wavetable mode for + * lost or late packets. + */ +class RingBufferWavetable : public RingBuffer +{ +public: + /** \brief The class constructor + * \param SlotSize Size of one slot in bytes + * \param NumSlots Number of slots + */ + RingBufferWavetable(int SlotSize, int NumSlots) : RingBuffer(SlotSize, NumSlots) {} + + /** \brief The class destructor + */ + virtual ~RingBufferWavetable() {} + +protected: + /** \brief Sets the memory in the Read Slot when uderrun occurs. This loops as a + * wavetable in the last received packet. + * \param ptrToReadSlot Pointer to read slot from the RingBuffer + */ + virtual void setUnderrunReadSlot(int8_t* ptrToReadSlot) + { + setMemoryInReadSlotWithLastReadSlot(ptrToReadSlot); + } + +}; + + +#endif //__RINGBUFFERWAVETABLE_H__ diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 0000000..5b764da --- /dev/null +++ b/src/Settings.cpp @@ -0,0 +1,376 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Settings.cpp + * \author Juan-Pablo Caceres + * \date July 2008 + */ + +#include "Settings.h" +#include "LoopBack.h" +#include "NetKS.h" +#include "UdpMasterListener.h" +#include "JackTripWorker.h" +#include "jacktrip_globals.h" + +#include +#include // for command line parsing +#include + +#include "ThreadPoolTest.h" + +using std::cout; using std::endl; + +int gVerboseFlag = 0; + + +//******************************************************************************* +Settings::Settings() : + mJackTrip(NULL), + mJackTripMode(JackTrip::SERVER), + mDataProtocol(JackTrip::UDP), + mNumChans(2), + mBufferQueueLength(gDefaultQueueLength), + mAudioBitResolution(JackAudioInterface::BIT16), + mPortNum(gDefaultPort), + mClientName(NULL), + mUnderrrunZero(false), + mLoopBack(false), + mJamLink(false), + mEmptyHeader(false), + mJackTripServer(false), + mRedundancy(1) +{} + +//******************************************************************************* +Settings::~Settings() +{ + stopJackTrip(); + delete mJackTrip; +} + +//******************************************************************************* +void Settings::parseInput(int argc, char** argv) +{ + // If no command arguments are given, print instructions + if(argc == 1) { + printUsage(); + std::exit(0); + } + + // Usage example at: + // http://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html#Getopt-Long-Option-Example + // options descriptor + //---------------------------------------------------------------------------- + static struct option longopts[] = { + // These options set a flag, has to be sepcified as a long option --verbose + { "verbose", no_argument, &gVerboseFlag, 1 }, + // These options don't set a flag. + { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels + { "server", no_argument, NULL, 's' }, // Run in server mode + { "client", required_argument, NULL, 'c' }, // Run in client mode, set server IP address + { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode + { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address + { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464 + { "queue", required_argument, NULL, 'q' }, // Queue Length + { "redundancy", required_argument, NULL, 'r' }, // Redundancy + { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution + { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode + { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode + { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode + { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode + { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode + { "version", no_argument, NULL, 'v' }, // Version Number + { "help", no_argument, NULL, 'h' }, // Print Help + { NULL, 0, NULL, 0 } + }; + + // Parse Command Line Arguments + //---------------------------------------------------------------------------- + /// \todo Specify mandatory arguments + int ch; + while ( (ch = getopt_long(argc, argv, "n:sc:SC:o:q:r:b:zljeJ:vh", longopts, NULL)) != -1 ) + switch (ch) { + + case 'n': // Number of input and output channels + //------------------------------------------------------- + mNumChans = atoi(optarg); + break; + case 's': // Run in server mode + //------------------------------------------------------- + mJackTripMode = JackTrip::SERVER; + break; + case 'S': // Run in jacktripserver mode + //------------------------------------------------------- + mJackTripServer = true; + break; + case 'c': // Client mode + //------------------------------------------------------- + mJackTripMode = JackTrip::CLIENT; + mPeerAddress = optarg; + break; + case 'C': // Ping to server + //------------------------------------------------------- + mJackTripMode = JackTrip::CLIENTTOPINGSERVER; + mPeerAddress = optarg; + break; + case 'o': // Port Offset + //------------------------------------------------------- + mPortNum += atoi(optarg); + break; + case 'b': + //------------------------------------------------------- + if ( atoi(optarg) == 8 ) { + mAudioBitResolution = JackAudioInterface::BIT8; } + else if ( atoi(optarg) == 16 ) { + mAudioBitResolution = JackAudioInterface::BIT16; } + else if ( atoi(optarg) == 24 ) { + mAudioBitResolution = JackAudioInterface::BIT24; } + else if ( atoi(optarg) == 32 ) { + mAudioBitResolution = JackAudioInterface::BIT32; } + else { + std::cerr << "--bitres ERROR: Wrong bit resolutions: " + << atoi(optarg) << " is not supported." << endl; + printUsage(); + std::exit(1); } + break; + case 'q': + //------------------------------------------------------- + if ( atoi(optarg) <= 0 ) { + std::cerr << "--queue ERROR: The queue has to be a positive integer" << endl; + printUsage(); + std::exit(1); } + else { + mBufferQueueLength = atoi(optarg); + } + break; + case 'r': + //------------------------------------------------------- + if ( atoi(optarg) <= 0 ) { + std::cerr << "--queue ERROR: The queue has to be a positive integer" << endl; + printUsage(); + std::exit(1); } + else { + mRedundancy = atoi(optarg); + } + break; + case 'z': // underrun to zero + //------------------------------------------------------- + mUnderrrunZero = true; + break; + case 'l': // loopback + //------------------------------------------------------- + mLoopBack = true; + break; + case 'e': // jamlink + //------------------------------------------------------- + mEmptyHeader = true; + break; + case 'j': // jamlink + //------------------------------------------------------- + mJamLink = true; + break; + case 'J': + //------------------------------------------------------- + mClientName = optarg; + break; + case 'v': + //------------------------------------------------------- + cout << "JackTrip VERSION: " << gVersion << endl; + cout << "Copyright (c) 2008-2009 Juan-Pablo Caceres, Chris Chafe." << endl; + cout << "SoundWIRE group at CCRMA, Stanford University" << endl; + cout << "" << endl; + std::exit(0); + break; + case 'h': + //------------------------------------------------------- + printUsage(); + std::exit(0); + break; + default: + //------------------------------------------------------- + printUsage(); + std::exit(0); + break; + } + + // Warn user if undefined options where entered + //---------------------------------------------------------------------------- + if (optind < argc) { + cout << gPrintSeparator << endl; + cout << "WARINING: The following entered options have no effect" << endl; + cout << " They will be ignored!" << endl; + cout << " Type jacktrip to see options." << endl; + for( ; optind < argc; optind++) { + printf("argument: %s\n", argv[optind]); + } + cout << gPrintSeparator << endl; + } +} + + +//******************************************************************************* +void Settings::printUsage() +{ + cout << "" << endl; + cout << "JackTrip: A System for High-Quality Audio Network Performance" << endl; + cout << "over the Internet" << endl; + cout << "Copyright (c) 2008-2009 Juan-Pablo Caceres, Chris Chafe." << endl; + cout << "SoundWIRE group at CCRMA, Stanford University" << endl; + cout << "VERSION: " << gVersion << endl; + cout << "-----------------------------------------------------------------------------" << endl; + cout << "" << endl; + cout << "Usage: jacktrip [-s|-c host] [options]" << endl; + cout << "" << endl; + cout << "Options: " << endl; + cout << " -s, --server Run in Server Mode" << endl; + cout << " -c, --client Run in Client Mode" << endl; + cout << " -n, --numchannels # Number of Input and Output Channels (default " + << 2 << ")" << endl; + cout << " -q, --queue # (1 or more) Queue Buffer Length, in Packet Size (default " + << gDefaultQueueLength << ")" << endl; + cout << " -r, --redundancy # (1 or more) Packet Redundancy to avoid glitches with packet losses (defaul 1)" + << endl; + cout << " -o, --portoffset # Receiving port offset from base port " << gDefaultPort << endl; + cout << " -b, --bitres # (8, 16, 24, 32) Audio Bit Rate Resolutions (default 16)" << endl; + cout << " -z, --zerounderrun Set buffer to zeros when underrun occurs (defaults to wavetable)" << endl; + cout << " -l, --loopback Run in Loop-Back Mode" << endl; + cout << " -j, --jamlink Run in JamLink Mode (Connect to a JamLink Box)" << endl; + cout << " --clientname Change default client name (default is JackTrip)" << endl; + cout << " -v, --version Prints Version Number" << endl; + cout << " -h, --help Prints this Help" << endl; + cout << "" << endl; +} + + +//******************************************************************************* +void Settings::startJackTrip() +{ + + ///\todo Change this, just here to test + if ( mJackTripServer ) { + UdpMasterListener* udpmaster = new UdpMasterListener; + udpmaster->start(); + + //---Thread Pool Test-------------------------------------------- + /* + cout << "BEFORE START" << endl; + ThreadPoolTest* thtest = new ThreadPoolTest(); + // QThreadPool takes ownership and deletes 'hello' automatically + QThreadPool::globalInstance()->start(thtest); + + cout << "AFTER START" << endl; + sleep(2); + thtest->stop(); + QThreadPool::globalInstance()->waitForDone(); + */ + //--------------------------------------------------------------- + } + + else { + + //JackTrip jacktrip(mJackTripMode, mDataProtocol, mNumChans, + // mBufferQueueLength, mAudioBitResolution); + mJackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans, + mBufferQueueLength, mRedundancy, mAudioBitResolution); + + // Change client name if different from default + if (mClientName != NULL) { + mJackTrip->setClientName(mClientName); + } + + // Set buffers to zero when underrun + if ( mUnderrrunZero ) { + cout << "Setting buffers to zero when underrun..." << endl; + cout << gPrintSeparator << std::endl; + mJackTrip->setUnderRunMode(JackTrip::ZEROS); + } + + // Set peer address in server mode + if ( mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER ) { + mJackTrip->setPeerAddress(mPeerAddress.toLatin1().data()); } + + // Set Ports + cout << "SETTING ALL PORTS" << endl; + mJackTrip->setAllPorts(mPortNum); + + // Set in JamLink Mode + if ( mJamLink ) { + cout << "Running in JamLink Mode..." << endl; + cout << gPrintSeparator << std::endl; + mJackTrip->setPacketHeaderType(DataProtocol::JAMLINK); + } + + // Set in EmptyHeader Mode + if ( mEmptyHeader ) { + cout << "Running in EmptyHeader Mode..." << endl; + cout << gPrintSeparator << std::endl; + mJackTrip->setPacketHeaderType(DataProtocol::EMPTY); + } + + // Add Plugins + if ( mLoopBack ) { + cout << "Running in Loop-Back Mode..." << endl; + cout << gPrintSeparator << std::endl; + //std::tr1::shared_ptr loopback(new LoopBack(mNumChans)); + //mJackTrip->appendProcessPlugin(loopback.get()); + + LoopBack* loopback = new LoopBack(mNumChans); + mJackTrip->appendProcessPlugin(loopback); + + // ----- Test Karplus Strong ----------------------------------- + //std::tr1::shared_ptr loopback(new NetKS()); + //mJackTrip->appendProcessPlugin(loopback); + //loopback->play(); + //NetKS* netks = new NetKS; + //mJackTrip->appendProcessPlugin(netks); + //netks->play(); + // ------------------------------------------------------------- + } + + // Start JackTrip + mJackTrip->start(); + + /* + sleep(10); + cout << "Stoping JackTrip..." << endl; + mJackTrip->stop(); + */ + } +} + + +//******************************************************************************* +void Settings::stopJackTrip() +{ + mJackTrip->stop(); +} diff --git a/src/Settings.h b/src/Settings.h new file mode 100644 index 0000000..5c08808 --- /dev/null +++ b/src/Settings.h @@ -0,0 +1,84 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Settings.h + * \author Juan-Pablo Caceres + * \date July 2008 + */ + + +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + +#include "DataProtocol.h" +#include "JackAudioInterface.h" +#include "JackTrip.h" + +/** \brief Class to set usage options and parse settings from input + */ +class Settings +{ +public: + Settings(); + virtual ~Settings(); + + /// \brief Parses command line input + void parseInput(int argc, char** argv); + + void startJackTrip(); + void stopJackTrip(); + + /// \brief Prints usage help + void printUsage(); + + bool getLoopBack() { return mLoopBack; }; + +private: + JackTrip* mJackTrip; ///< JackTrip class + JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT + JackTrip::dataProtocolT mDataProtocol; ///< Data Protocol + int mNumChans; ///< Number of Channels (inputs = outputs) + int mBufferQueueLength; ///< Audio Buffer from network queue length + JackAudioInterface::audioBitResolutionT mAudioBitResolution; + QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode + int mPortNum; ///< Port Number + char* mClientName; + bool mUnderrrunZero; ///< Use Underrun to Zero mode + + bool mLoopBack; ///< Loop-back mode + bool mJamLink; ///< JamLink mode + bool mEmptyHeader; ///< EmptyHeader mode + bool mJackTripServer; ///< JackTrip Server mode + unsigned int mRedundancy; ///< Redundancy factor for data in the network +}; + +#endif diff --git a/src/TestRingBuffer.h b/src/TestRingBuffer.h new file mode 100644 index 0000000..136710b --- /dev/null +++ b/src/TestRingBuffer.h @@ -0,0 +1,49 @@ +#ifndef __TESTRINGBUFFER__ +#define __TESTRINGBUFFER__ + +#include "RingBuffer.h" +#include +#include + +static RingBuffer rb(2,100); + +class TestRingBufferWrite : public QThread +{ +public: + + void run() + { + int8_t* writeSlot; + writeSlot = new int8_t[2]; + writeSlot[0] = *"a"; + writeSlot[1] = *"b"; + while (true) { + //std::cout << "writing BEFORE" << std::endl; + rb.insertSlotBlocking(writeSlot); + //std::cout << "writing AFTER" << std::endl; + } + } + +}; + + +class TestRingBufferRead : public QThread +{ +public: + + void run() + { + int8_t* readSlot; + readSlot = new int8_t[2]; + while (true) { + //std::cout << "reading BEFORE" << std::endl; + rb.readSlotBlocking(readSlot); + //std::cout << "reading AFTER" << std::endl; + //std::cout << *(readSlot) << std::endl; + //std::cout << *(readSlot+1) << std::endl; + } + } +}; + +#endif + diff --git a/src/ThreadPoolTest.h b/src/ThreadPoolTest.h new file mode 100644 index 0000000..1c85653 --- /dev/null +++ b/src/ThreadPoolTest.h @@ -0,0 +1,77 @@ +/** + * \file ThreadPoolTest.h + * \author Juan-Pablo Caceres + * \date October 2008 + */ + +#ifndef __THREADPOOLTEST_H__ +#define __THREADPOOLTEST_H__ + +#include +#include +#include +#include + +#include + +#include "NetKS.h" +#include "JackTripWorkerMessages.h" + + +class ThreadPoolTest : public QObject, public QRunnable +//class ThreadPoolTest : public QThread +{ + Q_OBJECT; + +public: + ThreadPoolTest() + { + setAutoDelete(false); + } + + void run() + { + JackTripWorkerMessages jtm; + QThread testThread; + //jtm.moveToThread(&testThread); + + //QObject::connect(&jtm, SIGNAL(signalTest()), &jtm, SLOT(slotTest()), Qt::QueuedConnection); + testThread.start(); + jtm.play(); + //testThread.wait(); + + //std::cout << "--------------- BEFORE ---------------" << std::endl; + //NetKS netks; + //netks.play(); + //std::cout << "--------------- AFTER ---------------" << std::endl; + + QEventLoop loop; + //QObject::connect(this, SIGNAL(stopELoop()), &loop, SLOT(quit()), Qt::QueuedConnection); + loop.exec(); + //std::cout << "--------------- EXITING QRUNNABLE---------------" << std::endl; + /* + while (true) { + std::cout << "Hello world from thread" << std::endl; + sleep(1); + } + */ + } + + void stop() + { + std::cout << "--------------- ELOOP STOP---------------" << std::endl; + emit stopELoop(); + } + +signals: + void stopELoop(); + +private slots: + void fromServer() + { + std::cout << "--------------- SIGNAL RECEIVED ---------------" << std::endl; + } + +}; + +#endif //__THREADPOOLTEST_H__ diff --git a/src/UdpDataProtocol.cpp b/src/UdpDataProtocol.cpp new file mode 100644 index 0000000..9e2275f --- /dev/null +++ b/src/UdpDataProtocol.cpp @@ -0,0 +1,505 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file UdpDataProtocol.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#include "UdpDataProtocol.h" +#include "jacktrip_globals.h" +#include "JackTrip.h" + +#include +#include +#include +#include +#include +#include // for POSIX Sockets + +using std::cout; using std::endl; + +// NOTE: It's better not to use +// using namespace std; +// because some functions (like exit()) get confused with QT functions + +// sJackMutex definition +QMutex UdpDataProtocol::sUdpMutex; + +//******************************************************************************* +UdpDataProtocol::UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, + int bind_port, int peer_port, + unsigned int udp_redundancy_factor) : +DataProtocol(jacktrip, runmode, bind_port, peer_port), +mBindPort(bind_port), mPeerPort(peer_port), +mRunMode(runmode), +mAudioPacket(NULL), mFullPacket(NULL), +mUdpRedundancyFactor(udp_redundancy_factor) +{ + if (mRunMode == RECEIVER) { + QObject::connect(this, SIGNAL(signalWatingTooLong(int)), + jacktrip, SLOT(slotUdpWatingTooLong(int)), Qt::QueuedConnection); + } +} + + +//******************************************************************************* +UdpDataProtocol::~UdpDataProtocol() +{ + delete[] mAudioPacket; + delete[] mFullPacket; + wait(); +} + + +//******************************************************************************* +void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) +{ + mPeerAddress.setAddress(peerHostOrIP); + // check if the ip address is valid + if ( mPeerAddress.isNull() ) { + std::cerr << "ERROR: Incorrect presentation format address" << endl; + std::cerr << "'" << peerHostOrIP <<"' does not seem to be a valid IP address" << endl; + throw std::invalid_argument(""); + } + /* + else { + std::cout << "Peer Address set to: " + << mPeerAddress.toString().toStdString() << std::endl; + cout << gPrintSeparator << endl; + usleep(100); + } + */ +} + + +//******************************************************************************* +void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) +{ + QMutexLocker locker(&sUdpMutex); + + // Creat socket descriptor + int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + + // Set local IPv4 Address + struct sockaddr_in local_addr; + ::bzero(&local_addr, sizeof(local_addr)); + local_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address + local_addr.sin_port = htons(mBindPort); //set local port + + // Set socket to be reusable, this is platform dependent + int one = 1; +#if defined ( __LINUX__ ) + ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); +#endif +#if defined ( __MAC_OSX__ ) + // This option is not avialable on Linux, and without it MAC OS X + // has problems rebinding a socket + ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); +#endif + + // Bind the Socket + if ( (::bind(sock_fd, (struct sockaddr *) &local_addr, sizeof(local_addr))) < 0 ) + { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } + + // To be able to use the two UDP sockets bound to the same port number, + // we connect the receiver and issue a SHUT_WR. + if (mRunMode == SENDER) { + // We use the sender as an unconnected UDP socket + UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::BoundState, + QUdpSocket::WriteOnly); + } + else if (mRunMode == RECEIVER) { + // Set peer IPv4 Address + struct sockaddr_in peer_addr; + bzero(&peer_addr, sizeof(peer_addr)); + peer_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol + peer_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address + peer_addr.sin_port = htons(mPeerPort); //set local port + // Connect the socket and issue a Write shutdown (to make it a + // reader socket only) + if ( (::inet_pton(AF_INET, mPeerAddress.toString().toLatin1().constData(), + &peer_addr.sin_addr)) < 1 ) + { throw std::runtime_error("ERROR: Invalid address presentation format"); } + if ( (::connect(sock_fd, (struct sockaddr *) &peer_addr, sizeof(peer_addr))) < 0) + { throw std::runtime_error("ERROR: Could not connect UDP socket"); } + if ( (::shutdown(sock_fd,SHUT_WR)) < 0) + { throw std::runtime_error("ERROR: Could suntdown SHUT_WR UDP socket"); } + + UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::ConnectedState, + QUdpSocket::ReadOnly); + cout << "UDP Socket Receiving in Port: " << mBindPort << endl; + cout << gPrintSeparator << endl; + } + + // OLD CODE WITHOUT POSIX FIX-------------------------------------------------- + /* + /// \todo if port is already used, try binding in a different port + QUdpSocket::BindMode bind_mode; + if (mRunMode == RECEIVER) { + bind_mode = QUdpSocket::DontShareAddress; } + else if (mRunMode == SENDER) { //Share sender socket + bind_mode = QUdpSocket::ShareAddress; } + + // QHostAddress::Any : let the kernel decide the active address + if ( !UdpSocket.bind(QHostAddress::Any, mBindPort, bind_mode) ) { + throw std::runtime_error("Could not bind UDP socket. It may be already binded."); + } + else { + if ( mRunMode == RECEIVER ) { + cout << "UDP Socket Receiving in Port: " << mBindPort << endl; + cout << gPrintSeparator << endl; + } + } + */ + // ---------------------------------------------------------------------------- +} + + +//******************************************************************************* +int UdpDataProtocol::receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n) +{ + // Block until There's something to read + while ( (UdpSocket.pendingDatagramSize() < n) && !mStopped ) { QThread::usleep(100); } + int n_bytes = UdpSocket.readDatagram(buf, n); + return n_bytes; +} + + +//******************************************************************************* +int UdpDataProtocol::sendPacket(QUdpSocket& UdpSocket, const QHostAddress& PeerAddress, + const char* buf, const size_t n) +{ + int n_bytes = UdpSocket.writeDatagram(buf, n, PeerAddress, mPeerPort); + return n_bytes; +} + + +//******************************************************************************* +void UdpDataProtocol::getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket, + QHostAddress& peerHostAddress, + uint16_t& port) +{ + while ( !UdpSocket.hasPendingDatagrams() ) { + msleep(100); + } + char buf[1]; + UdpSocket.readDatagram(buf, 1, &peerHostAddress, &port); +} + + +//******************************************************************************* +void UdpDataProtocol::run() +{ + mStopped = false; + + // Creat and bind sockets + QUdpSocket UdpSocket; + bindSocket(UdpSocket); // Bind Socket + QHostAddress PeerAddress; + PeerAddress = mPeerAddress; + + // Setup Audio Packet buffer + size_t audio_packet_size = getAudioPacketSizeInBites(); + //cout << "audio_packet_size: " << audio_packet_size << endl; + mAudioPacket = new int8_t[audio_packet_size]; + std::memset(mAudioPacket, 0, audio_packet_size); // set buffer to 0 + + // Setup Full Packet buffer + int full_packet_size = mJackTrip->getPacketSizeInBytes(); + //cout << "full_packet_size: " << full_packet_size << endl; + mFullPacket = new int8_t[full_packet_size]; + std::memset(mFullPacket, 0, full_packet_size); // set buffer to 0 + + bool timeout = false; // Time out flag for packets that arrive too late + + // Put header in first packet + mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); + + // Redundancy Variables + // (Algorithm explained at the end of this file) + // --------------------------------------------- + int full_redundant_packet_size = full_packet_size * mUdpRedundancyFactor; + int8_t* full_redundant_packet; + full_redundant_packet = new int8_t[full_redundant_packet_size]; + std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0 + + // Set realtime priority (function in jacktrip_globals.h) + set_crossplatform_realtime_priority(); + + // Connect signals and slots for packets arriving too late notifications + QObject::connect(this, SIGNAL(signalWatingTooLong(int)), + this, SLOT(printUdpWaitedTooLong(int)), + Qt::QueuedConnection); + + switch ( mRunMode ) + { + case RECEIVER : { + //----------------------------------------------------------------------------------- + // Wait for the first packet to be ready and obtain address + // from that packet + std::cout << "Waiting for Peer..." << std::endl; + // This blocks waiting for the first packet + while ( !UdpSocket.hasPendingDatagrams() ) { QThread::msleep(100); } + int first_packet_size = UdpSocket.pendingDatagramSize(); + // The following line is the same as + // int8_t* first_packet = new int8_t[first_packet_size]; + // but avoids memory leaks + std::tr1::shared_ptr first_packet(new int8_t[first_packet_size]); + receivePacket( UdpSocket, reinterpret_cast(first_packet.get()), first_packet_size); + // Check that peer has the same audio settings + mJackTrip->checkPeerSettings(first_packet.get()); + mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); + std::cout << "Received Connection for Peer!" << std::endl; + + // Redundancy Variables + // -------------------- + // NOTE: These types need to be the same unsigned integer as the sequence + // number in the header. That way, they wrap around in the "same place" + uint16_t current_seq_num = 0; // Store current sequence number + uint16_t last_seq_num = 0; // Store last package sequence number + uint16_t newer_seq_num = 0; // Store newer sequence number + + while ( !mStopped ) + { + // Timer to report packets arriving too late + // This QT method gave me a lot of trouble, so I replaced it with my own 'waitForReady' + // that uses signals and slots and can also report with packets have not + // arrive for a longer time + //timeout = UdpSocket.waitForReadyRead(30); + timeout = waitForReady(UdpSocket, 60000); //60 seconds + + // OLD CODE WITHOUT REDUNDANCY---------------------------------------------------- + /* + // This is blocking until we get a packet... + receivePacket( UdpSocket, reinterpret_cast(mFullPacket), full_packet_size); + + mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); + + // ...so we want to send the packet to the buffer as soon as we get in from + // the socket, i.e., non-blocking + //mRingBuffer->insertSlotNonBlocking(mAudioPacket); + mJackTrip->writeAudioBuffer(mAudioPacket); + */ + //---------------------------------------------------------------------------------- + receivePacketRedundancy(UdpSocket, + full_redundant_packet, + full_redundant_packet_size, + full_packet_size, + current_seq_num, + last_seq_num, + newer_seq_num); + } + break; } + + case SENDER : { + //----------------------------------------------------------------------------------- + while ( !mStopped ) + { + // OLD CODE WITHOUT REDUNDANCY ----------------------------------------------------- + /* + // We block until there's stuff available to read + mJackTrip->readAudioBuffer( mAudioPacket ); + mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); + // This will send the packet immediately + //int bytes_sent = sendPacket( reinterpret_cast(mFullPacket), full_packet_size); + sendPacket( UdpSocket, PeerAddress, reinterpret_cast(mFullPacket), full_packet_size); + */ + //---------------------------------------------------------------------------------- + sendPacketRedundancy(UdpSocket, + PeerAddress, + full_redundant_packet, + full_redundant_packet_size, + full_packet_size); + } + break; } + } +} + + +//******************************************************************************* +bool UdpDataProtocol::waitForReady(QUdpSocket& UdpSocket, int timeout_msec) +{ + int loop_resolution_usec = 100; // usecs to wait on each loop + int emit_resolution_usec = 10000; // 10 milliseconds + int timeout_usec = timeout_msec * 1000; + int ellaped_time_usec = 0; // Ellapsed time in milliseconds + + while ( ( !(UdpSocket.hasPendingDatagrams()) && (ellaped_time_usec <= timeout_usec) ) + && !mStopped ){ + //cout << mStopped << endl; + QThread::usleep(loop_resolution_usec); + ellaped_time_usec += loop_resolution_usec; + + if ( !(ellaped_time_usec % emit_resolution_usec) ) { + emit signalWatingTooLong(static_cast(ellaped_time_usec/1000)); + } + } + + if ( ellaped_time_usec >= timeout_usec ) + { + emit signalWatingTooLong(ellaped_time_usec/1000); + return false; + } + return true; +} + + +//******************************************************************************* +void UdpDataProtocol::printUdpWaitedTooLong(int wait_msec) +{ + int wait_time = 30; // msec + if ( !(wait_msec%wait_time) ) { + std::cerr << "UDP is waited too long (more than " << wait_time << "ms)..." << endl; + } +} + + +//******************************************************************************* +void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, + int8_t* full_redundant_packet, + int full_redundant_packet_size, + int full_packet_size, + uint16_t& current_seq_num, + uint16_t& last_seq_num, + uint16_t& newer_seq_num) +{ + // This is blocking until we get a packet... + receivePacket( UdpSocket, reinterpret_cast(full_redundant_packet), + full_redundant_packet_size); + + // Get Packet Sequence Number + newer_seq_num = + mJackTrip->getPeerSequenceNumber(full_redundant_packet); + current_seq_num = newer_seq_num; + + + //cout << current_seq_num << " "; + int redun_last_index = 0; + for (unsigned int i = 1; igetPeerSequenceNumber( full_redundant_packet + (i*full_packet_size) ); + //cout << current_seq_num << " "; + } + //cout << endl; + + last_seq_num = newer_seq_num; // Save last read packet + + // Send to audio all available audio packets, in order + for (int i = redun_last_index; i>=0; i--) { + memcpy(mFullPacket, + full_redundant_packet + (i*full_packet_size), + full_packet_size); + mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); + mJackTrip->writeAudioBuffer(mAudioPacket); + } +} + +//******************************************************************************* +void UdpDataProtocol::sendPacketRedundancy(QUdpSocket& UdpSocket, + QHostAddress& PeerAddress, + int8_t* full_redundant_packet, + int full_redundant_packet_size, + int full_packet_size) +{ + mJackTrip->readAudioBuffer( mAudioPacket ); + mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); + + // Move older packets to end of array of redundant packets + std::memmove(full_redundant_packet+full_packet_size, + full_redundant_packet, + full_packet_size*(mUdpRedundancyFactor-1)); + // Copy new packet to the begining of array + std::memcpy(full_redundant_packet, + mFullPacket, full_packet_size); + + // 10% (or other number) packet lost simulation. + // Uncomment the if to activate + //--------------------------------------------------------------------------------- + //int random_integer = rand(); + //if ( random_integer > (RAND_MAX/10) ) + //{ + sendPacket( UdpSocket, PeerAddress, reinterpret_cast(full_redundant_packet), + full_redundant_packet_size); + //} + //--------------------------------------------------------------------------------- + + mJackTrip->increaseSequenceNumber(); +} + + +/* + The Redundancy Algorythmn works as follows. We send a packet that contains + a mUdpRedundancyFactor number of packets (header+audio). This big packet looks + as follows + + ---------- ------------ ----------------------------------- + | UDP[n] | | UDP[n-1] | ... | UDP[n-(mUdpRedundancyFactor-1)] | + ---------- ------------ ----------------------------------- + + Then, for the new audio buffer, we shift everything to the right and send: + + ---------- ------------ ------------------------------------- + | UDP[n+1] | | UDP[n] | ... | UDP[n-(mUdpRedundancyFactor-1)+1] | + ---------- ------------ ------------------------------------- + + etc... + + For a redundancy factor of 4, this will look as follows: + ---------- ---------- ---------- ---------- + | UDP[4] | | UDP[3] | | UDP[2] | | UDP[1] | + ---------- ---------- ---------- ---------- + + ---------- ---------- ---------- ---------- + | UDP[5] | | UDP[4] | | UDP[3] | | UDP[2] | + ---------- ---------- ---------- ---------- + + ---------- ---------- ---------- ---------- + | UDP[6] | | UDP[5] | | UDP[4] | | UDP[3] | + ---------- ---------- ---------- ---------- + + etc... + + Then, the receiving end checks if the firs packet in the list is the one it should use, + otherwise it continure reding the mUdpRedundancyFactor packets until it finds the one that + should come next (this can better perfected by just jumping until the correct packet). + If it has more than one packet that it hasn't yet received, it sends it to the soundcard + one by one. +*/ diff --git a/src/UdpDataProtocol.h b/src/UdpDataProtocol.h new file mode 100644 index 0000000..fbce562 --- /dev/null +++ b/src/UdpDataProtocol.h @@ -0,0 +1,197 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file UdpDataProtocol.h + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#ifndef __UDPDATAPROTOCOL_H__ +#define __UDPDATAPROTOCOL_H__ + +#include +#include +#include + +#include "DataProtocol.h" +#include "jacktrip_types.h" +#include "jacktrip_globals.h" + +/** \brief UDP implementation of DataProtocol class + * + * The class has a bind port and a peer port. The meaning of these + * depends on the runModeT. If it's a SENDER, bind port is the source port and + * peer port is the destination port for each UDP packet. If it's a RECEIVER, + * the bind port destination port (for incoming packets) and the peer port + * is the source port. + * + * The SENDER and RECEIVER socket can share the same port/address pair (for compatibility + * with the JamLink boxes). This is achieved setting + * the resusable property in the socket for address and port. You have to + * externaly check if the port is already binded if you want to avoid re-binding to the + * same port. + */ +class UdpDataProtocol : public DataProtocol +{ + Q_OBJECT; + +public: + + /** \brief The class constructor + * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) + * \param runmode Sets the run mode, use either SENDER or RECEIVER + * \param bind_port Port number to bind for this socket (this is the receive or send port depending on the runmode) + * \param peer_port Peer port number (this is the receive or send port depending on the runmode) + * \param udp_redundancy_factor Number of redundant packets + */ + UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, + int bind_port, int peer_port, + unsigned int udp_redundancy_factor = 1); + + /** \brief The class destructor + */ + virtual ~UdpDataProtocol(); + + /** \brief Set the Peer address to connect to + * \param peerHostOrIP IPv4 number or host name + */ + void setPeerAddress(const char* peerHostOrIP); + + /** \brief Receives a packet. It blocks until a packet is received + * + * This function makes sure we recieve a complete packet + * of size n + * \param buf Buffer to store the recieved packet + * \param n size of packet to receive + * \return number of bytes read, -1 on error + */ + //virtual int receivePacket(char* buf, const size_t n); + virtual int receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n); + + /** \brief Sends a packet + * + * This function meakes sure we send a complete packet + * of size n + * \param buf Buffer to send + * \param n size of packet to receive + * \return number of bytes read, -1 on error + */ + virtual int sendPacket(QUdpSocket& UdpSocket, const QHostAddress& PeerAddress, + const char* buf, const size_t n); + + /** \brief Obtains the peer address from the first UDP packet received. This address + * is used by the SERVER mode to connect back to the client. + * \param peerHostAddress QHostAddress to store the peer address + * \param port Receiving port + */ + virtual void getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket, + QHostAddress& peerHostAddress, + uint16_t& port); + + /** \brief Sets the bind port number + */ + void setBindPort(int port) + { mBindPort = port; } + + /** \brief Sets the peer port number + */ + void setPeerPort(int port) + { mPeerPort = port; } + + /** \brief Implements the Thread Loop. To start the thread, call start() + * ( DO NOT CALL run() ) + * + * This function creats and binds all the socket and start the connection loop thread. + */ + virtual void run(); + + +private slots: + void printUdpWaitedTooLong(int wait_msec); + + +signals: + + /// \brief Signals when waiting every 10 milliseconds, with the total wait on wait_msec + /// \param wait_msec Total wait in milliseconds + void signalWatingTooLong(int wait_msec); + + +private: + + /** \brief Binds the UDP socket to the available address and specified port + */ + void bindSocket(QUdpSocket& UdpSocket); + + /** \brief This function blocks until data is available for reading in the + * QUdpSocket. The function will timeout after timeout_msec microseconds. + * + * This function is intended to replace QAbstractSocket::waitForReadyRead which has + * some problems with multithreading. + * + * \return returns true if there is data available for reading; + * otherwise it returns false (if an error occurred or the operation timed out) + */ + bool waitForReady(QUdpSocket& UdpSocket, int timeout_msec); + + /** \brief Redundancy algorythm at the receiving end + */ + void receivePacketRedundancy(QUdpSocket& UdpSocket, + int8_t* full_redundant_packet, + int full_redundant_packet_size, + int full_packet_size, + uint16_t& current_seq_num, + uint16_t& last_seq_num, + uint16_t& newer_seq_num); + + /** \brief Redundancy algorythm at the sender end + */ + void sendPacketRedundancy(QUdpSocket& UdpSocket, + QHostAddress& PeerAddress, + int8_t* full_redundant_packet, + int full_redundant_packet_size, + int full_packet_size); + + int mBindPort; ///< Local Port number to Bind + int mPeerPort; ///< Peer Port number + const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER + + QHostAddress mPeerAddress; ///< The Peer Address + + int8_t* mAudioPacket; ///< Buffer to store Audio Packets + int8_t* mFullPacket; ///< Buffer to store Full Packet (audio+header) + + unsigned int mUdpRedundancyFactor; ///< Factor of redundancy + static QMutex sUdpMutex; ///< Mutex to make thread safe the binding process +}; + +#endif // __UDPDATAPROTOCOL_H__ diff --git a/src/UdpDataProtocolPOSIX.cpp.tmp b/src/UdpDataProtocolPOSIX.cpp.tmp new file mode 100644 index 0000000..fe77daa --- /dev/null +++ b/src/UdpDataProtocolPOSIX.cpp.tmp @@ -0,0 +1,145 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file UdpDataProtocol.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#include "UdpDataProtocol.h" + +#include +#include +#include +#include + +// NOTE: It's better not to use +// using namespace std; +// because some functions (like exit()) get confused with QT functions + + + +//******************************************************************************* +UdpDataProtocol::UdpDataProtocol(const runModeT runmode, const char* peerHostOrIP) + : DataProtocol(runmode) +{ + setPeerIPv4Address(peerHostOrIP); + setBindSocket(); +} + + +//******************************************************************************* +void UdpDataProtocol::setBindSocket() +{ + // UDP socket creation + mSockFd = socket(AF_INET, SOCK_DGRAM, 0); + if ( mSockFd < 0 ) { + std::cerr << "ERROR: UDP Socket Error" << std::endl; + std::exit(0); + } + + // Bind local address and port + /// \todo Bind to a different port in case this one is used by a different instance + /// of the program + struct sockaddr_in LocalIPv4Addr = getLocalIPv4AddressStruct(); + int nBind = bind(mSockFd, (struct sockaddr *) &LocalIPv4Addr, sizeof(LocalIPv4Addr)); + if ( nBind < 0 ) { + std::cerr << "ERROR: UDP Socket Bind Error" << std::endl; + std::exit(0); + } + + std::cout << "Successful socket creation and port binding" << std::endl; + + //Connected UDP + struct sockaddr_in PeerIPv4Addr = getPeerIPv4AddressStruct(); + int nCon = ::connect(mSockFd, (struct sockaddr *) &PeerIPv4Addr, sizeof(PeerIPv4Addr)); + if ( nCon < 0) { + std::cerr << "ERROR: UDP Socket Connect Error" << std::endl; + std::exit(0); + } +} + + +//******************************************************************************* +// Adapted form Stevens' "Unix Network Programming", third edition +// Page 88 (readn) +size_t UdpDataProtocol::receivePacket(char* buff, size_t n) +{ + size_t nleft; + ssize_t nread; + char* ptr; + + ptr = buff; + nleft = n; + while (nleft > 0) { + if ( (nread = ::read(mSockFd, ptr, nleft)) < 0) { + if (errno == EINTR) + nread = 0; // and call read() again + else + return(-1); + } else if (nread == 0) + break; // EOF + + nleft -= nread; + ptr += nread; + } + return(n - nleft); +} + + + +//******************************************************************************* +// Adapted form Stevens' "Unix Network Programming", third edition +// Page 88 (writen) +// Write "n" bytes to a descriptor +size_t UdpDataProtocol::sendPacket(const char* buff, size_t n) +{ + size_t nleft; + ssize_t nwritten; + const char* ptr; + + ptr = buff; + nleft = n; + while (nleft > 0) { + if ( (nwritten = ::write(mSockFd, ptr, nleft)) <= 0) { + if (nwritten < 0 && errno == EINTR) + nwritten = 0; // and call write() again + else + return(-1); // error + } + + nleft -= nwritten; + ptr += nwritten; + } + return(n); +} + diff --git a/src/UdpDataProtocolPOSIX.h.tmp b/src/UdpDataProtocolPOSIX.h.tmp new file mode 100644 index 0000000..a7fb087 --- /dev/null +++ b/src/UdpDataProtocolPOSIX.h.tmp @@ -0,0 +1,92 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file UdpDataProtocol.h + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#ifndef __UDPDATAPROTOCOL_H__ +#define __UDPDATAROTOCOL_H__ + +#include "DataProtocol.h" + +/** \brief UDP implementation of DataProtocol class + * + * + * + */ +class UdpDataProtocol : public DataProtocol +{ +public: + + /** \brief The class constructor + * \param runmode Sets the run mode, use either SENDER or RECEIVER + * \param peerHostOrIP IPv4 number or host name + */ + UdpDataProtocol(const runModeT runmode, const char* peerHostOrIP); + + /** \brief The class destructor + */ + virtual ~UdpDataProtocol() {}; + + /** \brief Receives a packet + * + * This function makes sure we recieve a complete packet + * of size n + * \param buf Buffer to store the recieved packet + * \param n size of packet to receive + * \return number of bytes read, -1 on error + */ + virtual size_t receivePacket(char* buf, size_t n); + + /** \brief Sends a packet + * + * This function meakes sure we send a complete packet + * of size n + * \param buff Buffer to send + * \param n size of packet to receive + * \return number of bytes read, -1 on error + */ + virtual size_t sendPacket(const char* buff, size_t n); + + //virtual void run(); + + +private: + + void setBindSocket(); + + int mSockFd; ///< Socket file descriptor +}; + +#endif diff --git a/src/UdpMasterListener.cpp b/src/UdpMasterListener.cpp new file mode 100644 index 0000000..874eda4 --- /dev/null +++ b/src/UdpMasterListener.cpp @@ -0,0 +1,186 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file UdpMasterListener.cpp + * \author Juan-Pablo Caceres and Chris Chafe + * \date September 2008 + */ + +#include +#include +#include + +#include "UdpMasterListener.h" +#include "JackTripWorker.h" +#include "jacktrip_globals.h" + +using std::cout; using std::endl; + + +//******************************************************************************* +UdpMasterListener::UdpMasterListener(int server_port) : + mJTWorker(NULL), + mServerPort(server_port), + mStopped(false), + mTotalRunningThreads(0) +{ + // Register JackTripWorker with the master listener + mJTWorker = new JackTripWorker(this); + mThreadPool.setExpiryTimeout(3000); // msec (-1) = forever + // Inizialize IP addresses + for (int i = 0; i= 0) // old address is -1 + { + // redirect port and spawn listener + sendToPoolPrototype(id); + // wait until one is complete before another spawns + while (mJTWorker->isSpawning()) { QThread::msleep(1); } + mTotalRunningThreads++; + cout << "Total Running Threads: " << mTotalRunningThreads << endl; + cout << "=======================================================" << endl; + } + //cout << "ENDDDDDDDDDDDDDDDDDd === " << id << endl; + } + QThread::msleep(100); + } +} + + +//******************************************************************************* +void UdpMasterListener::sendToPoolPrototype(int id) +{ + cout << "id ID **********@@@@@@@@@@@@@@@@@@@@@************** " << id << endl; + mJTWorker->setJackTrip(id, mActiveAddress[id][0], + mBasePort+(2*id), mActiveAddress[id][1], + 1); /// \todo temp default to 1 channel + mThreadPool.start(mJTWorker, QThread::TimeCriticalPriority); //send one thread to the pool +} + + +//******************************************************************************* +void UdpMasterListener::bindUdpSocket(QUdpSocket& udpsocket, int port) +{ + // QHostAddress::Any : let the kernel decide the active address + if ( !udpsocket.bind(QHostAddress::Any, + port, QUdpSocket::DefaultForPlatform) ) { + //std::cerr << "ERROR: could not bind UDP socket" << endl; + //std::exit(1); + throw std::runtime_error("Could not bind UDP socket. It may be already binded."); + } + else { + cout << "UDP Socket Receiving in Port: " << port << endl; + } +} + + +//******************************************************************************* +// check by comparing 32-bit addresses +int UdpMasterListener::isNewAddress(uint32_t address, uint16_t port) +{ + /// \todo Add the port number in the comparison, i.e., compart IP/port pair + + bool busyAddress = false; + int id = 0; + while ( !busyAddress && (id + +#include +#include +#include +#include + +#include "jacktrip_types.h" +#include "jacktrip_globals.h" +class JackTripWorker; // forward declaration + + +/** \brief Master UDP listener on the Server. + * + * This creates a server that will listen on the well know port (the server port) and will + * spawn JackTrip threads into the Thread pool. Clients request a connection. + */ +class UdpMasterListener : public QThread +{ + Q_OBJECT; + +public: + UdpMasterListener(int server_port = gServerUdpPort); + virtual ~UdpMasterListener(); + + /** \brief Implements the Thread Loop. To start the thread, call start() + * ( DO NOT CALL run() ) + */ + void run(); + + /// \brief Stops the execution of the Thread + void stop() { mStopped = true; }; + + int releasePort(int id); + +private slots: + void testRecieve() + { + std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; + } + +signals: + void Listening(); + void ClientAddressSet(); + + +private: + /** \brief Binds a QUdpSocket. It chooses the available (active) interface. + * \param udpsocket a QUdpSocket + * \param port Port number + */ + static void bindUdpSocket(QUdpSocket& udpsocket, int port); + + /* \brief Send the JackTripWorker to the thread pool. This will run + * until it's done. We still have control over the prototype class. + * \param id Identification Number + */ + void sendToPoolPrototype(int id); + + /** \brief Check if address is already handled, if not add to array + * \param IPv4 address as a number + * \return -1 if address is busy, id number if not + */ + int isNewAddress(uint32_t address, uint16_t port); + + QUdpSocket mUdpMasterSocket; ///< The UDP socket + QHostAddress mPeerAddress; ///< The Peer Address + + JackTripWorker* mJTWorker; ///< Class that will be used as prototype + QThreadPool mThreadPool; ///< The Thread Pool + + int mServerPort; //< Server known port number + int mBasePort; + uint32_t mActiveAddress[gMaxThreads][2]; ///< Active addresses pool numbers (32 bits IPv4 numbers) + QHash mActiveAddresPortPair; + + /// Boolean stop the execution of the thread + volatile bool mStopped; + int mTotalRunningThreads; ///< Number of Threads running in the pool +}; + + +#endif //__UDPMASTERLISTENER_H__ diff --git a/src/build b/src/build new file mode 100755 index 0000000..546567d --- /dev/null +++ b/src/build @@ -0,0 +1,25 @@ +#!/bin/bash +## Created by Juan-Pablo Caceres + +# Check for Platform +platform='unknown' +unamestr=`uname` +if [[ "$unamestr" == 'Linux' ]]; then + echo "Building on Linux" + platform='linux' +elif [[ "$unamestr" == 'Darwin' ]]; then + echo "Building on Mac OS X" + platform='macosx' +fi + +# Set qmake command name +if [[ $platform == 'linux' ]]; then + QCMD=qmake-qt4 +elif [[ $platform == 'macosx' ]]; then + QCMD=qmake +fi + +# Build +$QCMD jacktrip.pro +make clean +make release diff --git a/src/jacktrip-1.0.5.diff b/src/jacktrip-1.0.5.diff new file mode 100644 index 0000000..30292b9 --- /dev/null +++ b/src/jacktrip-1.0.5.diff @@ -0,0 +1,26 @@ +Index: src/JackTripWorker.h +=================================================================== +--- src/JackTripWorker.h (revision 495) ++++ src/JackTripWorker.h (working copy) +@@ -46,6 +46,8 @@ + #include + #include + ++#include "jacktrip_types.h" ++ + class JackTrip; // forward declaration + class UdpMasterListener; // forward declaration + +Index: src/jacktrip_globals.cpp +=================================================================== +--- src/jacktrip_globals.cpp (revision 495) ++++ src/jacktrip_globals.cpp (working copy) +@@ -38,6 +38,8 @@ + #include "jacktrip_globals.h" + #include "jacktrip_types.h" + ++#include ++ + #if defined ( __LINUX__ ) + #include + #endif //__LINUX__ diff --git a/src/jacktrip.pro b/src/jacktrip.pro new file mode 100644 index 0000000..b92e69d --- /dev/null +++ b/src/jacktrip.pro @@ -0,0 +1,68 @@ +#****************************** +# Created by Juan-Pablo Caceres +#****************************** + +CONFIG += qt thread debug_and_release build_all +CONFIG(debug, debug|release) { + TARGET = jacktrip_debug + } else { + TARGET = jacktrip + } +QT -= gui +QT += network +INCLUDEPATH+=/usr/local/include +LIBS += -ljack -lm +macx { + message(MAC OS X) + CONFIG -= app_bundle + CONFIG += x86 #ppc + LIBS += -framework CoreAudio + DEFINES += __MAC_OSX__ + } +linux-g++ { + message(Linux) + QMAKE_CXXFLAGS += -g -O2 + DEFINES += __LINUX__ + } +DESTDIR = . +QMAKE_CLEAN += ./jacktrip ./jacktrip_debug +target.path = /usr/bin +INSTALLS += target + +# Input +HEADERS += DataProtocol.h \ + JackAudioInterface.h \ + JackTrip.h \ + jacktrip_globals.h \ + jacktrip_types.h \ + JackTripThread.h \ + JackTripWorker.h \ + JackTripWorkerMessages.h \ + LoopBack.h \ + NetKS.h \ + PacketHeader.h \ + ProcessPlugin.h \ + RingBuffer.h \ + RingBufferWavetable.h \ + Settings.h \ + TestRingBuffer.h \ + ThreadPoolTest.h \ + UdpDataProtocol.h \ + UdpMasterListener.h \ + jacktrip_tests.cpp +SOURCES += DataProtocol.cpp \ + JackAudioInterface.cpp \ + JackTrip.cpp \ + jacktrip_globals.cpp \ + jacktrip_main.cpp \ + jacktrip_tests.cpp \ + JackTripThread.cpp \ + JackTripWorker.cpp \ + LoopBack.cpp \ + PacketHeader.cpp \ + ProcessPlugin.cpp \ + RingBuffer.cpp \ + Settings.cpp \ + tests.cpp \ + UdpDataProtocol.cpp \ + UdpMasterListener.cpp diff --git a/src/jacktrip_globals.cpp b/src/jacktrip_globals.cpp new file mode 100644 index 0000000..df5c24d --- /dev/null +++ b/src/jacktrip_globals.cpp @@ -0,0 +1,196 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file globals.cpp + * \author Juan-Pablo Caceres + * \date August 2008 + */ + +#include "jacktrip_globals.h" +#include "jacktrip_types.h" + +#include + +#if defined ( __LINUX__ ) +#include +#endif //__LINUX__ + +#if defined ( __MAC_OSX__ ) +#include +#include + +//#include + +//#include +//#include +//#include + +//#include +//#include +//m#include +//#include +//#include + + +//#include +//#include +//#include +//#include +//#include + + + + +//#include +//#include +//#include +//#include +//#include + +#endif //__MAC_OSX__ + + +#if defined ( __MAC_OSX__ ) +//******************************************************************************* +//http://developer.apple.com/DOCUMENTATION/Darwin/Conceptual/KernelProgramming/scheduler/chapter_8_section_4.html +//http://lists.apple.com/archives/darwin-dev/2007/Sep/msg00035.html +int set_realtime(int period, int computation, int constraint) +{ + //AbsoluteTime time; + //clock_get_uptime((uint64_t *)&time); + + //uint64_t result; + //clock_get_uptime(&result); + //clock_get_system_microtime(&result,&result); + + struct thread_time_constraint_policy ttcpolicy; + int ret; + + ttcpolicy.period=period; // HZ/160 + ttcpolicy.computation=computation; // HZ/3300; + ttcpolicy.constraint=constraint; // HZ/2200; + ttcpolicy.preemptible=1; + + if ((ret=thread_policy_set(mach_thread_self(), + THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&ttcpolicy, + THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS) { + fprintf(stderr, "set_realtime() failed.\n"); + return 0; + } + return 1; +} +#endif //__MAC_OSX__ + + +#if defined ( __LINUX__ ) +//******************************************************************************* +int get_fifo_priority (bool half) +{ + int min, max, priority; + min = sched_get_priority_min (SCHED_FIFO); + max = sched_get_priority_max (SCHED_FIFO); + if (half) { + priority = (max - (max - min) / 2); } + else { + priority = max; } + + //priority=min; + return priority; +} +#endif //__LINUX__ + + +#if defined ( __LINUX__ ) +//******************************************************************************* +int set_fifo_priority (bool half) +{ + struct sched_param p; + int priority; + // scheduling priority + + + if (true) // (!getuid () || !geteuid ()) + { + priority = get_fifo_priority (half); + p.sched_priority = priority; + + if (sched_setscheduler (0, SCHED_FIFO, &p) == -1) + { + fprintf (stderr, + "\ncould not activate scheduling with priority %d\n", + priority); + return -1; + } + seteuid (getuid ()); + //fprintf (stderr, + // "\nset scheduling priority to %d (SCHED_FIFO)\n", + // priority); + } + else + { + fprintf (stderr, + "\ninsufficient privileges to set scheduling priority\n"); + priority = 0; + } + return priority; +} +#endif //__LINUX__ + + +#if defined ( __LINUX__ ) +//******************************************************************************* +int set_realtime_priority (void) +{ + struct sched_param schp; + + memset (&schp, 0, sizeof (schp)); + schp.sched_priority = sched_get_priority_max (SCHED_FIFO); + if (sched_setscheduler (0, SCHED_FIFO, &schp) != 0) + { + perror ("set_scheduler"); + return -1; + } + return 0; +} +#endif //__LINUX__ + +void set_crossplatform_realtime_priority() +{ +#if defined ( __LINUX__ ) + set_fifo_priority (false); +#endif //__LINUX__ +#if defined ( __MAC_OSX__ ) + set_realtime(1250000,60000,90000); +#endif //__MAC_OSX__ +} + + diff --git a/src/jacktrip_globals.h b/src/jacktrip_globals.h new file mode 100644 index 0000000..f9d2d76 --- /dev/null +++ b/src/jacktrip_globals.h @@ -0,0 +1,126 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file globals.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#ifndef __JACKTRIP_GLOBALS_H__ +#define __JACKTRIP_GLOBALS_H__ + +#include "JackAudioInterface.h" + + +//namespace JackTrip/// \todo Add this namespace + +const char* const gVersion = "1.0.5"; ///< JackTrip version + +//******************************************************************************* +/// \name Default Values +//@{ +const int gDefaultNumInChannels = 2; +const int gDefaultNumOutChannels = 2; +const JackAudioInterface::audioBitResolutionT gDefaultBitResolutionMode = + JackAudioInterface::BIT16; +const int gDefaultQueueLength = 4; +const int gDefaultOutputQueueLength = 4; +//@} + + +//******************************************************************************* +/// \name Network related ports +//@{ +const int gDefaultPort = 4464; ///< Default JackTrip Port +//const int gInputPort_0 = 4464; ///< Input base port +//const int gOutputPort_0 = 4465; ///< Output base port +//const int gDefaultSendPort = 4464; ///< Default for to use to send packet +//@} + + +//******************************************************************************* +/// \name Separator for terminal printing +//@{ +const char* const gPrintSeparator = "---------------------------------------------------------"; +//@} + + +//******************************************************************************* +/// \name Global flags +//@{ +extern int gVerboseFlag; ///< Verbose mode flag declaration +//@} + + +//******************************************************************************* +/// \name JackAudio +//@{ +const int gJackBitResolution = 32; ///< Audio Bit Resolution of the Jack Server +//@} + + +//******************************************************************************* +/// \name Global Functions + +void set_crossplatform_realtime_priority(); + +//@{ +// Linux Specific Functions +#if defined ( __LINUX__ ) +/// \brief Returns fifo priority +int get_fifo_priority (bool half); +/// \brief Set fifo priority (if user has sufficient privileges). +int set_fifo_priority (bool half); +int set_realtime_priority (void); +#endif //__LINUX__ +//@} + +//@{ +// Mac OS X Specific Functions +#if defined ( __MAC_OSX__ ) +int set_realtime(int period, int computation, int constraint); +#endif //__MAC_OSX__ +//@} + + +//******************************************************************************* +/// \name JackTrip Server parameters +//@{ +/// Maximum Threads that can be run at the same time +const int gMaxThreads = 290; // some pthread limit around 297? + +/// Public well-known UDP port to where the clients will connect +const int gServerUdpPort = 4464; +//@} + + +#endif diff --git a/src/jacktrip_main.cpp b/src/jacktrip_main.cpp new file mode 100644 index 0000000..e0d4203 --- /dev/null +++ b/src/jacktrip_main.cpp @@ -0,0 +1,86 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file main.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ + +#include + +#include + +#include "JackAudioInterface.h" +#include "UdpDataProtocol.h" +#include "RingBuffer.h" +#include "JackTrip.h" +#include "Settings.h" +//#include "TestRingBuffer.h" +#include "LoopBack.h" +#include "PacketHeader.h" +#include "JackTripThread.h" +#include "jacktrip_tests.cpp" + +#include "jacktrip_globals.h" + +using std::cout; using std::endl; + + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + + //--------TESTS-------------------------- + //main_tests(argc, argv); // test functions + //while (true) sleep(9999); + //--------------------------------------- + + // Get Settings from user + // ---------------------- + try + { + // Get Settings from user + // ---------------------- + Settings* settings = new Settings; + settings->parseInput(argc, argv); + settings->startJackTrip(); + } + catch ( const std::exception & e ) + { + std::cerr << "ERROR:" << endl; + std::cerr << e.what() << endl; + std::cerr << "Exiting JackTrip..." << endl; + std::cerr << gPrintSeparator << endl; + return -1; + } + return app.exec(); +} diff --git a/src/jacktrip_tests.cpp b/src/jacktrip_tests.cpp new file mode 100644 index 0000000..17cddf1 --- /dev/null +++ b/src/jacktrip_tests.cpp @@ -0,0 +1,103 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file jacktrip_tests.cpp + * \author Juan-Pablo Caceres + * \date September 2008 + */ + +#include + +#include + +#include "JackTripThread.h" + +using std::cout; using std::endl; + +const int num_jacktrips = 5; +const int base_port = 4464; + + +void main_tests(int argc, char** argv); +void test_threads_server(); +void test_threads_client(const char* peer_address); + + +void main_tests(int /*argc*/, char** argv) +{ + if (argv[1][0] == 's' ) + { + test_threads_server(); + } + else if (argv[1][0] == 'c' ) + { + test_threads_client("171.64.197.209"); + } +} + + +// Test many servers running at the same time +void test_threads_server() +{ + QVector jacktrips; + jacktrips.resize(num_jacktrips); + int port_num; + for (int i = 0; i < num_jacktrips; i++) + { + port_num = base_port + i*10; + cout << "Port Number: " << port_num << endl; + jacktrips[i] = new JackTripThread(JackTrip::SERVER); + jacktrips[i]->setPort(port_num); + jacktrips[i]->start(QThread::NormalPriority); + //sleep(1); + } +} + + +// Test many servers running at the same time +void test_threads_client(const char* peer_address) +{ + QVector jacktrips; + jacktrips.resize(num_jacktrips); + int port_num; + for (int i = 0; i < num_jacktrips; i++) + { + port_num = base_port + i*10; + cout << "Port Number: " << port_num << endl; + jacktrips[i] = new JackTripThread(JackTrip::CLIENT); + jacktrips[i]->setPort(port_num); + jacktrips[i]->setPeerAddress(peer_address); + //sleep(1); + jacktrips[i]->start(QThread::NormalPriority); + //sleep(1); + } +} diff --git a/src/jacktrip_types.h b/src/jacktrip_types.h new file mode 100644 index 0000000..08bfe87 --- /dev/null +++ b/src/jacktrip_types.h @@ -0,0 +1,85 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file jacktrip_types_jacktrip.h + * \author Juan-Pablo Caceres + * \date June 2008 + */ + + +#ifndef __JACKTRIP_TYPES_H__ +#define __JACKTRIP_TYPES_H__ + +#include +#include //For QT4 types + +//------------------------------------------------------------------------------- +/** \name Audio typedefs + * + */ +//------------------------------------------------------------------------------- +//@{ +/// Audio sample type +typedef jack_default_audio_sample_t sample_t; +//@} + + +//------------------------------------------------------------------------------- +/** \name Typedefs that guaranty some specific bit length + * + * It uses the QT4 types. This can be changed in the future, keeping + * compatibility for the rest of the code. + */ +//------------------------------------------------------------------------------- +//@{ +/// Typedef for unsigned char. This type is guaranteed to be 8-bit. +typedef quint8 uint8_t; +/// Typedef for unsigned short. This type is guaranteed to be 16-bit. +typedef quint16 uint16_t; +/// Typedef for unsigned int. This type is guaranteed to be 32-bit. +typedef quint32 uint32_t; +/// \brief Typedef for unsigned long long int. This type is guaranteed to +/// be 64-bit. +//typedef quint64 uint64_t; +/// Typedef for signed char. This type is guaranteed to be 8-bit. +typedef qint8 int8_t; +/// Typedef for signed short. This type is guaranteed to be 16-bit. +typedef qint16 int16_t; +/// Typedef for signed int. This type is guaranteed to be 32-bit. +typedef qint32 int32_t; +/// \brief Typedef for long long int. This type is guaranteed to +/// be 64-bit. +//typedef qint64 int64_t; +//@} + + +#endif diff --git a/src/tests.cpp b/src/tests.cpp new file mode 100644 index 0000000..6582563 --- /dev/null +++ b/src/tests.cpp @@ -0,0 +1,122 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file tests.cpp + * \author Juan-Pablo Caceres + * \date June 2008 + */ +#include +#include +#include + + +#include "JackAudioInterface.h" +#include "UdpDataProtocol.h" +#include "RingBuffer.h" +#include "JackTrip.h" +#include "Settings.h" +#include "TestRingBuffer.h" +#include "jacktrip_globals.h" + +using namespace std; + +void tests() +{ + // Test JackTrip + //================================================================ + //JackTrip jacktrip1; + //jacktrip1.startThreads(); + + //JackTrip jacktrip2; + //jacktrip2.startThreads(); + + /* + // TestRingBuffer + //================================================================ + TestRingBufferWrite tw; + TestRingBufferRead tr; + tr.start(); + tw.start(); + */ + + /* + // Test RingBuffer + //================================================================ + RingBuffer rb(2,2); + + int8_t* writeSlot; + writeSlot = new int8_t[2]; + writeSlot[0] = *"a"; + writeSlot[1] = *"b"; + std::cout << *writeSlot << std::endl; + std::cout << writeSlot[0] << std::endl; + std::cout << writeSlot[1] << std::endl; + std::cout << *(writeSlot+1) << std::endl; + rb.writeSlot(writeSlot); + + int8_t* readSlot; + readSlot = new int8_t[2]; + rb.readSlot(readSlot); + std::cout << *(readSlot) << std::endl; + std::cout << *(readSlot+1) << std::endl; + */ + + + /* + // Test UDP Socket + //================================================================ + UdpDataProtocol udp_rec(RECEIVER, "192.168.1.4"); + UdpDataProtocol udp_send(SENDER, "192.168.1.4"); + udp_rec.start(); + udp_send.start(); + */ + + /* + // Test JackAudioInterface + //================================================================ + JackAudioInterface jack_test(4); + cout << "SR: " << jack_test.getSampleRate() << endl; + cout << "Buffer Size: " << jack_test.getBufferSize() << endl; + jack_test.setProcessCallback(process); + jack_test.startProcess(); + */ + + + while (true) + { + //cout << "SR: " << test.getSampleRate() << endl; + //cout << "Buffer Size: " << test.getBufferSize() << endl; + usleep(1000000); + //usleep(1); + } + +}