jack2-1.9.22/000077500000000000000000000000001436671425200126115ustar00rootroot00000000000000jack2-1.9.22/.cirrus.yml000066400000000000000000000021631436671425200147230ustar00rootroot00000000000000task: freebsd_instance: matrix: - image_family: freebsd-12-3 - image_family: freebsd-13-1 environment: CFLAGS: -O2 -pipe -fPIC -fstack-protector-strong -fno-strict-aliasing -I/usr/local/include CPPFLAGS: -O2 -pipe -fPIC -fstack-protector-strong -fno-strict-aliasing -I/usr/local/include LDFLAGS: -L/usr/local/lib -fstack-protector-strong prepare_script: - mkdir /Install matrix: - name: FreeBSD Minimal Build dependencies_script: - pkg install -y pkgconf python3 libsamplerate libsysinfo expat config_script: - python3 ./waf configure --celt=no --samplerate=yes --alsa=no --classic --opus=no --prefix /Install --pkgconfigdir libdata/pkgconfig - name: FreeBSD All Options dependencies_script: - pkg install -y pkgconf python3 libsamplerate libsysinfo alsa-lib dbus expat opus config_script: - python3 ./waf configure --celt=no --samplerate=yes --alsa=yes --dbus --classic --autostart=dbus --opus=yes --prefix /Install --pkgconfigdir libdata/pkgconfig build_script: - python3 ./waf install_script: - python3 ./waf install jack2-1.9.22/.flake8000066400000000000000000000001321436671425200137600ustar00rootroot00000000000000[flake8] max-line-length = 120 output-file = flake8.txt exclude = .git,waflib,autooptions jack2-1.9.22/.github/000077500000000000000000000000001436671425200141515ustar00rootroot00000000000000jack2-1.9.22/.github/ISSUE_TEMPLATE/000077500000000000000000000000001436671425200163345ustar00rootroot00000000000000jack2-1.9.22/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000016301436671425200210260ustar00rootroot00000000000000--- name: Bug report 🐞 about: Create a report to help us improve title: '' labels: bug assignees: '' --- ## Describe the bug A clear and concise description of what the bug is. ## Environment * **JACK Version:** Which version of jack2 are you using? (e.g. the output of `jackd --version` or a git commit checksum) * **Operating System:** What operating system or distribution in which version are you using? (e.g. Linux, macOS, Windows) * **Installation:** How did you install jack2? (e.g. package manager, from source) ## Steps To Reproduce ```bash # Paste a minimal code example here (e.g. about how you started JACK) ``` ### Expected vs. actual behavior A clear and concise description of what you expected to happen vs. what happened. jack2-1.9.22/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011641436671425200220630ustar00rootroot00000000000000--- name: Feature Request 💡 about: Suggest a new idea for the project. labels: enhancement --- ## Summary Brief explanation of the feature. ### Basic example If the proposal involves a new feature or change in behavior, include a basic code example. Omit this section if it's not applicable. ### Motivation Why are we doing this? What use cases does it support? What is the expected outcome? jack2-1.9.22/.github/workflows/000077500000000000000000000000001436671425200162065ustar00rootroot00000000000000jack2-1.9.22/.github/workflows/build.yml000066400000000000000000000274531436671425200200430ustar00rootroot00000000000000name: build on: push: branches: - '*' pull_request: branches: - '*' env: CACHE_VERSION: 6 DEBIAN_FRONTEND: noninteractive HOMEBREW_NO_AUTO_UPDATE: 1 PAWPAW_VERSION: 86d947d472e1478af882175ee329a6f769b4d15c PAWPAW_SKIP_LTO: 1 jobs: # macOS native intel build macos: runs-on: macos-10.15 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Set up cache uses: actions/cache@v3 with: path: | ~/PawPawBuilds key: macos-v${{ env.CACHE_VERSION }} - name: Set up dependencies run: | brew install cmake jq meson - name: Bootstrap macOS intel shell: bash run: | if [ ! -d PawPaw ]; then git clone https://github.com/DISTRHO/PawPaw.git git -C PawPaw checkout ${PAWPAW_VERSION} fi ./PawPaw/bootstrap-jack2.sh macos && ./PawPaw/.cleanup.sh macos - name: Build jack2 shell: bash run: | pushd PawPaw && source local.env macos && popd python ./waf configure --platform=darwin --prefix=/usr/local python ./waf build -j $(sysctl -n hw.logicalcpu) python ./waf install --destdir=$(pwd)/destdir - name: Set sha8 id: slug run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" - name: Generate macOS package shell: bash run: | ./macosx/generate-pkg.sh $(pwd)/destdir/usr/local ${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} - uses: actions/upload-artifact@v3 with: name: jack2-macOS-intel-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} path: macosx/jack2-osx-*.pkg # macOS native universal build macos_universal: runs-on: macos-10.15 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Set up cache uses: actions/cache@v3 with: path: | ~/PawPawBuilds key: macos-universal-v${{ env.CACHE_VERSION }} - name: Set up dependencies run: | brew install cmake jq meson - name: Fix up Xcode run: | sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/* sudo xcode-select -s /Applications/Xcode_12.3.app - name: Bootstrap macOS universal shell: bash run: | if [ ! -d PawPaw ]; then git clone https://github.com/DISTRHO/PawPaw.git git -C PawPaw checkout ${PAWPAW_VERSION} fi ./PawPaw/bootstrap-jack2.sh macos-universal && ./PawPaw/.cleanup.sh macos-universal - name: Build jack2 shell: bash run: | pushd PawPaw && source local.env macos-universal && popd python ./waf configure --platform=darwin --prefix=/usr/local python ./waf build -j $(sysctl -n hw.logicalcpu) python ./waf install --destdir=$(pwd)/destdir - name: Set sha8 id: slug run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" - name: Generate macOS package shell: bash run: | ./macosx/generate-pkg.sh $(pwd)/destdir/usr/local ${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} - uses: actions/upload-artifact@v3 with: name: jack2-macOS-universal-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} path: macosx/jack2-osx-*.pkg # linux with win32 cross-compilation win32: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Set up cache uses: actions/cache@v3 with: path: | ~/PawPawBuilds key: win32-v${{ env.CACHE_VERSION }} - name: Restore debian packages cache run: | if [ -d ~/PawPawBuilds/debs ] && [ "$(ls ~/PawPawBuilds/debs | wc -l)" -ne 0 ]; then \ sudo cp ~/PawPawBuilds/debs/*.deb /var/cache/apt/archives/; \ fi - name: Fix GitHub's mess run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list sudo apt-get update -qq sudo apt-get install -yqq --allow-downgrades libgd3/focal libpcre2-8-0/focal libpcre2-16-0/focal libpcre2-32-0/focal libpcre2-posix2/focal sudo apt-get purge -yqq libmono* moby* mono* php* libgdiplus libpcre2-posix3 libzip4 - name: Set up dependencies run: | sudo dpkg --add-architecture i386 sudo apt-get update -qq sudo apt-get install -y autopoint build-essential curl cmake jq llvm mingw-w64 xvfb \ binutils-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64 wine-stable:i386 - name: Cache debian packages run: | mkdir -p ~/PawPawBuilds/debs && \ sudo mv /var/cache/apt/archives/*.deb ~/PawPawBuilds/debs/ - name: Bootstrap win32 cross-compiled shell: bash run: | if [ ! -d PawPaw ]; then git clone https://github.com/DISTRHO/PawPaw.git git -C PawPaw checkout ${PAWPAW_VERSION} fi ./PawPaw/bootstrap-jack2.sh win32 && ./PawPaw/.cleanup.sh win32 - name: Build jack2 shell: bash run: | pushd PawPaw && source local.env win32 && popd ./waf configure --platform=win32 --prefix=$(pwd)/destdir --static ./waf build -j $(nproc) ./waf install - name: Generate MSVC lib files shell: bash run: | pushd $(pwd)/destdir/lib llvm-dlltool -m i386 -D libjack.dll -d libjack.def -l libjack.lib llvm-dlltool -m i386 -D libjacknet.dll -d libjacknet.def -l libjacknet.lib llvm-dlltool -m i386 -D libjackserver.dll -d libjackserver.def -l libjackserver.lib popd - name: Set sha8 id: slug run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" - name: Generate Windows installer shell: bash run: | # Setup wine export WINEDLLOVERRIDES="mscoree,mshtml=" export WINEPREFIX=$(pwd)/innosetup xvfb-run wineboot -u # Download and install innosetup curl -L https://jrsoftware.org/download.php/is.exe?site=2 -o is.exe xvfb-run wine is.exe /allusers /dir=C:\\InnoSeup /nocancel /norestart /verysilent # create installer ln -sf $(pwd)/destdir windows/inno/win32 pushd windows/inno echo "#define VERSION \"${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}\"" > version.iss xvfb-run wine ${WINEPREFIX}/drive_c/InnoSeup/ISCC.exe win32-mini.iss popd - uses: actions/upload-artifact@v3 with: name: jack2-win32-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} path: windows/inno/jack2-*.exe # linux with win64 cross-compilation win64: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Set up cache uses: actions/cache@v3 with: path: | ~/PawPawBuilds key: win64-v${{ env.CACHE_VERSION }} - name: Restore debian packages cache run: | if [ -d ~/PawPawBuilds/debs ] && [ "$(ls ~/PawPawBuilds/debs | wc -l)" -ne 0 ]; then \ sudo cp ~/PawPawBuilds/debs/*.deb /var/cache/apt/archives/; \ fi - name: Fix GitHub's mess run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list sudo apt-get update -qq sudo apt-get install -yqq --allow-downgrades libgd3/focal libpcre2-8-0/focal libpcre2-16-0/focal libpcre2-32-0/focal libpcre2-posix2/focal sudo apt-get purge -yqq libmono* moby* mono* php* libgdiplus libpcre2-posix3 libzip4 - name: Set up dependencies run: | sudo dpkg --add-architecture i386 sudo apt-get update -qq sudo apt-get install -y autopoint build-essential curl cmake jq llvm mingw-w64 xvfb \ binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 wine-stable - name: Cache debian packages run: | mkdir -p ~/PawPawBuilds/debs && \ sudo mv /var/cache/apt/archives/*.deb ~/PawPawBuilds/debs/ - name: Bootstrap win64 cross-compiled shell: bash run: | if [ ! -d PawPaw ]; then git clone https://github.com/DISTRHO/PawPaw.git git -C PawPaw checkout ${PAWPAW_VERSION} fi ./PawPaw/bootstrap-jack2.sh win64 && ./PawPaw/.cleanup.sh win64 - name: Build jack2 shell: bash run: | pushd PawPaw && source local.env win64 && popd export PATH+=":/usr/i686-w64-mingw32/bin" export LDFLAGS+="-L~/PawPawBuilds/targets/win64/lib32" ./waf configure --platform=win32 --prefix=$(pwd)/destdir --static --mixed ./waf build -j $(nproc) ./waf install - name: Generate MSVC lib files shell: bash run: | # 32bit pushd $(pwd)/destdir/lib32 llvm-dlltool -m i386 -D libjack.dll -d libjack.def -l libjack.lib popd # 64bit pushd $(pwd)/destdir/lib llvm-dlltool -m i386:x86-64 -D libjack64.dll -d libjack64.def -l libjack64.lib llvm-dlltool -m i386:x86-64 -D libjacknet64.dll -d libjacknet64.def -l libjacknet64.lib llvm-dlltool -m i386:x86-64 -D libjackserver64.dll -d libjackserver64.def -l libjackserver64.lib popd - name: Set sha8 id: slug run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" - name: Generate Windows installer shell: bash run: | # Setup wine export WINEDLLOVERRIDES="mscoree,mshtml=" export WINEPREFIX=$(pwd)/innosetup xvfb-run wineboot -u # Download and install innosetup curl -L https://jrsoftware.org/download.php/is.exe?site=2 -o is.exe xvfb-run wine64 is.exe /allusers /dir=C:\\InnoSeup /nocancel /norestart /verysilent # create installer ln -sf $(pwd)/destdir windows/inno/win64 pushd windows/inno echo "#define VERSION \"${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}\"" > version.iss xvfb-run wine64 ${WINEPREFIX}/drive_c/InnoSeup/ISCC.exe win64-mini.iss popd - uses: actions/upload-artifact@v3 with: name: jack2-win64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} path: windows/inno/jack2-*.exe # ubuntu-20.04 #ubuntu_20_04: #runs-on: ubuntu-20.04 #steps: #- uses: actions/checkout@v3 #with: #submodules: recursive #- name: Set up cache #uses: actions/cache@v3 #with: #path: | #~/debs #key: ubuntu-20.04-v${{ env.CACHE_VERSION }} #- name: Restore debian packages cache #run: | #if [ -d ~/debs ] && [ "$(ls ~/debs | wc -l)" -ne 0 ]; then \ #sudo cp ~/debs/*.deb /var/cache/apt/archives/; \ #fi #- name: Set up dependencies #run: | #sudo add-apt-repository -y ppa:ubuntustudio-ppa/backports #sudo sed -i "s/# deb-src/deb-src/" /etc/apt/sources.list /etc/apt/sources.list.d/*.list #sudo apt-get update -qq #sudo apt-get build-dep jackd2 #sudo apt-get install devscripts #- name: Cache debian packages #run: | #mkdir -p ~/debs && \ #sudo mv /var/cache/apt/archives/*.deb ~/debs/ #- name: Set sha8 #id: slug #run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" #- name: Build jack2 packages #shell: bash #run: | #apt-get source -d jackd2 #tar xf *.debian.tar.xz #rm -rf debian/source #sed -i "s|--prefix=/usr --classic|--prefix=/usr --classic|" debian/rules #dch -M -b -v "$(cat wscript | awk 'sub("^VERSION = ","")' | tr -d "'")~$(date +"%Y%m%d")git${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}" -D focal "automated build" #debuild -rfakeroot --no-lintian || true #- uses: actions/upload-artifact@v3 #with: #name: jack2-ubuntu-20.04-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} #path: ~/work/jack2/*.deb jack2-1.9.22/.github/workflows/irc.yml000066400000000000000000000011011436671425200174770ustar00rootroot00000000000000name: irc on: [push] jobs: notification: runs-on: ubuntu-latest name: IRC notification steps: - name: Format message id: message run: | message="${{ github.actor }} pushed $(echo '${{ github.event.commits[0].message }}' | head -n 1) ${{ github.event.commits[0].url }}" echo ::set-output name=message::"${message}" - name: IRC notification uses: Gottox/irc-message-action@v2 with: channel: '#jack' nickname: jackaudio-bot message: ${{ steps.message.outputs.message }} jack2-1.9.22/.github/workflows/lint.yml000066400000000000000000000007171436671425200177040ustar00rootroot00000000000000--- name: Test wscript files on: push: branches: [master, develop] pull_request: branches: [master, develop] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install dependencies run: | sudo apt-get update sudo apt-get install flake8 findutils - name: Lint with flake8 run: find . -type f \( -iname wscript -or -iname jack_control \) -exec flake8 {} \; jack2-1.9.22/.gitignore000066400000000000000000000005311436671425200146000ustar00rootroot00000000000000.lock* .stamp_* .DS_Store __pycache__ *.dll *.pyc *.pkg flake8.txt codeBlocks /android/.server/ /android/.client/ /build/ /man/*.1 # common release files /destdir/ # macos release files /macos/package.xml /macos/package-welcome.txt # windows release files /innosetup/ /is.exe /windows/inno/version.iss /windows/inno/win32 /windows/inno/win64 jack2-1.9.22/.wafupdaterc000077500000000000000000000031711436671425200151240ustar00rootroot00000000000000# This file is sourced by wafupdate when waf is updated. # wafupdate home page: https://gitlab.com/karllinden/wafupdate WAFLIB_STRIP_TOOLS=" asm bison compiler_d compiler_fc cs d d_config d_scan dbus dmd fc fc_config fc_scan flex g95 gas gdc gfortran glib2 gnu_dirs ifort intltool javaw ldc2 lua md5_tstamp nasm nobuild perl python qt5 ruby tex vala winres " WAFLIB_STRIP_EXTRAS=" biber bjam blender boo boost buildcopy c_dumbpreproc c_emscripten cabal cfg_altoptions clang_compilation_database codelite compat15 color_gcc color_rvct cppcheck cpplint cross_gnu cython dcc distnet doxygen dpapi eclipse erlang fast_partial fc_bgxlf fc_cray fc_nag fc_nec fc_open64 fc_pgfortran fc_solstudio fc_xlf file_to_object fluid freeimage fsb fsc gccdeps gdbus gob2 halide javatest kde4 local_rpath make midl msvcdeps msvs netcache_client objcopy ocaml package parallel_debug pch pep8 pgicc pgicxx proc protoc pyqt5 pytest qnxnto qt4 relocation remote resx review rst run_do_script run_m_script run_py_script run_r_script sas satellite_assembly scala slow_qt4 softlink_libs stale stracedeps swig syms ticgt unity use_config valadoc waf_xattr why win32_opts wix " jack2-1.9.22/AUTHORS.rst000066400000000000000000000030711436671425200144710ustar00rootroot00000000000000Authors ####### (Ordered alphabetically) Adrian Knoth Alba Mendez Alexander Graf Alexandre Prokoudine Alexandru Tofan Andreas Müller Andrew Kelley Andrzej Szombierski Andy Wingo Anthony Van Groningen Arnout Diels Arnaud Rebillout Arnold Krille Bernhard M. Wiedemann Bruno Vernay Cédric Schieli Chris Caudle David Garcia Garzon David Robillard David Runge Deven Lahoti Devin Anderson Dmitry Baikov Edward Betts Eliot Blennerhassett Fernando Lopez-Lezcano Filipe Coelho Florian Faber Francis Pinteric Gabriel M. Beddingfield Gaël Portay Holger Dehnhardt Hunter L. Allen Ian Esten Jacek Konieczny Jack O'Quin James Cowgill James P. Thomas Jan Engelhardt Jaroslav Kysela Jeremy Hall John Emmas Johnny Petrantoni Jonathan Woithe Josh Green Joshua Moyerman Julien Acroute Jussi Laako Juuso Alasuutari Kai Vehmanen Karl Lindén Karsten Wiese Kim Jeong Yeon Kjetil S. Matheussen Lars-Peter Clausen Lee Revell Maks Naumov Marc-Olivier Barre Mario Lang Markus Seeber Matt Flax Matthias Geier Maxim Grishin Melanie Thielker Michael Voigt Michał Szymański Nedko Arnaudov Olaf Hering Olivier Humbert Paul Davis Peter Bridgman Peter L Jones Pieter Palmers Ricardo Crudo Robert Ham Robin Gareus Rohan Drape Romain Moret Rui Nuno Capela Samuel Martin Samuel Tracol Stefan Schwandter Stéphane Letz Steve Harris Steven Chamberlain Taybin Rutkin Thibault LeMeur Thomas Brand Thomas Petazzoni Thom Johansen Tilman Linneweh Tim Blechmann Timo Wischer Tom Szilagyi Torben Hohn Valentin David Valerio Pilo Viktor Wilhelmsson Yasuhiro Fujii Youri Westerman jack2-1.9.22/COPYING000066400000000000000000000355641436671425200136610ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS jack2-1.9.22/ChangeLog.rst000066400000000000000000001227261436671425200152040ustar00rootroot00000000000000ChangeLog ######### * 1.9.22 (2023-02-02) * The waf autooption ``--example-tools`` has been removed. The example clients and tools are no longer part of the jack2 project. * Drop dependencies that were only used for example clients and tools (readline, sndfile and zita libs) * Fix build with python3.11+ * Fix compatibility with macOS 12+ * Fix ringbuffer thread safety on ARM * 1.9.21 (2022-04-15) * Add shell mode to ``jack_control`` (executes commands from stdin until EOF) * The waf autooption ``--example-tools`` introduced in 1.9.20 is now off by default, To get the previous behavior back pass ``--example-tools`` during build. This flag (and the related tools and their code) are going to be removed in the next release. * Fix alignas() on non-packed architectures * Fix build of jack-example-tools man pages (1.9.20 regression) * Fix compatibility with macOS 12 * Fix missing symbols from jack client library (error and info callback pointers) * Fix potential memory corruption in midi_latency_test tool * Fix JackWeakAPI on Windows * Use predefined variables in pkg-config file (required for mingw) External changes, related to macOS/Windows installer: * Fix application style in QJackCtl (qwindowsvistastyle.dll was missing) * Update QjackCtl used in macOS/Windows installers to 0.9.7 * 1.9.20 (2022-01-15) * Add waf autooption ``--example-tools`` to allow optional build of executables, libraries and man pages provided by `jack-example-tools `_ (the files are built by default). Building and installing the additional files can be disabled by using ``--example-tools=no`` or ``--no-example-tools``. * Fix 32-bit support in ALSA driver * Fix incomplete ASIO support on Windows * Fix metadata usage with multiple users * Fix netsource tool missing on Windows * Fix semaphore usage on macOS * Official FreeBSD support External changes, related to macOS/Windows installer: * Update Qt5 used in macOS/Windows installers to 5.12.12 * Update QjackCtl used in macOS/Windows installers to 0.9.6 * 1.9.19 (2021-07-15) * Add jack_position_t::tick_double, and flags around it * Add zalsa "-w" argument to wait for soundcard to be available * Bump internal protocol version to 9 (due to struct alignment) * Fix alignment of fields for atomic accesses * Fix build for platforms needing __STDC_FORMAT_MACROS * Fix compilation of documentation * 1.9.18 (2021-04-15) * Add zalsa_in/out as internal client (based on zita-a2j/j2a and jack1 code) * Fix jack_midi_dump deadlock on close after the jack server is restarted * Fix interrupt signal for linux futex waits * Fix usage of meta-data in official macOS builds (private DB errors) * Log error message when cleaning previous DB (macOS and Windows) * 1.9.17 (2021-01-15) * Fix jack_control stopping after first command iteration * Fix library compat/current version under macOS * Fix return codes of jackd on success * Ignore DB_VERSION_MISMATCH error on windows, it is expected * Remove old workaround for asio4all, as it breaks with jack-router External changes, related to macOS/Windows installer: * Add jack-router to Windows installer, opt-in * Fix registry keys for Windows, add 32bit compat ones on 64bit * Support for arm64 macOS builds * Show welcome and license pages on windows installer * Update QjackCtl used in macOS/Windows installers to v0.9.0, with some commits cherry-picked from develop branch * 1.9.16 (2020-10-16) * Fix/revert a change in how meta-data definitions were exposed (regression in 1.9.15) * Remove jack-router Windows code from the repository * 1.9.15 (2020-10-15) * Automated builds for macOS and Windows (see jackaudio/jack2-releases repository) * Adapt wscript Windows build configuration to match old v1.9.11 installer * Bump maximum default number of clients and ports (now 256 clients and 2048 ports) * Delete various macOS and Windows-related files from the source code (no longer relevant) * Mark JACK-Session as deprecated, please use NSM instead * Remove unnecessary GPL include from LGPL code * Split example-clients and tools, as done in JACK1 many years ago (WIP) * Write Windows registry key during installation, so 3rd parties can find jackd.exe (as HKLM\\Software\\JACK\\Location) * jack_control: Fix handling of dbus bytes * jack_control: Return a proper exit status on DBus exception * jack_property: Fix possible crash with "-l" argument usage * jack_wait: Add client name option -n/--name * Fix compilation of documentation * Fix compilation of mixed mode with meta-data enabled * Fix compilation with mingw * Fix client-side crash if initial meta-data DB setup fails * Fix macOS semaphore usage, so it works again * Several fixes for Windows (with contributions from Kjetil S. Matheussen) * Several minor fixes and grammar corrections (with contributions from Adam Miartus and Timo Wischer) * 1.9.14 (2019-10-28) * Fix ARM build * Fix mixed mode build when meta-data is enabled * Fix blocking DBus device reservation, so it plays nice with others (like PipeWire) * Use python3 for the waf build scripts * 1.9.13 (2019-10-06) * Meta-data API implementation. (and a few tools updated with support for it) * Correct GPL licence to LGPL for files needed to build libjack. * Remove FreeBoB backend (superseded by FFADO). * define JACK_LIB_EXPORT, useful for internal clients. * Mark jack_midi_reset_buffer as deprecated. * Add example systemd unit file * Signal to systemd when jackd is ready. * Set "seq" alsa midi driver to maximum resolution possible. * Fix loading internal clients from another internal client. * Code cleanup and various fixes. (too many to mention here, see git log for details) * 1.9.12 (2017-12-13) * Fix Windows build issues. * Fix build with gcc-7. * Show hint when DBus device reservation fails. * Add support for internal session files. * 1.9.11-RC1 (2017-06-13) * Various corrections in NetJack2 code. Partial buffers can now be transmitted with libjacknet API. * Including S24_LE/BE formats to linux ALSA driver. * More robust shared memory allocator. * Allow autostart of jackd on OSX where device-names can contain spaces. * Correct CoreAudio devices aggregation code. * Waf and wscripts improvement and update. * More flexible RT priority setup on Windows. * New JackProxyDriver. * Various fixes in JACK MIDI code. * Fix return value of SetTimebaseCallback(). * Correct netmanager latency reporting. * Implement new jack_port_rename and JackPortRenameCallback API. * For OSX El Capitan support, use of Posix semaphore and move of Frameworks in /Library folder. * Fix CPU hogging of the midi_thread(). * Release audio devices when alsa_driver_new fails. * String management fix. * Correct JackDriver::Open: call to fGraphManager->SetBufferSize has to use current fEngineControl->fBufferSize value. * Use ARM neon intrinsics for AudioBufferMixdown. * Fix Netjack alignment. * Various wscript improvements and cleanup. * Fix initialization of several class variables. * Heap-allocate client matrix in topo sort. * Add a toggle command to transport utility, to allow toggling between play and stop state. * Avoid side effects from parsing of "version" option in jackd. * Allow firewire device be selected via -d. * Add ARM-NEON acceleration for all non-dithering sample conversion functions. * Add jack_simdtest utility. * Use Linux futex as JackSynchro. * Add autoclose option to jack_load. * 1.9.10 (2014-07-19) * More robust code in JackPortAudioDriver to handle buffer size change and backend switching. * Fix bus error on ARM platforms. * Dynamically scan and print backend and internal names in jackd. * CoreMIDI driver fixes. * Rework NetJack2 code (OPUS codec on OSX, latency management, libjacknet code). * Correct auto-connect for audioadapter. * Add IIO driver. * Merge of Nedko no-self-connect branch. * Fix freewheel mode. * JackServer::SwitchMaster now correctly notify buffer_size and sample_rate changes, cleanup/improvements in JackNetDriver. * Tim Mayberry : Add support for building with mingw compiler. * Merge of Kim Jeong Yeon Android branch. * Partial port of metadata API. * 1.9.9.5 (2012-11-26) * Adrian Knoth fix in midiseq.c. * Fix library symbols export issue. * Cleanup drivers and internals loading code. * jackctl_driver_params_parse API moved in public control.h. * More general drivers/internals loading model on Windows. * Factorize code the server/client request in JackRequestDecoder class. * More robust server/client protocol. * Implement shutdown for in server clients. * Better time-out management in NetJack2. * Experimental system port alias use in Windows JackRouter. * Improve ShutDown in NetManager. * Correct ShutDown in JackInternalClient and JackLibClient. * Fix NetJack2 initialisation bug. * Add EndTime function (especially for Windows). * Rename JackProcessSync in JackPosixProcessSync. * A bit more robust JackMessageBuffer implementation (in progress). * Check server API callback from notification thread. * Use a time-out in notification channel write function. * Fix lock management in JackEngine. * In control API, UNIX like sigset_t replaced by more abstract jackctl_sigmask_t * opaque struct. * Improve libjacknet master mode. * Remove JACK_32_64 flag, so POST_PACKED_STRUCTURE now always used. POST_PACKED_STRUCTURE used for jack_latency_range_t type. * Rework JackMessageBuffer. [firewire] * Introduce UpdateLatencies() in FFADO backend. [firewire] * Allow FFADO backend to change the buffer size. * Update waf. * New jack_get_cycle_times() implementation from Fons Adriennsen. * Align buffers to 32 byte boundaries to allow AVX processing. * Extend jack_control to have parameter reset commands. * Fix alsa driver parameter order. * Control API: Enforce driver/internal parameter order. * Fix in ALSA adapter. * Devin Anderson patch for Jack/CoreMIDI duplicated messages. * Change framework installation hierarchy for OSX Mountain Lion. * Update JackCoreAudioDriver and JackCoreAudioAdapter with more recent API. * jack_control: fix epr command. * Add opus support to NetJack2. * More robust channel mapping handling in JackCoreAudioDriver. * netjack1/netone opus support. * controlapi: fix double free on master switch. * Use string ids in the alsa device list. * netjack/opus: don't re-init en/decoders. * Correct JackPortAudioDriver::Open: special case for ASIO drivers. * 1.9.8 (2011-12-19) * Merge newer-midi branch (Devin Anderson redesign of the MIDI drivers: alsarawmidi, ffado, coremidi and winmme). * Correction in jackdmp.cpp: notify_server_stop should be done after server destruction. * Correct driver lifetime management. * Add XRun detection in PortAudio driver. * CELT code for NetJack2. * Merge branch switch-master-port-registration-notifications: correct driver port registration. * Libjacknet in progress. * Correct MIDI in NetJack2. * Correct OSX real-time thread setup. * Correct rd_acquire in dbus code. * Correct NetJack2 connection handling. * SaveConnections/RestoreConnections in NetDriver and JackAudioDriver. * Special version of jack_attach_shm/jack_release_shm on client side for POSIX shared memory, to solve a memory leak issue. * Another round of code improvements to handle completely buggy Digidesign CoreAudio user-land driver. * Special CATCH_CLOSE_EXCEPTION_RETURN to handle Close API calls. * Add JACK_NETJACK_PORT and JACK_NETJACK_MULTICAST environment variables for NetJack2. NetJack2 now only send data on network only is ports are connected both sides. * Fix for "starting two instances of same app in parallel does not work" bug. * Enable explicit channel mapping in CoreAudio driver. * New JackTimedDriver class to be used by JackDummyDriver, JackNetDriver and JackNetOneDriver classes. * More robust code in synchronization primitives and in JackMessageBuffer. * More robust Control API implementation. Add jackctl_driver_get_type in Control API. * Singleton behaviour for JackCoreMidiDriver and JackWinMMEDriver. * John Emmas patch for DSP CPU computation. * John Emmas Windows server launching patch. * Fix jack_set_port_name API. * Enable local access in NetJack2 code. * Dynamic port management in JACK/CoreMidi bridge. * 1.9.7 (2011-03-30) * Sync JackAlsaDriver::alsa_driver_check_card_type with JACK1 backend. * Correct JackServer::Open to avoid a race when control API is used on OSX. * Improve backend error handling: fatal error returned by Read/Write now cause a Process failure (so a thread exit for blocking backends). Recoverable ones (XRuns..) are now treated internally in ALSA, FreeBob and FFADO backends. * In jackdmp.cpp, jackctl_setup_signals moved before jackctl_server_start. * Correct symbols export in backends on OSX. ALSA backend: suspend/resume handling. * Correct dummy driver. * Adrian Knoth jack_lsp patch. * Remove JackPortIsActive flag. * New latency API implementation. * ComputeTotalLatencies now a client/server call. * Add latent test client for latency API. * Also print playback and capture latency in jack_lsp. jack_client_has_session_callback implementation. * Check requested buffer size and limit to 1..8192 - avoids weird behaviour caused by jack_bufsize foobar. * jack_port_type_get_buffer_size implementation. * Stop using alloca and allocate buffer on the heap for alsa_io. * Rename jdelay to jack_iodelay as per Fons' request. * Call buffer size callback in activate (actually this is done on client side in the RT thread Init method). * Add jack_midi_dump client. * Synchronize net JACK1 with JACK1 version. * Synchronize jack_connect/jack_disconnect with JACK1 version. * Correct JackNetMaster::SetBufferSize. * Use jack_default_audio_sample_t instead of float consistently, fix ticket #201. * -X now allows to add several slave backends, add -I to load several internal clients. * Rework internal slave driver management, JackServerGlobals now handle same parameters as jackdmp. * Correct JackEngine::NotifyGraphReorder, update JackDebugClient with latest API. * Devin Anderson server-ctl-proposal branch merged on trunk: improved control API, slave backend reworked. Implement renaming in JackDriver::Open to avoid name collision (thanks Devin Anderson). * Correct alsa_driver_restart (thanks Devin Anderson). Correction of jack_connect/jack_disconnect: use of jack_activate and volatile keyword for thread shared variable. * Correction of JackNetOneDriver for latest CELT API. * Synchronize JackWeakAPI.cpp with new APIs. * 1.9.6 (2010-08-30) * Improve JackCoreAudioDriver and JackCoreAudioAdapter : when no devices are described, takes default input and output and aggregate them. * Correct JackGraphManager::DeactivatePort. * Correct JackMachServerChannel::Execute : keep running even in error cases. Raise JACK_PROTOCOL_VERSION number. * Arnold Krille firewire patch. * Raise JACK_DRIVER_PARAM_STRING_MAX and JACK_PARAM_STRING_MAX to 127 otherwise some audio drivers cannot be loaded on OSX. * Fix some file header to have library side code use LGPL. * On Windows, now use TRE library for regexp (BSD license instead of GPL license). * ffado-portname-sync.patch from ticket #163 applied. * Remove call to exit in library code. * Make jack_connect/jack_disconnect wait for effective port connection/disconnection. * Add tests to validate intclient.h API. * On Linux, inter-process synchronization primitive switched to POSIX semaphore. * In JackCoreAudioDriver, move code called in MeasureCallback to be called once in IO thread. * David Garcia Garzon netone patch. * Fix from Fernando Lopez-Lezcano for compilation on fc13. * Fix JackPosixSemaphore::TimedWait : same behavior as JackPosixSemaphore::Wait regarding EINTR. * David Garcia Garzon unused_pkt_buf_field_jack2 netone patch. * Arnold Krille firewire snooping patch. * Jan Engelhardt patch for get_cycles on SPARC. * Adrian Knoth hurd.patch, kfreebsd-fix.patch and alpha_ia64-sigsegv.patch from ticket 177. * Adrian Knoth fix for linux cycle.h (ticket 188). * In JackCoreAudioDriver, fix an issue when no value is given for input. * 1.9.5 (2010-02-12) * Dynamic choice of maximum port number. * More robust sample rate change handling code in JackCoreAudioDriver. * Devin Anderson patch for Jack FFADO driver issues with lost MIDI bytes between periods (and more). * Fix port_rename callback: now both old name and new name are given as parameters. * Special code in JackCoreAudio driver to handle completely buggy Digidesign CoreAudio user-land driver. * Ensure that client-side message buffer thread calls thread_init callback if/when it is set by the client (backport of JACK1 rev 3838). * Check dynamic port-max value. * Fix JackCoreMidiDriver::ReadProcAux when ring buffer is full (thanks Devin Anderson). * Josh Green ALSA driver capture only patch. * When threads are cancelled, the exception has to be rethrown. * Use a QUIT notification to properly quit the server channel, the server channel thread can then be 'stopped' instead of 'canceled'. * Mario Lang alsa_io time calculation overflow patch. Shared memory manager was calling abort in case of fatal error, now return an error in caller. * Change JackEngineProfiling and JackAudioAdapterInterface gnuplot scripts to output SVG instead of PDF. * 1.9.4 (2009-11-19) * Solaris boomer backend now working in capture or playback only mode. * Add a -G parameter in CoreAudio backend (the computation value in RT thread expressed as percent of period). * Use SNDCTL_DSP_SYNCGROUP/SNDCTL_DSP_SYNCSTART API to synchronize input and output in Solaris boomer backend. * Big endian bug fix in memops.c. * Fix issues in JackNetDriver::DecodeTransportData and JackNetDriver::Initialize. * Correct CPU timing in JackNetDriver, now take cycle begin time after Read. * Simplify transport in NetJack2: master only can control transport. * Change CoreAudio notification thread setup for OSX Snow Leopard. * Correct server temporary mode: now set a global and quit after server/client message handling is finished. * Add a string parameter to server ==> client notification, add a new JackInfoShutdownCallback type. * CoreAudio backend now issue a JackInfoShutdownCallback when an unrecoverable error is detected (sampling rate change, stream configuration change). * Correct jackdmp.cpp (failures case were not correct..). * Improve JackCoreAudioDriver code. * Raise default port number to 2048. * Correct JackProcessSync::LockedTimedWait. * Correct JACK_MESSAGE_SIZE value, particularly in OSX RPC code. * Now start server channel thread only when backend has been started (so in JackServer::Start). * Should solve race conditions at start time. * jack_verbose moved to JackGlobals class. * Improve aggregate device management in JackCoreAudioDriver: now a "private" device only and cleanup properly. * Aggregate device code added to JackCoreAudioAdapter. * Implement "hog mode" (exclusive access of the audio device) in JackCoreAudioDriver. * Fix jack_set_sample_rate_callback to have he same behavior as in JACK1. * Dynamic system version detection in JackCoreAudioDriver to either create public or private aggregate device. * In JackCoreAudioDriver, force the SR value to the wanted one *before* creating aggregate device (otherwise creation will fail). * In JackCoreAudioDriver, better cleanup of AD when intermediate open failure. * In JackCoreAudioDriver::Start, wait for the audio driver to effectively start (use the MeasureCallback). * In JackCoreAudioDriver, improve management of input/output channels: -1 is now used internally to indicate a wanted max value. * In JackCoreAudioDriver::OpenAUHAL, correct stream format setup and cleanup. * Correct crash bug in JackAudioAdapterInterface when not input is used in adapter (temporary fix). * Sync JackCoreAudioAdapter code on JackCoreAudioDriver one. * JACK_SCHED_POLICY switched to SCHED_FIFO. * Now can aggregate device that are themselves AD. * No reason to make jack_on_shutdown deprecated, so revert the incorrect change. * Thread AcquireRealTime and DropRealTime were (incorrectly) using fThread field. * Use pthread_self()) (or GetCurrentThread() on Windows) to get the calling thread. * Correctly save and restore RT mode state in freewheel mode. * Correct freewheel code on client side. * Fix AcquireRealTime and DropRealTime: now distinguish when called from another thread (AcquireRealTime/DropRealTime) and from the thread itself (AcquireSelfRealTime/DropSelfRealTime). * Correct JackPosixThread::StartImp: thread priority setting now done in the RT case only. * Correct JackGraphManager::GetBuffer for the "client loop with one connection" case: buffer must be copied. * Correct JackInfoShutdownCallback prototype, two new JackClientProcessFailure and JackClientZombie JackStatus code. * Correct JackCoreAudio driver when empty strings are given as -C, -P or -d parameter. * Better memory allocation error checking on client (library) side. * Better memory allocation error checking in ringbuffer.c, weak import improvements. * Memory allocation error checking for jack_client_new and jack_client_open (server and client side). * Memory allocation error checking in server for RPC. * Simplify server temporary mode: now use a JackTemporaryException. * Lock/Unlock shared memory segments (to test...). * Sync with JACK1 : -r parameter now used for no-realtime, realtime (-R) is now default, usable backend given vie platform. * In JackCoreAudio driver, (possibly) clock drift compensation when needed in aggregated devices. * In JackCoreAudio driver, clock drift compensation in aggregated devices working. * In JackCoreAudio driver, clock drift compensation semantic changed a bit: when on, does not activate if not needed (same clock domain). * Sync JackCoreAudioAdapter code with JackCoreAudioDriver. * 1.9.3 (2009-07-21) * New JackBoomerDriver class for Boomer driver on Solaris. * Add mixed 32/64 bits mode (off by default). * Native MIDI backend (JackCoreMidiDriver, JackWinMMEDriver). * In ALSA audio card reservation code, tries to open the card even if reservation fails. * Clock source setting on Linux. * Add jackctl_server_switch_master API. * Fix transport callback (timebase master, sync) issue when used after jack_activate (RT thread was not running). * D-Bus access for jackctl_server_add_slave/jackctl_server_remove_slave API. * Cleanup "loopback" stuff in server. * Torben Hohn fix for InitTime and GetMicroSeconds in JackWinTime.c. * New jack_free function added in jack.h. * Reworked Torben Hohn fix for server restart issue on Windows. * Correct jack_set_error_function, jack_set_info_function and jack_set_thread_creator functions. * Correct JackFifo::TimedWait for EINTR handling. * Move DBus based audio device reservation code in ALSA backend compilation. * Correct JackTransportEngine::MakeAllLocating, sync callback has to be called in this case also. * NetJack2 code: better error checkout, method renaming. * Tim Bechmann patch: hammerfall, only release monitor thread, if it has been created. * Tim Bechmann memops.c optimization patches. * In combined --dbus and --classic compilation code, use PulseAudio acquire/release code. * Big rewrite of Solaris boomer driver, seems to work in duplex mode at least. * Loopback backend reborn as a dynamically loadable separated backend. * 1.9.2 (2009-02-11) * Solaris version. * New "profiling" tools. * Rework the mutex/signal classes. * Support for BIG_ENDIAN machines in NetJack2. * D-BUS based device reservation to better coexist with PulseAudio on Linux. * Add auto_connect parameter in netmanager and netadapter. * Use Torben Hohn PI controler code for adapters. * Client incorrect re-naming fixed : now done at socket and fifo level. * Virtualize and allow overriding of thread creation function, to allow Wine support (from JACK1). * 1.9.1 (2008-11-14) * Fix jackctl_server_unload_internal. * Filter SIGPIPE to avoid having client get a SIGPIPE when trying to access a died server. * Libjack shutdown handler does not "deactivate" (fActive = false) the client anymore, so that jack_deactivate correctly does the job later on. * Better isolation of server and clients system resources to allow starting the server in several user account at the same time. * Report ringbuffer.c fixes from JACK1. * Client and library global context cleanup in case of incorrect shutdown handling (that is applications not correctly closing client after server has shutdown). * Use JACK_DRIVER_DIR variable in internal clients loader. * For ALSA driver, synchronize with latest JACK1 memops functions. * Synchronize JACK2 public headers with JACK1 ones. * Implement jack_client_real_time_priority and jack_client_max_real_time_priority API. * Use up to BUFFER_SIZE_MAX frames in midi ports, fix for ticket #117. * Cleanup server starting code for clients directly linked with libjackserver.so. * JackMessageBuffer was using thread "Stop" scheme in destructor, now use the safer thread "Kill" way. * Synchronize ALSA backend code with JACK1 one. * Set default mode to 'slow' in JackNetDriver and JackNetAdapter. * Simplify audio packet order verification. * Fix JackNetInterface::SetNetBufferSize for socket buffer size computation and JackNetMasterInterface::DataRecv if synch packet is received, various cleanup. * Better recovery of network overload situations, now "resynchronize" by skipping cycles.". * Support for BIG_ENDIAN machines in NetJack2. * Support for BIG_ENDIAN machines in NetJack2 for MIDI ports. * Support for "-h" option in internal clients to print the parameters. * In NetJack2, fix a bug when capture or playback only channels are used. * Add a JACK_INTERNAL_DIR environment variable to be used for internal clients. * Add a resample quality parameter in audioadapter. * Now correctly return an error if JackServer::SetBufferSize could not change the buffer size (and was just restoring the current one). * Use PRIu32 kind of macro in JackAlsaDriver again. * Add a resample quality parameter in netadapter. * 1.9.0 (2008-03-18) * Waf based build system: Nedko Arnaudov, Grame for preliminary OSX support. * Control API, dbus based server control access: Nedko Arnaudov, Grame. * NetJack2 components (in progress): jack_net backend, netmanager, audioadapter, netadapter : Romain Moret, Grame. * Code restructuring to help port on other architectures: Michael Voigt. * Code cleanup/optimization: Tim Blechmann. * Improve handling of server internal clients that can now be loaded/unloaded using the new server control API: Grame. * A lot of bug fix and improvements. * 0.72 (2008-04-10) * 0.71 (2008-02-14) * Add port register/unregister notification in JackAlsaDriver. * Correct JACK_port_unregister in MIDI backend. * Add TimeCallback in JackDebugClient class. * Correct jack_get_time propotype. * Correct JackSocketClientChannel::ClientClose to use ServerSyncCall instead of ServerAsyncCall. * Better documentation in jack.h. libjackdmp.so renamed to libjackservermp.so and same for OSX framework. * Define an internal jack_client_open_aux needed for library wrapper feature. * Remove unneeded jack_port_connect API. * Correct jack_port_get_connections function (should return NULL when no connections). * In thread model, execute a dummy cycle to be sure thread has the correct properties (ensure thread creation is finished). * Fix engine real-time notification (was broken since ??). * Implements wrapper layer. * Correct jack_port_get_total_latency. * Correct all backend playback port latency in case of "asynchronous" mode (1 buffer more). * Add test for jack_cycle_wait, jack_cycle_wait and jack_set_process_thread API. * RT scheduling for OSX thread (when used in dummy driver). * Add -L (extra output latency in aynchronous mode) in CoreAudio driver. * New JackLockedEngine decorator class to serialize access from ALSA Midi thread, command thread and in-server clients. * Use engine in JackAlsaDriver::port_register and JackAlsaDriver::port_unregister. * Fix connect notification to deliver *one* notification only. * Correct JackClient::Activate so that first kGraphOrderCallback can be received by the client notification thread. * New jack_server_control client to test notifications when linked to the server library. * Synchronise transport.h with latest jackd version (Video handling). * Transport timebase fix. * Dmitry Baikov patch for alsa_rawmidi driver. * Pieter Palmers patch for FFADO driver. * Add an Init method for blocking drivers to be decorated using JackThreadedDriver class. * Correct PortRegister, port name checking must be done on server side. * Correct a missing parameter in the usage message of jack_midiseq. * New SetNonBlocking method for JackSocket. * Correct a dirty port array issue in JackGraphManager::GetPortsAux. * 0.70 (2008-01-24) * Updated API to match jack 0.109.0 version. * Update in usx2y.c and JackPort.cpp to match jackd 0.109.2. * Latest jack_lsp code from jack SVN. * Add jack_mp_thread_wait client example. * Add jack_thread_wait client example. * Remove checking thread in CoreAudio driver, better device state change recovery strategy: the driver is stopped and restarted. * Move transport related methods from JackEngine to JackServer. * Tim Blechmann sse optimization patch for JackaudioPort::MixAudioBuffer, use of Apple Accelerate framework on OSX. * Remove use of assert in JackFifo, JackMachSemaphore, and JackPosixSemaphore: print an error instead. * Correct "server_connect": close the communication channel. * More robust external API. * Use SetAlias for port naming. * Use jackd midi port naming scheme. * Notify ports unregistration in JackEngine::ClientCloseAux. * Fix in JackClient::Error(): when RT thread is failing and calling Shutdown, Shutdown was not desactivating the client correctly. * 0.69 * On OSX, use CFNotificationCenterPostNotificationWithOptions with kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions for server ==> JackRouter plugin notification. * On OSX, use jack server name in notification system. * Correct fPeriodUsecs computation in JackAudioDriver::SetBufferSize and JackAudioDriver::SetSampleRate. * Correct JackMachNotifyChannel::ClientNotify. * Correct bug in CoreAudio driver sample rate management. * Add a sample_rate change listener in CoreAudio driver. * Correct sample_rate management in JackCoreAudioDriver::Open. * Better handling in sample_rate change listener. * Pieter Palmers FFADO driver and scons based build. * Pieter Palmers second new build system: scons and Makefile based build. * Tim Blechmann scons patch. * Change string management for proper compilation with gcc 4.2.2. * JackLog cleanup. * Cleanup in CoreAudio driver. * Tim Blechmann patch for JackGraphManager::GetPortsAux memory leak, Tim Blechmann patch for scons install. * Dmitry Baikov MIDI patch: alsa_seqmidi and alsa_rammidi drivers. * CoreAudio driver improvement: detect and notify abnormal situations (stopped driver in case of SR change...). * 0.68 (2007-10-16) * Internal loadable client implementation, winpipe version added. * Reorganize jack headers. * Improve Linux install/remove scripts. * Use LIB_DIR variable for 64 bits related compilation (drivers location). * More generic Linux script. * Correct jack_acquire_real_time_scheduling on OSX. * Merge of Dmitry Baikov MIDI branch. * Correct JackGraphManager::GetPortsAux to use port type. * Remove JackEngineTiming class: code moved in JackEngineControl. * Add midiseq and midisine examples. * Cleanup old zombification code. * Linux Makefile now install jack headers. * Use of JACK_CLIENT_DEBUG environment variable to activate debug client mode. * Definition of JACK_LOCATION variable using -D in the Makefile. * Restore jack 0.103.0 MIDI API version. * Fix a bug in freewheel management in async mode: drivers now receive the kStartFreewheelCallback and kStopFreewheelCallback notifications. * Server and user directory related code moved in a JackTools file. * Client name rewriting to remove path characters (used in fifo naming). * Correct ALSA driver Attach method: internal driver may have changed the buffer_size and sample_rate values. * Add JackWinSemaphore class. * Add an implementation for obsolete jack_internal_client_new and jack_internal_client_close. * Add missing jack_port_type_size. * Use of JackWinSemaphore instead of JackWinEvent for inter-process synchronization. * Correct types.h for use with MINGW on Windows. * Move OSX start/stop notification mechanism in Jackdmp.cpp. * Correct CheckPort in JackAPI.cpp. * 0.67 (2007-09-28) * Correct jack_client_open "status" management. * Rename server_name from "default" to "jackdmp_default" to avoid conflict with regular jackd server. * Fix a resource leak issue in JackCoreAudioDriver::Close(). * Better implement "jack_client_open" when linking a client with the server library. * Correct "jack_register_server" in shm.c. * Add missing timestamps.c and timestamps.h files. * Correctly export public headers in OSX frameworks. * Suppress JackEngine::ClientInternalCloseIm method. * Use .jackdrc file (instead of .jackdmprc). * Install script now creates a link "jackd ==> jackdmp" so that automatic launch can work correctly. * Paul Davis patch for -r (--replace-registry) feature. * Internal loadable client implementation. * Fix JackEngine::Close() method. * Windows JackRouter.dll version 0.17: 32 integer sample format. * 0.66 (2007-09-06) * Internal cleanup. * Windows JackRouter.dll version 0.16: use of "jack_client_open" API to allow automatic client renaming, better Windows VISTA support, new JackRouter.ini file. * 0.65 (2007-08-30) * Fix backend port alias management (renaming in system:xxx). * Fix a bug in JackLibClient::Open introduced when adding automatic client renaming. * Fix a bug in jack_test. * Correct JackShmMem destructor. * Correct end case in JackClient::Execute. * Correct JackMachSemaphore::Disconnect. * Implement server temporary (-T) mode. * Make "Rename" a method of JackPort class, call it from driver Attach method. * Server/library protocol checking implementation. * 0.64 (2007-07-26) * Checking in the server to avoid calling the clients if no callback are registered. * Correct deprecated jack_set_sample_rate_callback to return 0 instead of -1. * Dmitry Baikov buffer size patch. * Correct notification for kActivateClient event. Correct JackEngine::ClientCloseAux (when called from JackEngine::ClientExternalOpen). * Correct JackWinEvent::Allocate. * Automatic client renaming. * Add "systemic" latencies management in CoreAudio driver. * Automatic server launch. * Removes unneeded 'volatile' for JackTransportEngine::fWriteCounter. * 0.63 (2007-04-05) * Correct back JackAlsaDriver::Read method. * Dmitry Baikov patch for JackGraphManager.cpp. Merge JackGraphManager Remove and Release method in a unique Release method. * Dmitry Baikov jackmp-time patch : add jack_get_time, jack_time_to_frames, jack_frames_to_time. Add missing -D__SMP__in OSX project. Add new jack_port_set_alias, jack_port_unset_alias and jack_port_get_aliases API. * Steven Chamberlain patch to fix jack_port_by_id export. * Steven Chamberlain patch to fix jack_port_type. Test for jack_port_type behaviour in jack_test.cpp tool. Add jack_set_client_registration_callback API. Add "callback exiting" and "jack_frame_time" tests in jack_test. * 0.62 (2007-02-16) * More client debug code: check if the client is still valid in every JackDebugClient method, check if the library context is still valid in every API call. * Uses a time out value of 10 sec in freewheel mode (like jack). * More robust activation/deactivation code, especially in case of client crash. * New LockAllMemory and UnlockAllMemory functions. * Use pthread_attr_setstacksize in JackPosixThread class. * Add Pieter Palmers FreeBob driver. * Thibault LeMeur ALSA driver patch. * Thom Johansen fix for port buffer alignment issues. * Better error checking in PortAudio driver. * 0.61 (2006-12-18) * Tom Szilagyi memory leak fix in ringbuffer.c. * Move client refnum management in JackEngine. * Shared_ports renamed to shared_graph. * Add call to the init callback (set up using the jack_set_thread_init_callback API) in Real-Time and Notification threads. * Define a new 'kActivateClient' notification. * New server/client data transfer model to fix a 64 bits system bug. * Fix a device name reversal bug in ALSA driver. * Implement thread.h API. * 0.60 (2006-11-23) * Improve audio driver synchronous code to better handle possible time-out cases. * Correct JackWinEnvent::Allocate (handle the ERROR_ALREADY_EXISTS case). * Correct JackEngine::ClientExternalNew. * 0.59 (2006-09-22) * Various fixes in Windows version. * Signal handling in the Windows server. * Improved JackRouter ASIO/Jack bridge on Windows. * Rename global "verbose" in "jack_verbose" to avoid symbol clash with PureData. * Add a new cpu testing/loading client. * Correct server SetBufferSize in case of failure. * Correct PortAudio driver help. * Use -D to setup ADDON_DIR on OSX and Linux. * Synchronize ALSA backend with jack one. * 0.58 (2006-09-06) * Correct a bug introduced in 0.55 version that was preventing coreaudio audio inputs to work. * Restructured code structure after import on svn. * 0.57 * Correct bug in Mutex code in JackClientPipeThread::HandleRequest. * ASIO JackRouter driver supports more applications. * Updated HTML documentation. * Windows dll binaries are compiled in "release" mode. * 0.56 * Correct SetBufferSize in coreaudio driver, portaudio driver and JackServer. * Real-time notifications for Windows version. * In the PortAudio backend, display more informations for installed WinMME, * DirectSound and ASIO drivers. * 0.55 * Windows version. * Correct management of monitor ports in ALSA driver. * Engine code cleanup. * Apply Rui patch for more consistent parameter naming in coreaudio driver. * Correct JackProcessSync::TimedWait: time-out was not computed correctly. * Check the return code of NotifyAddClient in JackEngine. * 0.54 * Use the latest shm implementation that solve the uncleaned shm segment problem on OSX. * Close still opened file descriptors (report from Giso Grimm). Updated html documentation. * 0.53 * Correct JackPilotMP tool on OSX. * Correct CoreAudio driver for half duplex cases. * Fix a bug in transport for "unactivated" clients. * Fix a bug when removing "unactivated" clients from the server. Tested on Linux/PPC. * 0.52 * Universal version for Mac Intel and PPC. * Improvement of CoreAudio driver for half duplex cases. * 0.51 * Correct bugs in transport API implementation. * 0.50 * Transport API implementation. * 0.49 * Internal connection manager code cleanup. * 0.48 * Finish software monitoring implementation for ALSA and CoreAudio drivers. * Simpler shared library management on OSX. * 0.47 * More fix for 64 bits compilation. * Correct ALSA driver. * Create a specific folder for jackdmp drivers. * Use /dev/shm as default for fifo and sockets. * "Install" and "Remove" script for smoother use with regular jack. * 0.46 * Fix a bug in loop management. * Fix a bug in driver loading/unloading code. * Internal code cleanup for better 64 bits architecture support. * Compilation on OSX/Intel. * Add the -d option for coreaudio driver (display CoreAudio devices internal name). * 0.45 * Script to remove the OSX binary stuff. * Correct an export symbol issue that was preventing QjackCtl to work on OSX. * Fix the consequences of the asynchronous semantic of connections/disconnections. * 0.44 * Patch from Dmitry Daikov: use clock_gettime by default for timing. * Correct dirty buffer issue in CoreAudio driver. Updated doc. * 0.43 * Correct freewheel mode. * Optimize ALSA and coreaudio drivers. * Correct OSX installation script. * 0.42 * Patch from Nick Mainsbridge. * Correct default mode for ALSA driver. * Correct XCode project. * 0.41 * Add the ALSA MMAP_COMPLEX support for ALSA driver. * Patch from Dmitry Daikov: compilation option to choose between "get_cycles" and "gettimeofday" to measure timing. * 0.4 * Linux version, code cleanup, new -L parameter to activate the loopback driver (see Documentation), a number of loopback ports can be defined. Client validation tool. * 0.31 * Correct bug in mixing code that caused Ardour + jackdmp to crash... * 0.3 * Implement client zombification + correct feedback loop management + code cleanup. * 0.2 * Implements jack_time_frame, new -S (sync) mode: when "synch" mode is activated, the jackdmp server waits for the graph to be finished in the current cycle before writing the output buffers. Note: To experiment with the -S option, jackdmp must be launched in a console. * 0.1 * First published version jack2-1.9.22/README.rst000066400000000000000000000041741436671425200143060ustar00rootroot00000000000000`JACK2 `_ ################################ .. image:: https://travis-ci.org/jackaudio/jack2.svg?branch=master :target: https://travis-ci.org/jackaudio/jack2 .. image:: https://repology.org/badge/tiny-repos/jack-audio-connection-kit.svg :target: https://repology.org/metapackage/jack-audio-connection-kit/versions JACK2 aka jackdmp is a C++ version of the JACK low-latency audio server for multi-processor machines. It is a new implementation of the JACK server core features that aims at removing some limitations of the JACK1 design. The activation system has been changed for a data flow model and lock-free programming techniques for graph access have been used to have a more dynamic and robust system. - uses a new client activation model, that allows simultaneous client execution (on a SMP machine) when parallel clients exist in the graph (client that have the same inputs). This activation model allows to better use available CPU on a smp machine, but also works on mono-processor machine. - uses a lock-free way to access (read/write) the client graph, thus allowing connections/disconnection to be done without interrupting the audio stream. The result is that connections/disconnections are glitch-free. - can work in two different modes at the server level: - *synchronous activation*: in a given cycle, the server waits for all clients to be finished (similar to normal jackd) - *asynchronous activation*: in a given cycle, the server does not wait for all clients to be finished and use output buffer computed the previous cycle. The audible result of this mode is that if a client is not activated during one cycle, other clients may still run and the resulting audio stream will still be produced (even if its partial in some way). This mode usually result in fewer (less audible) audio glitches in a loaded system. For further information, see the JACK `homepage `_ and `wiki `_. There are also the #jack and #lad chat channels on `Libera Chat IRC `_. jack2-1.9.22/README_NETJACK2000066400000000000000000000242041436671425200147140ustar00rootroot00000000000000------------------------------- NetJack2 for Jack2 ------------------------------- This release includes a version of netjack designed for jack2. Indeed, the original concept has been completely redesigned to better fit to the Jack2 architecture, but also in order to provide additional capabilities, and ultimately a greater robustness. This document describes the major changes between those two systems, then a simple how-to for setting up a basic usage of 'netjack2'. ------------------------------- Major changes and architecture ------------------------------- The biggest difference between netjack1 and netjack2 is the way of slicing audio and midi streams into network packets. For one audio cycle, netjack1 used to take all audio and midi buffers (one per channel), put butt all of them, then send it over the network. The problem is that a network packet has a fixed maximum size, depending on the network infrastructure (for 100mb, it reaches 1500bytes - MTU of the network). The solution is then to slice those buffers into smaller ones, and then send as many packets as we need. This cutting up can be done by network equipment, but it's more efficient and secure to include it in the software data management. Still this slicing brings another issue : all the packets are not pleased with any emission order and are unfortunately received in a random order, thanks to UDP. So we can't deal with data as it comes, we need to re-bufferize incoming streams in order to rebuild complete audio buffers. In netjack2, the main idea is to make this slicing depending on the network capabilities. If we can put only 128 complete audio frames (128 samples for all audio channels) in a network packet, the elementary packet will so carry 128 frames, and in one cycle, we will transmit as many packet as we need. We take the example of 128 frames because it's the current value for 2 channels. This value is determined by taking the maximum 'power of 2' frames we can put in a packet. If we take 2 channels, 4 bytes per sample (float values), we get 8 bytes per frame, with 128 frames, we now have 1024 bytes, so we can put these 1024 bytes in one packet, and add a small header which identify the packet. This technique allows to separate the packets (in time) so they can be received in the order they have been emitted. If the master is running at 512 frames per second, four audio packets are sent per cycle and the slave deals with them as they arrive. With gigabytes networks, the MTU is larger, so we can put more data in one packet (in this example, we can even put the complete cycle in one packet). For midi data, netjack1 used to send the whole buffer, in this example, 512 frames * 4 bytes per sample and per midi port. Those 2048 bytes are in 99% of the time filled to a few bytes, but rarely more. This means that if we have 2 audio and 2 midi channels to transmit, everything happens as if we had 4 audio channels, which is quite a waste of bandwidth. In netjack2, the idea is to take into account that fact, by sending only the useful bytes, and not more. It's completely inappropriate to overload the network with useless data. So we now have : 99% of the time one midi packet (of a few dozen of bytes), followed by four audio packets (in this example). This way of separating audio and midi is quite important. We deal here with network transmissions, and also need to be 'realtime'. We need a system which allow to carry as many audio and midi data streams as we need and can, as if the distant computer was in fact a simple jack client. With all those constraints, we can't avoid packets loss. The better thing to do is to deal with it. But to loose an audio packet is different from skipping a midi one. Indeed, an audio loss leads to audio click, or undesirable, but very short side effect. Whereas a midi data loss can be completely disastrous. Imagine that we play some notes, with sustain, and we loose the sustain 0 value, which stops the effect. The sustain keeps going on on all following notes until the next 'sustain off' event. A simple missing byte can put all the midi system offside (that's the purpose of all the big PANIC buttons on midi software...). That's why we need to separate audio (more than one per cycle) from midi (one packet at 99% of the time). If we loose an audio packet, we probably still have an available midi packet, so we can use what we received, even if some audio is missing. Those audio and midi packets are preceded by a synchronization packet, which will make the slave directly synchronized on the master's cycle rhythm. This packet also carries transport data. Thus it's actually possible to synchronize also transport. This feature goes a little further than in netjack1. The idea here is to make every computer of the network fully synchronized on the master's transport. That means the master needs to know the state of every slow sync clients of each of its slaves. The master can now manage the transport state (especially the 'rolling' state) of each slave thus the main transport waits for the last slow sync client before turning 'rolling'. By doing this, the transport can start (roll) in the same cycle for every computers managed by the master. The second main difference between netjack1 and netjack2 is the way the two computers (master and slave) synchronize their parameters and launch. In netjack1, once the slave configured (by the command line) and launched, it was waiting for the first incoming packet to synchronize (launch its first audio cycle) then run. The two computers needed to be configured separately but with the same parameters to run correctly. In netjack2, the only thing you have to set for the slave is its number of in/out midi and audio channels. No more need to choose and set parameters depending on the master, they are automatically determined and communicated to the slave. This first synchronization step uses a multicast communication, no more need to know by advance all the IP addresses. The slave says on a multicast address "hey, I'm available". A master get the message, and communicate parameterers to the slave. Once synchronization done, data transfers can start. Moreover, the master being still listening on the multicast address, it can catch other slaves and manage them (create a jack client to communicate with the slave, and neatly close everything when the slave is gone). The loaded internal client is no longer only an interface for the slave, like in netjack1. It's now called 'network manager', it doesn't deal with audio or midi, just with some kind of 'network logistical messages'. The manager automatically create a new internal client as soon as a new slave is seen on the network (by sending messages on the multicast address the manager is listening on). This manager is also able to remove one of its internal client as soon as a slave has left the network. This conception allow a complete separation of audio exchanges from parameterers and management. The 'unloading' of the internal client (the manager) will cause a full cleaning of the infrastructure. The jack clients are all removed from the server, the slave are all turned available again, ready to be caught by another master etc. When a slave quits, it's also automatically removed from the manager's slaves list. ------------------------------- How-to use this ? ------------------------------- Netjackmp is very simple to use. On the master's side, an internal client deals with the slaves, and the slaves themselves are classical jack servers running under a 'network audio driver'. The difference between the two versions is that the master now has a manager, which takes care of the slaves, while listening on the multicast address and create a new master as soon as a slave is available. But everything is transparent to the user, that's why it uses multicast (someone says "hello", and anyone who wants to hear it just has to listen). So, just compile and install Jack2 as you are used to, on linux, using './waf configure', './waf' and './waf install' as root. On macosx, you can use the xcode project. On Windows, you can use the Code::Blocks workspace (you also have a small script to make an all in one installer). On the master, just launch a classical jack server, the period size doesn't matter. Then, load the network manager using jack_load : 'jack_load netmanager' This will load the internal client, which will wait for an available slave (see the message window on QjackCtl - or the console output). If you want to listen to a specific multicast socket, you can add some options. To specify a complete command line, you can use : 'jack_load netmanager -i"-a xxx.xxx.xxx.xxx -p udp_port"' If you set another multicast address or port, you have to set the same on the slave's side. The default value should be working in many cases. On the slave, just launch a new jack server using : 'jackd -R -d net' As in a standard backend in Jack2, you can use '-S' (synchronous mode). The asynchronous mode (without '-S') allows to send the computed data during the next cycle. In synchronous mode, data are sent back at the end of the cycle, that means after the process. You can specify some options, like '-n name' (will give a name to the slave, default is the network hostname), '-C input_ports' (the number of master-->slave channels), '-P output_ports' (the number of slave-->master channels), default is 2 ; or '-i midi_in_ports' and '-o midi_out_ports', default is 0. If you set multicast address or port on the master, you can add '-a xxx.xxx.xxx.xxx' and '-p udp_port'. Latency (-n) is the number of buffers added in network transmission. Zero is for cases when the audio buffers can be sent to the other size, transformed by the process and returned in the same cycle. By default latency is 5 buffers. For additional information, you can go to the NetJack2 Wiki at : http://trac.jackaudio.org/wiki/WalkThrough/User/NetJack2. ------------------------------- What's next ? ------------------------------- The development of netjack2 continues and some things are always moving... If you use it, please report encountered bugs, ideas or anything you think about. If you have any question, you can subscribe the jackaudio developers mailing list at http://www.jackaudio.org/ or join the IRC channel '#jack' on FreeNode. jack2-1.9.22/android/000077500000000000000000000000001436671425200142315ustar00rootroot00000000000000jack2-1.9.22/android/Android.mk000066400000000000000000001462751436671425200161610ustar00rootroot00000000000000# # jack-1.9.10 # LOCAL_PATH := $(call my-dir) JACK_ROOT := $(call my-dir)/.. SUPPORT_ALSA_IN_JACK := true SUPPORT_ANDROID_REALTIME_SCHED := false ifeq ($(TARGET_BOARD_PLATFORM),mrvl) ALSA_INCLUDES := vendor/marvell/external/alsa-lib/include else ALSA_INCLUDES := vendor/samsung/common/external/alsa-lib/include endif JACK_STL_LDFLAGS := -Lprebuilts/ndk/current/sources/cxx-stl/gnu-libstdc++/libs/$(TARGET_CPU_ABI) -lgnustl_static JACK_STL_INCLUDES := $(JACK_ROOT)/android/cxx-stl/gnu-libstdc++/libs/$(TARGET_CPU_ABI)/include \ prebuilts/ndk/current/sources/cxx-stl/gnu-libstdc++/libs/$(TARGET_CPU_ABI)/include \ prebuilts/ndk/current/sources/cxx-stl/gnu-libstdc++/include ########################################################## # common ########################################################## common_cflags := -O0 -g -Wall -fexceptions -fvisibility=hidden -DHAVE_CONFIG_H common_cflags += -Wno-unused -Wno-sign-compare -Wno-deprecated-declarations -Wno-cpp common_cppflags := -frtti -Wno-sign-promo -fcheck-new common_shm_cflags := -O0 -g -Wall -fexceptions -DHAVE_CONFIG_H -Wno-unused ifeq ($(TARGET_BOARD_PLATFORM),clovertrail) common_ldflags := -ldl else common_ldflags := endif common_c_includes := \ $(JACK_ROOT) \ $(JACK_ROOT)/common \ $(JACK_ROOT)/common/jack \ $(JACK_ROOT)/android \ $(JACK_ROOT)/linux \ $(JACK_ROOT)/linux/alsa \ $(JACK_ROOT)/posix \ $(JACK_STL_INCLUDES) # copy common source file common_libsource_server_dir = .server common_libsource_client_dir = .client $(shell rm -rf $(LOCAL_PATH)/$(common_libsource_server_dir)) $(shell rm -rf $(LOCAL_PATH)/$(common_libsource_client_dir)) $(shell mkdir $(LOCAL_PATH)/$(common_libsource_server_dir)) $(shell mkdir $(LOCAL_PATH)/$(common_libsource_client_dir)) $(shell cp -f $(LOCAL_PATH)/../common/JackActivationCount.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackActivationCount.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackAPI.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackAPI.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackClient.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackClient.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackConnectionManager.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackConnectionManager.cpp) $(shell cp -f $(LOCAL_PATH)/../common/ringbuffer.c $(LOCAL_PATH)/$(common_libsource_server_dir)/ringbuffer.c) $(shell cp -f $(LOCAL_PATH)/JackError.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackError.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackException.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackException.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackFrameTimer.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackFrameTimer.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackGraphManager.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackGraphManager.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackPort.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackPort.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackPortType.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackPortType.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackAudioPort.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackAudioPort.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackMidiPort.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackMidiPort.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackMidiAPI.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackMidiAPI.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackEngineControl.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackEngineControl.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackShmMem.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackShmMem.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackGenericClientChannel.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackGenericClientChannel.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackGlobals.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackGlobals.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackDebugClient.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackDebugClient.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackTransportEngine.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackTransportEngine.cpp) $(shell cp -f $(LOCAL_PATH)/../common/timestamps.c $(LOCAL_PATH)/$(common_libsource_server_dir)/timestamps.c) $(shell cp -f $(LOCAL_PATH)/../common/JackTools.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackTools.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackMessageBuffer.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackMessageBuffer.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackEngineProfiling.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackEngineProfiling.cpp) $(shell cp -f $(LOCAL_PATH)/JackAndroidThread.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackAndroidThread.cpp) $(shell cp -f $(LOCAL_PATH)/JackAndroidSemaphore.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackAndroidSemaphore.cpp) $(shell cp -f $(LOCAL_PATH)/../posix/JackPosixProcessSync.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackPosixProcessSync.cpp) $(shell cp -f $(LOCAL_PATH)/../posix/JackPosixMutex.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackPosixMutex.cpp) $(shell cp -f $(LOCAL_PATH)/../posix/JackSocket.cpp $(LOCAL_PATH)/$(common_libsource_server_dir)/JackSocket.cpp) $(shell cp -f $(LOCAL_PATH)/../linux/JackLinuxTime.c $(LOCAL_PATH)/$(common_libsource_server_dir)/JackLinuxTime.c) $(shell cp -f $(LOCAL_PATH)/../common/JackActivationCount.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackActivationCount.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackAPI.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackAPI.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackClient.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackClient.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackConnectionManager.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackConnectionManager.cpp) $(shell cp -f $(LOCAL_PATH)/../common/ringbuffer.c $(LOCAL_PATH)/$(common_libsource_client_dir)/ringbuffer.c) $(shell cp -f $(LOCAL_PATH)/JackError.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackError.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackException.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackException.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackFrameTimer.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackFrameTimer.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackGraphManager.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackGraphManager.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackPort.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackPort.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackPortType.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackPortType.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackAudioPort.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackAudioPort.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackMidiPort.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackMidiPort.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackMidiAPI.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackMidiAPI.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackEngineControl.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackEngineControl.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackShmMem.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackShmMem.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackGenericClientChannel.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackGenericClientChannel.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackGlobals.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackGlobals.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackDebugClient.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackDebugClient.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackTransportEngine.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackTransportEngine.cpp) $(shell cp -f $(LOCAL_PATH)/../common/timestamps.c $(LOCAL_PATH)/$(common_libsource_client_dir)/timestamps.c) $(shell cp -f $(LOCAL_PATH)/../common/JackTools.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackTools.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackMessageBuffer.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackMessageBuffer.cpp) $(shell cp -f $(LOCAL_PATH)/../common/JackEngineProfiling.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackEngineProfiling.cpp) $(shell cp -f $(LOCAL_PATH)/JackAndroidThread.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackAndroidThread.cpp) $(shell cp -f $(LOCAL_PATH)/JackAndroidSemaphore.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackAndroidSemaphore.cpp) $(shell cp -f $(LOCAL_PATH)/../posix/JackPosixProcessSync.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackPosixProcessSync.cpp) $(shell cp -f $(LOCAL_PATH)/../posix/JackPosixMutex.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackPosixMutex.cpp) $(shell cp -f $(LOCAL_PATH)/../posix/JackSocket.cpp $(LOCAL_PATH)/$(common_libsource_client_dir)/JackSocket.cpp) $(shell cp -f $(LOCAL_PATH)/../linux/JackLinuxTime.c $(LOCAL_PATH)/$(common_libsource_client_dir)/JackLinuxTime.c) common_libsource_server := \ $(common_libsource_server_dir)/JackActivationCount.cpp \ $(common_libsource_server_dir)/JackAPI.cpp \ $(common_libsource_server_dir)/JackClient.cpp \ $(common_libsource_server_dir)/JackConnectionManager.cpp \ $(common_libsource_server_dir)/ringbuffer.c \ $(common_libsource_server_dir)/JackError.cpp \ $(common_libsource_server_dir)/JackException.cpp \ $(common_libsource_server_dir)/JackFrameTimer.cpp \ $(common_libsource_server_dir)/JackGraphManager.cpp \ $(common_libsource_server_dir)/JackPort.cpp \ $(common_libsource_server_dir)/JackPortType.cpp \ $(common_libsource_server_dir)/JackAudioPort.cpp \ $(common_libsource_server_dir)/JackMidiPort.cpp \ $(common_libsource_server_dir)/JackMidiAPI.cpp \ $(common_libsource_server_dir)/JackEngineControl.cpp \ $(common_libsource_server_dir)/JackShmMem.cpp \ $(common_libsource_server_dir)/JackGenericClientChannel.cpp \ $(common_libsource_server_dir)/JackGlobals.cpp \ $(common_libsource_server_dir)/JackDebugClient.cpp \ $(common_libsource_server_dir)/JackTransportEngine.cpp \ $(common_libsource_server_dir)/timestamps.c \ $(common_libsource_server_dir)/JackTools.cpp \ $(common_libsource_server_dir)/JackMessageBuffer.cpp \ $(common_libsource_server_dir)/JackEngineProfiling.cpp \ $(common_libsource_server_dir)/JackAndroidThread.cpp \ $(common_libsource_server_dir)/JackAndroidSemaphore.cpp \ $(common_libsource_server_dir)/JackPosixProcessSync.cpp \ $(common_libsource_server_dir)/JackPosixMutex.cpp \ $(common_libsource_server_dir)/JackSocket.cpp \ $(common_libsource_server_dir)/JackLinuxTime.c common_libsource_client := \ $(common_libsource_client_dir)/JackActivationCount.cpp \ $(common_libsource_client_dir)/JackAPI.cpp \ $(common_libsource_client_dir)/JackClient.cpp \ $(common_libsource_client_dir)/JackConnectionManager.cpp \ $(common_libsource_client_dir)/ringbuffer.c \ $(common_libsource_client_dir)/JackError.cpp \ $(common_libsource_client_dir)/JackException.cpp \ $(common_libsource_client_dir)/JackFrameTimer.cpp \ $(common_libsource_client_dir)/JackGraphManager.cpp \ $(common_libsource_client_dir)/JackPort.cpp \ $(common_libsource_client_dir)/JackPortType.cpp \ $(common_libsource_client_dir)/JackAudioPort.cpp \ $(common_libsource_client_dir)/JackMidiPort.cpp \ $(common_libsource_client_dir)/JackMidiAPI.cpp \ $(common_libsource_client_dir)/JackEngineControl.cpp \ $(common_libsource_client_dir)/JackShmMem.cpp \ $(common_libsource_client_dir)/JackGenericClientChannel.cpp \ $(common_libsource_client_dir)/JackGlobals.cpp \ $(common_libsource_client_dir)/JackDebugClient.cpp \ $(common_libsource_client_dir)/JackTransportEngine.cpp \ $(common_libsource_client_dir)/timestamps.c \ $(common_libsource_client_dir)/JackTools.cpp \ $(common_libsource_client_dir)/JackMessageBuffer.cpp \ $(common_libsource_client_dir)/JackEngineProfiling.cpp \ $(common_libsource_client_dir)/JackAndroidThread.cpp \ $(common_libsource_client_dir)/JackAndroidSemaphore.cpp \ $(common_libsource_client_dir)/JackPosixProcessSync.cpp \ $(common_libsource_client_dir)/JackPosixMutex.cpp \ $(common_libsource_client_dir)/JackSocket.cpp \ $(common_libsource_client_dir)/JackLinuxTime.c server_libsource := \ ../common/JackAudioDriver.cpp \ ../common/JackTimedDriver.cpp \ ../common/JackMidiDriver.cpp \ ../common/JackDriver.cpp \ ../common/JackEngine.cpp \ ../common/JackExternalClient.cpp \ ../common/JackFreewheelDriver.cpp \ ../common/JackInternalClient.cpp \ ../common/JackServer.cpp \ ../common/JackThreadedDriver.cpp \ ../common/JackRestartThreadedDriver.cpp \ ../common/JackWaitThreadedDriver.cpp \ ../common/JackServerAPI.cpp \ ../common/JackDriverLoader.cpp \ ../common/JackServerGlobals.cpp \ ../common/JackControlAPI.cpp \ JackControlAPIAndroid.cpp \ ../common/JackNetTool.cpp \ ../common/JackNetInterface.cpp \ ../common/JackArgParser.cpp \ ../common/JackRequestDecoder.cpp \ ../common/JackMidiAsyncQueue.cpp \ ../common/JackMidiAsyncWaitQueue.cpp \ ../common/JackMidiBufferReadQueue.cpp \ ../common/JackMidiBufferWriteQueue.cpp \ ../common/JackMidiRawInputWriteQueue.cpp \ ../common/JackMidiRawOutputWriteQueue.cpp \ ../common/JackMidiReadQueue.cpp \ ../common/JackMidiReceiveQueue.cpp \ ../common/JackMidiSendQueue.cpp \ ../common/JackMidiUtil.cpp \ ../common/JackMidiWriteQueue.cpp \ ../posix/JackSocketServerChannel.cpp \ ../posix/JackSocketNotifyChannel.cpp \ ../posix/JackSocketServerNotifyChannel.cpp \ ../posix/JackNetUnixSocket.cpp net_libsource := \ ../common/JackNetAPI.cpp \ ../common/JackNetInterface.cpp \ ../common/JackNetTool.cpp \ ../common/JackException.cpp \ ../common/JackAudioAdapterInterface.cpp \ ../common/JackLibSampleRateResampler.cpp \ ../common/JackResampler.cpp \ ../common/JackGlobals.cpp \ ../posix/JackPosixMutex.cpp \ ../common/ringbuffer.c \ ../posix/JackNetUnixSocket.cpp \ $(common_libsource_server_dir)/JackAndroidThread.cpp \ ../linux/JackLinuxTime.c client_libsource := \ ../common/JackLibClient.cpp \ ../common/JackLibAPI.cpp \ ../posix/JackSocketClientChannel.cpp \ ../posix/JackPosixServerLaunch.cpp netadapter_libsource := \ ../common/JackResampler.cpp \ ../common/JackLibSampleRateResampler.cpp \ ../common/JackAudioAdapter.cpp \ ../common/JackAudioAdapterInterface.cpp \ ../common/JackNetAdapter.cpp audioadapter_libsource := \ ../common/JackResampler.cpp \ ../common/JackLibSampleRateResampler.cpp \ ../common/JackAudioAdapter.cpp \ ../common/JackAudioAdapterInterface.cpp \ ../common/JackAudioAdapterFactory.cpp \ ../linux/alsa/JackAlsaAdapter.cpp ifeq ($(SUPPORT_ANDROID_REALTIME_SCHED), true) sched_c_include := bionic/libc/bionic \ frameworks/av/services/audioflinger endif # ======================================================== # libjackserver.so # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := $(common_libsource_server) $(server_libsource) LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libutils libjackshm LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := libjackserver ifeq ($(SUPPORT_ANDROID_REALTIME_SCHED), true) LOCAL_CFLAGS += -DJACK_ANDROID_REALTIME_SCHED LOCAL_C_INCLUDES += $(sched_c_include) LOCAL_SHARED_LIBRARIES += libbinder LOCAL_STATIC_LIBRARIES := libscheduling_policy endif include $(BUILD_SHARED_LIBRARY) ## ======================================================== ## libjacknet.so ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := $(net_libsource) #LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE #LOCAL_CPPFLAGS := $(common_cppflags) #LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsamplerate/include #LOCAL_SHARED_LIBRARIES := libc libdl libcutils libsamplerate #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := libjacknet # #include $(BUILD_SHARED_LIBRARY) # ======================================================== # libjack.so # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := $(common_libsource_client) $(client_libsource) LOCAL_CFLAGS := $(common_cflags) LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libutils libjackshm LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := libjack ifeq ($(SUPPORT_ANDROID_REALTIME_SCHED), true) LOCAL_CFLAGS += -DJACK_ANDROID_REALTIME_SCHED LOCAL_C_INCLUDES += $(sched_c_include) LOCAL_SHARED_LIBRARIES += libbinder LOCAL_STATIC_LIBRARIES := libscheduling_policy endif include $(BUILD_SHARED_LIBRARY) # ======================================================== # netmanager.so # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../common/JackNetManager.cpp LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := netmanager include $(BUILD_SHARED_LIBRARY) # ======================================================== # profiler.so # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../common/JackProfiler.cpp LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := profiler include $(BUILD_SHARED_LIBRARY) ## ======================================================== ## netadapter.so ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := $(netadapter_libsource) #LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE #LOCAL_CPPFLAGS := $(common_cppflags) #LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsamplerate/include #LOCAL_SHARED_LIBRARIES := libc libdl libcutils libsamplerate libjackserver #LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := netadapter # #include $(BUILD_SHARED_LIBRARY) ## ======================================================== ## audioadapter.so ## ======================================================== #ifeq ($(SUPPORT_ALSA_IN_JACK),true) #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := $(audioadapter_libsource) #LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE -D_POSIX_SOURCE #LOCAL_CPPFLAGS := $(common_cppflags) #LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsamplerate/include $(ALSA_INCLUDES) #LOCAL_SHARED_LIBRARIES := libc libdl libcutils libasound libsamplerate libjackserver #LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := audioadapter # #include $(BUILD_SHARED_LIBRARY) ##endif # ======================================================== # in.so - sapaproxy internal client # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := JackSapaProxy.cpp JackSapaProxyIn.cpp LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := in include $(BUILD_SHARED_LIBRARY) # ======================================================== # out.so - sapaproxy internal client # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := JackSapaProxy.cpp JackSapaProxyOut.cpp LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := out include $(BUILD_SHARED_LIBRARY) ########################################################## # linux ########################################################## # ======================================================== # jackd # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := \ ../common/Jackdmp.cpp # ../dbus/reserve.c # ../dbus/audio_reserve.c LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(JACK_STL_LDFLAGS) -ldl -Wl,--no-fatal-warnings LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libutils libjackserver LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jackd include $(BUILD_EXECUTABLE) # ======================================================== # driver - dummy # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../common/JackDummyDriver.cpp #'HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_dummy include $(BUILD_SHARED_LIBRARY) # ======================================================== # driver - alsa # ======================================================== ifeq ($(SUPPORT_ALSA_IN_JACK),true) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ ../linux/alsa/JackAlsaDriver.cpp \ ../linux/alsa/alsa_midi_jackmp.cpp \ ../common/memops.c \ ../linux/alsa/generic_hw.c \ ../linux/alsa/hdsp.c \ ../linux/alsa/alsa_driver.c \ ../linux/alsa/hammerfall.c \ ../linux/alsa/ice1712.c # ../linux/alsa/alsa_rawmidi.c # ../linux/alsa/alsa_seqmidi.c #'HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE -D_POSIX_SOURCE -D_XOPEN_SOURCE=600 LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) $(ALSA_INCLUDES) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver libasound LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_alsa include $(BUILD_SHARED_LIBRARY) endif ## ======================================================== ## driver - alsarawmidi ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := \ # ../linux/alsarawmidi/JackALSARawMidiDriver.cpp \ # ../linux/alsarawmidi/JackALSARawMidiInputPort.cpp \ # ../linux/alsarawmidi/JackALSARawMidiOutputPort.cpp \ # ../linux/alsarawmidi/JackALSARawMidiPort.cpp \ # ../linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp \ # ../linux/alsarawmidi/JackALSARawMidiSendQueue.cpp \ # ../linux/alsarawmidi/JackALSARawMidiUtil.cpp ##'HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD #LOCAL_CFLAGS := $(common_cflags) -D_POSIX_SOURCE -D__ALSA_RAWMIDI_H #LOCAL_CPPFLAGS := $(common_cppflags) #LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) #LOCAL_C_INCLUDES := $(common_c_includes) $(ALSA_INCLUDES) #LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver libasound #LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := jack_alsarawmidi # #include $(BUILD_SHARED_LIBRARY) ## LIBFFADO required ## ======================================================== ## driver - firewire ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := \ # ../linux/firewire/JackFFADODriver.cpp \ # ../linux/firewire/JackFFADOMidiInputPort.cpp \ # ../linux/firewire/JackFFADOMidiOutputPort.cpp \ # ../linux/firewire/JackFFADOMidiReceiveQueue.cpp \ # ../linux/firewire/JackFFADOMidiSendQueue.cpp ##'HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD #LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE #LOCAL_CPPFLAGS := $(common_cppflags) #LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) #LOCAL_C_INCLUDES := $(common_c_includes) #LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver #LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := jack_firewire # #include $(BUILD_SHARED_LIBRARY) # ======================================================== # driver - net # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../common/JackNetDriver.cpp #'HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_net include $(BUILD_SHARED_LIBRARY) # ======================================================== # driver - loopback # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../common/JackLoopbackDriver.cpp #'HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_loopback include $(BUILD_SHARED_LIBRARY) ##HAVE_SAMPLERATE, HAVE_CELT required ## ======================================================== ## driver - netone ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := \ # ../common/JackNetOneDriver.cpp \ # ../common/netjack.c \ # ../common/netjack_packet.c ##'HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD #LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE #LOCAL_CPPFLAGS := $(common_cppflags) #LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsamplerate/include #LOCAL_SHARED_LIBRARIES := libc libdl libcutils libsamplerate libjackserver #LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := jack_netone # #include $(BUILD_SHARED_LIBRARY) ########################################################## # android ########################################################## # ======================================================== # libjackshm.so # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := BnAndroidShm.cpp BpAndroidShm.cpp IAndroidShm.cpp AndroidShm.cpp Shm.cpp LOCAL_CFLAGS := $(common_shm_cflags) -DSERVER_SIDE LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libutils libbinder LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := libjackshm include $(BUILD_SHARED_LIBRARY) # ======================================================== # jack_goldfish.so - Goldfish driver for emulator # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := JackGoldfishDriver.cpp LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_goldfish include $(BUILD_SHARED_LIBRARY) # ======================================================== # jack_opensles.so - OpenSL ES driver # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := JackOpenSLESDriver.cpp opensl_io.c LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) frameworks/wilhelm/include LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver libOpenSLES LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_opensles include $(BUILD_SHARED_LIBRARY) ########################################################## # android/AndroidShmServer ########################################################## # ======================================================== # androidshmservice # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ./AndroidShmServer/main_androidshmservice.cpp LOCAL_CFLAGS := $(common_cflags) LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libcutils libutils libbinder libjackshm LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE:= androidshmservice include $(BUILD_EXECUTABLE) # ======================================================== # shmservicetest # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ./AndroidShmServer/test/shmservicetest.cpp LOCAL_CFLAGS := $(common_cflags) -DLOG_TAG=\"ShmServiceTest\" LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libcutils libutils libjackshm libbinder LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := shmservicetest include $(BUILD_EXECUTABLE) # ======================================================== # shmservicedump # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ./AndroidShmServer/test/shmservicedump.cpp LOCAL_CFLAGS := $(common_cflags) -DLOG_TAG=\"ShmServiceDump\" LOCAL_CPPFLAGS := $(common_cppflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_SHARED_LIBRARIES := libcutils libutils libjackshm libbinder LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := shmservicedump include $(BUILD_EXECUTABLE) ########################################################## # example-clients ########################################################## # ======================================================== # jack_freewheel # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/freewheel.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_freewheel include $(BUILD_EXECUTABLE) # ======================================================== # jack_connect # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/connect.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_connect include $(BUILD_EXECUTABLE) # ======================================================== # jack_disconnect # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/connect.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_disconnect include $(BUILD_EXECUTABLE) # ======================================================== # jack_lsp # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/lsp.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_lsp include $(BUILD_EXECUTABLE) # ======================================================== # jack_metro # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/metro.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_metro include $(BUILD_EXECUTABLE) # ======================================================== # jack_midiseq # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/midiseq.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_midiseq include $(BUILD_EXECUTABLE) # ======================================================== # jack_midisine # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/midisine.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_midisine include $(BUILD_EXECUTABLE) # ======================================================== # jack_showtime # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/showtime.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_showtime include $(BUILD_EXECUTABLE) # ======================================================== # jack_simple_client # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/simple_client.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_simple_client include $(BUILD_EXECUTABLE) # ======================================================== # jack_zombie # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/zombie.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_zombie include $(BUILD_EXECUTABLE) # ======================================================== # jack_load # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/ipload.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_load include $(BUILD_EXECUTABLE) # ======================================================== # jack_unload # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/ipunload.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_unload include $(BUILD_EXECUTABLE) # ======================================================== # jack_alias # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/alias.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_alias include $(BUILD_EXECUTABLE) # ======================================================== # jack_bufsize # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/bufsize.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_bufsize include $(BUILD_EXECUTABLE) # ======================================================== # jack_wait # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/wait.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_wait include $(BUILD_EXECUTABLE) # ======================================================== # jack_samplerate # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/samplerate.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_samplerate include $(BUILD_EXECUTABLE) # ======================================================== # jack_evmon # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/evmon.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_evmon include $(BUILD_EXECUTABLE) # ======================================================== # jack_monitor_client # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/monitor_client.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_monitor_client include $(BUILD_EXECUTABLE) # ======================================================== # jack_thru # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/thru_client.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_thru include $(BUILD_EXECUTABLE) # ======================================================== # jack_cpu_load # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/cpu_load.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_cpu_load include $(BUILD_EXECUTABLE) # ======================================================== # jack_simple_session_client # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/simple_session_client.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_simple_session_client include $(BUILD_EXECUTABLE) # ======================================================== # jack_session_notify # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/session_notify.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_session_notify include $(BUILD_EXECUTABLE) # ======================================================== # jack_server_control # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/server_control.cpp LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_server_control include $(BUILD_EXECUTABLE) ## ======================================================== ## jack_net_slave ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := ../example-clients/netslave.c #LOCAL_CFLAGS := $(common_cflags) #LOCAL_LDFLAGS := $(common_ldflags) #LOCAL_C_INCLUDES := $(common_c_includes) #LOCAL_SHARED_LIBRARIES := libjacknet #LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := jack_net_slave # #include $(BUILD_EXECUTABLE) ## ======================================================== ## jack_net_master ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := ../example-clients/netmaster.c #LOCAL_CFLAGS := $(common_cflags) #LOCAL_LDFLAGS := $(common_ldflags) #LOCAL_C_INCLUDES := $(common_c_includes) #LOCAL_SHARED_LIBRARIES := libjacknet #LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := jack_net_master # #include $(BUILD_EXECUTABLE) # ======================================================== # jack_latent_client # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/latent_client.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_latent_client include $(BUILD_EXECUTABLE) # ======================================================== # jack_midi_dump # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/midi_dump.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_midi_dump include $(BUILD_EXECUTABLE) # ======================================================== # jack_midi_latency_test # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/midi_latency_test.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_midi_latency_test include $(BUILD_EXECUTABLE) # ======================================================== # jack_transport # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/transport.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_transport include $(BUILD_EXECUTABLE) ## ======================================================== ## jack_rec ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := ../example-clients/capture_client.c #LOCAL_CFLAGS := $(common_cflags) #LOCAL_LDFLAGS := $(common_ldflags) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsndfile/src #LOCAL_SHARED_LIBRARIES := libjack libsndfile #LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := jack_rec # #include $(BUILD_EXECUTABLE) ## ======================================================== ## jack_netsource ## ======================================================== #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := ../example-clients/netsource.c ../common/netjack_packet.c #LOCAL_CFLAGS := $(common_cflags) -DNO_JACK_ERROR #LOCAL_LDFLAGS := $(common_ldflags) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsamplerate/include #LOCAL_SHARED_LIBRARIES := libsamplerate libjack #LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := jack_netsource # #include $(BUILD_EXECUTABLE) ## ======================================================== ## alsa_in ## ======================================================== #ifeq ($(SUPPORT_ALSA_IN_JACK),true) #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := ../example-clients/alsa_in.c ../common/memops.c #LOCAL_CFLAGS := $(common_cflags) -DNO_JACK_ERROR -D_POSIX_SOURCE -D_XOPEN_SOURCE=600 #LOCAL_LDFLAGS := $(common_ldflags) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsamplerate/include $(ALSA_INCLUDES) #LOCAL_SHARED_LIBRARIES := libasound libsamplerate libjack #LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := alsa_in # #include $(BUILD_EXECUTABLE) #endif ## ======================================================== ## alsa_out ## ======================================================== #ifeq ($(SUPPORT_ALSA_IN_JACK),true) #include $(CLEAR_VARS) # #LOCAL_SRC_FILES := ../example-clients/alsa_out.c ../common/memops.c #LOCAL_CFLAGS := $(common_cflags) -DNO_JACK_ERROR -D_POSIX_SOURCE -D_XOPEN_SOURCE=600 #LOCAL_LDFLAGS := $(common_ldflags) #LOCAL_C_INCLUDES := $(common_c_includes) $(JACK_ROOT)/../libsamplerate/include $(ALSA_INCLUDES) #LOCAL_SHARED_LIBRARIES := libasound libsamplerate libjack #LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) #LOCAL_MODULE_TAGS := eng optional #LOCAL_MODULE := alsa_out # #include $(BUILD_EXECUTABLE) #endif # ======================================================== # inprocess # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../example-clients/inprocess.c LOCAL_CFLAGS := $(common_cflags) -DSERVER_SIDE LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libc libdl libcutils libjackserver LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/jack LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := inprocess include $(BUILD_SHARED_LIBRARY) ########################################################## # tests ########################################################## # ======================================================== # jack_test # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../tests/test.cpp LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack libjackshm LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_test include $(BUILD_EXECUTABLE) # ======================================================== # jack_cpu # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../tests/cpu.c LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack libjackshm LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_cpu include $(BUILD_EXECUTABLE) # ======================================================== # jack_iodelay # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../tests/iodelay.cpp LOCAL_CFLAGS := $(common_cflags) LOCAL_CFLAGS += -Wno-narrowing LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack libjackshm LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_iodelay include $(BUILD_EXECUTABLE) # ======================================================== # jack_multiple_metro # ======================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES := ../tests/external_metro.cpp LOCAL_CFLAGS := $(common_cflags) LOCAL_LDFLAGS := $(common_ldflags) $(JACK_STL_LDFLAGS) LOCAL_C_INCLUDES := $(common_c_includes) LOCAL_SHARED_LIBRARIES := libjack libjackshm LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := eng optional LOCAL_MODULE := jack_multiple_metro include $(BUILD_EXECUTABLE) jack2-1.9.22/android/AndroidShm.cpp000066400000000000000000000176511436671425200167770ustar00rootroot00000000000000#define LOG_TAG "JAMSHMSERVICE" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BnAndroidShm.h" #include "AndroidShm.h" #include "JackConstants.h" #include #include #include #include #include #include #include #include #include "JackError.h" #include #define MEMORY_SIZE 10*1024 #define SEMAPHORE_NULL_CHAR '\0' // remove ALOGI log #undef ALOGI #define ALOGI namespace android { int AndroidShm::instantiate() { defaultServiceManager()->addService(String16("com.samsung.android.jam.IAndroidShm"), new AndroidShm); // SINGLETON WITH SAME NAME return 0; } int AndroidShm::sendCommand(const char* command) { ALOGI("I(pid:%d) send command is %s\n", getpid(), command); if(strcmp(command, "semaphore") == 0) { // print debug message about semaphore simulation for(int i = MAX_SEMAPHORE_MEMORY_COUNT -1 ; i >= 0; i--) { printf("index[%3d] = ptr[%p] name[%s]\n", i, (mSemaphore[i] != NULL)?mSemaphore[i]->getBase():0, mSemaphoreName[i]); ALOGI("index[%3d] = ptr[%p] name[%s]\n", i, (mSemaphore[i] != NULL)?mSemaphore[i]->getBase():0, mSemaphoreName[i]); } } return NO_ERROR; } int AndroidShm::testGetBufferByNewProcess() { ALOGI("testGetBufferByNewProcess..."); int status; int childPid = fork(); if(childPid > 0) { ALOGI("I(pid%d) made a child process(pid:%d)", getpid(), childPid); ALOGI("I(pid%d) wait until child(%d) was finish", getpid(), childPid); wait(&status); // wait child process . ALOGI("child(%d) was finished. ", childPid); } else if(childPid == 0) { ALOGI("im a new child process(pid:%d) ", getpid()); if(-1 == execlp("/system/bin/getbufferclient","getbufferclient",NULL)) { ALOGE("failed to execute getbufferclient"); } exit(0); } else { ALOGI("failed creating child process"); } return 0; } int AndroidShm::testGetBuffer() { ALOGI("I(pid:%d) trying to test get buffer...", getpid()); sp sm = defaultServiceManager(); ALOGI("get default ServiceManager is done"); sp b; //String16* serviceName = new String16("com.samsung.android.jam.IAndroidShm"); do { //ALOGI("here"); b = sm->getService(String16("com.samsung.android.jam.IAndroidShm")); //ALOGI("getservice is done"); if(b != 0) break; //ALOGI("AndroidShm is not working, waiting..."); usleep(500000); } while(true); sp service = interface_cast(b); //shared buffer. sp receiverMemBase = service->getBuffer(0); unsigned int *base = (unsigned int *) receiverMemBase->getBase(); int ret = 0; if(base != (unsigned int *) -1) { ALOGD("AndroidShm::testGetBuffer base=%p Data=0x%x\n",base, *base); *base = (*base)+1; ret = (unsigned int)(*base); ALOGI("AndroidShm::testGetBuffer base=%p Data=0x%x CHANGED\n",base, *base); receiverMemBase = 0; } else { ALOGE("Error shared memory not available\n"); } return 0; } sp AndroidShm::getBuffer(int index) { ALOGI("I(pid:%d) getBuffer index:%d", getpid(), index); if(index < 0 || index >= MAX_SHARED_MEMORY_COUNT) { ALOGE("error : out of index [%d]", index); return NULL; } return mMemHeap[index]; } int AndroidShm::MemAlloc(unsigned int size) { ALOGI("try to allocate memory size[%d]", size); for(int i = 0; i < MAX_SHARED_MEMORY_COUNT; i++) { if(mMemHeap[i] == NULL) { mMemHeap[i] = new MemoryHeapBase(size); if(mMemHeap[i] == NULL){ ALOGI("fail to alloc, try one more..."); continue; // try one more. } return i; } } ALOGE("fail to MemAlloc"); return -1; // fail to alloc } AndroidShm::AndroidShm() { ALOGI("AndroidShm is created"); for(int i = 0; i < MAX_SHARED_MEMORY_COUNT; i++) { mMemHeap[i] = NULL; } mRegistryIndex = 10000; //mMemHeap = new MemoryHeapBase(MEMORY_SIZE); //unsigned int *base = (unsigned int*) mMemHeap->getBase(); //*base = 0xdeadcafe;// for(int j = 0; j < MAX_SEMAPHORE_MEMORY_COUNT; j++) { mSemaphore[j] = NULL; memset(mSemaphoreName[j], SEMAPHORE_NULL_CHAR, MAX_SEMAPHORE_NAME_LENGTH); } } AndroidShm::~AndroidShm() { ALOGI("AndroidShm is destroyed"); for(int i = 0; i < MAX_SHARED_MEMORY_COUNT; i++) { (mMemHeap[i]).clear(); } for(int j = 0; j < MAX_SEMAPHORE_MEMORY_COUNT; j++) { (mSemaphore[j]).clear(); } } //status_t AndroidShm::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { // return BnAndroidShm::onTransact(code, data, reply, flags); //} int AndroidShm::allocShm(const int size) { // if negative return value is error ALOGI("try to alloc shared memory size[%d]", size); return MemAlloc(size); } int AndroidShm::removeShm(const unsigned int index) { // shared memory ALOGI("try to remove shared memory index[%d]", index); if(index >= MAX_SHARED_MEMORY_COUNT) { ALOGE("remove shared memory: out of index"); return -1; } (mMemHeap[index]).clear(); return 1; } int AndroidShm::isAllocated(const unsigned int index) { // allocated Ȯ ALOGI("try to check the memory allocation index[%d]", index); if(index >= MAX_SHARED_MEMORY_COUNT) { ALOGE("shared memory: out of index"); return 0; } if(mMemHeap[index] == NULL) return 0; else return 1; } int AndroidShm::setRegistryIndex(const unsigned int index) { ALOGI("set registry index %d", index); mRegistryIndex = index; return 1; } int AndroidShm::getRegistryIndex() { return mRegistryIndex; } sp AndroidShm::InitSemaphore(const char* name) { ALOGI("init semaphore [%s]", name); for(int i = 0; i < MAX_SEMAPHORE_MEMORY_COUNT; i++) { if(mSemaphoreName[i][0] == SEMAPHORE_NULL_CHAR) { mSemaphore[i] = new MemoryHeapBase(sizeof(sem_t)); if(mSemaphore[i] == NULL){ ALOGI("fail to alloc, try one more..."); continue; } if(sem_init((sem_t*)(mSemaphore[i]->getBase()), 1, 0) == 0) { strncpy(mSemaphoreName[i], name, MAX_SEMAPHORE_NAME_LENGTH - 1); mSemaphoreName[i][MAX_SEMAPHORE_NAME_LENGTH - 1] = '\0'; ALOGI("sem_init success"); return mSemaphore[i]; } else { (mSemaphore[i]).clear(); ALOGE("sem_init failed null returned"); return NULL; } } else { // find already exist name if(strcmp(mSemaphoreName[i], name) == 0) { // found ALOGI("found - return alread allocated semaphore"); return mSemaphore[i]; } } } ALOGE("sem_init failed null returned 2"); return NULL; } }; jack2-1.9.22/android/AndroidShm.h000066400000000000000000000034031436671425200164320ustar00rootroot00000000000000#ifndef ANDROIDSHM #define ANDROIDSHM #include #include "BnAndroidShm.h" #include #include #include "shm.h" #include "android/Shm.h" //android extension of shm.h namespace android { class AndroidShm : public BnAndroidShm { #define MAX_SHARED_MEMORY_COUNT 257 private: int MemAlloc(unsigned int size); public: virtual ~AndroidShm(); static int instantiate(); virtual int sendCommand(const char* command); virtual int allocShm(const int size); // if negative return value is error virtual int removeShm(const unsigned int index); // shared memory virtual int isAllocated(const unsigned int index); // allocated Ȯ virtual int setRegistryIndex(const unsigned int index); virtual int getRegistryIndex(); virtual sp InitSemaphore(const char* name); virtual sp getBuffer(int index); //virtual status_t onTransact( // uint32_t code, // const Parcel& data, // Parcel* reply, // uint32_t flags); private: int testGetBuffer(); int testGetBufferByNewProcess(); AndroidShm(); sp mMemHeap[MAX_SHARED_MEMORY_COUNT]; unsigned int mRegistryIndex; // for named semaphore simulation #define MAX_SEMAPHORE_MEMORY_COUNT 300 #define MAX_SEMAPHORE_NAME_LENGTH 300 sp mSemaphore[MAX_SEMAPHORE_MEMORY_COUNT]; char mSemaphoreName[MAX_SEMAPHORE_MEMORY_COUNT][MAX_SEMAPHORE_NAME_LENGTH]; }; }; #endif jack2-1.9.22/android/AndroidShmServer/000077500000000000000000000000001436671425200174505ustar00rootroot00000000000000jack2-1.9.22/android/AndroidShmServer/main_androidshmservice.cpp000066400000000000000000000007341436671425200246750ustar00rootroot00000000000000#define LOG_TAG "main_androidshmservice" #include #include #include #include #include "../../common/shm.h" #include "../Shm.h" //android extension of shm.h using namespace android; int main(int argc, char *argv[]) { jack_instantiate(); ProcessState::self()->startThreadPool(); ALOGI("AndroidShmService is starting now"); IPCThreadState::self()->joinThreadPool(); return 0; } jack2-1.9.22/android/AndroidShmServer/readme.txt000066400000000000000000000010211436671425200214400ustar00rootroot00000000000000IAndroidShm ׽Ʈ ϱ 뵵 Ǵ α׷Դϴ. AndroidShm service manager shared memory Ҵִ Դϴ. service name: com.sec.apa.IAndroidShm : /system/bin/androidshmservice ------------------------------------------- ./test AndroidShmService ׽Ʈϴ α׷ /system/bin/shmservicetest AndroidShmService ϴ UnitTestմϴ. Ȯ : adb logcat α׷ / Ȯ. : /system/bin/androidshmservice ̾ մϴ.jack2-1.9.22/android/AndroidShmServer/test/000077500000000000000000000000001436671425200204275ustar00rootroot00000000000000jack2-1.9.22/android/AndroidShmServer/test/shmservicedump.cpp000066400000000000000000000124521436671425200241750ustar00rootroot00000000000000#include "../../IAndroidShm.h" #include #include #include "../../../common/shm.h" namespace android { static sp receiverMemBase; #define MAX_SHARED_MEMORY_COUNT 257 sp getAndroidShmService() { sp shm = 0; /* Get the buffer service */ if (shm == NULL) { sp sm = defaultServiceManager(); sp binder; binder = sm->getService(String16("com.samsung.android.jam.IAndroidShm")); if (binder != 0) { shm = IAndroidShm::asInterface(binder); //shm = interface_cast(binder); } } return shm; } unsigned int * getBufferMemPointer(int index) { sp shm = getAndroidShmService(); if (shm == NULL) { printf("The EneaBufferServer is not published\n"); return (unsigned int *)-1; /* return an errorcode... */ } else { receiverMemBase = shm->getBuffer(index); if(receiverMemBase != NULL) return (unsigned int *) receiverMemBase->getBase(); else return (unsigned int*)-1; } } } using namespace android; void showStatus() { sp shm = getAndroidShmService(); if(shm == NULL) { printf("shm service is not available\n"); return; } printf("<<<<<<<<<<< dump memory allocation status >>>>>>>>>>\n"); for(int i = 256; i >= 0; i--) { if(shm->isAllocated(i) == 1) { printf("Mem[%3d] == 0x%x\n", i, (unsigned int)getBufferMemPointer(i)); } else { printf("Mem[%3d] == NULL\n", i); } } } void showRegistryIndex() { sp shm = getAndroidShmService(); if(shm == NULL) { printf("shm service is not available\n"); return; } printf("<<<<<<<<<<< show registry index >>>>>>>>>>\n"); printf("index [%3d]\n",shm->getRegistryIndex()); } void showHeader() { sp shm = getAndroidShmService(); if(shm == NULL) { printf("shm service is not available\n"); return; } if(shm->getRegistryIndex() > 256) { printf("don't have a registry header\n"); return; } unsigned int* buffer = getBufferMemPointer(shm->getRegistryIndex()); if(buffer) { jack_shm_header_t * header = (jack_shm_header_t*)buffer; printf("<<<<<<<<<< register header value >>>>>>>>>>\n"); printf("memory address 0x%x 0x%x\n", (unsigned int)(header), (unsigned int)buffer); printf("magic = %d\n", header->magic); printf("protocol = %d\n", header->protocol); printf("type = %d\n", header->type); printf("size = %d\n", header->size); printf("hdr_len = %d\n", header->hdr_len); printf("entry_len = %d\n", header->entry_len); for(int j = 0; j < MAX_SERVERS; j++) { //char name[256]; //memset(name, '\0', 256); //strncpy(name, header->server[j].name, 10); printf("server[%d] pid = %d, name = %s\n", j, header->server[j].pid, header->server[j].name); } } } void showBody() { sp shm = getAndroidShmService(); if(shm == NULL) { printf("shm service is not available\n"); return; } if(shm->getRegistryIndex() > 256) { printf("don't have a registry body\n"); return; } unsigned int* buffer = getBufferMemPointer(shm->getRegistryIndex()); if(buffer) { jack_shm_header_t * header = (jack_shm_header_t*)buffer; printf("<<<<<<<<<< registry body value >>>>>>>>>>\n"); jack_shm_registry_t * registry = (jack_shm_registry_t *) (header + 1); for(int k = 255; k >= 0; k--) { printf("registry[%3d] index[%3d],allocator[%3d],size[%6d],id[%10s],fd[%3d]\n", k, registry[k].index, registry[k].allocator, registry[k].size, registry[k].id, registry[k].fd); } } } void showSemaphore() { sp shm = getAndroidShmService(); if(shm == NULL) { printf("shm service is not available\n"); return; } shm->sendCommand("semaphore"); printf("log will be shown in the logcat log\n"); } int main(int argc, char** argv) { // base could be on same address as Servers base but this // is purely by luck do NEVER rely on this. Linux memory // management may put it wherever it likes. if(argc < 2) { printf("usage\n shmservicedump [status|header|body|index|semaphore]\n"); printf(" status: show the shared memory allocation status\n"); printf(" header: show the registry header infomations if the registry exist\n"); printf(" body: show the registry body infomations if the registry exist\n"); printf(" index: show the index of array that is allocated registry shared memory\n"); printf(" semaphore: show the memory array about semaphore simulation\n"); return 0; } if(strcmp(argv[1], "semaphore") == 0) { showSemaphore(); } else if(strcmp(argv[1], "index") == 0) { showRegistryIndex(); } else if(strcmp(argv[1], "status") == 0) { showStatus(); } else if(strcmp(argv[1], "header") == 0) { showHeader(); } else if(strcmp(argv[1], "body") == 0) { showBody(); } else { printf("%s is invalid parameter\n", argv[1]); } return 0; } jack2-1.9.22/android/AndroidShmServer/test/shmservicetest.cpp000066400000000000000000000113631436671425200242070ustar00rootroot00000000000000#include "../../IAndroidShm.h" #include #include namespace android { static sp receiverMemBase; #define MAX_SHARED_MEMORY_COUNT 257 sp getAndroidShmService() { sp shm = 0; /* Get the buffer service */ if (shm == NULL) { sp sm = defaultServiceManager(); sp binder; binder = sm->getService(String16("com.samsung.android.jam.IAndroidShm")); if (binder != 0) { shm = IAndroidShm::asInterface(binder); //shm = interface_cast(binder); } } return shm; } unsigned int * getBufferMemPointer(int index) { sp shm = getAndroidShmService(); if (shm == NULL) { ALOGE("The EneaBufferServer is not published"); return (unsigned int *)-1; /* return an errorcode... */ } else { receiverMemBase = shm->getBuffer(index); if(receiverMemBase != NULL) return (unsigned int *) receiverMemBase->getBase(); else return (unsigned int*)-1; } } } using namespace android; void setup_test() { sp shm = getAndroidShmService(); if(shm == NULL) return; for(int i = 0; i < MAX_SHARED_MEMORY_COUNT; i++) { shm->removeShm(i); } } void teardown_test() { } void increase_value_once() { ALOGD("*****test: increase_value_once*****\n"); sp shm = getAndroidShmService(); if(shm == NULL) return; int slot = shm->allocShm(10000); unsigned int *base = getBufferMemPointer(slot); if(base != (unsigned int *)-1) { ALOGD("ShmServiceTest base=%p Data=0x%x\n",base, *base); *base = (*base)+1; ALOGD("ShmServiceTest base=%p Data=0x%x CHANGED\n",base, *base); //receiverMemBase = 0; } else { ALOGE("Error shared memory not available\n"); } } void increase_value_10times() { ALOGD("*****test: increase_value_10times*****\n"); sp shm = getAndroidShmService(); if(shm == NULL) return; int slot = shm->allocShm(10000); for(int i = 0; i < 10; i++) { unsigned int *base = getBufferMemPointer(slot); if(base != (unsigned int *)-1) { ALOGD("ShmServiceTest base=%p Data=0x%x\n",base, *base); *base = (*base)+1; ALOGD("ShmServiceTest base=%p Data=0x%x CHANGED\n",base, *base); //receiverMemBase = 0; } else { ALOGE("Error shared memory not available\n"); } } } void check_allocated() { ALOGD("*****test: check_allocated*****\n"); sp shm = getAndroidShmService(); if(shm == NULL) return; int slot = shm->allocShm(10000); int i = 0; for(; i < MAX_SHARED_MEMORY_COUNT; i++) { if(slot == i) { if(shm->isAllocated(i) == 1) { //ALOGD("pass\n"); } else { ALOGD("failed\n"); } } else { if(shm->isAllocated(i) == 0) { //ALOGD("pass\n"); } else { ALOGD("failed\n"); } } } if(i == MAX_SHARED_MEMORY_COUNT) { ALOGD("pass\n"); } } void test_set_get_registry_index() { ALOGD("*****test: test_set_get_registry_index*****\n"); sp shm = getAndroidShmService(); if(shm == NULL) return; int registry = 1; shm->setRegistryIndex(registry); if(registry == shm->getRegistryIndex()) { ALOGD("pass\n"); } else { ALOGD("fail\n"); } registry = 0; shm->setRegistryIndex(registry); if(registry == shm->getRegistryIndex()) { ALOGD("pass\n"); } else { ALOGD("fail\n"); } } void test_memset() { ALOGD("*****test: test_memset*****\n"); sp shm = getAndroidShmService(); if(shm == NULL) return; int slot = shm->allocShm(10000); unsigned int * pnt = getBufferMemPointer(slot); memset (pnt, 0, 10000); ALOGD("result : 0 0 0 0\n"); ALOGD("memory dump : %d %d %d %d\n", pnt[0], pnt[1], pnt[2], pnt[3]); memset (pnt, 0xffffffff, 10000); ALOGD("result : -1 -1 -1 -1\n"); ALOGD("memory dump : %d %d %d %d", pnt[0], pnt[1], pnt[2], pnt[3]); } int main(int argc, char** argv) { // base could be on same address as Servers base but this // is purely by luck do NEVER rely on this. Linux memory // management may put it wherever it likes. setup_test(); increase_value_once(); teardown_test(); setup_test(); increase_value_10times(); teardown_test(); setup_test(); check_allocated(); teardown_test(); setup_test(); test_set_get_registry_index(); teardown_test(); setup_test(); test_memset(); teardown_test(); return 0; } jack2-1.9.22/android/BnAndroidShm.cpp000066400000000000000000000053141436671425200172500ustar00rootroot00000000000000#include "BnAndroidShm.h" #include namespace android { status_t BnAndroidShm::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch(code) { case HW_SENDCOMMAND:{ CHECK_INTERFACE(IAndroidShm, data, reply); const char *str; str = data.readCString(); reply->writeInt32(sendCommand(str)); return NO_ERROR; }break; case HW_GETBUFFER:{ CHECK_INTERFACE(IAndroidShm, data, reply); int32_t index; data.readInt32(&index); sp Data = getBuffer(index); if(Data != NULL){ reply->writeStrongBinder(Data->asBinder()); } return NO_ERROR; }break; case HW_ALLOC_SHM:{ CHECK_INTERFACE(IAndroidShm, data, reply); int32_t size; data.readInt32(&size); reply->writeInt32(allocShm(size)); return NO_ERROR; }break; case HW_REMOVE_SHM:{ CHECK_INTERFACE(IAndroidShm, data, reply); int32_t index; data.readInt32(&index); reply->writeInt32(removeShm(index)); return NO_ERROR; }break; case HW_IS_ALLOCATED:{ CHECK_INTERFACE(IAndroidShm, data, reply); int32_t index; data.readInt32(&index); reply->writeInt32(isAllocated(index)); return NO_ERROR; }break; case HW_SET_REGISTRY_INDEX:{ CHECK_INTERFACE(IAndroidShm, data, reply); int32_t index; data.readInt32(&index); reply->writeInt32(setRegistryIndex(index)); return NO_ERROR; }break; case HW_GET_REGISTRY_INDEX:{ CHECK_INTERFACE(IAndroidShm, data, reply); reply->writeInt32(getRegistryIndex()); return NO_ERROR; }break; case HW_INIT_SEMAPHORE:{ CHECK_INTERFACE(IAndroidShm, data, reply); const char *name; name = data.readCString(); sp Data = InitSemaphore(name); if(Data != NULL){ reply->writeStrongBinder(Data->asBinder()); } return NO_ERROR; }break; default: return BBinder::onTransact(code, data, reply, flags); } } }; jack2-1.9.22/android/BnAndroidShm.h000066400000000000000000000005541436671425200167160ustar00rootroot00000000000000#ifndef BNANDROIDSHM #define BNANDROIDSHM #include #include "IAndroidShm.h" namespace android { class BnAndroidShm : public BnInterface { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; }; #endif jack2-1.9.22/android/BpAndroidShm.cpp000066400000000000000000000075371436671425200172630ustar00rootroot00000000000000#include #include #include "BpAndroidShm.h" namespace android{ int BpAndroidShm::sendCommand(const char*command) { Parcel data, reply; data.writeInterfaceToken( IAndroidShm::getInterfaceDescriptor()); data.writeCString(command); status_t status = remote()->transact(HW_SENDCOMMAND, data, &reply); if(status != NO_ERROR) { ALOGE("print sendCommand error: %s", strerror(-status)); } else { status= reply.readInt32(); } return status; } sp BpAndroidShm::getBuffer(int index) { Parcel data, reply; sp memHeap = NULL; data.writeInterfaceToken(IAndroidShm::getInterfaceDescriptor()); data.writeInt32(index); remote()->transact(HW_GETBUFFER, data, &reply); memHeap = interface_cast (reply.readStrongBinder()); return memHeap; } BpAndroidShm::BpAndroidShm( const sp& impl) : BpInterface(impl) {} BpAndroidShm::~BpAndroidShm() {} int BpAndroidShm::allocShm(const int size) { // if negative return value is error Parcel data, reply; data.writeInterfaceToken(IAndroidShm::getInterfaceDescriptor()); data.writeInt32(size); status_t status = remote()->transact(HW_ALLOC_SHM, data, &reply); if(status != NO_ERROR) { ALOGE("print allocShm error: %s", strerror(-status)); } else { status= reply.readInt32(); } return status; } int BpAndroidShm::removeShm(const unsigned int index) { // shared memory Parcel data, reply; data.writeInterfaceToken(IAndroidShm::getInterfaceDescriptor()); data.writeInt32(index); status_t status = remote()->transact(HW_REMOVE_SHM, data, &reply); if(status != NO_ERROR) { ALOGE("print removeShm error: %s", strerror(-status)); } else { status= reply.readInt32(); } return status; } int BpAndroidShm::isAllocated(const unsigned int index) { // allocated Ȯ Parcel data, reply; data.writeInterfaceToken(IAndroidShm::getInterfaceDescriptor()); data.writeInt32(index); status_t status = remote()->transact(HW_IS_ALLOCATED, data, &reply); if(status != NO_ERROR) { ALOGE("print isAllocated error: %s", strerror(-status)); } else { status= reply.readInt32(); } return status; } int BpAndroidShm::setRegistryIndex(const unsigned int index) { Parcel data, reply; data.writeInterfaceToken(IAndroidShm::getInterfaceDescriptor()); data.writeInt32(index); status_t status = remote()->transact(HW_SET_REGISTRY_INDEX, data, &reply); if(status != NO_ERROR) { ALOGE("print setRegistryIndex error: %s", strerror(-status)); } else { status= reply.readInt32(); } return status; } int BpAndroidShm::getRegistryIndex() { Parcel data, reply; data.writeInterfaceToken(IAndroidShm::getInterfaceDescriptor()); status_t status = remote()->transact(HW_GET_REGISTRY_INDEX, data, &reply); if(status != NO_ERROR) { ALOGE("print getRegistryIndex error: %s", strerror(-status)); } else { status= reply.readInt32(); } return status; } sp BpAndroidShm::InitSemaphore(const char* name) { Parcel data, reply; sp memHeap = NULL; data.writeInterfaceToken(IAndroidShm::getInterfaceDescriptor()); data.writeCString(name); status_t status = remote()->transact(HW_INIT_SEMAPHORE, data, &reply); memHeap = interface_cast (reply.readStrongBinder()); return memHeap; } }; jack2-1.9.22/android/BpAndroidShm.h000066400000000000000000000014131436671425200167130ustar00rootroot00000000000000#ifndef BPANDROIDSHM #define BPANDROIDSHM #include #include "IAndroidShm.h" #include namespace android { class BpAndroidShm: public BpInterface { public: BpAndroidShm( const sp & impl); virtual ~BpAndroidShm(); virtual sp getBuffer(int index); virtual int sendCommand(const char *command); virtual int allocShm(const int size); // if negative return value is error virtual int removeShm(const unsigned int index); // shared memory virtual int isAllocated(const unsigned int index); // allocated Ȯ virtual int setRegistryIndex(const unsigned int index); virtual int getRegistryIndex(); virtual sp InitSemaphore(const char* name); }; }; #endif jack2-1.9.22/android/CleanSpec.mk000066400000000000000000000133311436671425200164200ustar00rootroot00000000000000 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/common) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/alsa_in_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/alsa_out_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/androidshmservice_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/shmservicetest_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/shmservicedump_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_samplerate_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_freewheel_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_connect_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_disconnect_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_latent_client_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_midiseq_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_zombie_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_lsp_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_load_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jackd_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_monitor_client_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_simple_looper_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_simple_client_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_cpu_load_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_iodelay_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_midisine_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_cpu_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_simple_keyboard_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_unload_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_wait_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_alias_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_metro_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_bufsize_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_thru_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_session_notify_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_midi_latency_test_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_rec_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_netsource_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_net_master_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_net_slave_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_showtime_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_server_control_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_evmon_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_test_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_transport_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_simple_session_client_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_simple_effect_intermediates) $(call add_clean_step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_multiple_metro_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/jack_midi_dump_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/common) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/posix) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjack_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjacknet_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjackserver_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjackshm_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/jack_alsa_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/jack_dummy_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/jack_net_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/jack_loopback_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/jack_netone_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/audioadapter_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/inprocess_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/netadapter_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/netmanager_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/profiler_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/jack_goldfish_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/jack_opensles_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/in_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/out_intermediates) jack2-1.9.22/android/IAndroidShm.cpp000066400000000000000000000002301436671425200170710ustar00rootroot00000000000000#include "IAndroidShm.h" #include "BpAndroidShm.h" namespace android{ IMPLEMENT_META_INTERFACE(AndroidShm, "com.samsung.android.jam.IAndroidShm"); }; jack2-1.9.22/android/IAndroidShm.h000066400000000000000000000020301436671425200165360ustar00rootroot00000000000000#ifndef IANDROIDSHM #define IANDROIDSHM #include #include namespace android { enum { HW_GETBUFFER = IBinder::FIRST_CALL_TRANSACTION, HW_MULTIPLY, HW_STARTSERVER, HW_MAKECLIENT, HW_SENDCOMMAND, HW_LOADSO, HW_ALLOC_SHM, HW_REMOVE_SHM, HW_IS_ALLOCATED, HW_SET_REGISTRY_INDEX, HW_GET_REGISTRY_INDEX, HW_INIT_SEMAPHORE }; class IAndroidShm: public IInterface { public: DECLARE_META_INTERFACE(AndroidShm); virtual sp getBuffer(int index) = 0; virtual int sendCommand(const char *command) = 0; virtual int allocShm(const int size) = 0; // if negative return value is error virtual int removeShm(const unsigned int index) = 0; // shared memory virtual int isAllocated(const unsigned int index) = 0; // allocated Ȯ virtual int setRegistryIndex(const unsigned int index) = 0; virtual int getRegistryIndex() = 0; // for named semaphore simulation virtual sp InitSemaphore(const char* name) = 0; }; }; #endif jack2-1.9.22/android/JackAndroidSemaphore.cpp000066400000000000000000000176221436671425200207620ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackAndroidSemaphore.h" #include "JackTools.h" #include "JackConstants.h" #include "JackError.h" #include #include #include #include "IAndroidShm.h" #include "shm.h" #include "android/Shm.h" //android extension of shm.h namespace Jack { pthread_mutex_t JackAndroidSemaphore::mutex = PTHREAD_MUTEX_INITIALIZER; void JackAndroidSemaphore::BuildName(const char* client_name, const char* server_name, char* res, int size) { char ext_client_name[SYNC_MAX_NAME_SIZE + 1]; JackTools::RewriteName(client_name, ext_client_name); if (getenv("JACK_PROMISCUOUS_SERVER")) { snprintf(res, size, "jack_sem.%s_%s", server_name, ext_client_name); } else { snprintf(res, size, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); } } bool JackAndroidSemaphore::Signal() { int res; if (!fSemaphore) { jack_error("JackAndroidSemaphore::Signal name = %s already deallocated!!", fName); return false; } if (fFlush) return true; if ((res = sem_post(fSemaphore)) != 0) { jack_error("JackAndroidSemaphore::Signal name = %s err = %s", fName, strerror(errno)); } return (res == 0); } bool JackAndroidSemaphore::SignalAll() { int res; if (!fSemaphore) { jack_error("JackAndroidSemaphore::SignalAll name = %s already deallocated!!", fName); return false; } if (fFlush) return true; if ((res = sem_post(fSemaphore)) != 0) { jack_error("JackAndroidSemaphore::SignalAll name = %s err = %s", fName, strerror(errno)); } return (res == 0); } /* bool JackAndroidSemaphore::Wait() { int res; if (!fSemaphore) { jack_error("JackAndroidSemaphore::Wait name = %s already deallocated!!", fName); return false; } if ((res = sem_wait(fSemaphore)) != 0) { jack_error("JackAndroidSemaphore::Wait name = %s err = %s", fName, strerror(errno)); } return (res == 0); } */ bool JackAndroidSemaphore::Wait() { int res; while ((res = sem_wait(fSemaphore) < 0)) { jack_error("JackAndroidSemaphore::Wait name = %s err = %s", fName, strerror(errno)); if (errno != EINTR) { break; } } return (res == 0); } bool JackAndroidSemaphore::TimedWait(long usec) { int res; struct timeval now; timespec time; if (!fSemaphore) { jack_error("JackAndroidSemaphore::TimedWait name = %s already deallocated!!", fName); return false; } gettimeofday(&now, 0); time.tv_sec = now.tv_sec + usec / 1000000; long tv_usec = (now.tv_usec + (usec % 1000000)); time.tv_sec += tv_usec / 1000000; time.tv_nsec = (tv_usec % 1000000) * 1000; while ((res = sem_timedwait(fSemaphore, &time)) < 0) { jack_error("JackAndroidSemaphore::TimedWait err = %s", strerror(errno)); jack_log("JackAndroidSemaphore::TimedWait now : %ld %ld ", now.tv_sec, now.tv_usec); jack_log("JackAndroidSemaphore::TimedWait next : %ld %ld ", time.tv_sec, time.tv_nsec/1000); if (errno == ETIMEDOUT) { timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec = time.tv_sec - ts.tv_sec; ts.tv_nsec = time.tv_nsec - ts.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000000000; ts.tv_sec -= 1; } if (ts.tv_sec < 0 || ts.tv_nsec < 0) { jack_error("JackAndroidSemaphore::TimedWait time may changed or client killed already! wait again!"); gettimeofday(&now, 0); time.tv_sec = now.tv_sec + usec / 1000000; long tv_usec = (now.tv_usec + (usec % 1000000)); time.tv_sec += tv_usec / 1000000; time.tv_nsec = (tv_usec % 1000000) * 1000; if ((res = sem_timedwait(fSemaphore, &time)) < 0) { jack_error("JackAndroidSemaphore::TimedWait err = %s", strerror(errno)); jack_log("JackAndroidSemaphore::TimedWait now : %ld %ld ", now.tv_sec, now.tv_usec); jack_log("JackAndroidSemaphore::TimedWait next : %ld %ld ", time.tv_sec, time.tv_nsec/1000); } } } if (errno != EINTR) { break; } } return (res == 0); } // Server side : publish the semaphore in the global namespace bool JackAndroidSemaphore::Allocate(const char* name, const char* server_name, int value) { pthread_mutex_lock (&mutex); BuildName(name, server_name, fName, sizeof(fName)); jack_log("JackAndroidSemaphore::Allocate name = %s val = %ld", fName, value); android::sp service = android::Shm::getShmService(); if(service == NULL){ jack_error("shm service is null"); return false; } fSemaphoreMemory = service->InitSemaphore(fName); if(fSemaphoreMemory != NULL){ fSemaphore = (sem_t*)(fSemaphoreMemory->getBase()); } if(fSemaphore == NULL) { jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno)); pthread_mutex_unlock (&mutex); return false; } else { sem_init(fSemaphore, 1, 0); jack_log("sem_init()"); pthread_mutex_unlock (&mutex); return true; } } // Client side : get the published semaphore from server bool JackAndroidSemaphore::ConnectInput(const char* name, const char* server_name) { pthread_mutex_lock (&mutex); BuildName(name, server_name, fName, sizeof(fName)); jack_log("JackAndroidSemaphore::Connect name = %s", fName); // Temporary... if (fSemaphore) { jack_log("Already connected name = %s", name); pthread_mutex_unlock (&mutex); return true; } android::sp service = android::Shm::getShmService(); if(service == NULL){ jack_error("shm service is null"); return false; } fSemaphoreMemory = service->InitSemaphore(fName); if(fSemaphoreMemory != NULL){ fSemaphore = (sem_t*)(fSemaphoreMemory->getBase()); } if(fSemaphore == NULL) { jack_error("Connect: can't connect named semaphore name = %s err = %s", fName, strerror(errno)); pthread_mutex_unlock (&mutex); return false; } else { int val = 0; sem_getvalue(fSemaphore, &val); jack_log("JackAndroidSemaphore::Connect sem_getvalue %ld", val); pthread_mutex_unlock (&mutex); return true; } } bool JackAndroidSemaphore::Connect(const char* name, const char* server_name) { return ConnectInput(name, server_name); } bool JackAndroidSemaphore::ConnectOutput(const char* name, const char* server_name) { return ConnectInput(name, server_name); } bool JackAndroidSemaphore::Disconnect() { if (fSemaphore) { jack_log("JackAndroidSemaphore::Disconnect name = %s", fName); fSemaphore = NULL; } return true; } // Server side : destroy the semaphore void JackAndroidSemaphore::Destroy() { if (fSemaphore != NULL) { jack_log("JackAndroidSemaphore::Disconnect name = %s", fName); fSemaphore = NULL; } else { jack_error("JackAndroidSemaphore::Destroy semaphore == NULL"); } } } // end of namespace jack2-1.9.22/android/JackAndroidSemaphore.h000066400000000000000000000037451436671425200204300ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackAndroidSemaphore__ #define __JackAndroidSemaphore__ #include "JackSynchro.h" #include "JackCompilerDeps.h" #include #include #include #include #include #include namespace Jack { /*! \brief Inter process synchronization using POSIX semaphore. */ class SERVER_EXPORT JackAndroidSemaphore : public detail::JackSynchro { private: sem_t* fSemaphore; android::sp fSemaphoreMemory; static pthread_mutex_t mutex; protected: void BuildName(const char* name, const char* server_name, char* res, int size); public: JackAndroidSemaphore():JackSynchro(), fSemaphore(NULL) {} bool Signal(); bool SignalAll(); bool Wait(); bool TimedWait(long usec); bool Allocate(const char* name, const char* server_name, int value); bool Connect(const char* name, const char* server_name); bool ConnectInput(const char* name, const char* server_name); bool ConnectOutput(const char* name, const char* server_name); bool Disconnect(); void Destroy(); }; } // end of namespace #endif jack2-1.9.22/android/JackAndroidThread.cpp000066400000000000000000000230421436671425200202370ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackAndroidThread.h" #include "JackError.h" #include "JackTime.h" #include "JackGlobals.h" #include // for memset #include // for _POSIX_PRIORITY_SCHEDULING check #include #ifdef JACK_ANDROID_REALTIME_SCHED #include "SchedulingPolicyService.h" #endif //#define JACK_SCHED_POLICY SCHED_RR #define JACK_SCHED_POLICY SCHED_FIFO namespace Jack { void JackAndroidThread::thread_exit_handler(int sig) { printf("this signal is %d \n", sig); pthread_exit(0); } void* JackAndroidThread::ThreadHandler(void* arg) { JackAndroidThread* obj = (JackAndroidThread*)arg; JackRunnableInterface* runnable = obj->fRunnable; int err; // Signal creation thread when started with StartSync jack_log("JackAndroidThread::ThreadHandler : start"); obj->fStatus = kIniting; // Call Init method if (!runnable->Init()) { jack_error("Thread init fails: thread quits"); return 0; } obj->fStatus = kRunning; // If Init succeed, start the thread loop bool res = true; while (obj->fStatus == kRunning && res) { res = runnable->Execute(); } jack_log("JackAndroidThread::ThreadHandler : exit"); pthread_exit(0); return 0; // never reached } int JackAndroidThread::Start() { fStatus = kStarting; // Check if the thread was correctly started if (StartImp(&fThread, fPriority, fRealTime, ThreadHandler, this) < 0) { fStatus = kIdle; return -1; } else { return 0; } } int JackAndroidThread::StartSync() { fStatus = kStarting; if (StartImp(&fThread, fPriority, fRealTime, ThreadHandler, this) < 0) { fStatus = kIdle; return -1; } else { int count = 0; while (fStatus == kStarting && ++count < 1000) { JackSleep(1000); } return (count == 1000) ? -1 : 0; } } int JackAndroidThread::StartImp(jack_native_thread_t* thread, int priority, int realtime, void*(*start_routine)(void*), void* arg) { pthread_attr_t attributes; struct sched_param rt_param; pthread_attr_init(&attributes); int res; struct sigaction actions; memset(&actions, 0, sizeof(actions)); sigemptyset(&actions.sa_mask); actions.sa_flags = 0; actions.sa_handler = thread_exit_handler; sigaction(SIGUSR1,&actions,NULL); if ((res = pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE))) { jack_error("Cannot request joinable thread creation for thread res = %d", res); return -1; } if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) { jack_error("Cannot set scheduling scope for thread res = %d", res); return -1; } if (realtime) { jack_log("JackAndroidThread::StartImp : create RT thread"); if ((res = pthread_attr_setschedpolicy(&attributes, JACK_SCHED_POLICY))) { jack_error("Cannot set RR scheduling class for RT thread res = %d", res); return -1; } memset(&rt_param, 0, sizeof(rt_param)); rt_param.sched_priority = priority; if ((res = pthread_attr_setschedparam(&attributes, &rt_param))) { jack_error("Cannot set scheduling priority for RT thread res = %d", res); return -1; } } else { jack_log("JackAndroidThread::StartImp : create non RT thread"); } if ((res = pthread_attr_setstacksize(&attributes, THREAD_STACK))) { jack_error("Cannot set thread stack size res = %d", res); return -1; } if ((res = JackGlobals::fJackThreadCreator(thread, &attributes, start_routine, arg))) { jack_error("Cannot create thread res = %d", res); return -1; } pthread_attr_destroy(&attributes); return 0; } int JackAndroidThread::Kill() { if (fThread != (jack_native_thread_t)NULL) { // If thread has been started jack_log("JackAndroidThread::Kill"); void* status; pthread_kill(fThread, SIGUSR1); pthread_join(fThread, &status); fStatus = kIdle; fThread = (jack_native_thread_t)NULL; return 0; } else { return -1; } } int JackAndroidThread::Stop() { if (fThread != (jack_native_thread_t)NULL) { // If thread has been started jack_log("JackAndroidThread::Stop"); void* status; fStatus = kIdle; // Request for the thread to stop pthread_join(fThread, &status); fThread = (jack_native_thread_t)NULL; return 0; } else { return -1; } } int JackAndroidThread::KillImp(jack_native_thread_t thread) { if (thread != (jack_native_thread_t)NULL) { // If thread has been started jack_log("JackAndroidThread::Kill"); void* status; pthread_kill(thread, SIGUSR1); pthread_join(thread, &status); return 0; } else { return -1; } } int JackAndroidThread::StopImp(jack_native_thread_t thread) { if (thread != (jack_native_thread_t)NULL) { // If thread has been started jack_log("JackAndroidThread::Stop"); void* status; pthread_join(thread, &status); return 0; } else { return -1; } } int JackAndroidThread::AcquireRealTime() { return (fThread != (jack_native_thread_t)NULL) ? AcquireRealTimeImp(fThread, fPriority) : -1; } int JackAndroidThread::AcquireSelfRealTime() { return AcquireRealTimeImp(pthread_self(), fPriority); } int JackAndroidThread::AcquireRealTime(int priority) { fPriority = priority; return AcquireRealTime(); } int JackAndroidThread::AcquireSelfRealTime(int priority) { fPriority = priority; return AcquireSelfRealTime(); } int JackAndroidThread::AcquireRealTimeImp(jack_native_thread_t thread, int priority) { struct sched_param rtparam; int res; memset(&rtparam, 0, sizeof(rtparam)); rtparam.sched_priority = priority; jack_log("JackAndroidThread::AcquireRealTimeImp priority = %d", priority); #ifndef JACK_ANDROID_REALTIME_SCHED if ((res = pthread_setschedparam(thread, JACK_SCHED_POLICY, &rtparam)) != 0) { jack_error("Cannot use real-time scheduling (RR/%d)" "(%d: %s)", rtparam.sched_priority, res, strerror(res)); return -1; } #else if ((res = android::requestPriority(getpid(), gettid(), priority)) != 0) { jack_log("Failed to get SCHED_FIFO priority pid %d tid %d; error %d", getpid(), gettid(), res); return -1; } #endif return 0; } int JackAndroidThread::DropRealTime() { return (fThread != (jack_native_thread_t)NULL) ? DropRealTimeImp(fThread) : -1; } int JackAndroidThread::DropSelfRealTime() { return DropRealTimeImp(pthread_self()); } int JackAndroidThread::DropRealTimeImp(jack_native_thread_t thread) { struct sched_param rtparam; int res; memset(&rtparam, 0, sizeof(rtparam)); rtparam.sched_priority = 0; if ((res = pthread_setschedparam(thread, SCHED_OTHER, &rtparam)) != 0) { jack_error("Cannot switch to normal scheduling priority(%s)", strerror(errno)); return -1; } return 0; } jack_native_thread_t JackAndroidThread::GetThreadID() { return fThread; } bool JackAndroidThread::IsThread() { return pthread_self() == fThread; } void JackAndroidThread::Terminate() { jack_log("JackAndroidThread::Terminate"); pthread_exit(0); } SERVER_EXPORT void ThreadExit() { jack_log("ThreadExit"); pthread_exit(0); } } // end of namespace bool jack_get_thread_realtime_priority_range(int * min_ptr, int * max_ptr) { #if defined(_POSIX_PRIORITY_SCHEDULING) && !defined(__APPLE__) int min, max; min = sched_get_priority_min(JACK_SCHED_POLICY); if (min == -1) { jack_error("sched_get_priority_min() failed."); return false; } max = sched_get_priority_max(JACK_SCHED_POLICY); if (max == -1) { jack_error("sched_get_priority_max() failed."); return false; } *min_ptr = min; *max_ptr = max; return true; #else return false; #endif } bool jack_tls_allocate_key(jack_tls_key *key_ptr) { int ret; ret = pthread_key_create(key_ptr, NULL); if (ret != 0) { jack_error("pthread_key_create() failed with error %d", ret); return false; } return true; } bool jack_tls_free_key(jack_tls_key key) { int ret; ret = pthread_key_delete(key); if (ret != 0) { jack_error("pthread_key_delete() failed with error %d", ret); return false; } return true; } bool jack_tls_set(jack_tls_key key, void *data_ptr) { int ret; ret = pthread_setspecific(key, (const void *)data_ptr); if (ret != 0) { jack_error("pthread_setspecific() failed with error %d", ret); return false; } return true; } void *jack_tls_get(jack_tls_key key) { return pthread_getspecific(key); } jack2-1.9.22/android/JackAndroidThread.h000066400000000000000000000063401436671425200177060ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackAndroidThread__ #define __JackAndroidThread__ #include "JackThread.h" #include namespace Jack { /* use 512KB stack per thread - the default is way too high to be feasible * with mlockall() on many systems */ #define THREAD_STACK 524288 enum { PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS }; /*! \brief The POSIX thread base class. */ class SERVER_EXPORT JackAndroidThread : public detail::JackThreadInterface { protected: jack_native_thread_t fThread; static void* ThreadHandler(void* arg); static void thread_exit_handler(int sig); public: JackAndroidThread(JackRunnableInterface* runnable, bool real_time, int priority, int cancellation) : JackThreadInterface(runnable, priority, real_time, cancellation), fThread((jack_native_thread_t)NULL) {} JackAndroidThread(JackRunnableInterface* runnable, int cancellation = PTHREAD_CANCEL_ASYNCHRONOUS) : JackThreadInterface(runnable, 0, false, cancellation), fThread((jack_native_thread_t)NULL) {} int Start(); int StartSync(); int Kill(); int Stop(); void Terminate(); int AcquireRealTime(); // Used when called from another thread int AcquireSelfRealTime(); // Used when called from thread itself int AcquireRealTime(int priority); // Used when called from another thread int AcquireSelfRealTime(int priority); // Used when called from thread itself int DropRealTime(); // Used when called from another thread int DropSelfRealTime(); // Used when called from thread itself jack_native_thread_t GetThreadID(); bool IsThread(); static int AcquireRealTimeImp(jack_native_thread_t thread, int priority); static int AcquireRealTimeImp(jack_native_thread_t thread, int priority, UInt64 period, UInt64 computation, UInt64 constraint) { return JackAndroidThread::AcquireRealTimeImp(thread, priority); } static int DropRealTimeImp(jack_native_thread_t thread); static int StartImp(jack_native_thread_t* thread, int priority, int realtime, void*(*start_routine)(void*), void* arg); static int StopImp(jack_native_thread_t thread); static int KillImp(jack_native_thread_t thread); }; SERVER_EXPORT void ThreadExit(); } // end of namespace #endif jack2-1.9.22/android/JackCompilerDeps_os.h000066400000000000000000000027301436671425200202640ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackCompilerDeps_android__ #define __JackCompilerDeps_android__ #include "JackConstants.h" #if __GNUC__ #define MEM_ALIGN(x,y) x __attribute__((aligned(y))) #define LIB_EXPORT __attribute__((visibility("default"))) #ifdef SERVER_SIDE #if (__GNUC__< 4) #define SERVER_EXPORT #else #define SERVER_EXPORT __attribute__((visibility("default"))) #endif #else #define SERVER_EXPORT __attribute__((visibility("hidden"))) #endif #else #define MEM_ALIGN(x,y) x #define LIB_EXPORT #define SERVER_EXPORT /* Add other things here for non-gcc platforms */ #endif #endif /* __JackCompilerDeps_android__ */ jack2-1.9.22/android/JackControlAPIAndroid.cpp000066400000000000000000000043541436671425200210070ustar00rootroot00000000000000// u/* -*- Mode: C++ ; c-basic-offset: 4 -*- */ /* JACK control API implementation Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef WIN32 #include #include #include #endif #include "types.h" #include #include #include #include #include #include "JackControlAPIAndroid.h" #include "JackConstants.h" #include "JackServerGlobals.h" using namespace Jack; struct jackctl_sigmask { sigset_t signals; }; static jackctl_sigmask sigmask; SERVER_EXPORT int jackctl_wait_signals_and_return(jackctl_sigmask_t * sigmask) { int sig; bool waiting = true; while (waiting) { #if defined(sun) && !defined(__sun__) // SUN compiler only, to check sigwait(&sigmask->signals); #else sigwait(&sigmask->signals, &sig); #endif fprintf(stderr, "Jack main caught signal %d\n", sig); switch (sig) { case SIGUSR1: //jack_dump_configuration(engine, 1); break; case SIGUSR2: // driver exit waiting = false; break; case SIGTTOU: break; default: waiting = false; break; } } if (sig != SIGSEGV) { // unblock signals so we can see them during shutdown. // this will help prod developers not to lose sight of // bugs that cause segfaults etc. during shutdown. sigprocmask(SIG_UNBLOCK, &sigmask->signals, 0); } return sig; } jack2-1.9.22/android/JackControlAPIAndroid.h000066400000000000000000000021601436671425200204450ustar00rootroot00000000000000/* JACK control API Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackControlAPIAndroid__ #define __JackControlAPIAndroid__ #include "JackCompilerDeps.h" /** opaque type for sigmask object */ typedef struct jackctl_sigmask jackctl_sigmask_t; #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT int jackctl_wait_signals_and_return( jackctl_sigmask_t * signals); #ifdef __cplusplus } /* extern "C" */ #endif #endif jack2-1.9.22/android/JackError.cpp000066400000000000000000000077211436671425200166260ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "JackError.h" #include "JackGlobals.h" #include "JackMessageBuffer.h" #define LOG_BUF_SIZE 1024 #undef LOG_TAG #ifdef SERVER_SIDE #define LOG_TAG "JackAudioServer" #else #define LOG_TAG "JackAudioClient" #endif #include using namespace Jack; static bool change_thread_log_function(jack_log_function_t log_function) { return (jack_tls_get(JackGlobals::fKeyLogFunction) == NULL && jack_tls_set(JackGlobals::fKeyLogFunction, (void*)log_function)); } SERVER_EXPORT int set_threaded_log_function() { return change_thread_log_function(JackMessageBufferAdd); } void jack_log_function(int level, const char *message) { void (* log_callback)(const char *); switch (level) { case LOG_LEVEL_INFO: log_callback = jack_info_callback; break; case LOG_LEVEL_ERROR: log_callback = jack_error_callback; break; default: return; } log_callback(message); } static void jack_format_and_log(int level, const char *prefix, const char *fmt, va_list ap) { char buffer[256]; size_t len; jack_log_function_t log_function; if (prefix != NULL) { len = strlen(prefix); assert(len < 256); memcpy(buffer, prefix, len); } else { len = 0; } vsnprintf(buffer + len, sizeof(buffer) - len, fmt, ap); log_function = (jack_log_function_t)jack_tls_get(JackGlobals::fKeyLogFunction); /* if log function is not overridden for thread, use default one */ if (log_function == NULL) { log_function = jack_log_function; //log_function(LOG_LEVEL_INFO, "------ Using default log function"); } else { //log_function(LOG_LEVEL_INFO, "++++++ Using thread-specific log function"); } log_function(level, buffer); } SERVER_EXPORT void jack_error(const char *fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, buf); } SERVER_EXPORT void jack_info(const char *fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); __android_log_write(ANDROID_LOG_INFO, LOG_TAG, buf); } SERVER_EXPORT void jack_log(const char *fmt,...) { va_list ap; char buf[LOG_BUF_SIZE]; if (JackGlobals::fVerbose) { va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); __android_log_write(ANDROID_LOG_VERBOSE, LOG_TAG, buf); } } SERVER_EXPORT void default_jack_error_callback(const char *desc) { fprintf(stderr, "%s\n", desc); fflush(stderr); } SERVER_EXPORT void default_jack_info_callback(const char *desc) { fprintf(stdout, "%s\n", desc); fflush(stdout); } SERVER_EXPORT void silent_jack_error_callback(const char *desc) {} SERVER_EXPORT void silent_jack_info_callback(const char *desc) {} SERVER_EXPORT void (*jack_error_callback)(const char *desc) = &default_jack_error_callback; SERVER_EXPORT void (*jack_info_callback)(const char *desc) = &default_jack_info_callback; jack2-1.9.22/android/JackGoldfishDriver.cpp000066400000000000000000000161251436671425200204460ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackGoldfishDriver.h" #include "JackDriverLoader.h" #include "JackThreadedDriver.h" #include "JackEngineControl.h" #include "JackGraphManager.h" #include "JackCompilerDeps.h" #include #include #include #include #define JACK_GOLDFISH_BUFFER_SIZE 4096 namespace Jack { static char const * const kAudioDeviceName = "/dev/eac"; int JackGoldfishDriver::Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_uid, const char* playback_driver_uid, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { jack_log("JackGoldfishDriver::Open"); // Generic JackAudioDriver Open if (JackAudioDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_uid, playback_driver_uid, capture_latency, playback_latency) != 0) { return -1; } mFd = ::open(kAudioDeviceName, O_RDWR); jack_log("JackGoldfishDriver::Open(mFd=%d)", mFd); if (!mBuffer) mBuffer = (short *) malloc(sizeof(short) * JACK_GOLDFISH_BUFFER_SIZE * 2); //JackAudioDriver::SetBufferSize(buffer_size); //JackAudioDriver::SetSampleRate(samplerate); return 0; } int JackGoldfishDriver::Close() { jack_log("JackGoldfishDriver::Close"); // Generic audio driver close int res = JackAudioDriver::Close(); if (mFd >= 0) ::close(mFd); if (mBuffer) { free(mBuffer); mBuffer = NULL; } return res; } int JackGoldfishDriver::Read() { jack_log("JackGoldfishDriver::Read"); for (int i = 0; i < fCaptureChannels; i++) { //silence memset(GetInputBuffer(i), 0, sizeof(jack_default_audio_sample_t) * JACK_GOLDFISH_BUFFER_SIZE /* fEngineControl->fBufferSize */); } return 0; } int JackGoldfishDriver::Write() { jack_log("JackGoldfishDriver::Write"); //write(mFd, GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); jack_default_audio_sample_t* outputBuffer_1 = GetOutputBuffer(0); jack_default_audio_sample_t* outputBuffer_2 = GetOutputBuffer(1); for(int i=0, j=0; ifBufferSize */; i++) { //convert float to short *(mBuffer + j) = (short) (*(outputBuffer_1 + i) * 32640); j++; *(mBuffer + j) = (short) (*(outputBuffer_2 + i) * 32640); j++; } write(mFd, mBuffer, sizeof(short) * JACK_GOLDFISH_BUFFER_SIZE * 2); return 0; } int JackGoldfishDriver::SetBufferSize(jack_nframes_t buffer_size) { jack_log("JackGoldfishDriver::SetBufferSize"); JackAudioDriver::SetBufferSize(buffer_size); return 0; } } // end of namespace #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("goldfish", JackDriverMaster, "Timer based backend", &filler); value.ui = 2U; jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamUInt, &value, NULL, "Number of capture ports", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamUInt, &value, NULL, "Number of playback ports", NULL); value.ui = 44100U; jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); value.ui = JACK_GOLDFISH_BUFFER_SIZE; jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); value.ui = 21333U; jack_driver_descriptor_add_parameter(desc, &filler, "wait", 'w', JackDriverParamUInt, &value, NULL, "Number of usecs to wait between engine processes", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { jack_nframes_t sample_rate = 44100; jack_nframes_t buffer_size = JACK_GOLDFISH_BUFFER_SIZE; unsigned int capture_ports = 2; unsigned int playback_ports = 2; int wait_time = 0; const JSList * node; const jack_driver_param_t * param; bool monitor = false; for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; switch (param->character) { case 'C': capture_ports = param->value.ui; break; case 'P': playback_ports = param->value.ui; break; case 'r': sample_rate = param->value.ui; break; case 'p': buffer_size = param->value.ui; break; case 'w': wait_time = param->value.ui; break; case 'm': monitor = param->value.i; break; } } if (wait_time > 0) { buffer_size = lroundf((wait_time * sample_rate) / 1000000.0f); if (buffer_size > BUFFER_SIZE_MAX) { buffer_size = BUFFER_SIZE_MAX; jack_error("Buffer size set to %d", BUFFER_SIZE_MAX); } } Jack::JackDriverClientInterface* driver = new Jack::JackThreadedDriver(new Jack::JackGoldfishDriver("system", "goldfish_pcm", engine, table)); if (driver->Open(buffer_size, sample_rate, 1, 1, capture_ports, playback_ports, monitor, "goldfish", "goldfish", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } #ifdef __cplusplus } #endif jack2-1.9.22/android/JackGoldfishDriver.h000066400000000000000000000035351436671425200201140ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackGoldfishDriver__ #define __JackGoldfishDriver__ #include "JackAudioDriver.h" namespace Jack { /*! \brief The goldfish driver. */ class JackGoldfishDriver : public JackAudioDriver { public: JackGoldfishDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackAudioDriver(name, alias, engine, table), mFd(-1), mBuffer(NULL) {} virtual ~JackGoldfishDriver() {} int Open(jack_nframes_t buffe_size, jack_nframes_t samplerate, bool capturing, bool playing, int chan_in, int chan_out, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); int Close(); int Read(); int Write(); int SetBufferSize(jack_nframes_t buffer_size); private: int mFd; short *mBuffer; }; } // end of namespace #endif jack2-1.9.22/android/JackOpenSLESDriver.cpp000066400000000000000000000175001436671425200202750ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackOpenSLESDriver.h" #include "JackDriverLoader.h" #include "JackThreadedDriver.h" #include "JackEngineControl.h" #include "JackGraphManager.h" #include "JackCompilerDeps.h" #include #include #include #include #include #include "opensl_io.h" #define JACK_OPENSLES_DEFAULT_SAMPLERATE 48000 #define JACK_OPENSLES_DEFAULT_BUFFER_SIZE 960 namespace Jack { static OPENSL_STREAM *pOpenSL_stream; int JackOpenSLESDriver::Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_uid, const char* playback_driver_uid, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { jack_log("JackOpenSLESDriver::Open"); // Generic JackAudioDriver Open if (JackAudioDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_uid, playback_driver_uid, capture_latency, playback_latency) != 0) { return -1; } if (capturing) { inbuffer = (float *) malloc(sizeof(float) * buffer_size); //mono input memset(inbuffer, 0, sizeof(float) * buffer_size); } if (playing) { outbuffer = (float *) malloc(sizeof(float) * buffer_size * 2); //stereo output memset(outbuffer, 0, sizeof(float) * buffer_size * 2); } pOpenSL_stream = android_OpenAudioDevice(samplerate, capturing ? 1 : 0, playing ? 2 : 0, buffer_size); if (pOpenSL_stream == NULL) return -1; return 0; } int JackOpenSLESDriver::Close() { jack_log("JackOpenSLESDriver::Close"); // Generic audio driver close int res = JackAudioDriver::Close(); android_CloseAudioDevice(pOpenSL_stream); if (inbuffer) { free(inbuffer); inbuffer = NULL; } if (outbuffer) { free(outbuffer); outbuffer = NULL; } return res; } int JackOpenSLESDriver::Read() { //jack_log("JackOpenSLESDriver::Read"); jack_default_audio_sample_t* inputBuffer_1 = GetInputBuffer(0); jack_default_audio_sample_t* inputBuffer_2 = GetInputBuffer(1); if (inbuffer) { int samps = android_AudioIn(pOpenSL_stream,inbuffer,fEngineControl->fBufferSize); for (int i = 0; i < samps; i++) { *(inputBuffer_1 + i) = *(inbuffer + i); *(inputBuffer_2 + i) = *(inbuffer + i); } } else { for (int i = 0; i < fCaptureChannels; i++) { memset(GetInputBuffer(i), 0, sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); //silence } } return 0; } int JackOpenSLESDriver::Write() { //jack_log("JackOpenSLESDriver::Write"); jack_default_audio_sample_t* outputBuffer_1 = GetOutputBuffer(0); jack_default_audio_sample_t* outputBuffer_2 = GetOutputBuffer(1); if (outbuffer) { android_AudioOut(pOpenSL_stream, outbuffer, fEngineControl->fBufferSize * 2); //stereo output for (unsigned int i = 0, j = 0; i < fEngineControl->fBufferSize; i++) { *(outbuffer + j) = *(outputBuffer_1 + i); j++; *(outbuffer + j) = *(outputBuffer_2 + i); j++; } } return 0; } int JackOpenSLESDriver::SetBufferSize(jack_nframes_t buffer_size) { jack_log("JackOpenSLESDriver::SetBufferSize"); JackAudioDriver::SetBufferSize(buffer_size); return 0; } } // end of namespace #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("opensles", JackDriverMaster, "Timer based backend", &filler); value.ui = 2U; jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamUInt, &value, NULL, "Number of capture ports", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamUInt, &value, NULL, "Number of playback ports", NULL); value.ui = 48000U; jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); value.ui = JACK_OPENSLES_DEFAULT_BUFFER_SIZE; jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); value.ui = 21333U; jack_driver_descriptor_add_parameter(desc, &filler, "wait", 'w', JackDriverParamUInt, &value, NULL, "Number of usecs to wait between engine processes", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { jack_nframes_t sample_rate = JACK_OPENSLES_DEFAULT_SAMPLERATE; jack_nframes_t buffer_size = JACK_OPENSLES_DEFAULT_BUFFER_SIZE; unsigned int capture_ports = 0; unsigned int playback_ports = 2; int wait_time = 0; const JSList * node; const jack_driver_param_t * param; bool monitor = false; for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; switch (param->character) { case 'C': capture_ports = param->value.ui; break; case 'P': playback_ports = param->value.ui; break; case 'r': sample_rate = param->value.ui; break; case 'p': buffer_size = param->value.ui; break; case 'w': wait_time = param->value.ui; break; case 'm': monitor = param->value.i; break; } } if (wait_time > 0) { buffer_size = lroundf((wait_time * sample_rate) / 1000000.0f); if (buffer_size > BUFFER_SIZE_MAX) { buffer_size = BUFFER_SIZE_MAX; jack_error("Buffer size set to %d", BUFFER_SIZE_MAX); } } Jack::JackDriverClientInterface* driver = new Jack::JackThreadedDriver(new Jack::JackOpenSLESDriver("system", "opensles_pcm", engine, table)); if (driver->Open(buffer_size, sample_rate, capture_ports? 1 : 0, playback_ports? 1 : 0, capture_ports, playback_ports, monitor, "opensles", "opensles", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } #ifdef __cplusplus } #endif jack2-1.9.22/android/JackOpenSLESDriver.h000066400000000000000000000035641436671425200177470ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackOpenSLESDriver__ #define __JackOpenSLESDriver__ #include "JackAudioDriver.h" namespace Jack { /*! \brief The OpenSLES driver. */ class JackOpenSLESDriver : public JackAudioDriver { public: JackOpenSLESDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackAudioDriver(name, alias, engine, table), inbuffer(NULL), outbuffer(NULL) {} virtual ~JackOpenSLESDriver() {} int Open(jack_nframes_t buffe_size, jack_nframes_t samplerate, bool capturing, bool playing, int chan_in, int chan_out, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); int Close(); int Read(); int Write(); int SetBufferSize(jack_nframes_t buffer_size); private: float *inbuffer; float *outbuffer; }; } // end of namespace #endif jack2-1.9.22/android/JackPlatformPlug_os.h000066400000000000000000000053441436671425200203160ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackPlatformPlug_android__ #define __JackPlatformPlug_android__ #define jack_server_dir "/data/misc/jack" #define jack_client_dir "/data/misc/jack" #define JACK_DEFAULT_DRIVER "alsa" namespace Jack { struct JackRequest; struct JackResult; class JackPosixMutex; class JackAndroidThread; class JackFifo; class JackSocketServerChannel; class JackSocketClientChannel; class JackSocketServerNotifyChannel; class JackSocketNotifyChannel; class JackClientSocket; class JackNetUnixSocket; } /* __JackPlatformMutex__ */ #include "JackPosixMutex.h" namespace Jack {typedef JackPosixMutex JackMutex; } /* __JackPlatformThread__ */ #include "JackAndroidThread.h" namespace Jack { typedef JackAndroidThread JackThread; } /* __JackPlatformSynchro__ client activation */ /* #include "JackFifo.h" namespace Jack { typedef JackFifo JackSynchro; } */ #include "JackAndroidSemaphore.h" namespace Jack { typedef JackAndroidSemaphore JackSynchro; } /* __JackPlatformChannelTransaction__ */ /* #include "JackSocket.h" namespace Jack { typedef JackClientSocket JackChannelTransaction; } */ /* __JackPlatformProcessSync__ */ #include "JackPosixProcessSync.h" namespace Jack { typedef JackPosixProcessSync JackProcessSync; } /* __JackPlatformServerChannel__ */ #include "JackSocketServerChannel.h" namespace Jack { typedef JackSocketServerChannel JackServerChannel; } /* __JackPlatformClientChannel__ */ #include "JackSocketClientChannel.h" namespace Jack { typedef JackSocketClientChannel JackClientChannel; } /* __JackPlatformServerNotifyChannel__ */ #include "JackSocketServerNotifyChannel.h" namespace Jack { typedef JackSocketServerNotifyChannel JackServerNotifyChannel; } /* __JackPlatformNotifyChannel__ */ #include "JackSocketNotifyChannel.h" namespace Jack { typedef JackSocketNotifyChannel JackNotifyChannel; } /* __JackPlatformNetSocket__ */ #include "JackNetUnixSocket.h" namespace Jack { typedef JackNetUnixSocket JackNetSocket; } #endif jack2-1.9.22/android/JackSapaProxy.cpp000066400000000000000000000145671436671425200174710ustar00rootroot00000000000000/* Copyright (C) 2014 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackSapaProxy.h" #include "JackServerGlobals.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" #include "JackArgParser.h" #include #include // Example) // sapaproxy // .----------. // sapaproxy:__system_capture_1 -->| |--> sapaproxy:capture_1 // sapaproxy:__system_capture_2 -->| |--> sapaproxy:capture_2 // ... | | ... // | | // sapaproxy:playback_1 ---------->| |--> sapaproxy:__system_playback_1 // sapaproxy:playback_2 ---------->| |--> sapaproxy:__system_playback_2 // sapaproxy:playback_3 ---------->| |--> sapaproxy:__system_playback_3 // sapaproxy:playback_4 ---------->| |--> sapaproxy:__system_playback_4 // ... | | ... // '----------' namespace Jack { JackSapaProxy::JackSapaProxy(jack_client_t* client, const JSList* params) :fClient(client) { jack_log("JackSapaProxy::JackSapaProxy"); fCapturePorts = fPlaybackPorts = 0; const JSList* node; const jack_driver_param_t* param; for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*)node->data; switch (param->character) { case 'C': fCapturePorts = (param->value.ui > SAPAPROXY_PORT_NUM_FOR_CLIENT)? SAPAPROXY_PORT_NUM_FOR_CLIENT : param->value.ui; break; case 'P': fPlaybackPorts = (param->value.ui > SAPAPROXY_PORT_NUM_FOR_CLIENT)? SAPAPROXY_PORT_NUM_FOR_CLIENT : param->value.ui; break; } } } JackSapaProxy::~JackSapaProxy() { jack_log("JackSapaProxy::~JackSapaProxy"); } int JackSapaProxy::Setup(jack_client_t* client) { jack_log("JackSapaProxy::Setup"); //refer to system ports and create sapaproxy ports unsigned int i = 0, j = 0; const char **ports_system_capture; const char **ports_system_playback; unsigned int ports_system_capture_cnt = 0; unsigned int ports_system_playback_cnt = 0; char port_name[JACK_PORT_NAME_SIZE] = {0,}; ports_system_capture = jack_get_ports(client, "system:.*", NULL, JackPortIsPhysical | JackPortIsOutput); if (ports_system_capture != NULL) { for (i = 0; i < fCapturePorts && ports_system_capture[i]; i++) { sprintf(port_name, "__system_capture_%d", i + 1); fInputPorts[i] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); sprintf(port_name, "capture_%d", i + 1); fOutputPorts[i] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); ports_system_capture_cnt++; } jack_free(ports_system_capture); } ports_system_playback = jack_get_ports(client, "system:.*", NULL, JackPortIsPhysical | JackPortIsInput); if (ports_system_playback != NULL) { for (j = 0; j < fPlaybackPorts && ports_system_playback[j]; j++, i++) { sprintf(port_name, "playback_%d", j + 1); fInputPorts[i] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); sprintf(port_name, "__system_playback_%d", j + 1); fOutputPorts[i] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); ports_system_playback_cnt++; } jack_free(ports_system_playback); } //store actual number of system ports fCapturePorts = ports_system_capture_cnt; fPlaybackPorts = ports_system_playback_cnt; jack_set_process_callback(client, Process, this); jack_activate(client); //connect between sapaproxy and system ports for (unsigned int i = 0; i < ports_system_capture_cnt; i++) { sprintf(port_name, "system:capture_%d", i + 1); jack_connect(client, port_name, jack_port_name(fInputPorts[i])); } for (unsigned int i = 0; i < ports_system_playback_cnt; i++) { sprintf(port_name, "system:playback_%d", i + 1); jack_connect(client, jack_port_name(fOutputPorts[ports_system_capture_cnt + i]), port_name); } return 0; } int JackSapaProxy::Process(jack_nframes_t nframes, void* arg) { JackSapaProxy* sapaproxy = static_cast(arg); jack_default_audio_sample_t *in, *out; //for capture for (unsigned int i = 0; i < sapaproxy->fCapturePorts; i++) { in = (jack_default_audio_sample_t*)jack_port_get_buffer(sapaproxy->fInputPorts[i] , nframes); out = (jack_default_audio_sample_t*)jack_port_get_buffer(sapaproxy->fOutputPorts[i], nframes); // TODO: adjust pcm gain each platform here memcpy(out, in, sizeof(jack_default_audio_sample_t) * nframes); } //for playback for (unsigned int i = sapaproxy->fCapturePorts; i < (sapaproxy->fCapturePorts + sapaproxy->fPlaybackPorts); i++) { in = (jack_default_audio_sample_t*)jack_port_get_buffer(sapaproxy->fInputPorts[i] , nframes); out = (jack_default_audio_sample_t*)jack_port_get_buffer(sapaproxy->fOutputPorts[i], nframes); // TODO: adjust pcm gain each platform here memcpy(out, in, sizeof(jack_default_audio_sample_t) * nframes); } return 0; } } // namespace Jack jack2-1.9.22/android/JackSapaProxy.h000066400000000000000000000027401436671425200171240ustar00rootroot00000000000000/* Copyright (C) 2014 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackSapaProxy__ #define __JackSapaProxy__ #include "JackConstants.h" #include "JackPlatformPlug.h" #include "jack.h" #include "jslist.h" #include #include #define SAPAPROXY_PORT_NUM_FOR_CLIENT 16 namespace Jack { class JackSapaProxy { private: jack_client_t* fClient; jack_port_t *fInputPorts[SAPAPROXY_PORT_NUM_FOR_CLIENT]; jack_port_t *fOutputPorts[SAPAPROXY_PORT_NUM_FOR_CLIENT]; public: unsigned int fCapturePorts; unsigned int fPlaybackPorts; JackSapaProxy(jack_client_t* jack_client, const JSList* params); ~JackSapaProxy(); int Setup(jack_client_t* jack_client); static int Process(jack_nframes_t nframes, void* arg); }; } #endif jack2-1.9.22/android/JackSapaProxyIn.cpp000066400000000000000000000060011436671425200177400ustar00rootroot00000000000000/* Copyright (C) 2014 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackSapaProxy.h" #include "JackServerGlobals.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" #include "JackArgParser.h" #include #include #ifdef __cplusplus extern "C" { #endif #include "driver_interface.h" using namespace Jack; static Jack::JackSapaProxy* sapaproxy = NULL; SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("in", JackDriverNone, "sapaproxy client", &filler); value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamUInt, &value, NULL, "Number of capture ports", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamUInt, &value, NULL, "Number of playback ports", NULL); return desc; } SERVER_EXPORT int jack_internal_initialize(jack_client_t* jack_client, const JSList* params) { if (sapaproxy) { jack_info("sapaproxy already loaded"); return 1; } jack_log("Loading sapaproxy"); sapaproxy = new Jack::JackSapaProxy(jack_client, params); if (!params) { sapaproxy->fCapturePorts = 2U; sapaproxy->fPlaybackPorts = 0U; } sapaproxy->Setup(jack_client); assert(sapaproxy); return 0; } SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); Jack::JackArgParser parser(load_init); if (parser.GetArgc() > 0) parse_params = parser.ParseParams(desc, ¶ms); if (parse_params) { res = jack_internal_initialize(jack_client, params); parser.FreeParams(params); } return res; } SERVER_EXPORT void jack_finish(void* arg) { Jack::JackSapaProxy* sapaproxy = static_cast(arg); if (sapaproxy) { jack_log("Unloading sapaproxy"); delete sapaproxy; } } #ifdef __cplusplus } #endif jack2-1.9.22/android/JackSapaProxyOut.cpp000066400000000000000000000060021436671425200201420ustar00rootroot00000000000000/* Copyright (C) 2014 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackSapaProxy.h" #include "JackServerGlobals.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" #include "JackArgParser.h" #include #include #ifdef __cplusplus extern "C" { #endif #include "driver_interface.h" using namespace Jack; static Jack::JackSapaProxy* sapaproxy = NULL; SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("out", JackDriverNone, "sapaproxy client", &filler); value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamUInt, &value, NULL, "Number of capture ports", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamUInt, &value, NULL, "Number of playback ports", NULL); return desc; } SERVER_EXPORT int jack_internal_initialize(jack_client_t* jack_client, const JSList* params) { if (sapaproxy) { jack_info("sapaproxy already loaded"); return 1; } jack_log("Loading sapaproxy"); sapaproxy = new Jack::JackSapaProxy(jack_client, params); if (!params) { sapaproxy->fCapturePorts = 0U; sapaproxy->fPlaybackPorts = 2U; } sapaproxy->Setup(jack_client); assert(sapaproxy); return 0; } SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); Jack::JackArgParser parser(load_init); if (parser.GetArgc() > 0) parse_params = parser.ParseParams(desc, ¶ms); if (parse_params) { res = jack_internal_initialize(jack_client, params); parser.FreeParams(params); } return res; } SERVER_EXPORT void jack_finish(void* arg) { Jack::JackSapaProxy* sapaproxy = static_cast(arg); if (sapaproxy) { jack_log("Unloading sapaproxy"); delete sapaproxy; } } #ifdef __cplusplus } #endif jack2-1.9.22/android/JackShmMem_os.h000066400000000000000000000025411436671425200170640ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackShmMem__android__ #define __JackShmMem__android__ #include #include #define CHECK_MLOCK(ptr, size) (mlock((ptr), (size)) == 0) #define CHECK_MUNLOCK(ptr, size) (munlock((ptr), (size)) == 0) #define CHECK_MLOCKALL() (false) #define CHECK_MUNLOCKALL() (false) /* fix for crash jack server issue: * case 1) jack_destroy_shm() in JackShmReadWritePtr1::Init() causes crash * because server lost shared memory by destroying client side ahead. */ #ifndef SERVER_SIDE #define jack_destroy_shm(x) (0) #endif #endif /* __JackShmMem__android__ */ jack2-1.9.22/android/JackSystemDeps_os.h000066400000000000000000000020031436671425200177670ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackSystemDeps_android__ #define __JackSystemDeps_android__ #include "../posix/JackSystemDeps_os.h" /** * bionic c dependent functions */ #define pthread_setcanceltype(x,y) (0) #endif /* __JackSystemDeps_android__ */ jack2-1.9.22/android/NOTICE000066400000000000000000001324061436671425200151430ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ------------------------------------------------------------------------------- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ------------------------------------------------------------------------------- opensl_io.c: Android OpenSL input/output module (header) Copyright (c) 2012, Victor Lazzarini All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jack2-1.9.22/android/Shm.cpp000066400000000000000000000670531436671425200154770ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2005-2012 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define LOG_TAG "JAMSHMSERVICE" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BnAndroidShm.h" #include "AndroidShm.h" #include "JackConstants.h" #include #include #include #include #include #include #include #include #include "JackError.h" // remove ALOGI log #define jack_d //#define jack_d ALOGI #define jack_error ALOGE #define MEMORY_SIZE 10*1024 namespace android { jack_shmtype_t Shm::jack_shmtype = shm_ANDROID; /* The JACK SHM registry is a chunk of memory for keeping track of the * shared memory used by each active JACK server. This allows the * server to clean up shared memory when it exits. To avoid memory * leakage due to kill -9, crashes or debugger-driven exits, this * cleanup is also done when a new instance of that server starts. */ /* per-process global data for the SHM interfaces */ jack_shm_id_t Shm::registry_id; /* SHM id for the registry */ jack_shm_fd_t Shm::registry_fd = JACK_SHM_REGISTRY_FD; jack_shm_info_t Shm::registry_info = { JACK_SHM_NULL_INDEX, 0, 0, { MAP_FAILED } }; /* pointers to registry header and array */ jack_shm_header_t *Shm::jack_shm_header = NULL; jack_shm_registry_t *Shm::jack_shm_registry = NULL; char Shm::jack_shm_server_prefix[JACK_SERVER_NAME_SIZE+1] = ""; /* jack_shm_lock_registry() serializes updates to the shared memory * segment JACK uses to keep track of the SHM segments allocated to * all its processes, including multiple servers. * * This is not a high-contention lock, but it does need to work across * multiple processes. High transaction rates and realtime safety are * not required. Any solution needs to at least be portable to POSIX * and POSIX-like systems. * * We must be particularly careful to ensure that the lock be released * if the owning process terminates abnormally. Otherwise, a segfault * or kill -9 at the wrong moment could prevent JACK from ever running * again on that machine until after a reboot. */ #define JACK_SEMAPHORE_KEY 0x282929 #define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY #define JACK_REGISTRY_NAME "/jack-shm-registry" int Shm::semid = -1; pthread_mutex_t Shm::mutex = PTHREAD_MUTEX_INITIALIZER; //sp Shm::mShmService; sp Shm::mShmMemBase[JACK_SHM_HEAP_ENOUGH_COUNT] = {0,}; Shm* Shm::ref = NULL; Shm* Shm::Instantiate() { if(Shm::ref == NULL) { jack_d("shm::Instantiate is called"); Shm::ref = new Shm; //AndroidShm::instantiate(); } return ref; } Shm::Shm() { } Shm::~Shm() { } sp Shm::getShmService(){ return interface_cast(defaultServiceManager()->getService(String16("com.samsung.android.jam.IAndroidShm"))); } //sp& Shm::getShmService() { // if (mShmService.get() == 0) { // sp sm = defaultServiceManager(); // sp binder; // do { // binder = sm->getService(String16("com.samsung.android.jam.IAndroidShm")); // if (binder != 0) // break; // ALOGW("CameraService not published, waiting..."); // usleep(500000); // 0.5 s // } while(true); // mShmService = interface_cast(binder); // } // ALOGE_IF(mShmService==0, "no CameraService!?"); // return mShmService; //} void Shm::shm_copy_from_registry (jack_shm_info_t* /*si*/, jack_shm_registry_index_t ) { // not used } void Shm::shm_copy_to_registry (jack_shm_info_t* /*si*/, jack_shm_registry_index_t*) { // not used } void Shm::jack_release_shm_entry (jack_shm_registry_index_t index) { /* the registry must be locked */ jack_shm_registry[index].size = 0; jack_shm_registry[index].allocator = 0; memset (&jack_shm_registry[index].id, 0, sizeof (jack_shm_registry[index].id)); jack_shm_registry[index].fd = 0; } int Shm::release_shm_info (jack_shm_registry_index_t index) { /* must NOT have the registry locked */ if (jack_shm_registry[index].allocator == GetPID()) { if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } jack_release_shm_entry (index); jack_shm_unlock_registry (); jack_d ("release_shm_info: success!"); } else jack_error ("release_shm_info: error!"); return 0; } char* Shm::shm_addr (unsigned int fd) { if(fd >= JACK_SHM_HEAP_ENOUGH_COUNT) { jack_error("ignore to get memory buffer : index[%d] is too big", fd); return NULL; } sp service = Shm::getShmService(); if(service == NULL){ jack_error("shm service is null"); return NULL; } mShmMemBase[fd] = service->getBuffer(fd); if(mShmMemBase[fd] == NULL) { jack_error("fail to get memory buffer"); return NULL; } return ((char *) mShmMemBase[fd]->getBase()); } int Shm::shm_lock_registry (void) { pthread_mutex_lock (&mutex); return 0; } void Shm::shm_unlock_registry (void) { pthread_mutex_unlock (&mutex); } void Shm::release_shm_entry (jack_shm_registry_index_t index) { /* the registry must be locked */ jack_shm_registry[index].size = 0; jack_shm_registry[index].allocator = 0; memset (&jack_shm_registry[index].id, 0, sizeof (jack_shm_registry[index].id)); } void Shm::remove_shm (jack_shm_id_t *id) { int shm_fd = -1; jack_d("remove_id [%s]",(char*)id); if(!strcmp((const char*)id, JACK_REGISTRY_NAME)) { shm_fd = registry_fd; } else { for (int i = 0; i < MAX_SHM_ID; i++) { if(!strcmp((const char*)id, jack_shm_registry[i].id)) { shm_fd = jack_shm_registry[i].fd; break; } } } if (shm_fd >= 0) { sp service = getShmService(); if(service != NULL) { service->removeShm(shm_fd); } else { jack_error("shm service is null"); } } jack_d ("[APA] jack_remove_shm : ok "); } int Shm::access_registry (jack_shm_info_t * ri) { jack_d("access_registry\n"); /* registry must be locked */ sp service = getShmService(); if(service == NULL){ jack_error("shm service is null"); return EINVAL; } int shm_fd = service->getRegistryIndex(); strncpy (registry_id, JACK_REGISTRY_NAME, sizeof (registry_id) - 1); registry_id[sizeof (registry_id) - 1] = '\0'; if(service->isAllocated(shm_fd) == FALSE) { //jack_error ("Cannot mmap shm registry segment (%s)", // strerror (errno)); jack_error ("Cannot mmap shm registry segment"); //close (shm_fd); ri->ptr.attached_at = NULL; registry_fd = JACK_SHM_REGISTRY_FD; return EINVAL; } ri->fd = shm_fd; registry_fd = shm_fd; ri->ptr.attached_at = shm_addr(shm_fd); if(ri->ptr.attached_at == NULL) { ALOGE("attached pointer is null !"); jack_shm_header = NULL; jack_shm_registry = NULL; return 0; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = (jack_shm_header_t*)(ri->ptr.attached_at); jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); jack_d("jack_shm_header[%p],jack_shm_registry[%p]", jack_shm_header, jack_shm_registry); //close (shm_fd); // steph return 0; } int Shm::GetUID() { return geteuid(); } int Shm::GetPID() { return getpid(); } int Shm::jack_shm_lock_registry (void) { // TODO: replace semaphore to mutex pthread_mutex_lock (&mutex); return 0; } void Shm::jack_shm_unlock_registry (void) { // TODO: replace semaphore to mutex pthread_mutex_unlock (&mutex); return; } void Shm::shm_init_registry () { if(jack_shm_header == NULL) return; /* registry must be locked */ memset (jack_shm_header, 0, JACK_SHM_REGISTRY_SIZE); jack_shm_header->magic = JACK_SHM_MAGIC; //jack_shm_header->protocol = JACK_PROTOCOL_VERSION; jack_shm_header->type = jack_shmtype; jack_shm_header->size = JACK_SHM_REGISTRY_SIZE; jack_shm_header->hdr_len = sizeof (jack_shm_header_t); jack_shm_header->entry_len = sizeof (jack_shm_registry_t); for (int i = 0; i < MAX_SHM_ID; ++i) { jack_shm_registry[i].index = i; } } void Shm::set_server_prefix (const char *server_name) { snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), "jack-%d:%s:", GetUID(), server_name); } /* create a new SHM registry segment * * sets up global registry pointers, if successful * * returns: 0 if registry created successfully * nonzero error code if unable to allocate a new registry */ int Shm::create_registry (jack_shm_info_t * ri) { jack_d("create_registry\n"); /* registry must be locked */ int shm_fd = 0; strncpy (registry_id, JACK_REGISTRY_NAME, sizeof (registry_id) - 1); registry_id[sizeof (registry_id) - 1] = '\0'; sp service = getShmService(); if(service == NULL){ jack_error("shm service is null"); return EINVAL; } if((shm_fd = service->allocShm(JACK_SHM_REGISTRY_SIZE)) < 0) { jack_error("Cannot create shm registry segment"); registry_fd = JACK_SHM_REGISTRY_FD; return EINVAL; } service->setRegistryIndex(shm_fd); /* set up global pointers */ ri->fd = shm_fd; ri->index = JACK_SHM_REGISTRY_INDEX; registry_fd = shm_fd; ri->ptr.attached_at = shm_addr(shm_fd); ri->size = JACK_SHM_REGISTRY_SIZE; jack_shm_header = (jack_shm_header_t*)(ri->ptr.attached_at); jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); jack_d("create_registry jack_shm_header[%p], jack_shm_registry[%p]", jack_shm_header, jack_shm_registry); /* initialize registry contents */ shm_init_registry (); //close (shm_fd); // steph return 0; } int Shm::shm_validate_registry () { /* registry must be locked */ if(jack_shm_header == NULL) { return -1; } if ((jack_shm_header->magic == JACK_SHM_MAGIC) //&& (jack_shm_header->protocol == JACK_PROTOCOL_VERSION) && (jack_shm_header->type == jack_shmtype) && (jack_shm_header->size == JACK_SHM_REGISTRY_SIZE) && (jack_shm_header->hdr_len == sizeof (jack_shm_header_t)) && (jack_shm_header->entry_len == sizeof (jack_shm_registry_t))) { return 0; /* registry OK */ } return -1; } int Shm::server_initialize_shm (int new_registry) { int rc; jack_d("server_initialize_shm\n"); if (jack_shm_header) return 0; /* already initialized */ if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } rc = access_registry (®istry_info); if (new_registry) { remove_shm (®istry_id); rc = ENOENT; } switch (rc) { case ENOENT: /* registry does not exist */ rc = create_registry (®istry_info); break; case 0: /* existing registry */ if (shm_validate_registry () == 0) break; /* else it was invalid, so fall through */ case EINVAL: /* bad registry */ /* Apparently, this registry was created by an older * JACK version. Delete it so we can try again. */ release_shm (®istry_info); remove_shm (®istry_id); if ((rc = create_registry (®istry_info)) != 0) { //jack_error ("incompatible shm registry (%s)", // strerror (errno)); jack_error ("incompatible shm registry"); //#ifndef USE_POSIX_SHM // jack_error ("to delete, use `ipcrm -M 0x%0.8x'", JACK_SHM_REGISTRY_KEY); //#endif } break; default: /* failure return code */ break; } shm_unlock_registry (); return rc; } // here begin the API int Shm::register_server (const char *server_name, int new_registry) { int i, res = 0; jack_d("register_server new_registry[%d]\n", new_registry); set_server_prefix (server_name); if (server_initialize_shm (new_registry)) return ENOMEM; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } /* See if server_name already registered. Since server names * are per-user, we register the unique server prefix string. */ for (i = 0; i < MAX_SERVERS; i++) { if (strncmp (jack_shm_header->server[i].name, jack_shm_server_prefix, JACK_SERVER_NAME_SIZE) != 0) continue; /* no match */ if (jack_shm_header->server[i].pid == GetPID()) { res = 0; /* it's me */ goto unlock; } /* see if server still exists */ if (kill (jack_shm_header->server[i].pid, 0) == 0) { res = EEXIST; /* other server running */ goto unlock; } /* it's gone, reclaim this entry */ memset (&jack_shm_header->server[i], 0, sizeof (jack_shm_server_t)); } /* find a free entry */ for (i = 0; i < MAX_SERVERS; i++) { if (jack_shm_header->server[i].pid == 0) break; } if (i >= MAX_SERVERS) { res = ENOSPC; /* out of space */ goto unlock; } /* claim it */ jack_shm_header->server[i].pid = GetPID(); strncpy (jack_shm_header->server[i].name, jack_shm_server_prefix, JACK_SERVER_NAME_SIZE - 1); jack_shm_header->server[i].name[JACK_SERVER_NAME_SIZE - 1] = '\0'; unlock: shm_unlock_registry (); return res; } int Shm::unregister_server (const char * /* server_name */) { int i; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } for (i = 0; i < MAX_SERVERS; i++) { if (jack_shm_header->server[i].pid == GetPID()) { memset (&jack_shm_header->server[i], 0, sizeof (jack_shm_server_t)); } } shm_unlock_registry (); return 0; } int Shm::initialize_shm (const char *server_name) { int rc; if (jack_shm_header) return 0; /* already initialized */ set_server_prefix (server_name); if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } if ((rc = access_registry (®istry_info)) == 0) { if ((rc = shm_validate_registry ()) != 0) { jack_error ("Incompatible shm registry, " "are jackd and libjack in sync?"); } } shm_unlock_registry (); return rc; } int Shm::initialize_shm_server (void) { // not used return 0; } int Shm::initialize_shm_client (void) { // not used return 0; } int Shm::cleanup_shm (void) { int i; int destroy; jack_shm_info_t copy; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } for (i = 0; i < MAX_SHM_ID; i++) { jack_shm_registry_t* r; r = &jack_shm_registry[i]; memcpy (©, r, sizeof (jack_shm_info_t)); destroy = FALSE; /* ignore unused entries */ if (r->allocator == 0) continue; /* is this my shm segment? */ if (r->allocator == GetPID()) { /* allocated by this process, so unattach and destroy. */ release_shm (©); destroy = TRUE; } else { /* see if allocator still exists */ if (kill (r->allocator, 0)) { if (errno == ESRCH) { /* allocator no longer exists, * so destroy */ destroy = TRUE; } } } if (destroy) { int index = copy.index; if ((index >= 0) && (index < MAX_SHM_ID)) { remove_shm (&jack_shm_registry[index].id); release_shm_entry (index); } r->size = 0; r->allocator = 0; } } shm_unlock_registry (); return TRUE; } jack_shm_registry_t * Shm::get_free_shm_info () { /* registry must be locked */ jack_shm_registry_t* si = NULL; int i; for (i = 0; i < MAX_SHM_ID; ++i) { if (jack_shm_registry[i].size == 0) { break; } } if (i < MAX_SHM_ID) { si = &jack_shm_registry[i]; } return si; } int Shm::shmalloc (const char * /*shm_name*/, jack_shmsize_t size, jack_shm_info_t* si) { jack_shm_registry_t* registry; int shm_fd; int rc = -1; char name[SHM_NAME_MAX+1]; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } sp service = getShmService(); if(service == NULL){ rc = errno; jack_error("shm service is null"); goto unlock; } if ((registry = get_free_shm_info ()) == NULL) { jack_error ("shm registry full"); goto unlock; } snprintf (name, sizeof (name), "/jack-%d-%d", GetUID(), registry->index); if (strlen (name) >= sizeof (registry->id)) { jack_error ("shm segment name too long %s", name); goto unlock; } if((shm_fd = service->allocShm(size)) < 0) { rc = errno; jack_error ("Cannot create shm segment %s", name); goto unlock; } //close (shm_fd); registry->size = size; strncpy (registry->id, name, sizeof (registry->id) - 1); registry->id[sizeof (registry->id) - 1] = '\0'; registry->allocator = GetPID(); registry->fd = shm_fd; si->fd = shm_fd; si->index = registry->index; si->ptr.attached_at = MAP_FAILED; /* not attached */ rc = 0; /* success */ jack_d ("[APA] jack_shmalloc : ok "); unlock: shm_unlock_registry (); return rc; } void Shm::release_shm (jack_shm_info_t* /*si*/) { // do nothing } void Shm::release_lib_shm (jack_shm_info_t* /*si*/) { // do nothing } void Shm::destroy_shm (jack_shm_info_t* si) { /* must NOT have the registry locked */ if (si->index == JACK_SHM_NULL_INDEX) return; /* segment not allocated */ remove_shm (&jack_shm_registry[si->index].id); release_shm_info (si->index); } int Shm::attach_shm (jack_shm_info_t* si) { jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if((si->ptr.attached_at = shm_addr(registry->fd)) == NULL) { jack_error ("Cannot mmap shm segment %s", registry->id); close (si->fd); return -1; } return 0; } int Shm::attach_lib_shm (jack_shm_info_t* si) { int res = attach_shm(si); if (res == 0) si->size = jack_shm_registry[si->index].size; // Keep size in si struct return res; } int Shm::attach_shm_read (jack_shm_info_t* si) { jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if((si->ptr.attached_at = shm_addr(registry->fd)) == NULL) { jack_error ("Cannot mmap shm segment %s", registry->id); close (si->fd); return -1; } return 0; } int Shm::attach_lib_shm_read (jack_shm_info_t* si) { int res = attach_shm_read(si); if (res == 0) si->size = jack_shm_registry[si->index].size; // Keep size in si struct return res; } int Shm::resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { jack_shm_id_t id; /* The underlying type of `id' differs for SYSV and POSIX */ memcpy (&id, &jack_shm_registry[si->index].id, sizeof (id)); release_shm (si); destroy_shm (si); if (shmalloc ((char *) id, size, si)) { return -1; } return attach_shm (si); } void Shm::jack_shm_copy_from_registry (jack_shm_info_t* si, jack_shm_registry_index_t t) { Shm::Instantiate()->shm_copy_from_registry(si,t); } void Shm::jack_shm_copy_to_registry (jack_shm_info_t* si, jack_shm_registry_index_t* t) { Shm::Instantiate()->shm_copy_to_registry(si,t); } int Shm::jack_release_shm_info (jack_shm_registry_index_t t) { return Shm::Instantiate()->release_shm_info(t); } char* Shm::jack_shm_addr (jack_shm_info_t* si) { if(si != NULL) { return (char*)si->ptr.attached_at; } else { jack_error ("jack_shm_addr : jack_shm_info_t is NULL!"); return NULL; } } int Shm::jack_register_server (const char *server_name, int new_registry) { return Shm::Instantiate()->register_server(server_name, new_registry); } int Shm::jack_unregister_server (const char *server_name) { return Shm::Instantiate()->unregister_server(server_name); } int Shm::jack_initialize_shm (const char *server_name) { return Shm::Instantiate()->initialize_shm(server_name); } int Shm::jack_initialize_shm_server (void) { return Shm::Instantiate()->initialize_shm_server(); } int Shm::jack_initialize_shm_client () { return Shm::Instantiate()->initialize_shm_client(); } int Shm::jack_cleanup_shm (void) { return Shm::Instantiate()->cleanup_shm(); } int Shm::jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result) { return Shm::Instantiate()->shmalloc(shm_name, size, result); } void Shm::jack_release_shm (jack_shm_info_t* si) { Shm::Instantiate()->release_shm(si); } void Shm::jack_release_lib_shm (jack_shm_info_t* si) { Shm::Instantiate()->release_lib_shm(si); } void Shm::jack_destroy_shm (jack_shm_info_t* si) { Shm::Instantiate()->destroy_shm(si); } int Shm::jack_attach_shm (jack_shm_info_t* si) { return Shm::Instantiate()->attach_shm(si); } int Shm::jack_attach_lib_shm (jack_shm_info_t* si) { return Shm::Instantiate()->attach_lib_shm(si); } int Shm::jack_attach_shm_read (jack_shm_info_t* si) { return Shm::Instantiate()->attach_shm_read(si); } int Shm::jack_attach_lib_shm_read (jack_shm_info_t* si) { return Shm::Instantiate()->attach_lib_shm_read(si); } int Shm::jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { return Shm::Instantiate()->resize_shm(si, size); } }; void jack_shm_copy_from_registry (jack_shm_info_t* si, jack_shm_registry_index_t t) { android::Shm::jack_shm_copy_from_registry(si, t); } void jack_shm_copy_to_registry (jack_shm_info_t* si, jack_shm_registry_index_t* t) { android::Shm::jack_shm_copy_to_registry(si, t); } int jack_release_shm_info (jack_shm_registry_index_t t) { return android::Shm::jack_release_shm_info(t); } char* jack_shm_addr (jack_shm_info_t* si) { return android::Shm::jack_shm_addr(si); } int jack_register_server (const char *server_name, int new_registry) { return android::Shm::jack_register_server(server_name, new_registry); } int jack_unregister_server (const char *server_name) { return android::Shm::jack_unregister_server(server_name); } int jack_initialize_shm (const char *server_name) { return android::Shm::jack_initialize_shm(server_name); } int jack_initialize_shm_server (void) { return android::Shm::jack_initialize_shm_server(); } int jack_initialize_shm_client (void) { return android::Shm::jack_initialize_shm_client(); } int jack_cleanup_shm (void) { return android::Shm::jack_cleanup_shm(); } int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result) { return android::Shm::jack_shmalloc(shm_name, size, result); } void jack_release_shm (jack_shm_info_t* si) { android::Shm::jack_release_shm(si); } void jack_release_lib_shm (jack_shm_info_t* si) { android::Shm::jack_release_lib_shm(si); } void jack_destroy_shm (jack_shm_info_t* si) { android::Shm::jack_destroy_shm(si); } int jack_attach_shm (jack_shm_info_t* si) { return android::Shm::jack_attach_shm(si); } int jack_attach_lib_shm (jack_shm_info_t* si) { return android::Shm::jack_attach_lib_shm(si); } int jack_attach_shm_read (jack_shm_info_t* si) { return android::Shm::jack_attach_shm_read(si); } int jack_attach_lib_shm_read (jack_shm_info_t* si) { return android::Shm::jack_attach_lib_shm_read(si); } int jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { return android::Shm::jack_resize_shm(si, size); } void jack_instantiate() { android::AndroidShm::instantiate(); } jack2-1.9.22/android/Shm.h000066400000000000000000000130171436671425200151330ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2005-2012 Grame Copyright (C) 2013 Samsung Electronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_shm_android_h__ #define __jack_shm_android_h__ #include #include #include "types.h" #include "JackCompilerDeps.h" #include #include #ifdef __cplusplus extern "C" { #endif #define JACK_SHM_REGISTRY_FD -1 #define JACK_SHM_HEAP_ENOUGH_COUNT 300 void jack_instantiate(); #ifdef __cplusplus } #endif namespace android { class IAndroidShm; class Shm { public: static Shm* Instantiate(); virtual ~Shm(); private: Shm(); Shm( const Shm&); Shm& operator=(const Shm); private: void set_server_prefix (const char *server_name); int server_initialize_shm (int new_registry); int shm_lock_registry (void); void shm_unlock_registry (void); int access_registry (jack_shm_info_t *ri); void remove_shm (jack_shm_id_t *id); int create_registry (jack_shm_info_t *ri); int shm_validate_registry (); int GetUID(); int GetPID(); void shm_init_registry (); void release_shm_entry (jack_shm_registry_index_t index); jack_shm_registry_t * get_free_shm_info (); public: static void jack_shm_copy_from_registry (jack_shm_info_t*, jack_shm_registry_index_t); static void jack_shm_copy_to_registry (jack_shm_info_t*, jack_shm_registry_index_t*); static int jack_release_shm_info (jack_shm_registry_index_t); static char* jack_shm_addr (jack_shm_info_t* si); static int jack_register_server (const char *server_name, int new_registry); static int jack_unregister_server (const char *server_name); static int jack_initialize_shm (const char *server_name); static int jack_initialize_shm_server (void); static int jack_initialize_shm_client (void); static int jack_cleanup_shm (void); static int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result); static void jack_release_shm (jack_shm_info_t*); static void jack_release_lib_shm (jack_shm_info_t*); static void jack_destroy_shm (jack_shm_info_t*); static int jack_attach_shm (jack_shm_info_t*); static int jack_attach_lib_shm (jack_shm_info_t*); static int jack_attach_shm_read (jack_shm_info_t*); static int jack_attach_lib_shm_read (jack_shm_info_t*); static int jack_resize_shm (jack_shm_info_t*, jack_shmsize_t size); public: void shm_copy_from_registry (jack_shm_info_t*, jack_shm_registry_index_t); void shm_copy_to_registry (jack_shm_info_t*, jack_shm_registry_index_t*); int release_shm_info (jack_shm_registry_index_t); char* shm_addr (unsigned int fd); // here begin the API int register_server (const char *server_name, int new_registry); int unregister_server (const char *server_name); int initialize_shm (const char *server_name); int initialize_shm_server (void); int initialize_shm_client (void); int cleanup_shm (void); int shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result); void release_shm (jack_shm_info_t*); void release_lib_shm (jack_shm_info_t*); void destroy_shm (jack_shm_info_t*); int attach_shm (jack_shm_info_t*); int attach_lib_shm (jack_shm_info_t*); int attach_shm_read (jack_shm_info_t*); int attach_lib_shm_read (jack_shm_info_t*); int resize_shm (jack_shm_info_t*, jack_shmsize_t size); private: static jack_shmtype_t jack_shmtype; static jack_shm_id_t registry_id; static jack_shm_fd_t registry_fd; static jack_shm_info_t registry_info; static jack_shm_header_t *jack_shm_header; static jack_shm_registry_t *jack_shm_registry; static char jack_shm_server_prefix[JACK_SERVER_NAME_SIZE+1]; static int semid; static pthread_mutex_t mutex; static Shm* ref; void jack_release_shm_entry (jack_shm_registry_index_t index); int jack_shm_lock_registry (void); void jack_shm_unlock_registry (void); //static sp mShmService; static sp mShmMemBase[JACK_SHM_HEAP_ENOUGH_COUNT]; public: static sp getShmService(); }; }; #endif /* __jack_shm_android_h__ */ jack2-1.9.22/android/config.h000066400000000000000000000012601436671425200156460ustar00rootroot00000000000000/* Configuration header created by Waf - do not edit */ #ifndef _CONFIG_H_WAF #define _CONFIG_H_WAF /* #define HAVE_SAMPLERATE 0 */ /* #define HAVE_PPOLL 0 */ /* #define HAVE_SNDFILE */ /* #define HAVE_NCURSES 0 */ /* #define HAVE_CELT 0 */ /* #define HAVE_CELT_API_0_11 0 */ /* #define HAVE_CELT_API_0_8 0 */ /* #define HAVE_CELT_API_0_7 0 */ /* #define HAVE_CELT_API_0_5 0 */ /* #define HAVE_READLINE 0 */ #define CLIENT_NUM 32 #define PORT_NUM_FOR_CLIENT 24 #define PORT_NUM 256 #define PORT_NUM_MAX 512 #define ADDON_DIR "/system/lib/jack" #define JACK_LOCATION "/system/bin" #define JACKMP 1 /* #define USE_POSIX_SHM 0 */ /* #define __CLIENTDEBUG__ 1 */ #endif /* _CONFIG_H_WAF */ jack2-1.9.22/android/cxx-stl/000077500000000000000000000000001436671425200156335ustar00rootroot00000000000000jack2-1.9.22/android/cxx-stl/gnu-libstdc++/000077500000000000000000000000001436671425200201745ustar00rootroot00000000000000jack2-1.9.22/android/cxx-stl/gnu-libstdc++/libs/000077500000000000000000000000001436671425200211255ustar00rootroot00000000000000jack2-1.9.22/android/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/000077500000000000000000000000001436671425200232205ustar00rootroot00000000000000jack2-1.9.22/android/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include/000077500000000000000000000000001436671425200246435ustar00rootroot00000000000000jack2-1.9.22/android/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include/bits/000077500000000000000000000000001436671425200256045ustar00rootroot00000000000000jack2-1.9.22/android/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include/bits/ctype_base.h000066400000000000000000000005131436671425200300720ustar00rootroot00000000000000 //fix build error in KitKat #define _U _CTYPE_U #define _L _CTYPE_L #define _N _CTYPE_N #define _S _CTYPE_S #define _P _CTYPE_P #define _C _CTYPE_C #define _X _CTYPE_X #define _B _CTYPE_B #include <../../../../../../../../../../../prebuilts/ndk/current/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include/bits/ctype_base.h> jack2-1.9.22/android/opensl_io.c000066400000000000000000000373751436671425200164030ustar00rootroot00000000000000/* opensl_io.c: Android OpenSL input/output module Copyright (c) 2012, Victor Lazzarini All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "opensl_io.h" //#define CONV16BIT 32768 //#define CONVMYFLT (1./32768.) #define CONV16BIT 32640 #define CONVMYFLT (1./32640.) static void* createThreadLock(void); static int waitThreadLock(void *lock); static void notifyThreadLock(void *lock); static void destroyThreadLock(void *lock); static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context); static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context); // creates the OpenSL ES audio engine static SLresult openSLCreateEngine(OPENSL_STREAM *p) { SLresult result; // create engine result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL); if(result != SL_RESULT_SUCCESS) goto engine_end; // realize the engine result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE); if(result != SL_RESULT_SUCCESS) goto engine_end; // get the engine interface, which is needed in order to create other objects result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine)); if(result != SL_RESULT_SUCCESS) goto engine_end; engine_end: return result; } // opens the OpenSL ES device for output static SLresult openSLPlayOpen(OPENSL_STREAM *p) { SLresult result; SLuint32 sr = p->sr; SLuint32 channels = p->outchannels; if(channels){ // configure audio source SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; switch(sr){ case 8000: sr = SL_SAMPLINGRATE_8; break; case 11025: sr = SL_SAMPLINGRATE_11_025; break; case 16000: sr = SL_SAMPLINGRATE_16; break; case 22050: sr = SL_SAMPLINGRATE_22_05; break; case 24000: sr = SL_SAMPLINGRATE_24; break; case 32000: sr = SL_SAMPLINGRATE_32; break; case 44100: sr = SL_SAMPLINGRATE_44_1; break; case 48000: sr = SL_SAMPLINGRATE_48; break; case 64000: sr = SL_SAMPLINGRATE_64; break; case 88200: sr = SL_SAMPLINGRATE_88_2; break; case 96000: sr = SL_SAMPLINGRATE_96; break; case 192000: sr = SL_SAMPLINGRATE_192; break; default: return -1; } const SLInterfaceID ids[] = {SL_IID_VOLUME}; const SLboolean req[] = {SL_BOOLEAN_FALSE}; result = (*p->engineEngine)->CreateOutputMix(p->engineEngine, &(p->outputMixObject), 1, ids, req); if(result != SL_RESULT_SUCCESS) goto end_openaudio; // realize the output mix result = (*p->outputMixObject)->Realize(p->outputMixObject, SL_BOOLEAN_FALSE); int speakers; if(channels > 1) speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; else speakers = SL_SPEAKER_FRONT_CENTER; SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,channels, sr, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, speakers, SL_BYTEORDER_LITTLEENDIAN}; SLDataSource audioSrc = {&loc_bufq, &format_pcm}; // configure audio sink SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, p->outputMixObject}; SLDataSink audioSnk = {&loc_outmix, NULL}; // create audio player const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; const SLboolean req1[] = {SL_BOOLEAN_TRUE}; result = (*p->engineEngine)->CreateAudioPlayer(p->engineEngine, &(p->bqPlayerObject), &audioSrc, &audioSnk, 1, ids1, req1); if(result != SL_RESULT_SUCCESS) goto end_openaudio; // realize the player result = (*p->bqPlayerObject)->Realize(p->bqPlayerObject, SL_BOOLEAN_FALSE); if(result != SL_RESULT_SUCCESS) goto end_openaudio; // get the play interface result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_PLAY, &(p->bqPlayerPlay)); if(result != SL_RESULT_SUCCESS) goto end_openaudio; // get the buffer queue interface result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(p->bqPlayerBufferQueue)); if(result != SL_RESULT_SUCCESS) goto end_openaudio; // register callback on the buffer queue result = (*p->bqPlayerBufferQueue)->RegisterCallback(p->bqPlayerBufferQueue, bqPlayerCallback, p); if(result != SL_RESULT_SUCCESS) goto end_openaudio; // set the player's state to playing result = (*p->bqPlayerPlay)->SetPlayState(p->bqPlayerPlay, SL_PLAYSTATE_PLAYING); end_openaudio: return result; } return SL_RESULT_SUCCESS; } // Open the OpenSL ES device for input static SLresult openSLRecOpen(OPENSL_STREAM *p){ SLresult result; SLuint32 sr = p->sr; SLuint32 channels = p->inchannels; if(channels){ switch(sr){ case 8000: sr = SL_SAMPLINGRATE_8; break; case 11025: sr = SL_SAMPLINGRATE_11_025; break; case 16000: sr = SL_SAMPLINGRATE_16; break; case 22050: sr = SL_SAMPLINGRATE_22_05; break; case 24000: sr = SL_SAMPLINGRATE_24; break; case 32000: sr = SL_SAMPLINGRATE_32; break; case 44100: sr = SL_SAMPLINGRATE_44_1; break; case 48000: sr = SL_SAMPLINGRATE_48; break; case 64000: sr = SL_SAMPLINGRATE_64; break; case 88200: sr = SL_SAMPLINGRATE_88_2; break; case 96000: sr = SL_SAMPLINGRATE_96; break; case 192000: sr = SL_SAMPLINGRATE_192; break; default: return -1; } // configure audio source SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL}; SLDataSource audioSrc = {&loc_dev, NULL}; // configure audio sink int speakers; if(channels > 1) speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; else speakers = SL_SPEAKER_FRONT_CENTER; SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sr, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, speakers, SL_BYTEORDER_LITTLEENDIAN}; SLDataSink audioSnk = {&loc_bq, &format_pcm}; // create audio recorder // (requires the RECORD_AUDIO permission) const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; const SLboolean req[1] = {SL_BOOLEAN_TRUE}; result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine, &(p->recorderObject), &audioSrc, &audioSnk, 1, id, req); if (SL_RESULT_SUCCESS != result) goto end_recopen; // realize the audio recorder result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE); if (SL_RESULT_SUCCESS != result) goto end_recopen; // get the record interface result = (*p->recorderObject)->GetInterface(p->recorderObject, SL_IID_RECORD, &(p->recorderRecord)); if (SL_RESULT_SUCCESS != result) goto end_recopen; // get the buffer queue interface result = (*p->recorderObject)->GetInterface(p->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(p->recorderBufferQueue)); if (SL_RESULT_SUCCESS != result) goto end_recopen; // register callback on the buffer queue result = (*p->recorderBufferQueue)->RegisterCallback(p->recorderBufferQueue, bqRecorderCallback, p); if (SL_RESULT_SUCCESS != result) goto end_recopen; result = (*p->recorderRecord)->SetRecordState(p->recorderRecord, SL_RECORDSTATE_RECORDING); end_recopen: return result; } else return SL_RESULT_SUCCESS; } // close the OpenSL IO and destroy the audio engine static void openSLDestroyEngine(OPENSL_STREAM *p){ // destroy buffer queue audio player object, and invalidate all associated interfaces if (p->bqPlayerObject != NULL) { (*p->bqPlayerObject)->Destroy(p->bqPlayerObject); p->bqPlayerObject = NULL; p->bqPlayerPlay = NULL; p->bqPlayerBufferQueue = NULL; p->bqPlayerEffectSend = NULL; } // destroy audio recorder object, and invalidate all associated interfaces if (p->recorderObject != NULL) { (*p->recorderObject)->Destroy(p->recorderObject); p->recorderObject = NULL; p->recorderRecord = NULL; p->recorderBufferQueue = NULL; } // destroy output mix object, and invalidate all associated interfaces if (p->outputMixObject != NULL) { (*p->outputMixObject)->Destroy(p->outputMixObject); p->outputMixObject = NULL; } // destroy engine object, and invalidate all associated interfaces if (p->engineObject != NULL) { (*p->engineObject)->Destroy(p->engineObject); p->engineObject = NULL; p->engineEngine = NULL; } } // open the android audio device for input and/or output OPENSL_STREAM *android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes){ OPENSL_STREAM *p; p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM),1); p->inchannels = inchannels; p->outchannels = outchannels; p->sr = sr; p->inlock = createThreadLock(); p->outlock = createThreadLock(); if((p->outBufSamples = bufferframes*outchannels) != 0) { if((p->outputBuffer[0] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL || (p->outputBuffer[1] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL) { android_CloseAudioDevice(p); return NULL; } } if((p->inBufSamples = bufferframes*inchannels) != 0){ if((p->inputBuffer[0] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL || (p->inputBuffer[1] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL){ android_CloseAudioDevice(p); return NULL; } } p->currentInputIndex = 0; p->currentOutputBuffer = 0; p->currentInputIndex = p->inBufSamples; p->currentInputBuffer = 0; if(openSLCreateEngine(p) != SL_RESULT_SUCCESS) { android_CloseAudioDevice(p); return NULL; } if(openSLRecOpen(p) != SL_RESULT_SUCCESS) { android_CloseAudioDevice(p); return NULL; } if(openSLPlayOpen(p) != SL_RESULT_SUCCESS) { android_CloseAudioDevice(p); return NULL; } notifyThreadLock(p->outlock); notifyThreadLock(p->inlock); p->time = 0.; return p; } // close the android audio device void android_CloseAudioDevice(OPENSL_STREAM *p){ if (p == NULL) return; openSLDestroyEngine(p); if (p->inlock != NULL) { notifyThreadLock(p->inlock); destroyThreadLock(p->inlock); p->inlock = NULL; } if (p->outlock != NULL) { notifyThreadLock(p->outlock); destroyThreadLock(p->outlock); p->inlock = NULL; } if (p->outputBuffer[0] != NULL) { free(p->outputBuffer[0]); p->outputBuffer[0] = NULL; } if (p->outputBuffer[1] != NULL) { free(p->outputBuffer[1]); p->outputBuffer[1] = NULL; } if (p->inputBuffer[0] != NULL) { free(p->inputBuffer[0]); p->inputBuffer[0] = NULL; } if (p->inputBuffer[1] != NULL) { free(p->inputBuffer[1]); p->inputBuffer[1] = NULL; } free(p); } // returns timestamp of the processed stream double android_GetTimestamp(OPENSL_STREAM *p){ return p->time; } // this callback handler is called every time a buffer finishes recording void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { OPENSL_STREAM *p = (OPENSL_STREAM *) context; notifyThreadLock(p->inlock); } // gets a buffer of size samples from the device int android_AudioIn(OPENSL_STREAM *p,float *buffer,int size){ short *inBuffer; int i, bufsamps, index; if(p == NULL) return 0; bufsamps = p->inBufSamples; if(bufsamps == 0) return 0; index = p->currentInputIndex; inBuffer = p->inputBuffer[p->currentInputBuffer]; for(i=0; i < size; i++){ if (index >= bufsamps) { waitThreadLock(p->inlock); (*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, inBuffer,bufsamps*sizeof(short)); p->currentInputBuffer = (p->currentInputBuffer ? 0 : 1); index = 0; inBuffer = p->inputBuffer[p->currentInputBuffer]; } buffer[i] = (float) inBuffer[index++]*CONVMYFLT; } p->currentInputIndex = index; if(p->outchannels == 0) p->time += (double) size/(p->sr*p->inchannels); return i; } // this callback handler is called every time a buffer finishes playing void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { OPENSL_STREAM *p = (OPENSL_STREAM *) context; notifyThreadLock(p->outlock); } // puts a buffer of size samples to the device int android_AudioOut(OPENSL_STREAM *p, float *buffer,int size){ short *outBuffer; int i, bufsamps, index; if(p == NULL) return 0; bufsamps = p->outBufSamples; if(bufsamps == 0) return 0; index = p->currentOutputIndex; outBuffer = p->outputBuffer[p->currentOutputBuffer]; for(i=0; i < size; i++){ outBuffer[index++] = (short) (buffer[i]*CONV16BIT); if (index >= p->outBufSamples) { waitThreadLock(p->outlock); (*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue, outBuffer,bufsamps*sizeof(short)); p->currentOutputBuffer = (p->currentOutputBuffer ? 0 : 1); index = 0; outBuffer = p->outputBuffer[p->currentOutputBuffer]; } } p->currentOutputIndex = index; p->time += (double) size/(p->sr*p->outchannels); return i; } //---------------------------------------------------------------------- // thread Locks // to ensure synchronisation between callbacks and processing code void* createThreadLock(void) { threadLock *p; p = (threadLock*) malloc(sizeof(threadLock)); if (p == NULL) return NULL; memset(p, 0, sizeof(threadLock)); if (pthread_mutex_init(&(p->m), (pthread_mutexattr_t*) NULL) != 0) { free((void*) p); return NULL; } if (pthread_cond_init(&(p->c), (pthread_condattr_t*) NULL) != 0) { pthread_mutex_destroy(&(p->m)); free((void*) p); return NULL; } p->s = (unsigned char) 1; return p; } int waitThreadLock(void *lock) { threadLock *p; int retval = 0; p = (threadLock*) lock; pthread_mutex_lock(&(p->m)); while (!p->s) { pthread_cond_wait(&(p->c), &(p->m)); } p->s = (unsigned char) 0; pthread_mutex_unlock(&(p->m)); return NULL; } void notifyThreadLock(void *lock) { threadLock *p; p = (threadLock*) lock; pthread_mutex_lock(&(p->m)); p->s = (unsigned char) 1; pthread_cond_signal(&(p->c)); pthread_mutex_unlock(&(p->m)); return; } void destroyThreadLock(void *lock) { threadLock *p; p = (threadLock*) lock; if (p == NULL) return; notifyThreadLock(p); pthread_cond_destroy(&(p->c)); pthread_mutex_destroy(&(p->m)); free(p); } jack2-1.9.22/android/opensl_io.h000066400000000000000000000070531436671425200163760ustar00rootroot00000000000000/* opensl_io.c: Android OpenSL input/output module header Copyright (c) 2012, Victor Lazzarini All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OPENSL_IO #define OPENSL_IO #include #include #include #include typedef struct threadLock_{ pthread_mutex_t m; pthread_cond_t c; unsigned char s; } threadLock; #ifdef __cplusplus extern "C" { #endif typedef struct opensl_stream { // engine interfaces SLObjectItf engineObject; SLEngineItf engineEngine; // output mix interfaces SLObjectItf outputMixObject; // buffer queue player interfaces SLObjectItf bqPlayerObject; SLPlayItf bqPlayerPlay; SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; SLEffectSendItf bqPlayerEffectSend; // recorder interfaces SLObjectItf recorderObject; SLRecordItf recorderRecord; SLAndroidSimpleBufferQueueItf recorderBufferQueue; // buffer indexes int currentInputIndex; int currentOutputIndex; // current buffer half (0, 1) int currentOutputBuffer; int currentInputBuffer; // buffers short *outputBuffer[2]; short *inputBuffer[2]; // size of buffers int outBufSamples; int inBufSamples; // locks void* inlock; void* outlock; double time; int inchannels; int outchannels; int sr; } OPENSL_STREAM; /* Open the audio device with a given sampling rate (sr), input and output channels and IO buffer size in frames. Returns a handle to the OpenSL stream */ OPENSL_STREAM* android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes); /* Close the audio device */ void android_CloseAudioDevice(OPENSL_STREAM *p); /* Read a buffer from the OpenSL stream *p, of size samples. Returns the number of samples read. */ int android_AudioIn(OPENSL_STREAM *p, float *buffer,int size); /* Write a buffer to the OpenSL stream *p, of size samples. Returns the number of samples written. */ int android_AudioOut(OPENSL_STREAM *p, float *buffer,int size); /* Get the current IO block time in seconds */ double android_GetTimestamp(OPENSL_STREAM *p); #ifdef __cplusplus }; #endif #endif // #ifndef OPENSL_IO jack2-1.9.22/autooptions/000077500000000000000000000000001436671425200151755ustar00rootroot00000000000000jack2-1.9.22/autooptions/README000066400000000000000000000004601436671425200160550ustar00rootroot00000000000000Do not modify the code in this directory. It has been copied from its upstream git repo [1]. When submodules are introduced this directory can be made a submodule, but until then a verbatim copy is better than inlining the code in the toplevel wscript. [1] https://gitlab.com/karllinden/waf-autooptions jack2-1.9.22/autooptions/__init__.py000066400000000000000000000343721436671425200173170ustar00rootroot00000000000000# # Copyright (C) 2017 Karl Linden # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the # distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # import optparse import sys from waflib import Configure, Logs, Options, Utils # A list of AutoOptions. It is local to each module, so all modules that # use AutoOptions need to run both opt.load and conf.load. In contrast # to the define and style options this does not need to and cannot be # declared in the OptionsContext, because it is needed both for the # options and the configure phase. auto_options = [] class AutoOption: """ This class represents an auto option that can be used in conjunction with the waf build system. By default it adds options --foo and --no-foo respectively to turn on or off foo respectively. Furthermore it incorporats logic and checks that are required for these features. An option can have an arbitrary number of dependencies that must be present for the option to be enabled. An option can be enabled or disabled by default. Here is how the logic works: 1. If the option is explicitly disabled, through --no-foo, then no checks are made and the option is disabled. 2. If the option is explicitly enabled, through --foo, then check for all required dependencies, and if some of them are not found, then print a fatal error telling the user there were dependencies missing. 3. Otherwise, if the option is enabled by default, then check for all dependencies. If all dependencies are found the option is enabled. Otherwise it is disabled. 4. Lastly, if no option was given and the option is disabled by default, then no checks are performed and the option is disabled. To add a dependency to an option use the check, check_cfg and find_program methods of this class. The methods are merely small wrappers around their configuration context counterparts and behave identically. Note that adding dependencies is done in the options phase and not in the configure phase, although the checks are actually executed during the configure phase. Custom check functions can be added using the add_function method. As with the other checks the check function will be invoked during the configuration. Refer to the documentation of the add_function method for details. When all checks have been made and the class has made a decision the result is saved in conf.env['NAME'] where 'NAME' by default is the uppercase of the name argument to __init__, with hyphens replaced by underscores. This default can be changed with the conf_dest argument to __init__. The class will define a preprocessor symbol with the result. The default name is WITH_NAME, to not collide with the standard define of check_cfg, but it can be changed using the define argument to __init__. It can also be changed globally using set_auto_options_define. """ def __init__(self, opt, name, help=None, default=True, conf_dest=None, define=None, style=None): """ Class initializing method. Arguments: opt OptionsContext name name of the option, e.g. alsa help help text that will be displayed in --help output conf_dest conf.env variable to define to the result define the preprocessor symbol to define with the result style the option style to use; see below for options """ # The dependencies to check for. The elements are on the form # (func, k, kw) where func is the function or function name that # is used for the check and k and kw are the arguments and # options to give the function. self.deps = [] # Whether or not the option should be enabled. None indicates # that the checks have not been performed yet. self.enable = None self.help = help if help: if default: help_comment = ' (enabled by default if possible)' else: help_comment = ' (disabled by default)' option_help = help + help_comment no_option_help = None else: option_help = no_option_help = optparse.SUPPRESS_HELP self.dest = 'auto_option_' + name self.default = default safe_name = Utils.quote_define_name(name) self.conf_dest = conf_dest or safe_name default_define = opt.get_auto_options_define() self.define = define or default_define % safe_name if not style: style = opt.get_auto_options_style() self.style = style # plain (default): # --foo | --no-foo # yesno: # --foo=yes | --foo=no # yesno_and_hack: # --foo[=yes] | --foo=no or --no-foo # enable: # --enable-foo | --disble-foo # with: # --with-foo | --without-foo if style in ['plain', 'yesno', 'yesno_and_hack']: self.no_option = '--no-' + name self.yes_option = '--' + name elif style == 'enable': self.no_option = '--disable-' + name self.yes_option = '--enable-' + name elif style == 'with': self.no_option = '--without-' + name self.yes_option = '--with-' + name else: opt.fatal('invalid style') if style in ['yesno', 'yesno_and_hack']: opt.add_option( self.yes_option, dest=self.dest, action='store', choices=['auto', 'no', 'yes'], default='auto', help=option_help, metavar='no|yes') else: opt.add_option( self.yes_option, dest=self.dest, action='store_const', const='yes', default='auto', help=option_help) opt.add_option( self.no_option, dest=self.dest, action='store_const', const='no', default='auto', help=no_option_help) def check(self, *k, **kw): self.deps.append(('check', k, kw)) def check_cfg(self, *k, **kw): self.deps.append(('check_cfg', k, kw)) def find_program(self, *k, **kw): self.deps.append(('find_program', k, kw)) def add_function(self, func, *k, **kw): """ Add a custom function to be invoked as part of the configuration. During the configuration the function will be invoked with the configuration context as first argument followed by the arguments to this method, except for the func argument. The function must print a 'Checking for...' message, because it is referred to if the check fails and this option is requested. On configuration error the function shall raise conf.errors.ConfigurationError. """ self.deps.append((func, k, kw)) def _check(self, conf, required): """ This private method checks all dependencies. It checks all dependencies (even if some dependency was not found) so that the user can install all missing dependencies in one go, instead of playing the infamous hit-configure-hit-configure game. This function returns True if all dependencies were found and False otherwise. """ all_found = True for (f,k,kw) in self.deps: if hasattr(f, '__call__'): # This is a function supplied by add_function. func = f k = list(k) k.insert(0, conf) k = tuple(k) else: func = getattr(conf, f) try: func(*k, **kw) except conf.errors.ConfigurationError: all_found = False if required: Logs.error('The above check failed, but the ' 'checkee is required for %s.' % self.yes_option) return all_found def configure(self, conf): """ This function configures the option examining the command line option. It sets self.enable to whether this options should be enabled or not, that is True or False respectively. If not all dependencies were found self.enable will be False. conf.env['NAME'] and a preprocessor symbol will be defined with the result. If the option was desired but one or more dependencies were not found the an error message will be printed for each missing dependency. This function returns True on success and False on if the option was requested but cannot be enabled. """ # If the option has already been configured once, do not # configure it again. if self.enable != None: return True argument = getattr(Options.options, self.dest) if argument == 'no': self.enable = False retvalue = True elif argument == 'yes': retvalue = self.enable = self._check(conf, True) else: self.enable = self.default and self._check(conf, False) retvalue = True conf.env[self.conf_dest] = self.enable conf.define(self.define, int(self.enable)) return retvalue def summarize(self, conf): """ This function displays a result summary with the help text and the result of the configuration. """ if self.help: if self.enable: conf.msg(self.help, 'yes', color='GREEN') else: conf.msg(self.help, 'no', color='YELLOW') def options(opt): """ This function declares necessary variables in the option context. The reason for saving variables in the option context is to allow autooptions to be loaded from modules (which will receive a new instance of this module, clearing any global variables) with a uniform style and default in the entire project. Call this function through opt.load('autooptions'). """ if not hasattr(opt, 'auto_options_style'): opt.auto_options_style = 'plain' if not hasattr(opt, 'auto_options_define'): opt.auto_options_define = 'WITH_%s' def opt(f): """ Decorator: attach a new option function to Options.OptionsContext. :param f: method to bind :type f: function """ setattr(Options.OptionsContext, f.__name__, f) @opt def add_auto_option(self, *k, **kw): """ This function adds an AutoOption to the options context. It takes the same arguments as the initializer function of the AutoOptions class. """ option = AutoOption(self, *k, **kw) auto_options.append(option) return option @opt def get_auto_options_define(self): """ This function gets the default define name. This default can be changed through set_auto_optoins_define. """ return self.auto_options_define @opt def set_auto_options_define(self, define): """ This function sets the default define name. The default is 'WITH_%s', where %s will be replaced with the name of the option in uppercase. """ self.auto_options_define = define @opt def get_auto_options_style(self): """ This function gets the default option style, which will be used for the subsequent options. """ return self.auto_options_style @opt def set_auto_options_style(self, style): """ This function sets the default option style, which will be used for the subsequent options. """ self.auto_options_style = style @opt def apply_auto_options_hack(self): """ This function applies the hack necessary for the yesno_and_hack option style. The hack turns --foo into --foo=yes and --no-foo into --foo=no. It must be called before options are parsed, that is before the configure phase. """ for option in auto_options: # With the hack the yesno options simply extend plain options. if option.style == 'yesno_and_hack': for i in range(1, len(sys.argv)): if sys.argv[i] == option.yes_option: sys.argv[i] = option.yes_option + '=yes' elif sys.argv[i] == option.no_option: sys.argv[i] = option.yes_option + '=no' @Configure.conf def summarize_auto_options(self): """ This function prints a summary of the configuration of the auto options. Obviously, it must be called after conf.load('autooptions'). """ for option in auto_options: option.summarize(self) def configure(conf): """ This configures all auto options. Call it through conf.load('autooptions'). """ ok = True for option in auto_options: if not option.configure(conf): ok = False if not ok: conf.fatal('Some requested options had unsatisfied ' + 'dependencies.\n' + 'See the above configuration for details.') jack2-1.9.22/common/000077500000000000000000000000001436671425200141015ustar00rootroot00000000000000jack2-1.9.22/common/JackAC3Encoder.cpp000066400000000000000000000217251436671425200172530ustar00rootroot00000000000000/* Copyright (C) 2006 Jesse Chappell (AC3Jack) Copyright (C) 2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackAC3Encoder.h" #include "JackError.h" #include #include #include #define max(x,y) (((x)>(y)) ? (x) : (y)) #define min(x,y) (((x)<(y)) ? (x) : (y)) namespace Jack { #ifndef __ppc__ JackAC3Encoder::JackAC3Encoder(const JackAC3EncoderParams& params) { aften_set_defaults(&fAftenContext); fAftenContext.channels = params.channels; fAftenContext.samplerate = params.sample_rate; fAftenContext.params.bitrate = params.bitrate; int acmod = A52_ACMOD_MONO; int lfe = params.lfe; switch (params.channels) { case 1: acmod = A52_ACMOD_MONO; break; case 2: acmod = A52_ACMOD_STEREO; break; case 3: acmod = A52_ACMOD_3_0; break; case 4: acmod = A52_ACMOD_2_2; break; case 5: acmod = A52_ACMOD_3_2; break; default: break; } if (lfe) { fAftenContext.channels += 1; } fAftenContext.acmod = acmod; fAftenContext.lfe = lfe; fAftenContext.sample_format = A52_SAMPLE_FMT_FLT; fAftenContext.verbose = 1; fAftenContext.system.n_threads = 1; // create interleaved framebuffer for MAX_AC3_CHANNELS fSampleBuffer = new float[MAX_AC3_CHANNELS * A52_SAMPLES_PER_FRAME]; // create AC3 buffer fAC3Buffer = new unsigned char[A52_MAX_CODED_FRAME_SIZE]; memset(fAC3Buffer, 0, A52_MAX_CODED_FRAME_SIZE); fZeroBuffer = new unsigned char[SPDIF_FRAME_SIZE]; memset(fZeroBuffer, 0, SPDIF_FRAME_SIZE); fRingBuffer = jack_ringbuffer_create(32768); fOutSizeByte = 0; fFramePos = 0; fSampleRate = 0; fByteRate = 0; } bool JackAC3Encoder::Init(jack_nframes_t sample_rate) { fSampleRate = sample_rate; fByteRate = fSampleRate * sizeof(short) * 2; return (aften_encode_init(&fAftenContext) == 0); } JackAC3Encoder::~JackAC3Encoder() { aften_encode_close(&fAftenContext); delete [] fSampleBuffer; delete [] fAC3Buffer; delete [] fZeroBuffer; if (fRingBuffer) { jack_ringbuffer_free(fRingBuffer); } } void JackAC3Encoder::Process(float** inputs_buffer, float** outputs_buffer, int nframes) { // fill and process frame buffers as appropriate jack_nframes_t frames_left = A52_SAMPLES_PER_FRAME - fFramePos; jack_nframes_t offset = 0; while (offset < nframes) { if ((nframes - offset) >= frames_left) { // copy only frames_left more data jack_nframes_t pos = fFramePos * fAftenContext.channels; for (jack_nframes_t spos = offset; spos < offset + frames_left; ++spos) { for (size_t i = 0; i < fAftenContext.channels; ++i) { fSampleBuffer[pos + i] = inputs_buffer[i][spos]; } pos += fAftenContext.channels; } // use interleaved version #ifdef HAVE_AFTEN_NEW_API // note additional parameter 'nframes' // added in commit e1cbb66628de8aa496a75092d8d694234c67aa95 git://aften.git.sourceforge.net/gitroot/aften/aften int res = aften_encode_frame(&fAftenContext, fAC3Buffer + SPDIF_HEADER_SIZE, fSampleBuffer, nframes); #else // released version 0.0.8 hasn't the 'count' parameter int res = aften_encode_frame(&fAftenContext, fAC3Buffer + SPDIF_HEADER_SIZE, fSampleBuffer); #endif if (res < 0) { jack_error("aften_encode_frame error !!"); return; } fOutSizeByte = res; FillSpdifHeader(fAC3Buffer, fOutSizeByte + SPDIF_HEADER_SIZE); // push AC3 output to SPDIF ring buffer float calc_ac3byterate = (fOutSizeByte * fSampleRate / (float) A52_SAMPLES_PER_FRAME); jack_nframes_t silencebytes = (jack_nframes_t) (fOutSizeByte * (fByteRate / calc_ac3byterate)) - fOutSizeByte - SPDIF_HEADER_SIZE; jack_ringbuffer_write(fRingBuffer, (const char *)fAC3Buffer, fOutSizeByte + SPDIF_HEADER_SIZE); // write the proper remainder of zero padding (inefficient, should be memsetting) jack_ringbuffer_write(fRingBuffer, (const char *)fZeroBuffer, silencebytes); offset += frames_left; frames_left = A52_SAMPLES_PER_FRAME; fFramePos = 0; } else { // copy incoming data into frame buffers without processing jack_nframes_t pos = fFramePos * fAftenContext.channels; for (jack_nframes_t spos = offset; spos < nframes; ++spos) { for (size_t i = 0; i < fAftenContext.channels; ++i) { fSampleBuffer[pos + i] = inputs_buffer[i][spos]; } pos += fAftenContext.channels; } fFramePos += (nframes - offset); offset += (nframes-offset); } } Output2Driver(outputs_buffer, nframes); } void JackAC3Encoder::FillSpdifHeader(unsigned char* buf, int outsize) { // todo, use outsize and not assume the fixed frame size? int ac3outsize = outsize - SPDIF_HEADER_SIZE; buf[0] = 0x72; buf[1] = 0xf8; /* spdif syncword */ buf[2] = 0x1f; buf[3] = 0x4e; /* .............. */ buf[4] = 0x01; /* AC3 data */ buf[5] = buf[13] & 7; /* bsmod, stream = 0 */ buf[6] = (ac3outsize << 3) & 0xff; buf[7] = (ac3outsize >> 5) & 0xff; #if !IS_BIGENDIAN swab(buf+SPDIF_HEADER_SIZE, buf + SPDIF_HEADER_SIZE, ac3outsize); #endif } int JackAC3Encoder::Output2Driver(float** outputs, jack_nframes_t nframes) { int wrotebytes = 0; jack_nframes_t nframes_left = nframes; if (jack_ringbuffer_read_space(fRingBuffer) == 0) { // just write silence memset(outputs[0], 0, nframes * sizeof(jack_default_audio_sample_t)); memset(outputs[1], 0, nframes * sizeof(jack_default_audio_sample_t)); } else { jack_ringbuffer_data_t rb_data[2]; jack_ringbuffer_get_read_vector(fRingBuffer, rb_data); while (nframes_left > 0 && rb_data[0].len > 4) { jack_nframes_t towrite_frames = (rb_data[0].len) / (sizeof(short) * 2); towrite_frames = min(towrite_frames, nframes_left); // write and deinterleave into the two channels #if 1 sample_move_dS_s16(outputs[0] + (nframes - nframes_left), (char *) rb_data[0].buf, towrite_frames, sizeof(short) * 2); sample_move_dS_s16(outputs[1] + (nframes - nframes_left), (char *) rb_data[0].buf + sizeof(short), towrite_frames, sizeof(short) * 2); #else sample_move_dS_s16_24ph(outputs[0] + (nframes - nframes_left), (char *) rb_data[0].buf, towrite_frames, sizeof(short) * 2); sample_move_dS_s16_24ph(outputs[1] + (nframes - nframes_left), (char *) rb_data[0].buf + sizeof(short), towrite_frames, sizeof(short) * 2); #endif wrotebytes = towrite_frames * sizeof(short) * 2; nframes_left -= towrite_frames; jack_ringbuffer_read_advance(fRingBuffer, wrotebytes); jack_ringbuffer_get_read_vector(fRingBuffer, rb_data); } if (nframes_left > 0) { // write silence memset(outputs[0] + (nframes - nframes_left), 0, (nframes_left) * sizeof(jack_default_audio_sample_t)); memset(outputs[1] + (nframes - nframes_left), 0, (nframes_left) * sizeof(jack_default_audio_sample_t)); } } return wrotebytes; } void JackAC3Encoder::sample_move_dS_s16(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip) { /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { *dst = (*((short *) src)) / SAMPLE_MAX_16BIT; dst++; src += src_skip; } } void JackAC3Encoder::sample_move_dS_s16_24ph(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip) { /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { *dst = (((int)(*((short *) src))) << 8) / SAMPLE_MAX_24BIT; dst++; src += src_skip; } } void JackAC3Encoder::GetChannelName(const char* name, const char* alias, char* portname, int channel) { /* * 2 channels = L, R * 3 channels = L, C, R * 4 channels = L, R, LS, RS * 5 ch = L, C, R, LS, RS * 6 ch = L, C, R, LS, RS, LFE */ const char* AC3_name = ""; switch (channel) { case 0: AC3_name = "AC3_1_Left"; break; case 1: if (fAftenContext.channels == 2 || fAftenContext.channels == 4) { AC3_name = "AC3_2_Right"; } else { AC3_name = "AC3_2_Center"; } break; case 2: if (fAftenContext.channels == 4) { AC3_name = "AC3_3_LeftSurround"; } else { AC3_name = "AC3_3_Right"; } break; case 3: if (fAftenContext.channels == 4) { AC3_name = "AC3_4_RightSurround"; } else { AC3_name = "AC3_4_LeftSurround"; } break; case 4: if (fAftenContext.channels > 4) { AC3_name = "AC3_5_RightSurround"; } break; default: break; } // Last channel if (fAftenContext.lfe && (channel == fAftenContext.channels - 1)) { sprintf(portname, "%s:%s:AC3_%d_LFE", name, alias, fAftenContext.channels); } else { sprintf(portname, "%s:%s:%s", name, alias, AC3_name); } } #endif } // end of namespace jack2-1.9.22/common/JackAC3Encoder.h000066400000000000000000000050271436671425200167150ustar00rootroot00000000000000/* Copyright (C) 2006 Jesse Chappell (AC3Jack) Copyright (C) 2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackAC3Encoder__ #define __JackAC3Encoder__ #include #include #include "ringbuffer.h" #include "types.h" #define MAX_AC3_CHANNELS 6 #define SPDIF_HEADER_SIZE 8 #define SPDIF_FRAME_SIZE 6144 #define SAMPLE_MAX_16BIT 32768.0f #define SAMPLE_MAX_24BIT 8388608.0f namespace Jack { struct JackAC3EncoderParams { int64_t duration; unsigned int channels; int bitdepth; int bitrate; unsigned int sample_rate; bool lfe; }; class JackAC3Encoder { private: AftenContext fAftenContext; jack_ringbuffer_t* fRingBuffer; float* fSampleBuffer; unsigned char* fAC3Buffer; unsigned char* fZeroBuffer; int fOutSizeByte; jack_nframes_t fFramePos; jack_nframes_t fSampleRate; jack_nframes_t fByteRate; void FillSpdifHeader(unsigned char* buf, int outsize); int Output2Driver(float** outputs, jack_nframes_t nframes); void sample_move_dS_s16(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip); void sample_move_dS_s16_24ph(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip); public: #ifdef __ppc__ JackAC3Encoder(const JackAC3EncoderParams& params) {} virtual ~JackAC3Encoder() {} bool Init(jack_nframes_t sample_rate) {return false;} void Process(float** inputs, float** outputs, int nframes) {} void GetChannelName(const char* name, const char* alias, char* portname, int channel) {} #else JackAC3Encoder(const JackAC3EncoderParams& params); virtual ~JackAC3Encoder(); bool Init(jack_nframes_t sample_rate); void Process(float** inputs, float** outputs, int nframes); void GetChannelName(const char* name, const char* alias, char* portname, int channel); #endif }; typedef JackAC3Encoder * JackAC3EncoderPtr; } // end of namespace #endif jack2-1.9.22/common/JackAPI.cpp000066400000000000000000002216311436671425200160140ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __STDC_FORMAT_MACROS 1 #include #include "JackClient.h" #include "JackError.h" #include "JackGraphManager.h" #include "JackEngineControl.h" #include "JackClientControl.h" #include "JackGlobals.h" #include "JackTime.h" #include "JackPortType.h" #include using namespace Jack; #ifdef __cplusplus extern "C" { #endif typedef void (*print_function)(const char*); typedef void *(*thread_routine)(void*); LIB_EXPORT void jack_get_version( int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr); LIB_EXPORT const char* jack_get_version_string(); jack_client_t * jack_client_new_aux(const char* client_name, jack_options_t options, jack_status_t *status); LIB_EXPORT jack_client_t * jack_client_open(const char* client_name, jack_options_t options, jack_status_t *status, ...); LIB_EXPORT jack_client_t * jack_client_new(const char* client_name); LIB_EXPORT int jack_client_name_size(void); LIB_EXPORT char* jack_get_client_name(jack_client_t *client); LIB_EXPORT int jack_internal_client_new(const char* client_name, const char* load_name, const char* load_init); LIB_EXPORT void jack_internal_client_close(const char* client_name); LIB_EXPORT int jack_is_realtime(jack_client_t *client); LIB_EXPORT void jack_on_shutdown(jack_client_t *client, JackShutdownCallback shutdown_callback, void *arg); LIB_EXPORT void jack_on_info_shutdown(jack_client_t *client, JackInfoShutdownCallback shutdown_callback, void *arg); LIB_EXPORT int jack_set_process_callback(jack_client_t *client, JackProcessCallback process_callback, void *arg); LIB_EXPORT jack_nframes_t jack_thread_wait(jack_client_t *client, int status); // new LIB_EXPORT jack_nframes_t jack_cycle_wait(jack_client_t*); LIB_EXPORT void jack_cycle_signal(jack_client_t*, int status); LIB_EXPORT int jack_set_process_thread(jack_client_t* client, JackThreadCallback fun, void *arg); LIB_EXPORT int jack_set_thread_init_callback(jack_client_t *client, JackThreadInitCallback thread_init_callback, void *arg); LIB_EXPORT int jack_set_freewheel_callback(jack_client_t *client, JackFreewheelCallback freewheel_callback, void *arg); LIB_EXPORT int jack_set_freewheel(jack_client_t* client, int onoff); LIB_EXPORT int jack_set_buffer_size(jack_client_t *client, jack_nframes_t nframes); LIB_EXPORT int jack_set_buffer_size_callback(jack_client_t *client, JackBufferSizeCallback bufsize_callback, void *arg); LIB_EXPORT int jack_set_sample_rate_callback(jack_client_t *client, JackSampleRateCallback srate_callback, void *arg); LIB_EXPORT int jack_set_client_registration_callback(jack_client_t *, JackClientRegistrationCallback registration_callback, void *arg); LIB_EXPORT int jack_set_port_registration_callback(jack_client_t *, JackPortRegistrationCallback registration_callback, void *arg); LIB_EXPORT int jack_set_port_connect_callback(jack_client_t *, JackPortConnectCallback connect_callback, void *arg); LIB_EXPORT int jack_set_port_rename_callback(jack_client_t *, JackPortRenameCallback rename_callback, void *arg); LIB_EXPORT int jack_set_graph_order_callback(jack_client_t *, JackGraphOrderCallback graph_callback, void *); LIB_EXPORT int jack_set_xrun_callback(jack_client_t *, JackXRunCallback xrun_callback, void *arg); LIB_EXPORT int jack_set_latency_callback(jack_client_t *client, JackLatencyCallback latency_callback, void *arg); LIB_EXPORT int jack_activate(jack_client_t *client); LIB_EXPORT int jack_deactivate(jack_client_t *client); LIB_EXPORT jack_port_t * jack_port_register(jack_client_t *client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); LIB_EXPORT int jack_port_unregister(jack_client_t *, jack_port_t *); LIB_EXPORT void * jack_port_get_buffer(jack_port_t *, jack_nframes_t); LIB_EXPORT jack_uuid_t jack_port_uuid(const jack_port_t*); LIB_EXPORT const char* jack_port_name(const jack_port_t *port); LIB_EXPORT const char* jack_port_short_name(const jack_port_t *port); LIB_EXPORT int jack_port_flags(const jack_port_t *port); LIB_EXPORT const char* jack_port_type(const jack_port_t *port); LIB_EXPORT jack_port_type_id_t jack_port_type_id(const jack_port_t *port); LIB_EXPORT int jack_port_is_mine(const jack_client_t *, const jack_port_t *port); LIB_EXPORT int jack_port_connected(const jack_port_t *port); LIB_EXPORT int jack_port_connected_to(const jack_port_t *port, const char* port_name); LIB_EXPORT const char* * jack_port_get_connections(const jack_port_t *port); LIB_EXPORT const char* * jack_port_get_all_connections(const jack_client_t *client, const jack_port_t *port); LIB_EXPORT int jack_port_tie(jack_port_t *src, jack_port_t *dst); LIB_EXPORT int jack_port_untie(jack_port_t *port); // Old latency API LIB_EXPORT jack_nframes_t jack_port_get_latency(jack_port_t *port); LIB_EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t *, jack_port_t *port); LIB_EXPORT void jack_port_set_latency(jack_port_t *, jack_nframes_t); LIB_EXPORT int jack_recompute_total_latency(jack_client_t*, jack_port_t* port); // New latency API LIB_EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); LIB_EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range); LIB_EXPORT int jack_recompute_total_latencies(jack_client_t*); LIB_EXPORT int jack_port_set_name(jack_port_t *port, const char* port_name); LIB_EXPORT int jack_port_rename(jack_client_t *client, jack_port_t *port, const char* port_name); LIB_EXPORT int jack_port_set_alias(jack_port_t *port, const char* alias); LIB_EXPORT int jack_port_unset_alias(jack_port_t *port, const char* alias); LIB_EXPORT int jack_port_get_aliases(const jack_port_t *port, char* const aliases[2]); LIB_EXPORT int jack_port_request_monitor(jack_port_t *port, int onoff); LIB_EXPORT int jack_port_request_monitor_by_name(jack_client_t *client, const char* port_name, int onoff); LIB_EXPORT int jack_port_ensure_monitor(jack_port_t *port, int onoff); LIB_EXPORT int jack_port_monitoring_input(jack_port_t *port); LIB_EXPORT int jack_connect(jack_client_t *, const char* source_port, const char* destination_port); LIB_EXPORT int jack_disconnect(jack_client_t *, const char* source_port, const char* destination_port); LIB_EXPORT int jack_port_disconnect(jack_client_t *, jack_port_t *); LIB_EXPORT int jack_port_name_size(void); LIB_EXPORT int jack_port_type_size(void); LIB_EXPORT size_t jack_port_type_get_buffer_size(jack_client_t *client, const char* port_type); LIB_EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t *); LIB_EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t *); LIB_EXPORT const char* * jack_get_ports(jack_client_t *, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); LIB_EXPORT jack_port_t * jack_port_by_name(jack_client_t *, const char* port_name); LIB_EXPORT jack_port_t * jack_port_by_id(jack_client_t *client, jack_port_id_t port_id); LIB_EXPORT int jack_engine_takeover_timebase(jack_client_t *); LIB_EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t *); LIB_EXPORT jack_time_t jack_get_time(); LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs); LIB_EXPORT jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames); LIB_EXPORT jack_nframes_t jack_frame_time(const jack_client_t *); LIB_EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t *client); LIB_EXPORT int jack_get_cycle_times(const jack_client_t *client, jack_nframes_t *current_frames, jack_time_t *current_usecs, jack_time_t *next_usecs, float *period_usecs); LIB_EXPORT float jack_cpu_load(jack_client_t *client); LIB_EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t *); LIB_EXPORT void jack_set_error_function(print_function); LIB_EXPORT void jack_set_info_function(print_function); LIB_EXPORT float jack_get_max_delayed_usecs(jack_client_t *client); LIB_EXPORT float jack_get_xrun_delayed_usecs(jack_client_t *client); LIB_EXPORT void jack_reset_max_delayed_usecs(jack_client_t *client); LIB_EXPORT int jack_release_timebase(jack_client_t *client); LIB_EXPORT int jack_set_sync_callback(jack_client_t *client, JackSyncCallback sync_callback, void *arg); LIB_EXPORT int jack_set_sync_timeout(jack_client_t *client, jack_time_t timeout); LIB_EXPORT int jack_set_timebase_callback(jack_client_t *client, int conditional, JackTimebaseCallback timebase_callback, void *arg); LIB_EXPORT int jack_transport_locate(jack_client_t *client, jack_nframes_t frame); LIB_EXPORT jack_transport_state_t jack_transport_query(const jack_client_t *client, jack_position_t *pos); LIB_EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t *client); LIB_EXPORT int jack_transport_reposition(jack_client_t *client, const jack_position_t *pos); LIB_EXPORT void jack_transport_start(jack_client_t *client); LIB_EXPORT void jack_transport_stop(jack_client_t *client); LIB_EXPORT void jack_get_transport_info(jack_client_t *client, jack_transport_info_t *tinfo); LIB_EXPORT void jack_set_transport_info(jack_client_t *client, jack_transport_info_t *tinfo); LIB_EXPORT int jack_client_real_time_priority(jack_client_t*); LIB_EXPORT int jack_client_max_real_time_priority(jack_client_t*); LIB_EXPORT int jack_acquire_real_time_scheduling(jack_native_thread_t thread, int priority); LIB_EXPORT int jack_client_create_thread(jack_client_t* client, jack_native_thread_t *thread, int priority, int realtime, // boolean thread_routine routine, void *arg); LIB_EXPORT int jack_drop_real_time_scheduling(jack_native_thread_t thread); LIB_EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread); LIB_EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread); #ifndef WIN32 LIB_EXPORT void jack_set_thread_creator(jack_thread_creator_t jtc); #endif LIB_EXPORT char * jack_get_internal_client_name(jack_client_t *client, jack_intclient_t intclient); LIB_EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t *client, const char* client_name, jack_status_t *status); LIB_EXPORT jack_intclient_t jack_internal_client_load(jack_client_t *client, const char* client_name, jack_options_t options, jack_status_t *status, ...); LIB_EXPORT jack_status_t jack_internal_client_unload(jack_client_t *client, jack_intclient_t intclient); LIB_EXPORT void jack_free(void* ptr); LIB_EXPORT int jack_set_session_callback(jack_client_t* ext_client, JackSessionCallback session_callback, void* arg); LIB_EXPORT jack_session_command_t *jack_session_notify(jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path); LIB_EXPORT int jack_session_reply(jack_client_t* ext_client, jack_session_event_t *event); LIB_EXPORT void jack_session_event_free(jack_session_event_t* ev); LIB_EXPORT char* jack_client_get_uuid (jack_client_t *client); LIB_EXPORT char* jack_get_uuid_for_client_name(jack_client_t* ext_client, const char* client_name); LIB_EXPORT char* jack_get_client_name_by_uuid(jack_client_t* ext_client, const char* client_uuid); LIB_EXPORT int jack_reserve_client_name(jack_client_t* ext_client, const char* name, const char* uuid); LIB_EXPORT void jack_session_commands_free(jack_session_command_t *cmds); LIB_EXPORT int jack_client_has_session_callback(jack_client_t *client, const char* client_name); LIB_EXPORT jack_uuid_t jack_client_uuid_generate(); LIB_EXPORT jack_uuid_t jack_port_uuid_generate(uint32_t port_id); LIB_EXPORT uint32_t jack_uuid_to_index(jack_uuid_t); LIB_EXPORT int jack_uuid_compare(jack_uuid_t, jack_uuid_t); LIB_EXPORT void jack_uuid_copy(jack_uuid_t* dst, jack_uuid_t src); LIB_EXPORT void jack_uuid_clear(jack_uuid_t*); LIB_EXPORT int jack_uuid_parse(const char* buf, jack_uuid_t*); LIB_EXPORT void jack_uuid_unparse(jack_uuid_t, char buf[JACK_UUID_STRING_SIZE]); LIB_EXPORT int jack_uuid_empty(jack_uuid_t); #ifdef __cplusplus } #endif static inline bool CheckPort(jack_port_id_t port_index) { return (port_index > 0 && port_index < PORT_NUM_MAX); } static inline bool CheckBufferSize(jack_nframes_t buffer_size) { return (buffer_size >= 1 && buffer_size <= BUFFER_SIZE_MAX); } static inline void WaitGraphChange() { /* TLS key that is set only in RT thread, so never waits for pending graph change in RT context (just read the current graph state). */ if (jack_tls_get(JackGlobals::fRealTimeThread) == NULL) { JackGraphManager* manager = GetGraphManager(); JackEngineControl* control = GetEngineControl(); assert(manager); assert(control); if (manager->IsPendingChange()) { jack_log("WaitGraphChange..."); JackSleep(int(control->fPeriodUsecs * 1.1f)); } } } LIB_EXPORT void jack_set_error_function(print_function func) { jack_error_callback = (func == NULL) ? &default_jack_error_callback : func; } LIB_EXPORT void jack_set_info_function(print_function func) { jack_info_callback = (func == NULL) ? &default_jack_info_callback : func; } LIB_EXPORT jack_client_t* jack_client_new(const char* client_name) { JackGlobals::CheckContext("jack_client_new"); try { assert(JackGlobals::fOpenMutex); JackGlobals::fOpenMutex->Lock(); jack_error("jack_client_new: deprecated"); int options = JackUseExactName; if (getenv("JACK_START_SERVER") == NULL) { options |= JackNoStartServer; } jack_client_t* res = jack_client_new_aux(client_name, (jack_options_t)options, NULL); JackGlobals::fOpenMutex->Unlock(); return res; } catch (std::bad_alloc& e) { jack_error("Memory allocation error..."); return NULL; } catch (...) { jack_error("Unknown error..."); return NULL; } } LIB_EXPORT void* jack_port_get_buffer(jack_port_t* port, jack_nframes_t frames) { JackGlobals::CheckContext("jack_port_get_buffer"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_get_buffer called with an incorrect port %ld", myport); return NULL; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetBuffer(myport, frames) : NULL); } } LIB_EXPORT jack_uuid_t jack_port_uuid(const jack_port_t* port) { JackGlobals::CheckContext("jack_port_uuid"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_uuid called with an incorrect port %ld", myport); return 0; } else { return jack_port_uuid_generate(myport); } } LIB_EXPORT const char* jack_port_name(const jack_port_t* port) { JackGlobals::CheckContext("jack_port_name"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_name called with an incorrect port %ld", myport); return NULL; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->GetName() : NULL); } } LIB_EXPORT const char* jack_port_short_name(const jack_port_t* port) { JackGlobals::CheckContext("jack_port_short_name"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_short_name called with an incorrect port %ld", myport); return NULL; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->GetShortName() : NULL); } } LIB_EXPORT int jack_port_flags(const jack_port_t* port) { JackGlobals::CheckContext("jack_port_flags"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_flags called with an incorrect port %ld", myport); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->GetFlags() : -1); } } LIB_EXPORT const char* jack_port_type(const jack_port_t* port) { JackGlobals::CheckContext("jack_port_type"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_flags called an incorrect port %ld", myport); return NULL; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->GetType() : NULL); } } LIB_EXPORT jack_port_type_id_t jack_port_type_id(const jack_port_t *port) { JackGlobals::CheckContext("jack_port_type_id"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_type_id called an incorrect port %ld", myport); return 0; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? GetPortTypeId(manager->GetPort(myport)->GetType()) : 0); } } LIB_EXPORT int jack_port_connected(const jack_port_t* port) { JackGlobals::CheckContext("jack_port_connected"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_connected called with an incorrect port %ld", myport); return -1; } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetConnectionsNum(myport) : -1); } } LIB_EXPORT int jack_port_connected_to(const jack_port_t* port, const char* port_name) { JackGlobals::CheckContext("jack_port_connected_to"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t src = (jack_port_id_t)port_aux; if (!CheckPort(src)) { jack_error("jack_port_connected_to called with an incorrect port %ld", src); return -1; } else if (port_name == NULL) { jack_error("jack_port_connected_to called with a NULL port name"); return -1; } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); jack_port_id_t dst = (manager ? manager->GetPort(port_name) : NO_PORT); if (dst == NO_PORT) { jack_error("Unknown destination port port_name = %s", port_name); return 0; } else { return manager->IsConnected(src, dst); } } } LIB_EXPORT int jack_port_tie(jack_port_t* src, jack_port_t* dst) { JackGlobals::CheckContext("jack_port_tie"); uintptr_t src_aux = (uintptr_t)src; jack_port_id_t mysrc = (jack_port_id_t)src_aux; if (!CheckPort(mysrc)) { jack_error("jack_port_tie called with a NULL src port"); return -1; } uintptr_t dst_aux = (uintptr_t)dst; jack_port_id_t mydst = (jack_port_id_t)dst_aux; if (!CheckPort(mydst)) { jack_error("jack_port_tie called with a NULL dst port"); return -1; } JackGraphManager* manager = GetGraphManager(); if (manager && manager->GetPort(mysrc)->GetRefNum() != manager->GetPort(mydst)->GetRefNum()) { jack_error("jack_port_tie called with ports not belonging to the same client"); return -1; } else { return (manager ? manager->GetPort(mydst)->Tie(mysrc) : -1); } } LIB_EXPORT int jack_port_untie(jack_port_t* port) { JackGlobals::CheckContext("jack_port_untie"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_untie called with an incorrect port %ld", myport); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->UnTie() : -1); } } LIB_EXPORT jack_nframes_t jack_port_get_latency(jack_port_t* port) { JackGlobals::CheckContext("jack_port_get_latency"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_get_latency called with an incorrect port %ld", myport); return 0; } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->GetLatency() : 0); } } LIB_EXPORT void jack_port_set_latency(jack_port_t* port, jack_nframes_t frames) { JackGlobals::CheckContext("jack_port_set_latency"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_set_latency called with an incorrect port %ld", myport); } else { JackGraphManager* manager = GetGraphManager(); if (manager) manager->GetPort(myport)->SetLatency(frames); } } LIB_EXPORT void jack_port_get_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { JackGlobals::CheckContext("jack_port_get_latency_range"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_get_latency_range called with an incorrect port %ld", myport); } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); if (manager) manager->GetPort(myport)->GetLatencyRange(mode, range); } } LIB_EXPORT void jack_port_set_latency_range(jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { JackGlobals::CheckContext("jack_port_set_latency_range"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_set_latency_range called with an incorrect port %ld", myport); } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); if (manager) manager->GetPort(myport)->SetLatencyRange(mode, range); } } LIB_EXPORT int jack_recompute_total_latency(jack_client_t* ext_client, jack_port_t* port) { JackGlobals::CheckContext("jack_recompute_total_latency"); JackClient* client = (JackClient*)ext_client; uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (client == NULL) { jack_error("jack_recompute_total_latency called with a NULL client"); return -1; } else if (!CheckPort(myport)) { jack_error("jack_recompute_total_latency called with a NULL port"); return -1; } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); return (manager ? manager->ComputeTotalLatency(myport) : -1); } } LIB_EXPORT int jack_recompute_total_latencies(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_recompute_total_latencies"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_recompute_total_latencies called with a NULL client"); return -1; } else { return client->ComputeTotalLatencies(); } } LIB_EXPORT int jack_port_set_name(jack_port_t* port, const char* name) { JackGlobals::CheckContext("jack_port_set_name"); jack_error("jack_port_set_name: deprecated"); // Find a valid client jack_client_t* client = NULL; for (int i = 0; i < CLIENT_NUM; i++) { if ((client = (jack_client_t*)JackGlobals::fClientTable[i])) { break; } } return (client) ? jack_port_rename(client, port, name) : -1; } LIB_EXPORT int jack_port_rename(jack_client_t* ext_client, jack_port_t* port, const char* name) { JackGlobals::CheckContext("jack_port_rename"); JackClient* client = (JackClient*)ext_client; uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (client == NULL) { jack_error("jack_port_rename called with a NULL client"); return -1; } else if (!CheckPort(myport)) { jack_error("jack_port_rename called with an incorrect port %ld", myport); return -1; } else if (name == NULL) { jack_error("jack_port_rename called with a NULL port name"); return -1; } else { return client->PortRename(myport, name); } } LIB_EXPORT int jack_port_set_alias(jack_port_t* port, const char* name) { JackGlobals::CheckContext("jack_port_set_alias"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_set_alias called with an incorrect port %ld", myport); return -1; } else if (name == NULL) { jack_error("jack_port_set_alias called with a NULL port name"); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->SetAlias(name) : -1); } } LIB_EXPORT int jack_port_unset_alias(jack_port_t* port, const char* name) { JackGlobals::CheckContext("jack_port_unset_alias"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_unset_alias called with an incorrect port %ld", myport); return -1; } else if (name == NULL) { jack_error("jack_port_unset_alias called with a NULL port name"); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->UnsetAlias(name) : -1); } } LIB_EXPORT int jack_port_get_aliases(const jack_port_t* port, char* const aliases[2]) { JackGlobals::CheckContext("jack_port_get_aliases"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_get_aliases called with an incorrect port %ld", myport); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->GetAliases(aliases) : -1); } } LIB_EXPORT int jack_port_request_monitor(jack_port_t* port, int onoff) { JackGlobals::CheckContext("jack_port_request_monitor"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_request_monitor called with an incorrect port %ld", myport); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->RequestMonitor(myport, onoff) : -1); } } LIB_EXPORT int jack_port_request_monitor_by_name(jack_client_t* ext_client, const char* port_name, int onoff) { JackGlobals::CheckContext("jack_port_request_monitor_by_name"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_request_monitor_by_name called with a NULL client"); return -1; } else { JackGraphManager* manager = GetGraphManager(); if (!manager) return -1; jack_port_id_t myport = manager->GetPort(port_name); if (!CheckPort(myport)) { jack_error("jack_port_request_monitor_by_name called with an incorrect port %s", port_name); return -1; } else { return manager->RequestMonitor(myport, onoff); } } } LIB_EXPORT int jack_port_ensure_monitor(jack_port_t* port, int onoff) { JackGlobals::CheckContext("jack_port_ensure_monitor"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_ensure_monitor called with an incorrect port %ld", myport); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->EnsureMonitor(onoff) : -1); } } LIB_EXPORT int jack_port_monitoring_input(jack_port_t* port) { JackGlobals::CheckContext("jack_port_monitoring_input"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_monitoring_input called with an incorrect port %ld", myport); return -1; } else { JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPort(myport)->MonitoringInput() : -1); } } LIB_EXPORT int jack_is_realtime(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_is_realtime"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_is_realtime called with a NULL client"); return -1; } else { JackEngineControl* control = GetEngineControl(); return (control ? control->fRealTime : -1); } } LIB_EXPORT void jack_on_shutdown(jack_client_t* ext_client, JackShutdownCallback callback, void* arg) { JackGlobals::CheckContext("jack_on_shutdown"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_on_shutdown called with a NULL client"); } else { client->OnShutdown(callback, arg); } } LIB_EXPORT void jack_on_info_shutdown(jack_client_t* ext_client, JackInfoShutdownCallback callback, void* arg) { JackGlobals::CheckContext("jack_on_info_shutdown"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_on_info_shutdown called with a NULL client"); } else { client->OnInfoShutdown(callback, arg); } } LIB_EXPORT int jack_set_process_callback(jack_client_t* ext_client, JackProcessCallback callback, void* arg) { JackGlobals::CheckContext("jack_set_process_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_process_callback called with a NULL client"); return -1; } else { return client->SetProcessCallback(callback, arg); } } LIB_EXPORT jack_nframes_t jack_thread_wait(jack_client_t* ext_client, int status) { JackGlobals::CheckContext("jack_thread_wait"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_thread_wait called with a NULL client"); return 0; } else { jack_error("jack_thread_wait: deprecated, use jack_cycle_wait/jack_cycle_signal"); return 0; } } LIB_EXPORT jack_nframes_t jack_cycle_wait(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_cycle_wait"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_cycle_wait called with a NULL client"); return 0; } else { return client->CycleWait(); } } LIB_EXPORT void jack_cycle_signal(jack_client_t* ext_client, int status) { JackGlobals::CheckContext("jack_cycle_signal"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_cycle_signal called with a NULL client"); } else { client->CycleSignal(status); } } LIB_EXPORT int jack_set_process_thread(jack_client_t* ext_client, JackThreadCallback fun, void *arg) { JackGlobals::CheckContext("jack_set_process_thread"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_process_thread called with a NULL client"); return -1; } else { return client->SetProcessThread(fun, arg); } } LIB_EXPORT int jack_set_freewheel_callback(jack_client_t* ext_client, JackFreewheelCallback freewheel_callback, void* arg) { JackGlobals::CheckContext("jack_set_freewheel_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_freewheel_callback called with a NULL client"); return -1; } else { return client->SetFreewheelCallback(freewheel_callback, arg); } } LIB_EXPORT int jack_set_freewheel(jack_client_t* ext_client, int onoff) { JackGlobals::CheckContext("jack_set_freewheel"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_freewheel called with a NULL client"); return -1; } else { return client->SetFreeWheel(onoff); } } LIB_EXPORT int jack_set_buffer_size(jack_client_t* ext_client, jack_nframes_t buffer_size) { JackGlobals::CheckContext("jack_set_buffer_size"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_buffer_size called with a NULL client"); return -1; } else if (!CheckBufferSize(buffer_size)) { return -1; } else { return client->SetBufferSize(buffer_size); } } LIB_EXPORT int jack_set_buffer_size_callback(jack_client_t* ext_client, JackBufferSizeCallback bufsize_callback, void* arg) { JackGlobals::CheckContext("jack_set_buffer_size_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_buffer_size_callback called with a NULL client"); return -1; } else { return client->SetBufferSizeCallback(bufsize_callback, arg); } } LIB_EXPORT int jack_set_sample_rate_callback(jack_client_t* ext_client, JackSampleRateCallback srate_callback, void* arg) { JackGlobals::CheckContext("jack_set_sample_rate_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_sample_rate_callback called with a NULL client"); return -1; } else { return client->SetSampleRateCallback(srate_callback, arg); } } LIB_EXPORT int jack_set_client_registration_callback(jack_client_t* ext_client, JackClientRegistrationCallback registration_callback, void* arg) { JackGlobals::CheckContext("jack_set_client_registration_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_client_registration_callback called with a NULL client"); return -1; } else { return client->SetClientRegistrationCallback(registration_callback, arg); } } LIB_EXPORT int jack_set_port_registration_callback(jack_client_t* ext_client, JackPortRegistrationCallback registration_callback, void* arg) { JackGlobals::CheckContext("jack_set_port_registration_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_port_registration_callback called with a NULL client"); return -1; } else { return client->SetPortRegistrationCallback(registration_callback, arg); } } LIB_EXPORT int jack_set_port_connect_callback(jack_client_t* ext_client, JackPortConnectCallback portconnect_callback, void* arg) { JackGlobals::CheckContext("jack_set_port_connect_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_port_connect_callback called with a NULL client"); return -1; } else { return client->SetPortConnectCallback(portconnect_callback, arg); } } LIB_EXPORT int jack_set_port_rename_callback(jack_client_t* ext_client, JackPortRenameCallback rename_callback, void* arg) { JackGlobals::CheckContext("jack_set_port_rename_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_port_rename_callback called with a NULL client"); return -1; } else { return client->SetPortRenameCallback(rename_callback, arg); } } LIB_EXPORT int jack_set_graph_order_callback(jack_client_t* ext_client, JackGraphOrderCallback graph_callback, void* arg) { JackGlobals::CheckContext("jack_set_graph_order_callback"); JackClient* client = (JackClient*)ext_client; jack_log("jack_set_graph_order_callback ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_set_graph_order_callback called with a NULL client"); return -1; } else { return client->SetGraphOrderCallback(graph_callback, arg); } } LIB_EXPORT int jack_set_xrun_callback(jack_client_t* ext_client, JackXRunCallback xrun_callback, void* arg) { JackGlobals::CheckContext("jack_set_xrun_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_xrun_callback called with a NULL client"); return -1; } else { return client->SetXRunCallback(xrun_callback, arg); } } LIB_EXPORT int jack_set_latency_callback(jack_client_t* ext_client, JackLatencyCallback latency_callback, void *arg) { JackGlobals::CheckContext("jack_set_latency_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_latency_callback called with a NULL client"); return -1; } else { return client->SetLatencyCallback(latency_callback, arg); } } LIB_EXPORT int jack_set_thread_init_callback(jack_client_t* ext_client, JackThreadInitCallback init_callback, void *arg) { JackGlobals::CheckContext("jack_set_thread_init_callback"); JackClient* client = (JackClient*)ext_client; jack_log("jack_set_thread_init_callback ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_set_thread_init_callback called with a NULL client"); return -1; } else { return client->SetInitCallback(init_callback, arg); } } LIB_EXPORT int jack_activate(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_activate"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_activate called with a NULL client"); return -1; } else { return client->Activate(); } } LIB_EXPORT int jack_deactivate(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_deactivate"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_deactivate called with a NULL client"); return -1; } else { return client->Deactivate(); } } LIB_EXPORT jack_port_t* jack_port_register(jack_client_t* ext_client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) { JackGlobals::CheckContext("jack_port_register"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_register called with a NULL client"); return NULL; } else if ((port_name == NULL) || (port_type == NULL)) { jack_error("jack_port_register called with a NULL port name or a NULL port_type"); return NULL; } else { return (jack_port_t *)((uintptr_t)client->PortRegister(port_name, port_type, flags, buffer_size)); } } LIB_EXPORT int jack_port_unregister(jack_client_t* ext_client, jack_port_t* port) { JackGlobals::CheckContext("jack_port_unregister"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_unregister called with a NULL client"); return -1; } uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_unregister called with an incorrect port %ld", myport); return -1; } return client->PortUnRegister(myport); } LIB_EXPORT int jack_port_is_mine(const jack_client_t* ext_client, const jack_port_t* port) { JackGlobals::CheckContext("jack_port_is_mine"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_is_mine called with a NULL client"); return -1; } uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_is_mine called with an incorrect port %ld", myport); return -1; } return client->PortIsMine(myport); } LIB_EXPORT const char** jack_port_get_connections(const jack_port_t* port) { JackGlobals::CheckContext("jack_port_get_connections"); uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_get_connections called with an incorrect port %ld", myport); return NULL; } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetConnections(myport) : NULL); } } // Calling client does not need to "own" the port LIB_EXPORT const char** jack_port_get_all_connections(const jack_client_t* ext_client, const jack_port_t* port) { JackGlobals::CheckContext("jack_port_get_all_connections"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_get_all_connections called with a NULL client"); return NULL; } uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_get_all_connections called with an incorrect port %ld", myport); return NULL; } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetConnections(myport) : NULL); } } LIB_EXPORT jack_nframes_t jack_port_get_total_latency(jack_client_t* ext_client, jack_port_t* port) { JackGlobals::CheckContext("jack_port_get_total_latency"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_get_total_latency called with a NULL client"); return 0; } uintptr_t port_aux = (uintptr_t)port; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_get_total_latency called with an incorrect port %ld", myport); return 0; } else { WaitGraphChange(); JackGraphManager* manager = GetGraphManager(); if (manager) { manager->ComputeTotalLatency(myport); return manager->GetPort(myport)->GetTotalLatency(); } else { return 0; } } } LIB_EXPORT int jack_connect(jack_client_t* ext_client, const char* src, const char* dst) { JackGlobals::CheckContext("jack_connect"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_connect called with a NULL client"); return -1; } else if ((src == NULL) || (dst == NULL)) { jack_error("jack_connect called with a NULL port name"); return -1; } else { return client->PortConnect(src, dst); } } LIB_EXPORT int jack_disconnect(jack_client_t* ext_client, const char* src, const char* dst) { JackGlobals::CheckContext("jack_disconnect"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_disconnect called with a NULL client"); return -1; } else if ((src == NULL) || (dst == NULL)) { jack_error("jack_disconnect called with a NULL port name"); return -1; } else { return client->PortDisconnect(src, dst); } } LIB_EXPORT int jack_port_disconnect(jack_client_t* ext_client, jack_port_t* src) { JackGlobals::CheckContext("jack_port_disconnect"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_disconnect called with a NULL client"); return -1; } uintptr_t port_aux = (uintptr_t)src; jack_port_id_t myport = (jack_port_id_t)port_aux; if (!CheckPort(myport)) { jack_error("jack_port_disconnect called with an incorrect port %ld", myport); return -1; } return client->PortDisconnect(myport); } LIB_EXPORT jack_nframes_t jack_get_sample_rate(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_get_sample_rate"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_sample_rate called with a NULL client"); return 0; } else { JackEngineControl* control = GetEngineControl(); return (control ? control->fSampleRate : 0); } } LIB_EXPORT jack_nframes_t jack_get_buffer_size(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_get_buffer_size"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_buffer_size called with a NULL client"); return 0; } else { JackEngineControl* control = GetEngineControl(); return (control ? control->fBufferSize : 0); } } LIB_EXPORT const char** jack_get_ports(jack_client_t* ext_client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) { JackGlobals::CheckContext("jack_get_ports"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_ports called with a NULL client"); return NULL; } JackGraphManager* manager = GetGraphManager(); return (manager ? manager->GetPorts(port_name_pattern, type_name_pattern, flags) : NULL); } LIB_EXPORT jack_port_t* jack_port_by_name(jack_client_t* ext_client, const char* portname) { JackGlobals::CheckContext("jack_port_by_name"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_by_name called with a NULL client"); return NULL; } if (portname == NULL) { jack_error("jack_port_by_name called with a NULL port name"); return NULL; } JackGraphManager* manager = GetGraphManager(); if (manager) { int res = manager->GetPort(portname); // returns a port index at least > 1 return (res == NO_PORT) ? NULL : (jack_port_t*)((uintptr_t)res); } else { return NULL; } } LIB_EXPORT jack_port_t* jack_port_by_id(jack_client_t* ext_client, jack_port_id_t id) { JackGlobals::CheckContext("jack_port_by_id"); /* jack_port_t* type is actually the port index */ return (jack_port_t*)((uintptr_t)id); } LIB_EXPORT int jack_engine_takeover_timebase(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_engine_takeover_timebase"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_engine_takeover_timebase called with a NULL client"); return -1; } else { jack_error("jack_engine_takeover_timebase: deprecated\n"); return 0; } } LIB_EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t* ext_client) { JackGlobals::CheckContext("jack_frames_since_cycle_start"); JackTimer timer; JackEngineControl* control = GetEngineControl(); if (control) { control->ReadFrameTime(&timer); return timer.FramesSinceCycleStart(GetMicroSeconds(), control->fSampleRate); } else { return 0; } } LIB_EXPORT jack_time_t jack_get_time() { JackGlobals::CheckContext("jack_get_time"); return GetMicroSeconds(); } LIB_EXPORT jack_time_t jack_frames_to_time(const jack_client_t* ext_client, jack_nframes_t frames) { JackGlobals::CheckContext("jack_frames_to_time"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_frames_to_time called with a NULL client"); return 0; } else { JackTimer timer; JackEngineControl* control = GetEngineControl(); if (control) { control->ReadFrameTime(&timer); return timer.Frames2Time(frames, control->fBufferSize); } else { return 0; } } } LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t* ext_client, jack_time_t usecs) { JackGlobals::CheckContext("jack_time_to_frames"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_time_to_frames called with a NULL client"); return 0; } else { JackTimer timer; JackEngineControl* control = GetEngineControl(); if (control) { control->ReadFrameTime(&timer); return timer.Time2Frames(usecs, control->fBufferSize); } else { return 0; } } } LIB_EXPORT jack_nframes_t jack_frame_time(const jack_client_t* ext_client) { JackGlobals::CheckContext("jack_frame_time"); return jack_time_to_frames(ext_client, GetMicroSeconds()); } LIB_EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t* ext_client) { JackGlobals::CheckContext("jack_last_frame_time"); JackEngineControl* control = GetEngineControl(); return (control) ? control->fFrameTimer.ReadCurrentState()->CurFrame() : 0; } LIB_EXPORT int jack_get_cycle_times(const jack_client_t *client, jack_nframes_t *current_frames, jack_time_t *current_usecs, jack_time_t *next_usecs, float *period_usecs) { JackGlobals::CheckContext("jack_get_cycle_times"); JackEngineControl* control = GetEngineControl(); if (control) { JackTimer timer; control->ReadFrameTime(&timer); return timer.GetCycleTimes(current_frames, current_usecs, next_usecs, period_usecs); } else { return -1; } } LIB_EXPORT float jack_cpu_load(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_cpu_load"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_cpu_load called with a NULL client"); return 0.0f; } else { JackEngineControl* control = GetEngineControl(); return (control ? control->fCPULoad : 0.0f); } } LIB_EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_client_thread_id"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_client_thread_id called with a NULL client"); return (jack_native_thread_t)NULL; } else { return client->GetThreadID(); } } LIB_EXPORT char* jack_get_client_name(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_get_client_name"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_client_name called with a NULL client"); return NULL; } else { return client->GetClientControl()->fName; } } LIB_EXPORT int jack_client_name_size(void) { return JACK_CLIENT_NAME_SIZE+1; } LIB_EXPORT int jack_port_name_size(void) { return REAL_JACK_PORT_NAME_SIZE+1; } LIB_EXPORT int jack_port_type_size(void) { return JACK_PORT_TYPE_SIZE; } LIB_EXPORT size_t jack_port_type_get_buffer_size(jack_client_t* ext_client, const char* port_type) { JackGlobals::CheckContext("jack_port_type_get_buffer_size"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_port_type_get_buffer_size called with a NULL client"); return 0; } else { jack_port_type_id_t port_id = GetPortTypeId(port_type); if (port_id == PORT_TYPES_MAX) { jack_error("jack_port_type_get_buffer_size called with an unknown port type = %s", port_type); return 0; } else { return GetPortType(port_id)->size(); } } } // transport.h LIB_EXPORT int jack_release_timebase(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_release_timebase"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_release_timebase called with a NULL client"); return -1; } else { return client->ReleaseTimebase(); } } LIB_EXPORT int jack_set_sync_callback(jack_client_t* ext_client, JackSyncCallback sync_callback, void *arg) { JackGlobals::CheckContext("jack_set_sync_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_sync_callback called with a NULL client"); return -1; } else { return client->SetSyncCallback(sync_callback, arg); } } LIB_EXPORT int jack_set_sync_timeout(jack_client_t* ext_client, jack_time_t timeout) { JackGlobals::CheckContext("jack_set_sync_timeout"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_sync_timeout called with a NULL client"); return -1; } else { return client->SetSyncTimeout(timeout); } } LIB_EXPORT int jack_set_timebase_callback(jack_client_t* ext_client, int conditional, JackTimebaseCallback timebase_callback, void* arg) { JackGlobals::CheckContext("jack_set_timebase_callback"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_set_timebase_callback called with a NULL client"); return -1; } else { return client->SetTimebaseCallback(conditional, timebase_callback, arg); } } LIB_EXPORT int jack_transport_locate(jack_client_t* ext_client, jack_nframes_t frame) { JackGlobals::CheckContext("jack_transport_locate"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_transport_locate called with a NULL client"); return -1; } else { client->TransportLocate(frame); return 0; } } LIB_EXPORT jack_transport_state_t jack_transport_query(const jack_client_t* ext_client, jack_position_t* pos) { JackGlobals::CheckContext("jack_transport_query"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_transport_query called with a NULL client"); return JackTransportStopped; } else { return client->TransportQuery(pos); } } LIB_EXPORT jack_nframes_t jack_get_current_transport_frame(const jack_client_t* ext_client) { JackGlobals::CheckContext("jack_get_current_transport_frame"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_current_transport_frame called with a NULL client"); return 0; } else { return client->GetCurrentTransportFrame(); } } LIB_EXPORT int jack_transport_reposition(jack_client_t* ext_client, const jack_position_t* pos) { JackGlobals::CheckContext("jack_transport_reposition"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_transport_reposition called with a NULL client"); return -1; } else { client->TransportReposition(pos); return 0; } } LIB_EXPORT void jack_transport_start(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_transport_start"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_transport_start called with a NULL client"); } else { client->TransportStart(); } } LIB_EXPORT void jack_transport_stop(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_transport_stop"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_transport_stop called with a NULL client"); } else { client->TransportStop(); } } // deprecated LIB_EXPORT void jack_get_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) { JackGlobals::CheckContext("jack_get_transport_info"); jack_error("jack_get_transport_info: deprecated"); if (tinfo) memset(tinfo, 0, sizeof(jack_transport_info_t)); } LIB_EXPORT void jack_set_transport_info(jack_client_t* ext_client, jack_transport_info_t* tinfo) { JackGlobals::CheckContext("jack_set_transport_info"); jack_error("jack_set_transport_info: deprecated"); if (tinfo) memset(tinfo, 0, sizeof(jack_transport_info_t)); } // statistics.h LIB_EXPORT float jack_get_max_delayed_usecs(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_get_max_delayed_usecs"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_max_delayed_usecs called with a NULL client"); return 0.f; } else { JackEngineControl* control = GetEngineControl(); return (control ? control->fMaxDelayedUsecs : 0.f); } } LIB_EXPORT float jack_get_xrun_delayed_usecs(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_get_xrun_delayed_usecs"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_xrun_delayed_usecs called with a NULL client"); return 0.f; } else { JackEngineControl* control = GetEngineControl(); return (control ? control->fXrunDelayedUsecs : 0.f); } } LIB_EXPORT void jack_reset_max_delayed_usecs(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_reset_max_delayed_usecs"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_reset_max_delayed_usecs called with a NULL client"); } else { JackEngineControl* control = GetEngineControl(); control->ResetXRun(); } } // thread.h LIB_EXPORT int jack_client_real_time_priority(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_client_real_time_priority"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_client_real_time_priority called with a NULL client"); return -1; } else { JackEngineControl* control = GetEngineControl(); return (control->fRealTime) ? control->fClientPriority : -1; } } LIB_EXPORT int jack_client_max_real_time_priority(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_client_max_real_time_priority"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_client_max_real_time_priority called with a NULL client"); return -1; } else { JackEngineControl* control = GetEngineControl(); return (control->fRealTime) ? control->fMaxClientPriority : -1; } } LIB_EXPORT int jack_acquire_real_time_scheduling(jack_native_thread_t thread, int priority) { JackEngineControl* control = GetEngineControl(); return (control ? JackThread::AcquireRealTimeImp(thread, priority, control->fPeriod, control->fComputation, control->fConstraint) : -1); } LIB_EXPORT int jack_client_create_thread(jack_client_t* client, jack_native_thread_t *thread, int priority, int realtime, /* boolean */ thread_routine routine, void *arg) { JackGlobals::CheckContext("jack_client_create_thread"); JackEngineControl* control = GetEngineControl(); int res = JackThread::StartImp(thread, priority, realtime, routine, arg); return (res == 0) ? ((realtime ? JackThread::AcquireRealTimeImp(*thread, priority, control->fPeriod, control->fComputation, control->fConstraint) : res)) : res; } LIB_EXPORT int jack_drop_real_time_scheduling(jack_native_thread_t thread) { return JackThread::DropRealTimeImp(thread); } LIB_EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) { JackGlobals::CheckContext("jack_client_stop_thread"); return JackThread::StopImp(thread); } LIB_EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) { JackGlobals::CheckContext("jack_client_kill_thread"); return JackThread::KillImp(thread); } #ifndef WIN32 LIB_EXPORT void jack_set_thread_creator (jack_thread_creator_t jtc) { if (jtc == NULL) { JackGlobals::fJackThreadCreator = pthread_create; } else { JackGlobals::fJackThreadCreator = jtc; } } #endif // intclient.h LIB_EXPORT int jack_internal_client_new (const char* client_name, const char* load_name, const char* load_init) { JackGlobals::CheckContext("jack_internal_client_new"); jack_error("jack_internal_client_new: deprecated"); return -1; } LIB_EXPORT void jack_internal_client_close (const char* client_name) { JackGlobals::CheckContext("jack_internal_client_close"); jack_error("jack_internal_client_close: deprecated"); } LIB_EXPORT char* jack_get_internal_client_name(jack_client_t* ext_client, jack_intclient_t intclient) { JackGlobals::CheckContext("jack_get_internal_client_name"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_get_internal_client_name called with a NULL client"); return NULL; } else if (intclient >= CLIENT_NUM) { jack_error("jack_get_internal_client_name: incorrect client"); return NULL; } else { return client->GetInternalClientName(intclient); } } LIB_EXPORT jack_intclient_t jack_internal_client_handle(jack_client_t* ext_client, const char* client_name, jack_status_t* status) { JackGlobals::CheckContext("jack_internal_client_handle"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_internal_client_handle called with a NULL client"); return 0; } else { jack_status_t my_status; if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; return client->InternalClientHandle(client_name, status); } } static jack_intclient_t jack_internal_client_load_aux(jack_client_t* ext_client, const char* client_name, jack_options_t options, jack_status_t* status, va_list ap) { JackGlobals::CheckContext("jack_internal_client_load_aux"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_internal_client_load called with a NULL client"); return 0; } else { jack_varargs_t va; jack_status_t my_status; if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; /* validate parameters */ if ((options & ~JackLoadOptions)) { int my_status1 = *status | (JackFailure | JackInvalidOption); *status = (jack_status_t)my_status1; return 0; } /* parse variable arguments */ jack_varargs_parse(options, ap, &va); return client->InternalClientLoad(client_name, options, status, &va); } } LIB_EXPORT jack_intclient_t jack_internal_client_load(jack_client_t *client, const char* client_name, jack_options_t options, jack_status_t *status, ...) { JackGlobals::CheckContext("jack_internal_client_load"); va_list ap; va_start(ap, status); jack_intclient_t res = jack_internal_client_load_aux(client, client_name, options, status, ap); va_end(ap); return res; } LIB_EXPORT jack_status_t jack_internal_client_unload(jack_client_t* ext_client, jack_intclient_t intclient) { JackGlobals::CheckContext("jack_internal_client_load"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_internal_client_unload called with a NULL client"); return (jack_status_t)(JackNoSuchClient | JackFailure); } else if (intclient >= CLIENT_NUM) { jack_error("jack_internal_client_unload: incorrect client"); return (jack_status_t)(JackNoSuchClient | JackFailure); } else { jack_status_t my_status; client->InternalClientUnload(intclient, &my_status); return my_status; } } LIB_EXPORT void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr) { JackGlobals::CheckContext("jack_get_version"); // FIXME: We need these coming from build system *major_ptr = 0; *minor_ptr = 0; *micro_ptr = 0; *proto_ptr = 0; } LIB_EXPORT const char* jack_get_version_string() { JackGlobals::CheckContext("jack_get_version_string"); return VERSION; } LIB_EXPORT void jack_free(void* ptr) { JackGlobals::CheckContext("jack_free"); if (ptr) { free(ptr); } } // session.h LIB_EXPORT int jack_set_session_callback(jack_client_t* ext_client, JackSessionCallback session_callback, void* arg) { JackGlobals::CheckContext("jack_set_session_callback"); JackClient* client = (JackClient*)ext_client; jack_log("jack_set_session_callback ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_set_session_callback called with a NULL client"); return -1; } else { return client->SetSessionCallback(session_callback, arg); } } LIB_EXPORT jack_session_command_t* jack_session_notify(jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path) { JackGlobals::CheckContext("jack_session_notify"); JackClient* client = (JackClient*)ext_client; jack_log("jack_session_notify ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_session_notify called with a NULL client"); return NULL; } else { return client->SessionNotify(target, ev_type, path); } } LIB_EXPORT int jack_session_reply(jack_client_t* ext_client, jack_session_event_t *event) { JackGlobals::CheckContext("jack_session_reply"); JackClient* client = (JackClient*)ext_client; jack_log("jack_session_reply ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_session_reply called with a NULL client"); return -1; } else { return client->SessionReply(event); } } LIB_EXPORT void jack_session_event_free(jack_session_event_t* ev) { JackGlobals::CheckContext("jack_session_event_free"); if (ev) { if (ev->session_dir) free((void *)ev->session_dir); if (ev->client_uuid) free((void *)ev->client_uuid); if (ev->command_line) free(ev->command_line); free(ev); } } LIB_EXPORT char *jack_client_get_uuid(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_client_get_uuid"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_client_get_uuid called with a NULL client"); return NULL; } else { char retval[JACK_UUID_STRING_SIZE]; jack_uuid_unparse(client->GetClientControl()->fSessionID, retval); return strdup(retval); } } LIB_EXPORT char* jack_get_uuid_for_client_name(jack_client_t* ext_client, const char* client_name) { JackGlobals::CheckContext("jack_get_uuid_for_client_name"); JackClient* client = (JackClient*)ext_client; jack_log("jack_get_uuid_for_client_name ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_get_uuid_for_client_name called with a NULL client"); return NULL; } else { return client->GetUUIDForClientName(client_name); } } LIB_EXPORT char* jack_get_client_name_by_uuid(jack_client_t* ext_client, const char* client_uuid) { JackGlobals::CheckContext("jack_get_client_name_by_uuid"); JackClient* client = (JackClient*)ext_client; jack_log("jack_get_uuid_for_client_name ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_get_client_name_by_uuid called with a NULL client"); return NULL; } else { return client->GetClientNameByUUID(client_uuid); } } LIB_EXPORT int jack_reserve_client_name(jack_client_t* ext_client, const char* client_name, const char* uuid) { JackGlobals::CheckContext("jack_reserve_client_name"); JackClient* client = (JackClient*)ext_client; jack_log("jack_reserve_client_name ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_reserve_client_name called with a NULL client"); return -1; } else { return client->ReserveClientName(client_name, uuid); } } LIB_EXPORT void jack_session_commands_free(jack_session_command_t *cmds) { JackGlobals::CheckContext("jack_session_commands_free"); if (!cmds) { return; } int i = 0; while (1) { if (cmds[i].client_name) { free ((char *)cmds[i].client_name); } if (cmds[i].command) { free ((char *)cmds[i].command); } if (cmds[i].uuid) { free ((char *)cmds[i].uuid); } else { break; } i += 1; } free(cmds); } LIB_EXPORT int jack_client_has_session_callback(jack_client_t* ext_client, const char* client_name) { JackGlobals::CheckContext("jack_client_has_session_callback"); JackClient* client = (JackClient*)ext_client; jack_log("jack_client_has_session_callback ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_client_has_session_callback called with a NULL client"); return -1; } else { return client->ClientHasSessionCallback(client_name); } } LIB_EXPORT jack_uuid_t jack_client_uuid_generate() { static uint32_t uuid_cnt = 0; jack_uuid_t uuid = 0x2; /* JackUUIDClient */; uuid = (uuid << 32) | ++uuid_cnt; return uuid; } LIB_EXPORT jack_uuid_t jack_port_uuid_generate(uint32_t port_id) { jack_uuid_t uuid = 0x1; /* JackUUIDPort */ uuid = (uuid << 32) | (port_id + 1); return uuid; } LIB_EXPORT uint32_t jack_uuid_to_index(jack_uuid_t u) { return (u & 0xffff) - 1; } LIB_EXPORT int jack_uuid_compare(jack_uuid_t a, jack_uuid_t b) { if (a == b) { return 0; } if (a < b) { return -1; } return 1; } LIB_EXPORT void jack_uuid_copy(jack_uuid_t* dst, jack_uuid_t src) { *dst = src; } LIB_EXPORT void jack_uuid_clear(jack_uuid_t* u) { *u = JACK_UUID_EMPTY_INITIALIZER; } LIB_EXPORT int jack_uuid_parse(const char* b, jack_uuid_t* u) { if (sscanf (b, "%" PRIu64, u) == 1) { if (*u < (0x1LL << 32)) { /* has not type bits set - not legal */ return -1; } return 0; } return -1; } LIB_EXPORT void jack_uuid_unparse(jack_uuid_t u, char b[JACK_UUID_STRING_SIZE]) { snprintf (b, JACK_UUID_STRING_SIZE, "%" PRIu64, u); } LIB_EXPORT int jack_uuid_empty(jack_uuid_t u) { return u == JACK_UUID_EMPTY_INITIALIZER; } jack2-1.9.22/common/JackActivationCount.cpp000066400000000000000000000024431436671425200205130ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackAtomic.h" #include "JackActivationCount.h" #include "JackConstants.h" #include "JackClientControl.h" #include "JackError.h" namespace Jack { bool JackActivationCount::Signal(JackSynchro* synchro, JackClientControl* control) { if (fValue == 0) { // Transfer activation to next clients jack_log("JackActivationCount::Signal value = 0 ref = %ld", control->fRefNum); return synchro->Signal(); } else if (DEC_ATOMIC(&fValue) == 1) { return synchro->Signal(); } else { return true; } } } // end of namespace jack2-1.9.22/common/JackActivationCount.h000066400000000000000000000035211436671425200201560ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackActivationCount__ #define __JackActivationCount__ #include "JackPlatformPlug.h" #include "JackTime.h" #include "JackTypes.h" namespace Jack { struct JackClientControl; /*! \brief Client activation counter. */ PRE_PACKED_STRUCTURE class JackActivationCount { private: alignas(SInt32) SInt32 fValue; SInt32 fCount; public: JackActivationCount(): fValue(0), fCount(0) { static_assert(offsetof(JackActivationCount, fValue) % sizeof(fValue) == 0, "fValue must be aligned within JackActivationCount"); } bool Signal(JackSynchro* synchro, JackClientControl* control); inline void Reset() { fValue = fCount; } inline void SetValue(int val) { fCount = val; } inline void IncValue() { fCount++; } inline void DecValue() { fCount--; } inline int GetValue() const { return fValue; } } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackArgParser.cpp000066400000000000000000000177711436671425200173010ustar00rootroot00000000000000/* Copyright (C) 2006-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackDriverLoader.h" #include "JackArgParser.h" #include #include #include using namespace std; namespace Jack { // class JackArgParser *************************************************** JackArgParser::JackArgParser ( const char* arg ) { jack_log ( "JackArgParser::JackArgParser, arg_string : '%s'", arg ); fArgc = 0; //if empty string if ( strlen(arg) == 0 ) return; fArgString = string(arg); //else parse the arg string const size_t arg_len = fArgString.length(); unsigned int i = 0; size_t pos = 0; size_t start = 0; size_t copy_start = 0; size_t copy_length = 0; //we need a 'space terminated' string fArgString += " "; //first fill a vector with args do { //find the first non-space character from the actual position start = fArgString.find_first_not_of ( ' ', start ); //get the next quote or space position pos = fArgString.find_first_of ( " \"" , start ); //no more quotes or spaces, consider the end of the string if ( pos == string::npos ) pos = arg_len; //if double quote if ( fArgString[pos] == '\"' ) { //first character : copy the substring if ( pos == start ) { copy_start = start + 1; pos = fArgString.find ( '\"', ++pos ); copy_length = pos - copy_start; start = pos + 1; } //else there is something before the quote, first copy that else { copy_start = start; copy_length = pos - copy_start; start = pos; } } //if space if ( fArgString[pos] == ' ' ) { //short option descriptor if ( ( fArgString[start] == '-' ) && ( fArgString[start + 1] != '-' ) ) { copy_start = start; copy_length = 2; start += copy_length; } //else copy all the space delimitated string else { copy_start = start; copy_length = pos - copy_start; start = pos + 1; } } //then push the substring to the args vector fArgv.push_back ( fArgString.substr ( copy_start, copy_length ) ); jack_log ( "JackArgParser::JackArgParser, add : '%s'", (*fArgv.rbegin()).c_str() ); } while ( start < arg_len ); //finally count the options for ( i = 0; i < fArgv.size(); i++ ) if ( fArgv[i].at(0) == '-' ) fArgc++; } JackArgParser::~JackArgParser() {} string JackArgParser::GetArgString() { return fArgString; } int JackArgParser::GetNumArgv() { return fArgv.size(); } int JackArgParser::GetArgc() { return fArgc; } int JackArgParser::GetArgv ( vector& argv ) { argv = fArgv; return 0; } int JackArgParser::GetArgv ( char** argv ) { //argv must be NULL if ( argv ) return -1; //else allocate and fill it argv = (char**)calloc (fArgv.size(), sizeof(char*)); if (argv == NULL) { return -1; } for ( unsigned int i = 0; i < fArgv.size(); i++ ) { argv[i] = (char*)calloc(fArgv[i].length(), sizeof(char)); fill_n ( argv[i], fArgv[i].length() + 1, 0 ); fArgv[i].copy ( argv[i], fArgv[i].length() ); } return 0; } void JackArgParser::DeleteArgv ( const char** argv ) { unsigned int i; for ( i = 0; i < fArgv.size(); i++ ) free((void*)argv[i]); free((void*)argv); } bool JackArgParser::ParseParams ( jack_driver_desc_t* desc, JSList** param_list ) { string options_list; unsigned long i = 0; unsigned int param = 0; size_t param_id = 0; JSList* params = NULL; jack_driver_param_t* intclient_param; for ( i = 0; i < desc->nparams; i++ ) options_list += desc->params[i].character; for ( param = 0; param < fArgv.size(); param++ ) { if ( fArgv[param][0] == '-' ) { //valid option if ( ( param_id = options_list.find_first_of ( fArgv[param].at(1) ) ) != string::npos ) { intclient_param = static_cast ( calloc ( 1, sizeof ( jack_driver_param_t) ) ); intclient_param->character = desc->params[param_id].character; switch ( desc->params[param_id].type ) { case JackDriverParamInt: if (param + 1 < fArgv.size()) // something to parse intclient_param->value.i = atoi ( fArgv[param + 1].c_str() ); break; case JackDriverParamUInt: if (param + 1 < fArgv.size()) // something to parse intclient_param->value.ui = strtoul ( fArgv[param + 1].c_str(), NULL, 10 ); break; case JackDriverParamChar: if (param + 1 < fArgv.size()) // something to parse intclient_param->value.c = fArgv[param + 1][0]; break; case JackDriverParamString: if (param + 1 < fArgv.size()) // something to parse fArgv[param + 1].copy ( intclient_param->value.str, min(static_cast(fArgv[param + 1].length()), JACK_DRIVER_PARAM_STRING_MAX) ); break; case JackDriverParamBool: intclient_param->value.i = true; break; } //add to the list params = jack_slist_append ( params, intclient_param ); } //invalid option else { if (fArgv[param][1] == 'h') { fprintf(stdout, "Internal client parameters:\n"); jack_print_driver_options (desc, stdout); return false; } else { jack_error ( "Invalid option '%c'", fArgv[param][1] ); } } } } assert(param_list); *param_list = params; return true; } void JackArgParser::FreeParams ( JSList* param_list ) { JSList *node_ptr = param_list; JSList *next_node_ptr; while (node_ptr) { next_node_ptr = node_ptr->next; free(node_ptr->data); free(node_ptr); node_ptr = next_node_ptr; } } } jack2-1.9.22/common/JackArgParser.h000066400000000000000000000032361436671425200167350ustar00rootroot00000000000000/* Copyright (C) 2006-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackArgParser__ #define __JackArgParser__ #include "jslist.h" #include "driver_interface.h" #include "JackCompilerDeps.h" #include "JackError.h" #include #include #include #include #include namespace Jack { class SERVER_EXPORT JackArgParser { private: std::string fArgString; int fArgc; std::vector fArgv; public: JackArgParser ( const char* arg ); ~JackArgParser(); std::string GetArgString(); int GetNumArgv(); int GetArgc(); int GetArgv ( std::vector& argv ); int GetArgv ( char** argv ); void DeleteArgv ( const char** argv ); bool ParseParams ( jack_driver_desc_t* desc, JSList** param_list ); void FreeParams ( JSList* param_list ); }; } #endif jack2-1.9.22/common/JackAtomic.h000066400000000000000000000022461436671425200162630ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackAtomic__ #define __JackAtomic__ #include "JackTypes.h" #include "JackAtomic_os.h" static inline long INC_ATOMIC(volatile SInt32* val) { SInt32 actual; do { actual = *val; } while (!CAS(actual, actual + 1, val)); return actual; } static inline long DEC_ATOMIC(volatile SInt32* val) { SInt32 actual; do { actual = *val; } while (!CAS(actual, actual - 1, val)); return actual; } #endif jack2-1.9.22/common/JackAtomicArrayState.h000066400000000000000000000205221436671425200202600ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackAtomicArrayState__ #define __JackAtomicArrayState__ #include "JackAtomic.h" #include "JackCompilerDeps.h" #include // for memcpy #include namespace Jack { /*! \brief Counter for CAS */ PRE_PACKED_STRUCTURE struct AtomicArrayCounter { union { struct { unsigned char fByteVal[4]; } scounter; UInt32 fLongVal; }info; AtomicArrayCounter() { info.fLongVal = 0; } AtomicArrayCounter(volatile const AtomicArrayCounter& obj) { info.fLongVal = obj.info.fLongVal; } AtomicArrayCounter(volatile AtomicArrayCounter& obj) { info.fLongVal = obj.info.fLongVal; } AtomicArrayCounter& operator=(volatile AtomicArrayCounter& obj) { info.fLongVal = obj.info.fLongVal; return *this; } AtomicArrayCounter& operator=(AtomicArrayCounter& obj) { info.fLongVal = obj.info.fLongVal; return *this; } } POST_PACKED_STRUCTURE; #define Counter1(e) (e).info.fLongVal #define GetIndex1(e, state) ((e).info.scounter.fByteVal[state]) #define SetIndex1(e, state, val) ((e).info.scounter.fByteVal[state] = val) #define IncIndex1(e, state) ((e).info.scounter.fByteVal[state]++) #define SwapIndex1(e, state) (((e).info.scounter.fByteVal[0] == state) ? 0 : state) /*! \brief A class to handle several states in a lock-free manner Requirement: - a "current" state - several possible "pending" state - an TrySwitchState(int state) operation to atomically switch a "pending" to the "current" state (the pending becomes the current). The TrySwitchState operation returns a "current" state (either the same if switch fails or the new one, one can know if the switch has succeeded) - a WriteNextStartState(int state) returns a "pending" state to be written into - a WriteNextStartStop(int state) make the written "pending" state become "switchable" Different pending states can be written independently and concurrently. GetCurrentIndex() *must* return an increasing value to be able to check reading current state coherency The fCounter is an array of indexes to access the current and 3 different "pending" states. WriteNextStateStart(int index) must return a valid state to be written into, and must invalidate state "index" ==> cur state switch. WriteNextStateStop(int index) makes the "index" state become "switchable" with the current state. TrySwitchState(int index) must detect that pending state is a new state, and does the switch ReadCurrentState() must return the state GetCurrentIndex() must return an index increased each new switch. WriteNextStateStart(int index1) and WriteNextStateStart(int index2) can be interleaved [switch counter][index state][index state][cur index] */ // CHECK livelock PRE_PACKED_STRUCTURE template class JackAtomicArrayState { protected: // fState[0] ==> current // fState[1] ==> pending // fState[2] ==> request T fState[3]; alignas(UInt32) alignas(AtomicArrayCounter) volatile AtomicArrayCounter fCounter; UInt32 WriteNextStateStartAux(int state, bool* result) { AtomicArrayCounter old_val; AtomicArrayCounter new_val; UInt32 cur_index; UInt32 next_index; bool need_copy; do { old_val = fCounter; new_val = old_val; *result = GetIndex1(new_val, state); cur_index = GetIndex1(new_val, 0); next_index = SwapIndex1(fCounter, state); need_copy = (GetIndex1(new_val, state) == 0); // Written = false, switch just occurred SetIndex1(new_val, state, 0); // Written = false, invalidate state } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); if (need_copy) memcpy(&fState[next_index], &fState[cur_index], sizeof(T)); return next_index; } void WriteNextStateStopAux(int state) { AtomicArrayCounter old_val; AtomicArrayCounter new_val; do { old_val = fCounter; new_val = old_val; SetIndex1(new_val, state, 1); // Written = true, state becomes "switchable" } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); } public: JackAtomicArrayState() { static_assert(offsetof(JackAtomicArrayState, fCounter) % sizeof(fCounter) == 0, "fCounter must be aligned within JackAtomicArrayState"); Counter1(fCounter) = 0; } ~JackAtomicArrayState() // Not virtual ?? {} /*! \brief Returns the current state : only valid in the RT reader thread */ T* ReadCurrentState() { return &fState[GetIndex1(fCounter, 0)]; } /*! \brief Returns the current switch counter */ UInt16 GetCurrentIndex() { return GetIndex1(fCounter, 3); } /*! \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) */ T* TrySwitchState(int state) { AtomicArrayCounter old_val; AtomicArrayCounter new_val; do { old_val = fCounter; new_val = old_val; if (GetIndex1(new_val, state)) { // If state has been written SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch SetIndex1(new_val, state, 0); // Invalidate the state "state" IncIndex1(new_val, 3); // Inc switch } } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); return &fState[GetIndex1(fCounter, 0)]; // Read the counter again } /*! \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) */ T* TrySwitchState(int state, bool* result) { AtomicArrayCounter old_val; AtomicArrayCounter new_val; do { old_val = fCounter; new_val = old_val; if ((*result = GetIndex1(new_val, state))) { // If state has been written SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch SetIndex1(new_val, state, 0); // Invalidate the state "state" IncIndex1(new_val, 3); // Inc switch } } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter)); return &fState[GetIndex1(fCounter, 0)]; // Read the counter again } /*! \brief Start write operation : setup and returns the next state to update, check for recursive write calls. */ T* WriteNextStateStart(int state) { bool tmp; UInt32 index = WriteNextStateStartAux(state, &tmp); return &fState[index]; } T* WriteNextStateStart(int state, bool* result) { UInt32 index = WriteNextStateStartAux(state, result); return &fState[index]; } /*! \brief Stop write operation : make the next state ready to be used by the RT thread */ void WriteNextStateStop(int state) { WriteNextStateStopAux(state); } } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackAtomicState.h000066400000000000000000000164571436671425200172750ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackAtomicState__ #define __JackAtomicState__ #include "JackAtomic.h" #include "JackCompilerDeps.h" #include // for memcpy #include namespace Jack { /*! \brief Counter for CAS */ PRE_PACKED_STRUCTURE struct AtomicCounter { union { struct { UInt16 fShortVal1; // Cur UInt16 fShortVal2; // Next } scounter; UInt32 fLongVal; }info; AtomicCounter() { info.fLongVal = 0; } AtomicCounter(volatile const AtomicCounter& obj) { info.fLongVal = obj.info.fLongVal; } AtomicCounter(volatile AtomicCounter& obj) { info.fLongVal = obj.info.fLongVal; } AtomicCounter& operator=(AtomicCounter& obj) { info.fLongVal = obj.info.fLongVal; return *this; } AtomicCounter& operator=(volatile AtomicCounter& obj) { info.fLongVal = obj.info.fLongVal; return *this; } } POST_PACKED_STRUCTURE; #define Counter(e) (e).info.fLongVal #define CurIndex(e) (e).info.scounter.fShortVal1 #define NextIndex(e) (e).info.scounter.fShortVal2 #define CurArrayIndex(e) (CurIndex(e) & 0x0001) #define NextArrayIndex(e) ((CurIndex(e) + 1) & 0x0001) /*! \brief A class to handle two states (switching from one to the other) in a lock-free manner */ // CHECK livelock PRE_PACKED_STRUCTURE template class JackAtomicState { protected: T fState[2]; alignas(UInt32) alignas(AtomicCounter) volatile AtomicCounter fCounter; SInt32 fCallWriteCounter; UInt32 WriteNextStateStartAux() { AtomicCounter old_val; AtomicCounter new_val; UInt32 cur_index; UInt32 next_index; bool need_copy; do { old_val = fCounter; new_val = old_val; cur_index = CurArrayIndex(new_val); next_index = NextArrayIndex(new_val); need_copy = (CurIndex(new_val) == NextIndex(new_val)); NextIndex(new_val) = CurIndex(new_val); // Invalidate next index } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); if (need_copy) memcpy(&fState[next_index], &fState[cur_index], sizeof(T)); return next_index; } void WriteNextStateStopAux() { AtomicCounter old_val; AtomicCounter new_val; do { old_val = fCounter; new_val = old_val; NextIndex(new_val)++; // Set next index } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); } public: JackAtomicState() { static_assert(offsetof(JackAtomicState, fCounter) % sizeof(fCounter) == 0, "fCounter must be aligned within JackAtomicState"); Counter(fCounter) = 0; fCallWriteCounter = 0; } ~JackAtomicState() // Not virtual ?? {} /*! \brief Returns the current state : only valid in the RT reader thread */ T* ReadCurrentState() { return &fState[CurArrayIndex(fCounter)]; } /*! \brief Returns the current state index */ UInt16 GetCurrentIndex() { return CurIndex(fCounter); } /*! \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) */ T* TrySwitchState() { AtomicCounter old_val; AtomicCounter new_val; do { old_val = fCounter; new_val = old_val; CurIndex(new_val) = NextIndex(new_val); // Prepare switch } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); return &fState[CurArrayIndex(fCounter)]; // Read the counter again } /*! \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one) */ T* TrySwitchState(bool* result) { AtomicCounter old_val; AtomicCounter new_val; do { old_val = fCounter; new_val = old_val; *result = (CurIndex(new_val) != NextIndex(new_val)); CurIndex(new_val) = NextIndex(new_val); // Prepare switch } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter)); return &fState[CurArrayIndex(fCounter)]; // Read the counter again } /*! \brief Start write operation : setup and returns the next state to update, check for recursive write calls. */ T* WriteNextStateStart() { UInt32 next_index = (fCallWriteCounter++ == 0) ? WriteNextStateStartAux() : NextArrayIndex(fCounter); // We are inside a wrapping WriteNextStateStart call, NextArrayIndex can be read safely return &fState[next_index]; } /*! \brief Stop write operation : make the next state ready to be used by the RT thread */ void WriteNextStateStop() { if (--fCallWriteCounter == 0) WriteNextStateStopAux(); } bool IsPendingChange() { return CurIndex(fCounter) != NextIndex(fCounter); } /* // Single writer : write methods get the *next* state to be updated void TestWriteMethod() { T* state = WriteNextStateStart(); ...... ...... WriteNextStateStop(); } // First RT call possibly switch state void TestReadRTMethod1() { T* state = TrySwitchState(); ...... ...... } // Other RT methods can safely use the current state during the *same* RT cycle void TestReadRTMethod2() { T* state = ReadCurrentState(); ...... ...... } // Non RT read methods : must check state coherency void TestReadMethod() { T* state; UInt16 cur_index; UInt16 next_index = GetCurrentIndex(); do { cur_index = next_index; state = ReadCurrentState(); ...... ...... next_index = GetCurrentIndex(); } while (cur_index != next_index); } */ } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackAudioAdapter.cpp000066400000000000000000000154231436671425200177450ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackAudioAdapter.h" #include "JackError.h" #include "JackCompilerDeps.h" #include "JackTools.h" #include "JackTime.h" #include "jslist.h" #include #include #include using namespace std; namespace Jack { int JackAudioAdapter::Process(jack_nframes_t frames, void* arg) { JackAudioAdapter* adapter = static_cast(arg); return adapter->ProcessAux(frames); } int JackAudioAdapter::ProcessAux(jack_nframes_t frames) { // Always clear output for (int i = 0; i < fAudioAdapter->GetInputs(); i++) { fInputBufferList[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(fCapturePortList[i], frames); memset(fInputBufferList[i], 0, frames * sizeof(jack_default_audio_sample_t)); } for (int i = 0; i < fAudioAdapter->GetOutputs(); i++) { fOutputBufferList[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(fPlaybackPortList[i], frames); } fAudioAdapter->PullAndPush(fInputBufferList, fOutputBufferList, frames); return 0; } int JackAudioAdapter::BufferSize(jack_nframes_t buffer_size, void* arg) { JackAudioAdapter* adapter = static_cast(arg); adapter->Reset(); adapter->fAudioAdapter->SetHostBufferSize(buffer_size); return 0; } int JackAudioAdapter::SampleRate(jack_nframes_t sample_rate, void* arg) { JackAudioAdapter* adapter = static_cast(arg); adapter->Reset(); adapter->fAudioAdapter->SetHostSampleRate(sample_rate); return 0; } void JackAudioAdapter::Latency(jack_latency_callback_mode_t mode, void* arg) { JackAudioAdapter* adapter = static_cast(arg); if (mode == JackCaptureLatency) { for (int i = 0; i < adapter->fAudioAdapter->GetInputs(); i++) { jack_latency_range_t range; range.min = range.max = adapter->fAudioAdapter->GetInputLatency(i); jack_port_set_latency_range(adapter->fCapturePortList[i], JackCaptureLatency, &range); } } else { for (int i = 0; i < adapter->fAudioAdapter->GetOutputs(); i++) { jack_latency_range_t range; range.min = range.max = adapter->fAudioAdapter->GetOutputLatency(i); jack_port_set_latency_range(adapter->fPlaybackPortList[i], JackPlaybackLatency, &range); } } } JackAudioAdapter::JackAudioAdapter(jack_client_t* client, JackAudioAdapterInterface* audio_io, const JSList* params) :fClient(client), fAudioAdapter(audio_io) { const JSList* node; const jack_driver_param_t* param; fAutoConnect = false; for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*)node->data; switch (param->character) { case 'c': fAutoConnect = true; break; } } } JackAudioAdapter::~JackAudioAdapter() { // When called, Close has already been used for the client, thus ports are already unregistered. delete fAudioAdapter; } void JackAudioAdapter::FreePorts() { for (int i = 0; i < fAudioAdapter->GetInputs(); i++) { if (fCapturePortList[i]) { jack_port_unregister(fClient, fCapturePortList[i]); } } for (int i = 0; i < fAudioAdapter->GetOutputs(); i++) { if (fPlaybackPortList[i]) { jack_port_unregister(fClient, fPlaybackPortList[i]); } } delete[] fCapturePortList; delete[] fPlaybackPortList; delete[] fInputBufferList; delete[] fOutputBufferList; } void JackAudioAdapter::ConnectPorts() { const char** ports; ports = jack_get_ports(fClient, NULL, NULL, JackPortIsPhysical | JackPortIsInput); if (ports != NULL) { for (int i = 0; i < fAudioAdapter->GetInputs() && ports[i]; i++) { jack_connect(fClient, jack_port_name(fCapturePortList[i]), ports[i]); } jack_free(ports); } ports = jack_get_ports(fClient, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); if (ports != NULL) { for (int i = 0; i < fAudioAdapter->GetOutputs() && ports[i]; i++) { jack_connect(fClient, ports[i], jack_port_name(fPlaybackPortList[i])); } jack_free(ports); } } void JackAudioAdapter::Reset() { fAudioAdapter->Reset(); } int JackAudioAdapter::Open() { char name[32]; jack_log("JackAudioAdapter::Open fCaptureChannels %d fPlaybackChannels %d", fAudioAdapter->GetInputs(), fAudioAdapter->GetOutputs()); fAudioAdapter->Create(); //jack ports fCapturePortList = new jack_port_t*[fAudioAdapter->GetInputs()]; fPlaybackPortList = new jack_port_t*[fAudioAdapter->GetOutputs()]; fInputBufferList = new jack_default_audio_sample_t*[fAudioAdapter->GetInputs()]; fOutputBufferList = new jack_default_audio_sample_t*[fAudioAdapter->GetOutputs()]; for (int i = 0; i < fAudioAdapter->GetInputs(); i++) { snprintf(name, sizeof(name), "capture_%d", i + 1); if ((fCapturePortList[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, 0)) == NULL) { goto fail; } } for (int i = 0; i < fAudioAdapter->GetOutputs(); i++) { snprintf(name, sizeof(name), "playback_%d", i + 1); if ((fPlaybackPortList[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, 0)) == NULL) { goto fail; } } //callbacks and activation if (jack_set_process_callback(fClient, Process, this) < 0) { goto fail; } if (jack_set_buffer_size_callback(fClient, BufferSize, this) < 0) { goto fail; } if (jack_set_sample_rate_callback(fClient, SampleRate, this) < 0) { goto fail; } if (jack_set_latency_callback(fClient, Latency, this) < 0) { goto fail; } if (jack_activate(fClient) < 0) { goto fail; } if (fAutoConnect) { ConnectPorts(); } // Ring buffers are now allocated... return fAudioAdapter->Open(); return 0; fail: FreePorts(); fAudioAdapter->Destroy(); return -1; } int JackAudioAdapter::Close() { fAudioAdapter->Close(); fAudioAdapter->Destroy(); return 0; } } //namespace jack2-1.9.22/common/JackAudioAdapter.h000066400000000000000000000042061436671425200174070ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackAudioAdapter__ #define __JackAudioAdapter__ #include "JackAudioAdapterInterface.h" #include "driver_interface.h" namespace Jack { /*! \brief Audio adapter : Jack client side. */ class JackAudioAdapter { private: static int Process(jack_nframes_t, void* arg); static int BufferSize(jack_nframes_t buffer_size, void* arg); static int SampleRate(jack_nframes_t sample_rate, void* arg); static void Latency(jack_latency_callback_mode_t mode, void* arg); jack_port_t** fCapturePortList; jack_port_t** fPlaybackPortList; jack_default_audio_sample_t** fInputBufferList; jack_default_audio_sample_t** fOutputBufferList; jack_client_t* fClient; JackAudioAdapterInterface* fAudioAdapter; bool fAutoConnect; void FreePorts(); void ConnectPorts(); void Reset(); int ProcessAux(jack_nframes_t frames); public: JackAudioAdapter(jack_client_t* client, JackAudioAdapterInterface* audio_io, const JSList* params = NULL); ~JackAudioAdapter(); int Open(); int Close(); }; } #define CaptureDriverFlags static_cast(JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal) #define PlaybackDriverFlags static_cast(JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal) #endif jack2-1.9.22/common/JackAudioAdapterFactory.cpp000066400000000000000000000057701436671425200213010ustar00rootroot00000000000000/* Copyright (C) 2008-2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackAudioAdapter.h" #include "JackPlatformPlug.h" #include "JackArgParser.h" #include #include #include #ifdef __APPLE__ #include "JackCoreAudioAdapter.h" #define JackPlatformAdapter JackCoreAudioAdapter #endif #ifdef __linux__ #include "JackAlsaAdapter.h" #define JackPlatformAdapter JackAlsaAdapter #endif #if defined(__sun__) || defined(sun) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include "JackOSSAdapter.h" #define JackPlatformAdapter JackOSSAdapter #endif #ifdef WIN32 #include "JackPortAudioAdapter.h" #define JackPlatformAdapter JackPortAudioAdapter #endif #ifdef __cplusplus extern "C" { #endif using namespace Jack; SERVER_EXPORT int jack_internal_initialize(jack_client_t* jack_client, const JSList* params) { jack_log("Loading audioadapter"); Jack::JackAudioAdapter* adapter; jack_nframes_t buffer_size = jack_get_buffer_size(jack_client); jack_nframes_t sample_rate = jack_get_sample_rate(jack_client); try { adapter = new Jack::JackAudioAdapter(jack_client, new Jack::JackPlatformAdapter(buffer_size, sample_rate, params), params); assert(adapter); if (adapter->Open() == 0) { return 0; } else { delete adapter; return 1; } } catch (...) { jack_info("audioadapter allocation error"); return 1; } } SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); Jack::JackArgParser parser(load_init); if (parser.GetArgc() > 0) { parse_params = parser.ParseParams(desc, ¶ms); } if (parse_params) { res = jack_internal_initialize(jack_client, params); parser.FreeParams(params); } return res; } SERVER_EXPORT void jack_finish(void* arg) { Jack::JackAudioAdapter* adapter = static_cast(arg); if (adapter) { jack_log("Unloading audioadapter"); adapter->Close(); delete adapter; } } #ifdef __cplusplus } #endif jack2-1.9.22/common/JackAudioAdapterInterface.cpp000066400000000000000000000346361436671425200215750ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __APPLE__ #include #endif #include "JackAudioAdapter.h" #ifndef MY_TARGET_OS_IPHONE #include "JackLibSampleRateResampler.h" #endif #include "JackTime.h" #include "JackError.h" #include namespace Jack { #ifdef JACK_MONITOR void MeasureTable::Write(int time1, int time2, float r1, float r2, int pos1, int pos2) { int pos = (++fCount) % TABLE_MAX; fTable[pos].time1 = time1; fTable[pos].time2 = time2; fTable[pos].r1 = r1; fTable[pos].r2 = r2; fTable[pos].pos1 = pos1; fTable[pos].pos2 = pos2; } void MeasureTable::Save(unsigned int fHostBufferSize, unsigned int fHostSampleRate, unsigned int fAdaptedSampleRate, unsigned int fAdaptedBufferSize) { FILE* file = fopen("JackAudioAdapter.log", "w"); int max = (fCount) % TABLE_MAX - 1; for (int i = 1; i < max; i++) { fprintf(file, "%d \t %d \t %d \t %f \t %f \t %d \t %d \n", fTable[i].delta, fTable[i].time1, fTable[i].time2, fTable[i].r1, fTable[i].r2, fTable[i].pos1, fTable[i].pos2); } fclose(file); // No used for now // Adapter timing 1 file = fopen("AdapterTiming1.plot", "w"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Ringbuffer error\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Ringbuffer error with timing correction\" with lines"); fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming1.svg\n"); fprintf(file, "set terminal svg\n"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Consumer interrupt period\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Producer interrupt period\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); fclose(file); // Adapter timing 2 file = fopen("AdapterTiming2.plot", "w"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"resampling ratio\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines"); fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming2.svg\n"); fprintf(file, "set terminal svg\n"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"resampling ratio\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); fclose(file); // Adapter timing 3 file = fopen("AdapterTiming3.plot", "w"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines"); fprintf(file, "\n unset multiplot\n"); fprintf(file, "set output 'AdapterTiming3.svg\n"); fprintf(file, "set terminal svg\n"); fprintf(file, "set multiplot\n"); fprintf(file, "set grid\n"); fprintf(file, "set title \"Audio adapter timing: host [rate = %.1f kHz buffer = %d frames] adapter [rate = %.1f kHz buffer = %d frames] \"\n" ,float(fHostSampleRate)/1000.f, fHostBufferSize, float(fAdaptedSampleRate)/1000.f, fAdaptedBufferSize); fprintf(file, "set xlabel \"audio cycles\"\n"); fprintf(file, "set ylabel \"frames\"\n"); fprintf(file, "plot "); fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines\n"); fprintf(file, "unset multiplot\n"); fprintf(file, "unset output\n"); fclose(file); } #endif void JackAudioAdapterInterface::GrowRingBufferSize() { fRingbufferCurSize *= 2; } void JackAudioAdapterInterface::AdaptRingBufferSize() { if (fHostBufferSize > fAdaptedBufferSize) { fRingbufferCurSize = 4 * fHostBufferSize; } else { fRingbufferCurSize = 4 * fAdaptedBufferSize; } } void JackAudioAdapterInterface::ResetRingBuffers() { if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; } for (int i = 0; i < fCaptureChannels; i++) { fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++) { fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } } void JackAudioAdapterInterface::Reset() { ResetRingBuffers(); fRunning = false; } #ifdef MY_TARGET_OS_IPHONE void JackAudioAdapterInterface::Create() {} #else void JackAudioAdapterInterface::Create() { //ringbuffers fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; if (fAdaptative) { AdaptRingBufferSize(); jack_info("Ringbuffer automatic adaptative mode size = %d frames", fRingbufferCurSize); } else { if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; } jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize); } for (int i = 0; i < fCaptureChannels; i++ ) { fCaptureRingBuffer[i] = new JackLibSampleRateResampler(fQuality); fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++ ) { fPlaybackRingBuffer[i] = new JackLibSampleRateResampler(fQuality); fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } if (fCaptureChannels > 0) { jack_log("ReadSpace = %ld", fCaptureRingBuffer[0]->ReadSpace()); } if (fPlaybackChannels > 0) { jack_log("WriteSpace = %ld", fPlaybackRingBuffer[0]->WriteSpace()); } } #endif void JackAudioAdapterInterface::Destroy() { for (int i = 0; i < fCaptureChannels; i++) { delete(fCaptureRingBuffer[i]); } for (int i = 0; i < fPlaybackChannels; i++) { delete (fPlaybackRingBuffer[i]); } delete[] fCaptureRingBuffer; delete[] fPlaybackRingBuffer; } int JackAudioAdapterInterface::PushAndPull(float** inputBuffer, float** outputBuffer, unsigned int frames) { bool failure = false; fRunning = true; // Finer estimation of the position in the ringbuffer int delta_frames = (fPullAndPushTime > 0) ? (int)((float(long(GetMicroSeconds() - fPullAndPushTime)) * float(fAdaptedSampleRate)) / 1000000.f) : 0; double ratio = 1; // TODO : done like this just to avoid crash when input only or output only... if (fCaptureChannels > 0) { ratio = fPIControler.GetRatio(fCaptureRingBuffer[0]->GetError() - delta_frames); } else if (fPlaybackChannels > 0) { ratio = fPIControler.GetRatio(fPlaybackRingBuffer[0]->GetError() - delta_frames); } #ifdef JACK_MONITOR if (fCaptureRingBuffer && fCaptureRingBuffer[0] != NULL) fTable.Write(fCaptureRingBuffer[0]->GetError(), fCaptureRingBuffer[0]->GetError() - delta_frames, ratio, 1/ratio, fCaptureRingBuffer[0]->ReadSpace(), fCaptureRingBuffer[0]->ReadSpace()); #endif // Push/pull from ringbuffer for (int i = 0; i < fCaptureChannels; i++) { fCaptureRingBuffer[i]->SetRatio(ratio); if (inputBuffer[i]) { if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], frames) < frames) { failure = true; } } } for (int i = 0; i < fPlaybackChannels; i++) { fPlaybackRingBuffer[i]->SetRatio(1/ratio); if (outputBuffer[i]) { if (fPlaybackRingBuffer[i]->ReadResample(outputBuffer[i], frames) < frames) { failure = true; } } } // Reset all ringbuffers in case of failure if (failure) { jack_error("JackAudioAdapterInterface::PushAndPull ringbuffer failure... reset"); if (fAdaptative) { GrowRingBufferSize(); jack_info("Ringbuffer size = %d frames", fRingbufferCurSize); } ResetRingBuffers(); return -1; } else { return 0; } } int JackAudioAdapterInterface::PullAndPush(float** inputBuffer, float** outputBuffer, unsigned int frames) { fPullAndPushTime = GetMicroSeconds(); if (!fRunning) { return 0; } int res = 0; // Push/pull from ringbuffer for (int i = 0; i < fCaptureChannels; i++) { if (inputBuffer[i]) { if (fCaptureRingBuffer[i]->Read(inputBuffer[i], frames) < frames) { res = -1; } } } for (int i = 0; i < fPlaybackChannels; i++) { if (outputBuffer[i]) { if (fPlaybackRingBuffer[i]->Write(outputBuffer[i], frames) < frames) { res = -1; } } } return res; } int JackAudioAdapterInterface::SetHostBufferSize(jack_nframes_t buffer_size) { fHostBufferSize = buffer_size; if (fAdaptative) { AdaptRingBufferSize(); } return 0; } int JackAudioAdapterInterface::SetAdaptedBufferSize(jack_nframes_t buffer_size) { fAdaptedBufferSize = buffer_size; if (fAdaptative) { AdaptRingBufferSize(); } return 0; } int JackAudioAdapterInterface::SetBufferSize(jack_nframes_t buffer_size) { SetHostBufferSize(buffer_size); SetAdaptedBufferSize(buffer_size); return 0; } int JackAudioAdapterInterface::SetHostSampleRate(jack_nframes_t sample_rate) { fHostSampleRate = sample_rate; fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); return 0; } int JackAudioAdapterInterface::SetAdaptedSampleRate(jack_nframes_t sample_rate) { fAdaptedSampleRate = sample_rate; fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); return 0; } int JackAudioAdapterInterface::SetSampleRate(jack_nframes_t sample_rate) { SetHostSampleRate(sample_rate); SetAdaptedSampleRate(sample_rate); return 0; } void JackAudioAdapterInterface::SetInputs(int inputs) { jack_log("JackAudioAdapterInterface::SetInputs %d", inputs); fCaptureChannels = inputs; } void JackAudioAdapterInterface::SetOutputs(int outputs) { jack_log("JackAudioAdapterInterface::SetOutputs %d", outputs); fPlaybackChannels = outputs; } int JackAudioAdapterInterface::GetInputs() { //jack_log("JackAudioAdapterInterface::GetInputs %d", fCaptureChannels); return fCaptureChannels; } int JackAudioAdapterInterface::GetOutputs() { //jack_log ("JackAudioAdapterInterface::GetOutputs %d", fPlaybackChannels); return fPlaybackChannels; } } // namespace jack2-1.9.22/common/JackAudioAdapterInterface.h000066400000000000000000000130361436671425200212310ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackAudioAdapterInterface__ #define __JackAudioAdapterInterface__ #include "JackResampler.h" #include "JackFilters.h" #include namespace Jack { #ifdef JACK_MONITOR #define TABLE_MAX 100000 struct Measure { int delta; int time1; int time2; float r1; float r2; int pos1; int pos2; }; struct MeasureTable { Measure fTable[TABLE_MAX]; int fCount; MeasureTable() :fCount(0) {} void Write(int time1, int time2, float r1, float r2, int pos1, int pos2); void Save(unsigned int fHostBufferSize, unsigned int fHostSampleRate, unsigned int fAdaptedSampleRate, unsigned int fAdaptedBufferSize); }; #endif /*! \brief Base class for audio adapters. */ class JackAudioAdapterInterface { protected: #ifdef JACK_MONITOR MeasureTable fTable; #endif //channels int fCaptureChannels; int fPlaybackChannels; //host parameters jack_nframes_t fHostBufferSize; jack_nframes_t fHostSampleRate; //adapted parameters jack_nframes_t fAdaptedBufferSize; jack_nframes_t fAdaptedSampleRate; //PI controller JackPIControler fPIControler; JackResampler** fCaptureRingBuffer; JackResampler** fPlaybackRingBuffer; unsigned int fQuality; unsigned int fRingbufferCurSize; jack_time_t fPullAndPushTime; bool fRunning; bool fAdaptative; void ResetRingBuffers(); void AdaptRingBufferSize(); void GrowRingBufferSize(); public: JackAudioAdapterInterface(jack_nframes_t buffer_size, jack_nframes_t sample_rate, jack_nframes_t ring_buffer_size = DEFAULT_ADAPTATIVE_SIZE): fCaptureChannels(0), fPlaybackChannels(0), fHostBufferSize(buffer_size), fHostSampleRate(sample_rate), fAdaptedBufferSize(buffer_size), fAdaptedSampleRate(sample_rate), fPIControler(sample_rate / sample_rate, 256), fCaptureRingBuffer(NULL), fPlaybackRingBuffer(NULL), fQuality(0), fRingbufferCurSize(ring_buffer_size), fPullAndPushTime(0), fRunning(false), fAdaptative(true) {} JackAudioAdapterInterface(jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate, jack_nframes_t ring_buffer_size = DEFAULT_ADAPTATIVE_SIZE) : fCaptureChannels(0), fPlaybackChannels(0), fHostBufferSize(host_buffer_size), fHostSampleRate(host_sample_rate), fAdaptedBufferSize(adapted_buffer_size), fAdaptedSampleRate(adapted_sample_rate), fPIControler(host_sample_rate / host_sample_rate, 256), fQuality(0), fRingbufferCurSize(ring_buffer_size), fPullAndPushTime(0), fRunning(false), fAdaptative(true) {} virtual ~JackAudioAdapterInterface() {} virtual void Reset(); virtual void Create(); virtual void Destroy(); virtual int Open() { return 0; } virtual int Close() { return 0; } virtual int SetHostBufferSize(jack_nframes_t buffer_size); virtual int SetAdaptedBufferSize(jack_nframes_t buffer_size); virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetHostSampleRate(jack_nframes_t sample_rate); virtual int SetAdaptedSampleRate(jack_nframes_t sample_rate); virtual int SetSampleRate(jack_nframes_t sample_rate); void SetInputs(int inputs); void SetOutputs(int outputs); int GetInputs(); int GetOutputs(); virtual int GetInputLatency(int port_index) { return 0; } virtual int GetOutputLatency(int port_index) { return 0; } int PushAndPull(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int frames); int PullAndPush(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int frames); }; } #endif jack2-1.9.22/common/JackAudioDriver.cpp000066400000000000000000000316621436671425200176230ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the (at your option) any later version. GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackAudioDriver.h" #include "JackTime.h" #include "JackError.h" #include "JackEngineControl.h" #include "JackPort.h" #include "JackGraphManager.h" #include "JackLockedEngine.h" #include "JackException.h" #include using namespace std; namespace Jack { JackAudioDriver::JackAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackDriver(name, alias, engine, table) {} JackAudioDriver::~JackAudioDriver() {} int JackAudioDriver::SetBufferSize(jack_nframes_t buffer_size) { // Update engine and graph manager state fEngineControl->fBufferSize = buffer_size; fGraphManager->SetBufferSize(buffer_size); fEngineControl->UpdateTimeOut(); UpdateLatencies(); // Redirected on slaves drivers... return JackDriver::SetBufferSize(buffer_size); } int JackAudioDriver::SetSampleRate(jack_nframes_t sample_rate) { fEngineControl->fSampleRate = sample_rate; fEngineControl->UpdateTimeOut(); // Redirected on slaves drivers... return JackDriver::SetSampleRate(sample_rate); } int JackAudioDriver::Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { fCaptureChannels = inchannels; fPlaybackChannels = outchannels; fWithMonitorPorts = monitor; memset(fCapturePortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); memset(fPlaybackPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); memset(fMonitorPortList, 0, sizeof(jack_port_id_t) * DRIVER_PORT_NUM); return JackDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } void JackAudioDriver::UpdateLatencies() { jack_latency_range_t input_range; jack_latency_range_t output_range; jack_latency_range_t monitor_range; for (int i = 0; i < fCaptureChannels; i++) { input_range.max = input_range.min = fEngineControl->fBufferSize + fCaptureLatency; fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &input_range); } for (int i = 0; i < fPlaybackChannels; i++) { output_range.max = output_range.min = fPlaybackLatency; if (fEngineControl->fSyncMode) { output_range.max = output_range.min += fEngineControl->fBufferSize; } else { output_range.max = output_range.min += fEngineControl->fBufferSize * 2; } fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &output_range); if (fWithMonitorPorts) { monitor_range.min = monitor_range.max = fEngineControl->fBufferSize; fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &monitor_range); } } } int JackAudioDriver::Attach() { JackPort* port; jack_port_id_t port_index; char name[REAL_JACK_PORT_NAME_SIZE+1]; char alias[REAL_JACK_PORT_NAME_SIZE+1]; int i; jack_log("JackAudioDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); for (i = 0; i < fCaptureChannels; i++) { snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); fCapturePortList[i] = port_index; jack_log("JackAudioDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } for (i = 0; i < fPlaybackChannels; i++) { snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); fPlaybackPortList[i] = port_index; jack_log("JackAudioDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); // Monitor ports if (fWithMonitorPorts) { jack_log("Create monitor port"); snprintf(name, sizeof(name), "%s:monitor_%u", fClientControl.fName, i + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("Cannot register monitor port for %s", name); return -1; } else { fMonitorPortList[i] = port_index; } } } UpdateLatencies(); return 0; } int JackAudioDriver::Detach() { int i; jack_log("JackAudioDriver::Detach"); for (i = 0; i < fCaptureChannels; i++) { fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[i]); } for (i = 0; i < fPlaybackChannels; i++) { fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[i]); if (fWithMonitorPorts) { fEngine->PortUnRegister(fClientControl.fRefNum, fMonitorPortList[i]); } } return 0; } int JackAudioDriver::Write() { for (int i = 0; i < fPlaybackChannels; i++) { if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { jack_default_audio_sample_t* buffer = GetOutputBuffer(i); int size = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; // Monitor ports if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) { memcpy(GetMonitorBuffer(i), buffer, size); } } } return 0; } int JackAudioDriver::Process() { return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); } /* The driver "asynchronous" mode: output buffers computed at the *previous cycle* are used, the server does not synchronize to the end of client graph execution. */ int JackAudioDriver::ProcessAsync() { // Read input buffers for the current cycle if (Read() < 0) { jack_error("JackAudioDriver::ProcessAsync: read error, stopping..."); return -1; } // Write output buffers from the previous cycle if (Write() < 0) { jack_error("JackAudioDriver::ProcessAsync: write error, stopping..."); return -1; } // Process graph ProcessGraphAsync(); // Keep end cycle time JackDriver::CycleTakeEndTime(); return 0; } void JackAudioDriver::ProcessGraphAsync() { // Process graph if (fIsMaster) { ProcessGraphAsyncMaster(); } else { ProcessGraphAsyncSlave(); } } /* Used when the driver works in master mode. */ void JackAudioDriver::ProcessGraphAsyncMaster() { // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle if (!fEngine->Process(fBeginDateUst, fEndDateUst)) { jack_error("JackAudioDriver::ProcessGraphAsyncMaster: Process error"); } if (ResumeRefNum() < 0) { jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ResumeRefNum error"); } if (ProcessReadSlaves() < 0) { jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessReadSlaves error"); } if (ProcessWriteSlaves() < 0) { jack_error("JackAudioDriver::ProcessGraphAsyncMaster: ProcessWriteSlaves error"); } // Does not wait on graph execution end } /* Used when the driver works in slave mode. */ void JackAudioDriver::ProcessGraphAsyncSlave() { if (ResumeRefNum() < 0) { jack_error("JackAudioDriver::ProcessGraphAsyncSlave: ResumeRefNum error"); } } /* The driver "synchronous" mode: the server does synchronize to the end of client graph execution, if graph process succeed, output buffers computed at the *current cycle* are used. */ int JackAudioDriver::ProcessSync() { // Read input buffers for the current cycle if (Read() < 0) { jack_error("JackAudioDriver::ProcessSync: read error, stopping..."); return -1; } // Process graph ProcessGraphSync(); // Write output buffers from the current cycle if (Write() < 0) { jack_error("JackAudioDriver::ProcessSync: write error, stopping..."); return -1; } // Keep end cycle time JackDriver::CycleTakeEndTime(); return 0; } void JackAudioDriver::ProcessGraphSync() { // Process graph if (fIsMaster) { ProcessGraphSyncMaster(); } else { ProcessGraphSyncSlave(); } } /* Used when the driver works in master mode. */ void JackAudioDriver::ProcessGraphSyncMaster() { // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle if (fEngine->Process(fBeginDateUst, fEndDateUst)) { if (ResumeRefNum() < 0) { jack_error("JackAudioDriver::ProcessGraphSyncMaster: ResumeRefNum error"); } if (ProcessReadSlaves() < 0) { jack_error("JackAudioDriver::ProcessGraphSync: ProcessReadSlaves error, engine may now behave abnormally!!"); } if (ProcessWriteSlaves() < 0) { jack_error("JackAudioDriver::ProcessGraphSync: ProcessWriteSlaves error, engine may now behave abnormally!!"); } // Waits for graph execution end if (SuspendRefNum() < 0) { jack_error("JackAudioDriver::ProcessGraphSync: SuspendRefNum error, engine may now behave abnormally!!"); } } else { // Graph not finished: do not activate it jack_error("JackAudioDriver::ProcessGraphSync: Process error"); } } /* Used when the driver works in slave mode. */ void JackAudioDriver::ProcessGraphSyncSlave() { if (ResumeRefNum() < 0) { jack_error("JackAudioDriver::ProcessGraphSyncSlave: ResumeRefNum error"); } } jack_default_audio_sample_t* JackAudioDriver::GetInputBuffer(int port_index) { return fCapturePortList[port_index] ? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize) : NULL; } jack_default_audio_sample_t* JackAudioDriver::GetOutputBuffer(int port_index) { return fPlaybackPortList[port_index] ? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize) : NULL; } jack_default_audio_sample_t* JackAudioDriver::GetMonitorBuffer(int port_index) { return fMonitorPortList[port_index] ? (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize) : NULL; } int JackAudioDriver::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { switch (notify) { case kLatencyCallback: HandleLatencyCallback(value1); break; default: JackDriver::ClientNotify(refnum, name, notify, sync, message, value1, value2); break; } return 0; } void JackAudioDriver::HandleLatencyCallback(int status) { jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency; for (int i = 0; i < fCaptureChannels; i++) { if (mode == JackPlaybackLatency) { fGraphManager->RecalculateLatency(fCapturePortList[i], mode); } } for (int i = 0; i < fPlaybackChannels; i++) { if (mode == JackCaptureLatency) { fGraphManager->RecalculateLatency(fPlaybackPortList[i], mode); } } } } // end of namespace jack2-1.9.22/common/JackAudioDriver.h000066400000000000000000000066611436671425200172710ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackAudioDriver__ #define __JackAudioDriver__ #include "JackDriver.h" namespace Jack { /*! \brief The base class for audio drivers: drivers with audio ports. A concrete derived class will have to be defined with a real audio driver API, either callback based one (like CoreAudio, PortAudio..) ones or blocking ones (like ALSA). Most of the generic audio handing code is part of this class : - concrete callback basedd derived subclasses typically have to Open/Close the underlying audio API, setup the audio callback and implement the Read/Write methods - concrete blocking based derived subclasses typically have to Open/Close the underlying audio API, implement the Read/Write methods and "wraps" the driver with the JackThreadDriver class. */ class SERVER_EXPORT JackAudioDriver : public JackDriver { protected: jack_default_audio_sample_t* GetInputBuffer(int port_index); jack_default_audio_sample_t* GetOutputBuffer(int port_index); jack_default_audio_sample_t* GetMonitorBuffer(int port_index); void HandleLatencyCallback(int status); virtual void UpdateLatencies(); int ProcessAsync(); void ProcessGraphAsync(); void ProcessGraphAsyncMaster(); void ProcessGraphAsyncSlave(); int ProcessSync(); void ProcessGraphSync(); void ProcessGraphSyncMaster(); void ProcessGraphSyncSlave(); public: JackAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); virtual ~JackAudioDriver(); virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); /* To be called by the underlying driver audio callback, or possibly by a RT thread (using JackThreadedDriver decorator) when a blocking read/write underlying API is used (like ALSA) */ virtual int Process(); virtual int Attach(); virtual int Detach(); virtual int Write(); virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetSampleRate(jack_nframes_t sample_rate); virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); }; } // end of namespace #endif jack2-1.9.22/common/JackAudioPort.cpp000066400000000000000000000123231436671425200173050ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackGlobals.h" #include "JackEngineControl.h" #include "JackPortType.h" #include #if defined (__APPLE__) #include #elif defined (__SSE__) && !defined (__sun__) #include #elif defined (__ARM_NEON__) || defined (__ARM_NEON) #include #endif namespace Jack { static void AudioBufferInit(void* buffer, size_t buffer_size, jack_nframes_t) { memset(buffer, 0, buffer_size); } static inline void MixAudioBuffer(jack_default_audio_sample_t* mixbuffer, jack_default_audio_sample_t* buffer, jack_nframes_t frames) { #ifdef __APPLE__ vDSP_vadd(buffer, 1, mixbuffer, 1, mixbuffer, 1, frames); #else jack_nframes_t frames_group = frames / 4; frames = frames % 4; while (frames_group > 0) { #if defined (__SSE__) && !defined (__sun__) __m128 vec = _mm_add_ps(_mm_load_ps(mixbuffer), _mm_load_ps(buffer)); _mm_store_ps(mixbuffer, vec); mixbuffer += 4; buffer += 4; frames_group--; #elif defined (__ARM_NEON__) || defined (__ARM_NEON) float32x4_t vec = vaddq_f32(vld1q_f32(mixbuffer), vld1q_f32(buffer)); vst1q_f32(mixbuffer, vec); mixbuffer += 4; buffer += 4; frames_group--; #else register jack_default_audio_sample_t mixFloat1 = *mixbuffer; register jack_default_audio_sample_t sourceFloat1 = *buffer; register jack_default_audio_sample_t mixFloat2 = *(mixbuffer + 1); register jack_default_audio_sample_t sourceFloat2 = *(buffer + 1); register jack_default_audio_sample_t mixFloat3 = *(mixbuffer + 2); register jack_default_audio_sample_t sourceFloat3 = *(buffer + 2); register jack_default_audio_sample_t mixFloat4 = *(mixbuffer + 3); register jack_default_audio_sample_t sourceFloat4 = *(buffer + 3); buffer += 4; frames_group--; mixFloat1 += sourceFloat1; mixFloat2 += sourceFloat2; mixFloat3 += sourceFloat3; mixFloat4 += sourceFloat4; *mixbuffer = mixFloat1; *(mixbuffer + 1) = mixFloat2; *(mixbuffer + 2) = mixFloat3; *(mixbuffer + 3) = mixFloat4; mixbuffer += 4; #endif } while (frames > 0) { register jack_default_audio_sample_t mixFloat1 = *mixbuffer; register jack_default_audio_sample_t sourceFloat1 = *buffer; buffer++; frames--; mixFloat1 += sourceFloat1; *mixbuffer = mixFloat1; mixbuffer++; } #endif } static void AudioBufferMixdown(void* mixbuffer, void** src_buffers, int src_count, jack_nframes_t nframes) { void* buffer; // Copy first buffer #if defined (__SSE__) && !defined (__sun__) jack_nframes_t frames_group = nframes / 4; jack_nframes_t remaining_frames = nframes % 4; jack_default_audio_sample_t* source = static_cast(src_buffers[0]); jack_default_audio_sample_t* target = static_cast(mixbuffer); while (frames_group > 0) { __m128 vec = _mm_load_ps(source); _mm_store_ps(target, vec); source += 4; target += 4; --frames_group; } for (jack_nframes_t i = 0; i != remaining_frames; ++i) { target[i] = source[i]; } #elif defined (__ARM_NEON__) || defined (__ARM_NEON) jack_nframes_t frames_group = nframes / 4; jack_nframes_t remaining_frames = nframes % 4; jack_default_audio_sample_t* source = static_cast(src_buffers[0]); jack_default_audio_sample_t* target = static_cast(mixbuffer); while (frames_group > 0) { float32x4_t vec = vld1q_f32(source); vst1q_f32(target, vec); source += 4; target += 4; --frames_group; } for (jack_nframes_t i = 0; i != remaining_frames; ++i) { target[i] = source[i]; } #else memcpy(mixbuffer, src_buffers[0], nframes * sizeof(jack_default_audio_sample_t)); #endif // Mix remaining buffers for (int i = 1; i < src_count; ++i) { buffer = src_buffers[i]; MixAudioBuffer(static_cast(mixbuffer), static_cast(buffer), nframes); } } static size_t AudioBufferSize() { return GetEngineControl()->fBufferSize * sizeof(jack_default_audio_sample_t); } const JackPortType gAudioPortType = { JACK_DEFAULT_AUDIO_TYPE, AudioBufferSize, AudioBufferInit, AudioBufferMixdown }; } // namespace Jack jack2-1.9.22/common/JackChannel.h000066400000000000000000000144021436671425200164140ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackChannel__ #define __JackChannel__ #include "types.h" #include "JackSession.h" #include "JackMetadata.h" namespace Jack { class JackClientInterface; class JackClient; class JackServer; struct JackEngineControl; class JackGraphManager; namespace detail { class JackChannelTransactionInterface { public: JackChannelTransactionInterface() {} virtual ~JackChannelTransactionInterface() {} virtual int Read(void* data, int len) = 0; virtual int Write(void* data, int len) = 0; }; class JackRequestInterface { public: JackRequestInterface() {} virtual ~JackRequestInterface() {} virtual int Connect(const char* dir, const char* name, int which) = 0; virtual int Close() = 0; }; class JackClientRequestInterface : public JackChannelTransactionInterface, public JackRequestInterface { public: JackClientRequestInterface() {} virtual ~JackClientRequestInterface() {} virtual int Read(void* data, int len) { return -1; } virtual int Write(void* data, int len) { return -1; } virtual int Connect(const char* dir, const char* name, int which) { return -1; } virtual int Close() { return -1; } }; /*! \brief Inter process channel for server/client bidirectionnal communication : request and (receiving) notifications. */ class JackClientChannelInterface { public: JackClientChannelInterface() {} virtual ~JackClientChannelInterface() {} // Open the Server/Client connection virtual int Open(const char* server_name, const char* name, jack_uuid_t uuid, char* name_res, JackClient* obj, jack_options_t options, jack_status_t* status) { return 0; } // Close the Server/Client connection virtual void Close() {} // Start listening for messages from the server virtual int Start() { return 0; } // Stop listening for messages from the server virtual void Stop() {} virtual int ServerCheck(const char* server_name) { return -1; } virtual void ClientCheck(const char* name, jack_uuid_t uuid, char* name_res, int protocol, int options, int* status, int* result, int open) {} virtual void ClientOpen(const char* name, int pid, jack_uuid_t uuid, int* shared_engine, int* shared_client, int* shared_graph, int* result) {} virtual void ClientOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, int* result) {} virtual void ClientClose(int refnum, int* result) {} virtual void ClientActivate(int refnum, int is_real_time, int* result) {} virtual void ClientDeactivate(int refnum, int* result) {} virtual void PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) {} virtual void PortUnRegister(int refnum, jack_port_id_t port_index, int* result) {} virtual void PortConnect(int refnum, const char* src, const char* dst, int* result) {} virtual void PortDisconnect(int refnum, const char* src, const char* dst, int* result) {} virtual void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) {} virtual void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) {} virtual void PortRename(int refnum, jack_port_id_t port, const char* name, int* result) {} virtual void SetBufferSize(jack_nframes_t buffer_size, int* result) {} virtual void SetFreewheel(int onoff, int* result) {} virtual void ComputeTotalLatencies(int* result) {} virtual void ReleaseTimebase(int refnum, int* result) {} virtual void SetTimebaseCallback(int refnum, int conditional, int* result) {} virtual void GetInternalClientName(int refnum, int int_ref, char* name_res, int* result) {} virtual void InternalClientHandle(int refnum, const char* client_name, int* status, int* int_ref, int* result) {} virtual void InternalClientLoad(int refnum, const char* client_name, const char* so_name, const char* objet_data, int options, int* status, int* int_ref, jack_uuid_t uuid, int* result) {} virtual void InternalClientUnload(int refnum, int int_ref, int* status, int* result) {} virtual void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char* path, jack_session_command_t** result) {} virtual void SessionReply(int refnum, int* result) {} virtual void GetUUIDForClientName(int refnum, const char* client_name, char* uuid_res, int* result) {} virtual void GetClientNameForUUID(int refnum, const char* uuid, char* name_res, int* result) {} virtual void ReserveClientName(int refnum, const char* client_name, const char *uuid, int* result) {} virtual void ClientHasSessionCallback(const char* client_name, int* result) {} virtual void PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change, int* result) {} virtual bool IsChannelThread() { return false; } }; } } // end of namespace #endif jack2-1.9.22/common/JackClient.cpp000066400000000000000000001220211436671425200166120ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2016-2023 Filipe Coelho This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackSystemDeps.h" #include "JackGraphManager.h" #include "JackClientControl.h" #include "JackEngineControl.h" #include "JackGlobals.h" #include "JackChannel.h" #include "JackTransportEngine.h" #include "driver_interface.h" #include "JackLibGlobals.h" #include #include #include #include using namespace std; namespace Jack { #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL)) JackClient::JackClient(JackSynchro* table):fThread(this) { fSynchroTable = table; fProcess = NULL; fGraphOrder = NULL; fXrun = NULL; fShutdown = NULL; fInfoShutdown = NULL; fInit = NULL; fBufferSize = NULL; fClientRegistration = NULL; fFreewheel = NULL; fPortRegistration = NULL; fPortConnect = NULL; fPortRename = NULL; fTimebase = NULL; fSync = NULL; fThreadFun = NULL; fSession = NULL; fLatency = NULL; fPropertyChange = NULL; fProcessArg = NULL; fGraphOrderArg = NULL; fXrunArg = NULL; fShutdownArg = NULL; fInfoShutdownArg = NULL; fInitArg = NULL; fBufferSizeArg = NULL; fFreewheelArg = NULL; fClientRegistrationArg = NULL; fPortRegistrationArg = NULL; fPortConnectArg = NULL; fPortRenameArg = NULL; fSyncArg = NULL; fTimebaseArg = NULL; fThreadFunArg = NULL; fSessionArg = NULL; fLatencyArg = NULL; fPropertyChangeArg = NULL; fSessionReply = kPendingSessionReply; } JackClient::~JackClient() {} void JackClient::ShutDown(jack_status_t code, const char* message) { jack_log("JackClient::ShutDown"); // If "fInfoShutdown" callback, then call it if (fInfoShutdown) { fInfoShutdown(code, message, fInfoShutdownArg); fInfoShutdown = NULL; // Otherwise possibly call the normal "fShutdown" } else if (fShutdown) { fShutdown(fShutdownArg); fShutdown = NULL; } } int JackClient::Close() { jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum); int result = 0; Deactivate(); // Channels is stopped first to avoid receiving notifications while closing fChannel->Stop(); // Then close client fChannel->ClientClose(GetClientControl()->fRefNum, &result); fChannel->Close(); assert(JackGlobals::fSynchroMutex); JackGlobals::fSynchroMutex->Lock(); fSynchroTable[GetClientControl()->fRefNum].Disconnect(); JackGlobals::fSynchroMutex->Unlock(); JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL; return result; } bool JackClient::IsActive() { return (GetClientControl()) ? GetClientControl()->fActive : false; } jack_native_thread_t JackClient::GetThreadID() { return fThread.GetThreadID(); } /*! In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations. The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations. Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel. */ void JackClient::SetupDriverSync(bool freewheel) { if (!freewheel && !GetEngineControl()->fSyncMode) { jack_log("JackClient::SetupDriverSync driver sem in flush mode"); for (int i = 0; i < GetEngineControl()->fDriverNum; i++) { fSynchroTable[i].SetFlush(true); } } else { jack_log("JackClient::SetupDriverSync driver sem in normal mode"); for (int i = 0; i < GetEngineControl()->fDriverNum; i++) { fSynchroTable[i].SetFlush(false); } } } /*! \brief Notification received from the server. */ int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { return 0; } int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { int res = 0; jack_log("JackClient::ClientNotify ref = %ld name = %s notify = %ld", refnum, name, notify); // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient switch (notify) { case kAddClient: res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2); break; case kRemoveClient: res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2); break; case kActivateClient: jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum); InitAux(); break; } /* The current semantic is that notifications can only be received when the client has been activated, although is this implementation, one could imagine calling notifications as soon as the client has be opened. */ if (IsActive()) { switch (notify) { case kAddClient: jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name); if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself fClientRegistration(name, 1, fClientRegistrationArg); } break; case kRemoveClient: jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name); if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself fClientRegistration(name, 0, fClientRegistrationArg); } break; case kBufferSizeCallback: jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1); if (fBufferSize) { res = fBufferSize(value1, fBufferSizeArg); } break; case kSampleRateCallback: jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1); if (fSampleRate) { res = fSampleRate(value1, fSampleRateArg); } break; case kGraphOrderCallback: jack_log("JackClient::kGraphOrderCallback"); if (fGraphOrder) { res = fGraphOrder(fGraphOrderArg); } break; case kStartFreewheelCallback: jack_log("JackClient::kStartFreewheel"); SetupDriverSync(true); // Drop RT only when the RT thread is actually running if (fThread.GetStatus() == JackThread::kRunning) { fThread.DropRealTime(); } if (fFreewheel) { fFreewheel(1, fFreewheelArg); } break; case kStopFreewheelCallback: jack_log("JackClient::kStopFreewheel"); SetupDriverSync(false); if (fFreewheel) { fFreewheel(0, fFreewheelArg); } // Acquire RT only when the RT thread is actually running if (GetEngineControl()->fRealTime && fThread.GetStatus() == JackThread::kRunning) { if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) { jack_error("JackClient::AcquireRealTime error"); } } break; case kPortRegistrationOnCallback: jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1); if (fPortRegistration) { fPortRegistration(value1, 1, fPortRegistrationArg); } break; case kPortRegistrationOffCallback: jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1); if (fPortRegistration) { fPortRegistration(value1, 0, fPortRegistrationArg); } break; case kPortConnectCallback: jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2); if (fPortConnect) { fPortConnect(value1, value2, 1, fPortConnectArg); } break; case kPortDisconnectCallback: jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2); if (fPortConnect) { fPortConnect(value1, value2, 0, fPortConnectArg); } break; case kPortRenameCallback: jack_log("JackClient::kPortRenameCallback port = %ld", value1); if (fPortRename) { fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg); } break; case kXRunCallback: jack_log("JackClient::kXRunCallback"); if (fXrun) { res = fXrun(fXrunArg); } break; case kShutDownCallback: jack_log("JackClient::kShutDownCallback"); ShutDown(jack_status_t(value1), message); break; case kSessionCallback: jack_log("JackClient::kSessionCallback"); if (fSession) { jack_session_event_t* event = (jack_session_event_t*)malloc( sizeof(jack_session_event_t)); char uuid_buf[JACK_UUID_STRING_SIZE]; event->type = (jack_session_event_type_t)value1; event->session_dir = strdup(message); event->command_line = NULL; event->flags = (jack_session_flags_t)0; jack_uuid_unparse(GetClientControl()->fSessionID, uuid_buf); event->client_uuid = strdup(uuid_buf); fSessionReply = kPendingSessionReply; // Session callback may change fSessionReply by directly using jack_session_reply fSession(event, fSessionArg); res = fSessionReply; } break; case kLatencyCallback: res = HandleLatencyCallback(value1); break; case kPropertyChangeCallback: { jack_uuid_t subject; jack_uuid_parse(name, &subject); const char* key = message; jack_property_change_t change = (jack_property_change_t)value1; jack_log("JackClient::kPropertyChangeCallback subject = %x key = %s change = %x", subject, key, change); if (fPropertyChange) fPropertyChange(subject, key, change, fPropertyChangeArg); break; } } } return res; } int JackClient::HandleLatencyCallback(int status) { jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency; jack_latency_range_t latency = { UINT32_MAX, 0 }; /* first setup all latency values of the ports. * this is based on the connections of the ports. */ list::iterator it; for (it = fPortList.begin(); it != fPortList.end(); it++) { JackPort* port = GetGraphManager()->GetPort(*it); if ((port->GetFlags() & JackPortIsOutput) && (mode == JackPlaybackLatency)) { GetGraphManager()->RecalculateLatency(*it, mode); } if ((port->GetFlags() & JackPortIsInput) && (mode == JackCaptureLatency)) { GetGraphManager()->RecalculateLatency(*it, mode); } } if (!fLatency) { /* * default action is to assume all ports depend on each other. * then always take the maximum latency. */ if (mode == JackPlaybackLatency) { /* iterate over all OutputPorts, to find maximum playback latency */ for (it = fPortList.begin(); it != fPortList.end(); it++) { JackPort* port = GetGraphManager()->GetPort(*it); if (port->GetFlags() & JackPortIsOutput) { jack_latency_range_t other_latency; port->GetLatencyRange(mode, &other_latency); if (other_latency.max > latency.max) { latency.max = other_latency.max; } if (other_latency.min < latency.min) { latency.min = other_latency.min; } } } if (latency.min == UINT32_MAX) { latency.min = 0; } /* now set the found latency on all input ports */ for (it = fPortList.begin(); it != fPortList.end(); it++) { JackPort* port = GetGraphManager()->GetPort(*it); if (port->GetFlags() & JackPortIsInput) { port->SetLatencyRange(mode, &latency); } } } if (mode == JackCaptureLatency) { /* iterate over all InputPorts, to find maximum playback latency */ for (it = fPortList.begin(); it != fPortList.end(); it++) { JackPort* port = GetGraphManager()->GetPort(*it); if (port->GetFlags() & JackPortIsInput) { jack_latency_range_t other_latency; port->GetLatencyRange(mode, &other_latency); if (other_latency.max > latency.max) { latency.max = other_latency.max; } if (other_latency.min < latency.min) { latency.min = other_latency.min; } } } if (latency.min == UINT32_MAX) { latency.min = 0; } /* now set the found latency on all output ports */ for (it = fPortList.begin(); it != fPortList.end(); it++) { JackPort* port = GetGraphManager()->GetPort(*it); if (port->GetFlags() & JackPortIsOutput) { port->SetLatencyRange(mode, &latency); } } } return 0; } /* we have a latency callback setup by the client, * lets use it... */ fLatency(mode, fLatencyArg); return 0; } /*! \brief We need to start thread before activating in the server, otherwise the FW driver connected to the client may not be activated. */ int JackClient::Activate() { jack_log("JackClient::Activate"); if (IsActive()) { return 0; } // RT thread is started only when needed... if (IsRealTime()) { if (StartThread() < 0) { return -1; } } /* Insertion of client in the graph will cause a kGraphOrderCallback notification to be delivered by the server, the client wants to receive it. */ GetClientControl()->fActive = true; // Transport related callback become "active" GetClientControl()->fTransportSync = true; GetClientControl()->fTransportTimebase = true; int result = -1; GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime(); fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result); return result; } /*! \brief Need to stop thread after deactivating in the server. */ int JackClient::Deactivate() { jack_log("JackClient::Deactivate"); if (!IsActive()) { return 0; } GetClientControl()->fActive = false; // Transport related callback become "unactive" GetClientControl()->fTransportSync = false; GetClientControl()->fTransportTimebase = false; // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate int result = -1; fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result); jack_log("JackClient::Deactivate res = %ld", result); // RT thread is stopped only when needed... if (IsRealTime()) { fThread.Kill(); } return result; } //---------------------- // RT thread management //---------------------- void JackClient::InitAux() { if (fInit) { jack_log("JackClient::Init calling client thread init callback"); fInit(fInitArg); } } /*! \brief Called once when the thread starts. */ bool JackClient::Init() { /* Execute buffer_size callback. Since StartThread uses fThread.StartSync, we are sure that buffer_size callback is executed before StartThread returns (and then IsActive will be true). So no RT callback can be called at the same time. */ jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", GetEngineControl()->fBufferSize); if (fBufferSize) { fBufferSize(GetEngineControl()->fBufferSize, fBufferSizeArg); } // Init callback InitAux(); // Setup context if (!jack_tls_set(JackGlobals::fRealTimeThread, this)) { jack_error("Failed to set thread realtime key"); } // Setup RT if (GetEngineControl()->fRealTime) { set_threaded_log_function(); SetupRealTime(); } return true; } void JackClient::SetupRealTime() { jack_log("JackClient::Init : period = %ld computation = %ld constraint = %ld", long(int64_t(GetEngineControl()->fPeriod) / 1000.0f), long(int64_t(GetEngineControl()->fComputation) / 1000.0f), long(int64_t(GetEngineControl()->fConstraint) / 1000.0f)); // Will do "something" on OSX only... fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) { jack_error("JackClient::AcquireSelfRealTime error"); } } int JackClient::StartThread() { if (fThread.StartSync() < 0) { jack_error("Start thread error"); return -1; } return 0; } /*! \brief RT thread. */ bool JackClient::Execute() { // Execute a dummy cycle to be sure thread has the correct properties DummyCycle(); if (fThreadFun) { fThreadFun(fThreadFunArg); } else { ExecuteThread(); } return false; } void JackClient::DummyCycle() { WaitSync(); SignalSync(); } inline void JackClient::ExecuteThread() { while (true) { CycleWaitAux(); CycleSignalAux(CallProcessCallback()); } } inline jack_nframes_t JackClient::CycleWaitAux() { if (!WaitSync()) { Error(); // Terminates the thread } CallSyncCallbackAux(); return GetEngineControl()->fBufferSize; } inline void JackClient::CycleSignalAux(int status) { if (status == 0) { CallTimebaseCallbackAux(); } SignalSync(); if (status != 0) { End(); // Terminates the thread } } jack_nframes_t JackClient::CycleWait() { return CycleWaitAux(); } void JackClient::CycleSignal(int status) { CycleSignalAux(status); } inline int JackClient::CallProcessCallback() { return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0; } inline bool JackClient::WaitSync() { // Suspend itself: wait on the input synchro if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, LONG_MAX) < 0) { #ifdef __APPLE__ // FIXME macOS reports wait failures when closing down, due to aborted semaphore, ignore it if (!GetClientControl()->fActive) { fThread.Terminate(); return true; } #endif jack_error("SuspendRefNum error"); return false; } else { return true; } } inline void JackClient::SignalSync() { // Resume: signal output clients connected to the running client if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) { jack_error("ResumeRefNum error"); } } inline void JackClient::End() { jack_log("JackClient::Execute end name = %s", GetClientControl()->fName); // Hum... not sure about this, the following "close" code is called in the RT thread... int result; fThread.DropSelfRealTime(); GetClientControl()->fActive = false; fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result); fThread.Terminate(); } inline void JackClient::Error() { jack_error("JackClient::Execute error name = %s", GetClientControl()->fName); // Hum... not sure about this, the following "close" code is called in the RT thread... int result; fThread.DropSelfRealTime(); GetClientControl()->fActive = false; fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result); ShutDown(jack_status_t(JackFailure | JackServerError), JACK_SERVER_FAILURE); fThread.Terminate(); } //----------------- // Port management //----------------- int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) { // Check if port name is empty string port_short_name_str = string(port_name); if (port_short_name_str.size() == 0) { jack_error("port_name is empty"); return 0; // Means failure here... } // Check port name length string port_full_name_str = string(GetClientControl()->fName) + string(":") + port_short_name_str; if (port_full_name_str.size() >= REAL_JACK_PORT_NAME_SIZE) { jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n" "Please use %lu characters or less", GetClientControl()->fName, port_name, JACK_PORT_NAME_SIZE - 1); return 0; // Means failure here... } int result = -1; jack_port_id_t port_index = NO_PORT; fChannel->PortRegister(GetClientControl()->fRefNum, port_full_name_str.c_str(), port_type, flags, buffer_size, &port_index, &result); if (result == 0) { jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, port_full_name_str.c_str(), port_type, port_index); fPortList.push_back(port_index); return port_index; } else { return 0; } } int JackClient::PortUnRegister(jack_port_id_t port_index) { jack_log("JackClient::PortUnRegister port_index = %ld", port_index); list::iterator it = find(fPortList.begin(), fPortList.end(), port_index); if (it != fPortList.end()) { fPortList.erase(it); int result = -1; fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result); return result; } else { jack_error("unregistering a port %ld that is not own by the client", port_index); return -1; } } int JackClient::PortConnect(const char* src, const char* dst) { jack_log("JackClient::Connect src = %s dst = %s", src, dst); if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) { jack_error("\"%s\" is too long to be used as a JACK port name.\n", src); return -1; } if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) { jack_error("\"%s\" is too long to be used as a JACK port name.\n", dst); return -1; } int result = -1; fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result); return result; } int JackClient::PortDisconnect(const char* src, const char* dst) { jack_log("JackClient::Disconnect src = %s dst = %s", src, dst); if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) { jack_error("\"%s\" is too long to be used as a JACK port name.\n", src); return -1; } if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) { jack_error("\"%s\" is too long to be used as a JACK port name.\n", dst); return -1; } int result = -1; fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result); return result; } int JackClient::PortDisconnect(jack_port_id_t src) { jack_log("JackClient::PortDisconnect src = %ld", src); int result = -1; fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result); return result; } int JackClient::PortIsMine(jack_port_id_t port_index) { JackPort* port = GetGraphManager()->GetPort(port_index); return GetClientControl()->fRefNum == port->GetRefNum(); } int JackClient::PortRename(jack_port_id_t port_index, const char* name) { int result = -1; fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result); return result; } //-------------------- // Context management //-------------------- int JackClient::SetBufferSize(jack_nframes_t buffer_size) { int result = -1; fChannel->SetBufferSize(buffer_size, &result); return result; } int JackClient::SetFreeWheel(int onoff) { int result = -1; fChannel->SetFreewheel(onoff, &result); return result; } int JackClient::ComputeTotalLatencies() { int result = -1; fChannel->ComputeTotalLatencies(&result); return result; } //---------------------- // Transport management //---------------------- inline int JackClient::ActivateAux() { // If activated without RT thread... if (IsActive() && fThread.GetStatus() != JackThread::kRunning) { jack_log("JackClient::ActivateAux"); // RT thread is started if (StartThread() < 0) { return -1; } int result = -1; GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime(); fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result); return result; } else { return 0; } } int JackClient::ReleaseTimebase() { int result = -1; fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result); if (result == 0) { GetClientControl()->fTransportTimebase = false; fTimebase = NULL; fTimebaseArg = NULL; } return result; } /* Call the server if the client is active, otherwise keeps the arguments */ int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg) { GetClientControl()->fTransportSync = (fSync != NULL); fSyncArg = arg; fSync = sync_callback; return ActivateAux(); } int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg) { int result = -1; fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result); if (result == 0) { GetClientControl()->fTransportTimebase = true; fTimebase = timebase_callback; fTimebaseArg = arg; return ActivateAux(); } else { fTimebase = NULL; fTimebaseArg = NULL; return result; } } int JackClient::SetSyncTimeout(jack_time_t timeout) { GetEngineControl()->fTransport.SetSyncTimeout(timeout); return 0; } // Must be RT safe void JackClient::TransportLocate(jack_nframes_t frame) { jack_position_t pos; pos.frame = frame; pos.valid = (jack_position_bits_t)0; jack_log("JackClient::TransportLocate pos = %ld", pos.frame); GetEngineControl()->fTransport.RequestNewPos(&pos); } int JackClient::TransportReposition(const jack_position_t* pos) { jack_position_t tmp = *pos; jack_log("JackClient::TransportReposition pos = %ld", pos->frame); if (tmp.valid & ~JACK_POSITION_MASK) { return EINVAL; } else { GetEngineControl()->fTransport.RequestNewPos(&tmp); return 0; } } jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos) { return GetEngineControl()->fTransport.Query(pos); } jack_nframes_t JackClient::GetCurrentTransportFrame() { return GetEngineControl()->fTransport.GetCurrentFrame(); } // Must be RT safe: directly write in the transport shared mem void JackClient::TransportStart() { GetEngineControl()->fTransport.SetCommand(TransportCommandStart); } // Must be RT safe: directly write in the transport shared mem void JackClient::TransportStop() { GetEngineControl()->fTransport.SetCommand(TransportCommandStop); } // Never called concurrently with the server // TODO check concurrency with SetSyncCallback void JackClient::CallSyncCallback() { CallSyncCallbackAux(); } inline void JackClient::CallSyncCallbackAux() { if (GetClientControl()->fTransportSync) { JackTransportEngine& transport = GetEngineControl()->fTransport; jack_position_t* cur_pos = transport.ReadCurrentState(); jack_transport_state_t transport_state = transport.GetState(); if (fSync != NULL) { if (fSync(transport_state, cur_pos, fSyncArg)) { GetClientControl()->fTransportState = JackTransportRolling; GetClientControl()->fTransportSync = false; } } else { GetClientControl()->fTransportState = JackTransportRolling; GetClientControl()->fTransportSync = false; } } } void JackClient::CallTimebaseCallback() { CallTimebaseCallbackAux(); } inline void JackClient::CallTimebaseCallbackAux() { JackTransportEngine& transport = GetEngineControl()->fTransport; int master; bool unused; transport.GetTimebaseMaster(master, unused); if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase... jack_transport_state_t transport_state = transport.GetState(); jack_position_t* cur_pos = transport.WriteNextStateStart(1); if (GetClientControl()->fTransportTimebase) { fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg); GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true } else if (transport_state == JackTransportRolling) { fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg); } transport.WriteNextStateStop(1); } } //--------------------- // Callback management //--------------------- void JackClient::OnShutdown(JackShutdownCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); } else { // Shutdown callback will either be an old API version or the new version (with info) GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL); fShutdownArg = arg; fShutdown = callback; } } void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); } else { // Shutdown callback will either be an old API version or the new version (with info) GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL); fInfoShutdownArg = arg; fInfoShutdown = callback; } } int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else if (fThreadFun) { jack_error ("A thread callback has already been setup, both models cannot be used at the same time!"); return -1; } else { fProcessArg = arg; fProcess = callback; return 0; } } int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kXRunCallback] = (callback != NULL); fXrunArg = arg; fXrun = callback; return 0; } } int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { fInitArg = arg; fInit = callback; /* make sure that the message buffer thread is initialized too */ return JackMessageBuffer::fInstance->SetInitCallback(callback, arg); } } int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL); fGraphOrder = callback; fGraphOrderArg = arg; return 0; } } int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL); fBufferSizeArg = arg; fBufferSize = callback; return 0; } } int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL); fSampleRateArg = arg; fSampleRate = callback; // Now invoke it if (callback) { callback(GetEngineControl()->fSampleRate, arg); } return 0; } } int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { // kAddClient and kRemoveClient notifications must be delivered by the server in any case fClientRegistrationArg = arg; fClientRegistration = callback; return 0; } } int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL); GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL); fFreewheelArg = arg; fFreewheel = callback; return 0; } } int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL); GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL); fPortRegistrationArg = arg; fPortRegistration = callback; return 0; } } int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL); GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL); fPortConnectArg = arg; fPortConnect = callback; return 0; } } int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL); fPortRenameArg = arg; fPortRename = callback; return 0; } } int JackClient::SetProcessThread(JackThreadCallback fun, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else if (fProcess) { jack_error("A process callback has already been setup, both models cannot be used at the same time!"); return -1; } else { fThreadFun = fun; fThreadFunArg = arg; return 0; } } int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { GetClientControl()->fCallback[kSessionCallback] = (callback != NULL); fSessionArg = arg; fSession = callback; return 0; } } int JackClient::SetLatencyCallback(JackLatencyCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { // fCallback[kLatencyCallback] must always be 'true' fLatencyArg = arg; fLatency = callback; return 0; } } int JackClient::SetPropertyChangeCallback(JackPropertyChangeCallback callback, void *arg) { if (IsActive()) { jack_error("You cannot set callbacks on an active client"); return -1; } else { fPropertyChangeArg = arg; fPropertyChange = callback; return 0; } } //------------------ // Internal clients //------------------ char* JackClient::GetInternalClientName(int ref) { char name_res[JACK_CLIENT_NAME_SIZE+1]; int result = -1; fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result); return (result < 0) ? NULL : strdup(name_res); } int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status) { int int_ref, result = -1; fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result); return int_ref; } int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va) { if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) { jack_error ("\"%s\" is too long for a JACK client name.\n" "Please use %lu characters or less.", client_name, JACK_CLIENT_NAME_SIZE); return 0; } if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) { jack_error("\"%s\" is too long for a shared object name.\n" "Please use %lu characters or less.", va->load_name, JACK_PATH_MAX); int my_status1 = *status | (JackFailure | JackInvalidOption); *status = (jack_status_t)my_status1; return 0; } if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) { jack_error ("\"%s\" is too long for internal client init " "string.\nPlease use %lu characters or less.", va->load_init, JACK_LOAD_INIT_LIMIT); int my_status1 = *status | (JackFailure | JackInvalidOption); *status = (jack_status_t)my_status1; return 0; } int int_ref, result = -1; fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, -1, &result); return int_ref; } void JackClient::InternalClientUnload(int ref, jack_status_t* status) { int result = -1; fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result); } //------------------ // Session API //------------------ jack_session_command_t* JackClient::SessionNotify(const char* target, jack_session_event_type_t type, const char* path) { jack_session_command_t* res; fChannel->SessionNotify(GetClientControl()->fRefNum, target, type, path, &res); return res; } int JackClient::SessionReply(jack_session_event_t* ev) { if (ev->command_line) { strncpy(GetClientControl()->fSessionCommand, ev->command_line, sizeof(GetClientControl()->fSessionCommand)); } else { GetClientControl()->fSessionCommand[0] = '\0'; } GetClientControl()->fSessionFlags = ev->flags; jack_log("JackClient::SessionReply... we are here"); if (fChannel->IsChannelThread()) { jack_log("JackClient::SessionReply... in callback reply"); // OK, immediate reply... fSessionReply = kImmediateSessionReply; return 0; } jack_log("JackClient::SessionReply... out of cb"); int result = -1; fChannel->SessionReply(GetClientControl()->fRefNum, &result); return result; } char* JackClient::GetUUIDForClientName(const char* client_name) { char uuid_res[JACK_UUID_STRING_SIZE]; int result = -1; fChannel->GetUUIDForClientName(GetClientControl()->fRefNum, client_name, uuid_res, &result); return (result) ? NULL : strdup(uuid_res); } char* JackClient::GetClientNameByUUID(const char* uuid) { char name_res[JACK_CLIENT_NAME_SIZE + 1]; int result = -1; fChannel->GetClientNameForUUID(GetClientControl()->fRefNum, uuid, name_res, &result); return (result) ? NULL : strdup(name_res); } int JackClient::ReserveClientName(const char* client_name, const char* uuid) { int result = -1; fChannel->ReserveClientName( GetClientControl()->fRefNum, client_name, uuid, &result); return result; } int JackClient::ClientHasSessionCallback(const char* client_name) { int result = -1; fChannel->ClientHasSessionCallback(client_name, &result); return result; } //------------------ // Metadata API //------------------ int JackClient::PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change) { int result = -1; fChannel->PropertyChangeNotify(subject, key, change, &result); return result; } } // end of namespace jack2-1.9.22/common/JackClient.h000066400000000000000000000212541436671425200162650ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackClient__ #define __JackClient__ #include "JackClientInterface.h" #include "JackThread.h" #include "JackConstants.h" #include "JackSynchro.h" #include "JackPlatformPlug.h" #include "JackChannel.h" #include "JackRequest.h" #include "JackMetadata.h" #include "varargs.h" #include namespace Jack { class JackGraphManager; class JackServer; class JackEngine; struct JackClientControl; struct JackEngineControl; /*! \brief The base class for clients: share part of the implementation for JackInternalClient and JackLibClient. */ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnableInterface { friend class JackDebugClient; protected: JackProcessCallback fProcess; JackGraphOrderCallback fGraphOrder; JackXRunCallback fXrun; JackShutdownCallback fShutdown; JackInfoShutdownCallback fInfoShutdown; JackThreadInitCallback fInit; JackBufferSizeCallback fBufferSize; JackSampleRateCallback fSampleRate; JackClientRegistrationCallback fClientRegistration; JackFreewheelCallback fFreewheel; JackPortRegistrationCallback fPortRegistration; JackPortConnectCallback fPortConnect; JackPortRenameCallback fPortRename; JackTimebaseCallback fTimebase; JackSyncCallback fSync; JackThreadCallback fThreadFun; JackSessionCallback fSession; JackLatencyCallback fLatency; JackPropertyChangeCallback fPropertyChange; void* fProcessArg; void* fGraphOrderArg; void* fXrunArg; void* fShutdownArg; void* fInfoShutdownArg; void* fInitArg; void* fBufferSizeArg; void* fSampleRateArg; void* fClientRegistrationArg; void* fFreewheelArg; void* fPortRegistrationArg; void* fPortConnectArg; void* fPortRenameArg; void* fTimebaseArg; void* fSyncArg; void* fThreadFunArg; void* fSessionArg; void* fLatencyArg; void* fPropertyChangeArg; char fServerName[JACK_SERVER_NAME_SIZE+1]; JackThread fThread; /*! Thread to execute the Process function */ detail::JackClientChannelInterface* fChannel; JackSynchro* fSynchroTable; std::list fPortList; JackSessionReply fSessionReply; int StartThread(); void SetupDriverSync(bool freewheel); bool IsActive(); void CallSyncCallback(); void CallTimebaseCallback(); virtual int ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value); inline void DummyCycle(); inline void ExecuteThread(); inline bool WaitSync(); inline void SignalSync(); inline int CallProcessCallback(); inline void End(); inline void Error(); inline jack_nframes_t CycleWaitAux(); inline void CycleSignalAux(int status); inline void CallSyncCallbackAux(); inline void CallTimebaseCallbackAux(); inline int ActivateAux(); inline void InitAux(); inline void SetupRealTime(); int HandleLatencyCallback(int status); public: JackClient(JackSynchro* table); virtual ~JackClient(); char* GetServerName() { return fServerName; } virtual int Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status) = 0; virtual int Close(); virtual JackGraphManager* GetGraphManager() const = 0; virtual JackEngineControl* GetEngineControl() const = 0; // Notifications virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); virtual int Activate(); virtual int Deactivate(); // Context virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetFreeWheel(int onoff); virtual int ComputeTotalLatencies(); virtual void ShutDown(jack_status_t code, const char* message); virtual jack_native_thread_t GetThreadID(); // Port management virtual int PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); virtual int PortUnRegister(jack_port_id_t port); virtual int PortConnect(const char* src, const char* dst); virtual int PortDisconnect(const char* src, const char* dst); virtual int PortDisconnect(jack_port_id_t src); virtual int PortIsMine(jack_port_id_t port_index); virtual int PortRename(jack_port_id_t port_index, const char* name); // Transport virtual int ReleaseTimebase(); virtual int SetSyncCallback(JackSyncCallback sync_callback, void* arg); virtual int SetSyncTimeout(jack_time_t timeout); virtual int SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg); virtual void TransportLocate(jack_nframes_t frame); virtual jack_transport_state_t TransportQuery(jack_position_t* pos); virtual jack_nframes_t GetCurrentTransportFrame(); virtual int TransportReposition(const jack_position_t* pos); virtual void TransportStart(); virtual void TransportStop(); // Callbacks virtual void OnShutdown(JackShutdownCallback callback, void *arg); virtual void OnInfoShutdown(JackInfoShutdownCallback callback, void *arg); virtual int SetProcessCallback(JackProcessCallback callback, void* arg); virtual int SetXRunCallback(JackXRunCallback callback, void* arg); virtual int SetInitCallback(JackThreadInitCallback callback, void* arg); virtual int SetGraphOrderCallback(JackGraphOrderCallback callback, void* arg); virtual int SetBufferSizeCallback(JackBufferSizeCallback callback, void* arg); virtual int SetSampleRateCallback(JackBufferSizeCallback callback, void* arg); virtual int SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg); virtual int SetFreewheelCallback(JackFreewheelCallback callback, void* arg); virtual int SetPortRegistrationCallback(JackPortRegistrationCallback callback, void* arg); virtual int SetPortConnectCallback(JackPortConnectCallback callback, void *arg); virtual int SetPortRenameCallback(JackPortRenameCallback callback, void *arg); virtual int SetSessionCallback(JackSessionCallback callback, void *arg); virtual int SetLatencyCallback(JackLatencyCallback callback, void *arg); virtual int SetPropertyChangeCallback(JackPropertyChangeCallback callback, void* arg); // Internal clients virtual char* GetInternalClientName(int ref); virtual int InternalClientHandle(const char* client_name, jack_status_t* status); virtual int InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va); virtual void InternalClientUnload(int ref, jack_status_t* status); // RT Thread jack_nframes_t CycleWait(); void CycleSignal(int status); virtual int SetProcessThread(JackThreadCallback fun, void *arg); // Session API virtual jack_session_command_t* SessionNotify(const char* target, jack_session_event_type_t type, const char* path); virtual int SessionReply(jack_session_event_t* ev); virtual char* GetUUIDForClientName(const char* client_name); virtual char* GetClientNameByUUID(const char* uuid); virtual int ReserveClientName(const char* client_name, const char* uuid); virtual int ClientHasSessionCallback(const char* client_name); // Metadata API virtual int PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change); // JackRunnableInterface interface bool Init(); bool Execute(); }; } // end of namespace #endif jack2-1.9.22/common/JackClientControl.h000066400000000000000000000053451436671425200176310ustar00rootroot00000000000000/* Copyright (C) 2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackClientControl__ #define __JackClientControl__ #include "JackShmMem.h" #include "JackPort.h" #include "JackSynchro.h" #include "JackNotification.h" #include "JackSession.h" namespace Jack { /*! \brief Client control possibly in shared memory. */ PRE_PACKED_STRUCTURE struct JackClientControl : public JackShmMemAble { char fName[JACK_CLIENT_NAME_SIZE + 1]; bool fCallback[kMaxNotification]; volatile jack_transport_state_t fTransportState; volatile bool fTransportSync; /* Will be true when slow-sync cb has to be called */ volatile bool fTransportTimebase; /* Will be true when timebase cb is called with new_pos on */ int fRefNum; int fPID; bool fActive; jack_uuid_t fSessionID; char fSessionCommand[JACK_SESSION_COMMAND_SIZE]; jack_session_flags_t fSessionFlags; JackClientControl(const char* name, int pid, int refnum, jack_uuid_t uuid) { Init(name, pid, refnum, uuid); } JackClientControl(const char* name, jack_uuid_t uuid) { Init(name, 0, -1, uuid); } JackClientControl() { Init("", 0, -1, JACK_UUID_EMPTY_INITIALIZER); } void Init(const char* name, int pid, int refnum, jack_uuid_t uuid) { strcpy(fName, name); for (int i = 0; i < kMaxNotification; i++) { fCallback[i] = false; } // Always activated fCallback[kAddClient] = true; fCallback[kRemoveClient] = true; fCallback[kActivateClient] = true; fCallback[kLatencyCallback] = true; // So that driver synchro are correctly setup in "flush" or "normal" mode fCallback[kStartFreewheelCallback] = true; fCallback[kStopFreewheelCallback] = true; fRefNum = refnum; fPID = pid; fTransportState = JackTransportStopped; fTransportSync = false; fTransportTimebase = false; fActive = false; fSessionID = uuid; } } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackClientInterface.h000066400000000000000000000025221436671425200201030ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackClientInterface__ #define __JackClientInterface__ #include "JackCompilerDeps.h" namespace Jack { struct JackClientControl; /*! \brief Client interface. */ class SERVER_EXPORT JackClientInterface { public: JackClientInterface() {} virtual ~JackClientInterface() {} virtual int Close() = 0; virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) = 0; virtual JackClientControl* GetClientControl() const = 0; }; } // end of namespace #endif jack2-1.9.22/common/JackCompilerDeps.h000066400000000000000000000015241436671425200174330ustar00rootroot00000000000000/* Copyright (C) 2004-2005 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackCompilerDeps__ #define __JackCompilerDeps__ #include "JackCompilerDeps_os.h" #endif jack2-1.9.22/common/JackConnectionManager.cpp000066400000000000000000000317451436671425200210020ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackConnectionManager.h" #include "JackClientControl.h" #include "JackEngineControl.h" #include "JackGlobals.h" #include "JackError.h" #include #include #include namespace Jack { JackConnectionManager::JackConnectionManager() { int i; static_assert(offsetof(JackConnectionManager, fInputCounter) % sizeof(UInt32) == 0, "fInputCounter must be aligned within JackConnectionManager"); jack_log("JackConnectionManager::InitConnections size = %ld ", sizeof(JackConnectionManager)); for (i = 0; i < PORT_NUM_MAX; i++) { fConnection[i].Init(); } fLoopFeedback.Init(); jack_log("JackConnectionManager::InitClients"); for (i = 0; i < CLIENT_NUM; i++) { InitRefNum(i); } } JackConnectionManager::~JackConnectionManager() {} //-------------- // Internal API //-------------- bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const { jack_log("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld", ref1, ref2); if (ref1 < GetEngineControl()->fDriverNum || ref2 < GetEngineControl()->fDriverNum) { return false; } else if (ref1 == ref2) { // Same refnum return true; } else { jack_int_t output[CLIENT_NUM]; fConnectionRef.GetOutputTable(ref1, output); if (fConnectionRef.IsInsideTable(ref2, output)) { // If ref2 is contained in the outputs of ref1 return true; } else { for (int i = 0; i < CLIENT_NUM && output[i] != EMPTY; i++) { // Otherwise recurse for all ref1 outputs if (IsLoopPathAux(output[i], ref2)) { return true; // Stop when a path is found } } return false; } } } //-------------- // External API //-------------- /*! \brief Connect port_src to port_dst. */ int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst) { jack_log("JackConnectionManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst); if (fConnection[port_src].AddItem(port_dst)) { return 0; } else { jack_error("Connection table is full !!"); return -1; } } /*! \brief Disconnect port_src from port_dst. */ int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst) { jack_log("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst); if (fConnection[port_src].RemoveItem(port_dst)) { return 0; } else { jack_error("Connection not found !!"); return -1; } } /*! \brief Check if port_src and port_dst are connected. */ bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const { return fConnection[port_src].CheckItem(port_dst); } /*! \brief Get the connection port array. */ const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const { return fConnection[port_index].GetItems(); } //------------------------ // Client port management //------------------------ /*! \brief Add an input port to a client. */ int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index) { if (fInputPort[refnum].AddItem(port_index)) { jack_log("JackConnectionManager::AddInputPort ref = %ld port = %ld", refnum, port_index); return 0; } else { jack_error("Maximum number of input ports is reached for application ref = %ld", refnum); return -1; } } /*! \brief Add an output port to a client. */ int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index) { if (fOutputPort[refnum].AddItem(port_index)) { jack_log("JackConnectionManager::AddOutputPort ref = %ld port = %ld", refnum, port_index); return 0; } else { jack_error("Maximum number of output ports is reached for application ref = %ld", refnum); return -1; } } /*! \brief Remove an input port from a client. */ int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index) { jack_log("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld ", refnum, port_index); if (fInputPort[refnum].RemoveItem(port_index)) { return 0; } else { jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum); return -1; } } /*! \brief Remove an output port from a client. */ int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index) { jack_log("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld ", refnum, port_index); if (fOutputPort[refnum].RemoveItem(port_index)) { return 0; } else { jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum); return -1; } } /*! \brief Get the input port array of a given refnum. */ const jack_int_t* JackConnectionManager::GetInputPorts(int refnum) { return fInputPort[refnum].GetItems(); } /*! \brief Get the output port array of a given refnum. */ const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum) { return fOutputPort[refnum].GetItems(); } /*! \brief Init the refnum. */ void JackConnectionManager::InitRefNum(int refnum) { fInputPort[refnum].Init(); fOutputPort[refnum].Init(); fConnectionRef.Init(refnum); fInputCounter[refnum].SetValue(0); } /*! \brief Reset all clients activation. */ void JackConnectionManager::ResetGraph(JackClientTiming* timing) { // Reset activation counter : must be done *before* starting to resume clients for (int i = 0; i < CLIENT_NUM; i++) { fInputCounter[i].Reset(); timing[i].fStatus = NotTriggered; } } /*! \brief Wait on the input synchro. */ int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing, long time_out_usec) { bool res; if ((res = table[control->fRefNum].TimedWait(time_out_usec))) { timing[control->fRefNum].fStatus = Running; timing[control->fRefNum].fAwakeAt = GetMicroSeconds(); } return (res) ? 0 : -1; } /*! \brief Signal clients connected to the given client. */ int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing) { jack_time_t current_date = GetMicroSeconds(); const jack_int_t* output_ref = fConnectionRef.GetItems(control->fRefNum); int res = 0; // Update state and timestamp of current client timing[control->fRefNum].fStatus = Finished; timing[control->fRefNum].fFinishedAt = current_date; for (int i = 0; i < CLIENT_NUM; i++) { // Signal connected clients or drivers if (output_ref[i] > 0) { // Update state and timestamp of destination clients timing[i].fStatus = Triggered; timing[i].fSignaledAt = current_date; if (!fInputCounter[i].Signal(table + i, control)) { jack_log("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld ", control->fRefNum, i); res = -1; } } } return res; } static bool HasNoConnection(jack_int_t* table) { for (int ref = 0; ref < CLIENT_NUM; ref++) { if (table[ref] > 0) return false; } return true; } // Using http://en.wikipedia.org/wiki/Topological_sorting void JackConnectionManager::TopologicalSort(std::vector& sorted) { JackFixedMatrix* tmp = new JackFixedMatrix; std::set level; fConnectionRef.Copy(*tmp); // Inputs of the graph level.insert(AUDIO_DRIVER_REFNUM); level.insert(FREEWHEEL_DRIVER_REFNUM); while (level.size() > 0) { jack_int_t refnum = *level.begin(); sorted.push_back(refnum); level.erase(level.begin()); const jack_int_t* output_ref1 = tmp->GetItems(refnum); for (int dst = 0; dst < CLIENT_NUM; dst++) { if (output_ref1[dst] > 0) { tmp->ClearItem(refnum, dst); jack_int_t output_ref2[CLIENT_NUM]; tmp->GetOutputTable1(dst, output_ref2); if (HasNoConnection(output_ref2)) { level.insert(dst); } } } } delete tmp; } /*! \brief Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated. */ void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst) { int ref1 = GetOutputRefNum(port_src); int ref2 = GetInputRefNum(port_dst); assert(ref1 >= 0 && ref2 >= 0); DirectConnect(ref1, ref2); jack_log("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2); } /*! \brief Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated. */ void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst) { int ref1 = GetOutputRefNum(port_src); int ref2 = GetInputRefNum(port_dst); assert(ref1 >= 0 && ref2 >= 0); DirectDisconnect(ref1, ref2); jack_log("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2); } /*! \brief Directly connect 2 reference numbers. */ void JackConnectionManager::DirectConnect(int ref1, int ref2) { assert(ref1 >= 0 && ref2 >= 0); if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2 jack_log("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld", ref1, ref2); fInputCounter[ref2].IncValue(); } } /*! \brief Directly disconnect 2 reference numbers. */ void JackConnectionManager::DirectDisconnect(int ref1, int ref2) { assert(ref1 >= 0 && ref2 >= 0); if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2 jack_log("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld", ref1, ref2); fInputCounter[ref2].DecValue(); } } /*! \brief Returns the connections state between 2 refnum. */ bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const { assert(ref1 >= 0 && ref2 >= 0); return (fConnectionRef.GetItemCount(ref1, ref2) > 0); } /*! \brief Get the client refnum of a given input port. */ int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const { for (int i = 0; i < CLIENT_NUM; i++) { if (fInputPort[i].CheckItem(port_index)) { return i; } } return -1; } /*! \brief Get the client refnum of a given output port. */ int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const { for (int i = 0; i < CLIENT_NUM; i++) { if (fOutputPort[i].CheckItem(port_index)) { return i; } } return -1; } /*! \brief Test is a connection path exists between port_src and port_dst. */ bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const { return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src)); } bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const { return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0); } bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) { int ref1 = GetOutputRefNum(port_src); int ref2 = GetInputRefNum(port_dst); // Add an activation connection in the other direction jack_log("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2); assert(ref1 >= 0 && ref2 >= 0); if (ref1 != ref2) { DirectConnect(ref2, ref1); } return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection } bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) { int ref1 = GetOutputRefNum(port_src); int ref2 = GetInputRefNum(port_dst); // Remove an activation connection in the other direction jack_log("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2); assert(ref1 >= 0 && ref2 >= 0); if (ref1 != ref2) { DirectDisconnect(ref2, ref1); } return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection } } // end of namespace jack2-1.9.22/common/JackConnectionManager.h000066400000000000000000000325501436671425200204420ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackConnectionManager__ #define __JackConnectionManager__ #include "JackConstants.h" #include "JackActivationCount.h" #include "JackError.h" #include "JackCompilerDeps.h" #include #include namespace Jack { struct JackClientControl; /*! \brief Utility class. */ PRE_PACKED_STRUCTURE template class JackFixedArray { private: jack_int_t fTable[SIZE]; uint32_t fCounter; public: JackFixedArray() { Init(); } void Init() { for (int i = 0; i < SIZE; i++) fTable[i] = EMPTY; fCounter = 0; } bool AddItem(jack_int_t index) { for (int i = 0; i < SIZE; i++) { if (fTable[i] == EMPTY) { fTable[i] = index; fCounter++; return true; } } return false; } bool RemoveItem(jack_int_t index) { for (int i = 0; i < SIZE; i++) { if (fTable[i] == index) { fCounter--; // Shift all indexes if (i == SIZE - 1) { fTable[i] = EMPTY; } else { int j; for (j = i; j <= SIZE - 2 && fTable[j] != EMPTY; j++) { fTable[j] = fTable[j + 1]; } fTable[j] = EMPTY; } return true; } } return false; } jack_int_t GetItem(jack_int_t index) const { return (index < SIZE) ? fTable[index] : EMPTY; } const jack_int_t* GetItems() const { return fTable; } bool CheckItem(jack_int_t index) const { for (int i = 0; i < SIZE && fTable[i] != EMPTY; i++) { if (fTable[i] == index) return true; } return false; } uint32_t GetItemCount() const { return fCounter; } } POST_PACKED_STRUCTURE; /*! \brief Utility class. */ PRE_PACKED_STRUCTURE template class JackFixedArray1 : public JackFixedArray { private: bool fUsed; public: JackFixedArray1() { Init(); } void Init() { JackFixedArray::Init(); fUsed = false; } bool IsAvailable() { if (fUsed) { return false; } else { fUsed = true; return true; } } } POST_PACKED_STRUCTURE; /*! \brief Utility class. */ PRE_PACKED_STRUCTURE template class JackFixedMatrix { private: jack_int_t fTable[SIZE][SIZE]; public: JackFixedMatrix() {} void Init(jack_int_t index) { for (int i = 0; i < SIZE; i++) { fTable[index][i] = 0; fTable[i][index] = 0; } } const jack_int_t* GetItems(jack_int_t index) const { return fTable[index]; } jack_int_t IncItem(jack_int_t index1, jack_int_t index2) { fTable[index1][index2]++; return fTable[index1][index2]; } jack_int_t DecItem(jack_int_t index1, jack_int_t index2) { fTable[index1][index2]--; return fTable[index1][index2]; } jack_int_t GetItemCount(jack_int_t index1, jack_int_t index2) const { return fTable[index1][index2]; } void ClearItem(jack_int_t index1, jack_int_t index2) { fTable[index1][index2] = 0; } /*! \brief Get the output indexes of a given index. */ void GetOutputTable(jack_int_t index, jack_int_t* output) const { int i, j; for (i = 0; i < SIZE; i++) output[i] = EMPTY; for (i = 0, j = 0; i < SIZE; i++) { if (fTable[index][i] > 0) { output[j] = i; j++; } } } void GetOutputTable1(jack_int_t index, jack_int_t* output) const { for (int i = 0; i < SIZE; i++) { output[i] = fTable[i][index]; } } bool IsInsideTable(jack_int_t index, jack_int_t* output) const { for (int i = 0; i < SIZE && output[i] != EMPTY; i++) { if (output[i] == index) return true; } return false; } void Copy(JackFixedMatrix& copy) { for (int i = 0; i < SIZE; i++) { memcpy(copy.fTable[i], fTable[i], sizeof(jack_int_t) * SIZE); } } } POST_PACKED_STRUCTURE; /*! \brief Utility class. */ PRE_PACKED_STRUCTURE template class JackLoopFeedback { private: int fTable[SIZE][3]; /*! \brief Add a feedback connection between 2 refnum. */ bool AddConnectionAux(int ref1, int ref2) { for (int i = 0; i < SIZE; i++) { if (fTable[i][0] == EMPTY) { fTable[i][0] = ref1; fTable[i][1] = ref2; fTable[i][2] = 1; jack_log("JackLoopFeedback::AddConnectionAux ref1 = %ld ref2 = %ld", ref1, ref2); return true; } } jack_error("Feedback table is full !!\n"); return false; } /*! \brief Remove a feedback connection between 2 refnum. */ bool RemoveConnectionAux(int ref1, int ref2) { for (int i = 0; i < SIZE; i++) { if (fTable[i][0] == ref1 && fTable[i][1] == ref2) { fTable[i][0] = EMPTY; fTable[i][1] = EMPTY; fTable[i][2] = 0; jack_log("JackLoopFeedback::RemoveConnectionAux ref1 = %ld ref2 = %ld", ref1, ref2); return true; } } jack_error("Feedback connection not found\n"); return false; } int IncConnection(int index) { fTable[index][2]++; return fTable[index][2]; } int DecConnection(int index) { fTable[index][2]--; return fTable[index][2]; } public: JackLoopFeedback() { Init(); } void Init() { for (int i = 0; i < SIZE; i++) { fTable[i][0] = EMPTY; fTable[i][1] = EMPTY; fTable[i][2] = 0; } } bool IncConnection(int ref1, int ref2) { int index = GetConnectionIndex(ref1, ref2); if (index >= 0) { // Feedback connection is already added, increment counter IncConnection(index); return true; } else { return AddConnectionAux(ref1, ref2); // Add the feedback connection } } bool DecConnection(int ref1, int ref2) { int index = GetConnectionIndex(ref1, ref2); if (index >= 0) { jack_log("JackLoopFeedback::DecConnection ref1 = %ld ref2 = %ld index = %ld", ref1, ref2, index); return (DecConnection(index) == 0) ? RemoveConnectionAux(ref1, ref2) : true; } else { return false; } } /*! \brief Test if a connection between 2 refnum is a feedback connection. */ int GetConnectionIndex(int ref1, int ref2) const { for (int i = 0; i < SIZE; i++) { if (fTable[i][0] == ref1 && fTable[i][1] == ref2) return i; } return -1; } } POST_PACKED_STRUCTURE; /*! \brief For client timing measurements. */ PRE_PACKED_STRUCTURE struct JackClientTiming { jack_time_t fSignaledAt; jack_time_t fAwakeAt; jack_time_t fFinishedAt; jack_client_state_t fStatus; JackClientTiming() { Init(); } ~JackClientTiming() {} void Init() { fSignaledAt = 0; fAwakeAt = 0; fFinishedAt = 0; fStatus = NotTriggered; } } POST_PACKED_STRUCTURE; /*! \brief Connection manager.
  • The fConnection array contains the list (array line) of connected ports for a given port.
  • The fInputPort array contains the list (array line) of input connected ports for a given client.
  • The fOutputPort array contains the list (array line) of output connected ports for a given client.
  • The fConnectionRef array contains the number of ports connected between two clients.
  • The fInputCounter array contains the number of input clients connected to a given for activation purpose.
*/ PRE_PACKED_STRUCTURE class SERVER_EXPORT JackConnectionManager { private: JackFixedArray fConnection[PORT_NUM_MAX]; /*! Connection matrix: list of connected ports for a given port: needed to compute Mix buffer */ JackFixedArray1 fInputPort[CLIENT_NUM]; /*! Table of input port per refnum : to find a refnum for a given port */ JackFixedArray fOutputPort[CLIENT_NUM]; /*! Table of output port per refnum : to find a refnum for a given port */ JackFixedMatrix fConnectionRef; /*! Table of port connections by (refnum , refnum) */ alignas(UInt32) alignas(JackActivationCount) JackActivationCount fInputCounter[CLIENT_NUM]; /*! Activation counter per refnum */ JackLoopFeedback fLoopFeedback; /*! Loop feedback connections */ bool IsLoopPathAux(int ref1, int ref2) const; public: JackConnectionManager(); ~JackConnectionManager(); // Connections management int Connect(jack_port_id_t port_src, jack_port_id_t port_dst); int Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst); bool IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const; /*! \brief Get the connection number of a given port. */ jack_int_t Connections(jack_port_id_t port_index) const { return fConnection[port_index].GetItemCount(); } jack_port_id_t GetPort(jack_port_id_t port_index, int connection) const { assert(connection < CONNECTION_NUM_FOR_PORT); return (jack_port_id_t)fConnection[port_index].GetItem(connection); } const jack_int_t* GetConnections(jack_port_id_t port_index) const; bool IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst); bool DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst); bool IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const; bool IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const; void IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst); void DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst); // Ports management int AddInputPort(int refnum, jack_port_id_t port_index); int AddOutputPort(int refnum, jack_port_id_t port_index); int RemoveInputPort(int refnum, jack_port_id_t port_index); int RemoveOutputPort(int refnum, jack_port_id_t port_index); const jack_int_t* GetInputPorts(int refnum); const jack_int_t* GetOutputPorts(int refnum); // Client management void InitRefNum(int refnum); int GetInputRefNum(jack_port_id_t port_index) const; int GetOutputRefNum(jack_port_id_t port_index) const; // Connect/Disconnect 2 refnum "directly" bool IsDirectConnection(int ref1, int ref2) const; void DirectConnect(int ref1, int ref2); void DirectDisconnect(int ref1, int ref2); int GetActivation(int refnum) const { return fInputCounter[refnum].GetValue(); } // Graph void ResetGraph(JackClientTiming* timing); int ResumeRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing); int SuspendRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing, long time_out_usec); void TopologicalSort(std::vector& sorted); } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackConstants.h000066400000000000000000000051101436671425200170140ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackConstants__ #define __JackConstants__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define VERSION "1.9.22" #define BUFFER_SIZE_MAX 8192 #define JACK_PORT_NAME_SIZE 256 #define JACK_PORT_TYPE_SIZE 32 #define JACK_SERVER_NAME_SIZE 256 #define JACK_CLIENT_NAME_SIZE 64 #define JACK_MESSAGE_SIZE 256 #define JACK_UUID_SIZE 36 // to match jack1 and uuid.h #define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1) /* includes trailing null */ #define JACK_UUID_EMPTY_INITIALIZER 0 #define JACK_SESSION_COMMAND_SIZE 256 #define SYNC_MAX_NAME_SIZE 256 #define REAL_JACK_PORT_NAME_SIZE JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE // full name like "client_name:short_port_name" #ifndef PORT_NUM #define PORT_NUM 2048 #endif #ifndef PORT_NUM_MAX #define PORT_NUM_MAX 4096 // The "max" value for ports used in connection manager, although port number in graph manager is dynamic #endif #define DRIVER_PORT_NUM 256 #ifndef PORT_NUM_FOR_CLIENT #define PORT_NUM_FOR_CLIENT 768 #endif #define FIRST_AVAILABLE_PORT 1 #define CONNECTION_NUM_FOR_PORT PORT_NUM_FOR_CLIENT #ifndef CLIENT_NUM #define CLIENT_NUM 64 #endif #define AUDIO_DRIVER_REFNUM 0 // Audio driver is initialized first, it will get the refnum 0 #define FREEWHEEL_DRIVER_REFNUM 1 // Freewheel driver is initialized second, it will get the refnum 1 #define JACK_DEFAULT_SERVER_NAME "default" #define ALL_CLIENTS -1 // for notification #define JACK_PROTOCOL_VERSION 9 #define SOCKET_TIME_OUT 2 // in sec #define DRIVER_OPEN_TIMEOUT 5 // in sec #define FREEWHEEL_DRIVER_TIMEOUT 10 // in sec #define DRIVER_TIMEOUT_FACTOR 10 #define JACK_SERVER_FAILURE "JACK server has been closed" #define NO_PORT 0xFFFE #define EMPTY 0xFFFD #define FREE 0xFFFC #define JACK_DEFAULT_SELF_CONNECT_MODE ' ' /* allow all requests */ #endif jack2-1.9.22/common/JackControlAPI.cpp000066400000000000000000001212021436671425200173460ustar00rootroot00000000000000// u/* -*- Mode: C++ ; c-basic-offset: 4 -*- */ /* JACK control API implementation Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef WIN32 #include #include #include #endif #include "types.h" #include #include #include #include #include #include "jslist.h" #include "driver_interface.h" #include "JackError.h" #include "JackServer.h" #include "shm.h" #include "JackTools.h" #include "JackControlAPI.h" #include "JackLockedEngine.h" #include "JackConstants.h" #include "JackDriverLoader.h" #include "JackServerGlobals.h" using namespace Jack; /* JackEngine::CheckPortsConnect() has some assumptions about char values */ static struct jack_constraint_enum_char_descriptor self_connect_mode_constraint_descr_array[] = { { ' ', "Don't restrict self connect requests" }, { 'E', "Fail self connect requests to external ports only" }, { 'e', "Ignore self connect requests to external ports only" }, { 'A', "Fail all self connect requests" }, { 'a', "Ignore all self connect requests" }, { 0 } }; struct jackctl_server { JSList * drivers; JSList * internals; JSList * parameters; class JackServer * engine; /* string, server name */ union jackctl_parameter_value name; union jackctl_parameter_value default_name; /* bool, whether to be "realtime" */ union jackctl_parameter_value realtime; union jackctl_parameter_value default_realtime; /* int32_t */ union jackctl_parameter_value realtime_priority; union jackctl_parameter_value default_realtime_priority; /* bool, whether to exit once all clients have closed their connections */ union jackctl_parameter_value temporary; union jackctl_parameter_value default_temporary; /* bool, whether to be verbose */ union jackctl_parameter_value verbose; union jackctl_parameter_value default_verbose; /* int32_t, msecs; if zero, use period size. */ union jackctl_parameter_value client_timeout; union jackctl_parameter_value default_client_timeout; /* uint32_t, clock source type */ union jackctl_parameter_value clock_source; union jackctl_parameter_value default_clock_source; /* uint32_t, max port number */ union jackctl_parameter_value port_max; union jackctl_parameter_value default_port_max; /* bool */ union jackctl_parameter_value replace_registry; union jackctl_parameter_value default_replace_registry; /* bool, synchronous or asynchronous engine mode */ union jackctl_parameter_value sync; union jackctl_parameter_value default_sync; /* char enum, self connect mode mode */ union jackctl_parameter_value self_connect_mode; union jackctl_parameter_value default_self_connect_mode; }; struct jackctl_driver { jack_driver_desc_t * desc_ptr; JSList * parameters; JSList * infos; }; struct jackctl_internal { jack_driver_desc_t * desc_ptr; JSList * parameters; int refnum; }; struct jackctl_parameter { const char * name; const char * short_description; const char * long_description; jackctl_param_type_t type; bool is_set; union jackctl_parameter_value * value_ptr; union jackctl_parameter_value * default_value_ptr; union jackctl_parameter_value value; union jackctl_parameter_value default_value; struct jackctl_driver * driver_ptr; char id; jack_driver_param_constraint_desc_t * constraint_ptr; }; const char * jack_get_self_connect_mode_description(char mode) { struct jack_constraint_enum_char_descriptor * descr_ptr; for (descr_ptr = self_connect_mode_constraint_descr_array; descr_ptr->value; descr_ptr++) if (descr_ptr->value == mode) return descr_ptr->short_desc; return NULL; } static struct jackctl_parameter * jackctl_add_parameter( JSList ** parameters_list_ptr_ptr, const char * name, const char * short_description, const char * long_description, jackctl_param_type_t type, union jackctl_parameter_value * value_ptr, union jackctl_parameter_value * default_value_ptr, union jackctl_parameter_value value, jack_driver_param_constraint_desc_t * constraint_ptr = NULL) { struct jackctl_parameter * parameter_ptr; parameter_ptr = (struct jackctl_parameter *)malloc(sizeof(struct jackctl_parameter)); if (parameter_ptr == NULL) { jack_error("Cannot allocate memory for jackctl_parameter structure."); goto fail; } parameter_ptr->name = name; parameter_ptr->short_description = short_description; parameter_ptr->long_description = long_description; parameter_ptr->type = type; parameter_ptr->is_set = false; if (value_ptr == NULL) { value_ptr = ¶meter_ptr->value; } if (default_value_ptr == NULL) { default_value_ptr = ¶meter_ptr->default_value; } parameter_ptr->value_ptr = value_ptr; parameter_ptr->default_value_ptr = default_value_ptr; *value_ptr = *default_value_ptr = value; parameter_ptr->driver_ptr = NULL; parameter_ptr->id = 0; parameter_ptr->constraint_ptr = constraint_ptr; *parameters_list_ptr_ptr = jack_slist_append(*parameters_list_ptr_ptr, parameter_ptr); return parameter_ptr; fail: return NULL; } static void jackctl_free_driver_parameters( struct jackctl_driver * driver_ptr) { JSList * next_node_ptr; while (driver_ptr->parameters) { next_node_ptr = driver_ptr->parameters->next; jack_constraint_free(((jackctl_parameter *)driver_ptr->parameters->data)->constraint_ptr); free(driver_ptr->parameters->data); free(driver_ptr->parameters); driver_ptr->parameters = next_node_ptr; } } static bool jackctl_add_driver_parameters( struct jackctl_driver * driver_ptr) { unsigned int i; union jackctl_parameter_value jackctl_value; jackctl_param_type_t jackctl_type; struct jackctl_parameter * parameter_ptr; jack_driver_param_desc_t * descriptor_ptr; for (i = 0 ; i < driver_ptr->desc_ptr->nparams ; i++) { descriptor_ptr = driver_ptr->desc_ptr->params + i; switch (descriptor_ptr->type) { case JackDriverParamInt: jackctl_type = JackParamInt; jackctl_value.i = descriptor_ptr->value.i; break; case JackDriverParamUInt: jackctl_type = JackParamUInt; jackctl_value.ui = descriptor_ptr->value.ui; break; case JackDriverParamChar: jackctl_type = JackParamChar; jackctl_value.c = descriptor_ptr->value.c; break; case JackDriverParamString: jackctl_type = JackParamString; strcpy(jackctl_value.str, descriptor_ptr->value.str); break; case JackDriverParamBool: jackctl_type = JackParamBool; jackctl_value.b = descriptor_ptr->value.i; break; default: jack_error("Unknown driver parameter type %i", (int)descriptor_ptr->type); assert(0); goto fail; } parameter_ptr = jackctl_add_parameter( &driver_ptr->parameters, descriptor_ptr->name, descriptor_ptr->short_desc, descriptor_ptr->long_desc, jackctl_type, NULL, NULL, jackctl_value, descriptor_ptr->constraint); if (parameter_ptr == NULL) { goto fail; } parameter_ptr->driver_ptr = driver_ptr; parameter_ptr->id = descriptor_ptr->character; } return true; fail: jackctl_free_driver_parameters(driver_ptr); return false; } /* destroy jack_driver_param_desc_t list created by jackctl_create_param_list() */ static void jackctl_destroy_param_list( JSList * params) { JSList * next; while (params) { next = params->next; free(params->data); free(params); params = next; } } /* for drivers and internals are configured through jack_driver_param_t JSList */ /* this function creates such list from a jackctl_parameter list */ static bool jackctl_create_param_list( const JSList * paramlist, JSList ** retparamlist) { jackctl_parameter * param_ptr; jack_driver_param_t * retparam_ptr; *retparamlist = NULL; while (paramlist != NULL) { param_ptr = (jackctl_parameter *)paramlist->data; if (param_ptr->is_set) { /* jack_info("setting driver parameter %p ...", parameter_ptr); */ retparam_ptr = (jack_driver_param_t *)malloc(sizeof(jack_driver_param_t)); if (retparam_ptr == NULL) { jack_error ("Allocation of jack_driver_param_t structure failed"); goto destroy; } retparam_ptr->character = param_ptr->id; switch (param_ptr->type) { case JackParamInt: retparam_ptr->value.i = param_ptr->value_ptr->i; break; case JackParamUInt: retparam_ptr->value.ui = param_ptr->value_ptr->ui; break; case JackParamChar: retparam_ptr->value.c = param_ptr->value_ptr->c; break; case JackParamString: strcpy(retparam_ptr->value.str, param_ptr->value_ptr->str); break; case JackParamBool: retparam_ptr->value.i = param_ptr->value_ptr->b; break; default: jack_error("Unknown parameter type %i", (int)param_ptr->type); assert(0); goto free; } *retparamlist = jack_slist_append(*retparamlist, retparam_ptr); } paramlist = paramlist->next; } return true; free: free(retparam_ptr); destroy: jackctl_destroy_param_list(*retparamlist); return false; } static int jackctl_drivers_load( struct jackctl_server * server_ptr) { struct jackctl_driver * driver_ptr; JSList *node_ptr; JSList *descriptor_node_ptr; descriptor_node_ptr = jack_drivers_load(NULL); if (descriptor_node_ptr == NULL) { jack_error("Could not find any drivers in driver directory!"); return false; } while (descriptor_node_ptr != NULL) { driver_ptr = (struct jackctl_driver *)malloc(sizeof(struct jackctl_driver)); if (driver_ptr == NULL) { jack_error("Memory allocation of jackctl_driver structure failed."); goto next; } driver_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data; driver_ptr->parameters = NULL; driver_ptr->infos = NULL; if (!jackctl_add_driver_parameters(driver_ptr)) { assert(driver_ptr->parameters == NULL); free(driver_ptr); goto next; } server_ptr->drivers = jack_slist_append(server_ptr->drivers, driver_ptr); next: node_ptr = descriptor_node_ptr; descriptor_node_ptr = descriptor_node_ptr->next; free(node_ptr); } return true; } static void jackctl_server_free_drivers( struct jackctl_server * server_ptr) { JSList * next_node_ptr; struct jackctl_driver * driver_ptr; while (server_ptr->drivers) { next_node_ptr = server_ptr->drivers->next; driver_ptr = (struct jackctl_driver *)server_ptr->drivers->data; jackctl_free_driver_parameters(driver_ptr); free(driver_ptr->desc_ptr->params); free(driver_ptr->desc_ptr); free(driver_ptr); free(server_ptr->drivers); server_ptr->drivers = next_node_ptr; } } static int jackctl_internals_load( struct jackctl_server * server_ptr) { struct jackctl_internal * internal_ptr; JSList *node_ptr; JSList *descriptor_node_ptr; descriptor_node_ptr = jack_internals_load(NULL); if (descriptor_node_ptr == NULL) { jack_error("Could not find any internals in driver directory!"); return false; } while (descriptor_node_ptr != NULL) { internal_ptr = (struct jackctl_internal *)malloc(sizeof(struct jackctl_internal)); if (internal_ptr == NULL) { jack_error("Memory allocation of jackctl_driver structure failed."); goto next; } internal_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data; internal_ptr->parameters = NULL; internal_ptr->refnum = -1; if (!jackctl_add_driver_parameters((struct jackctl_driver *)internal_ptr)) { assert(internal_ptr->parameters == NULL); free(internal_ptr); goto next; } server_ptr->internals = jack_slist_append(server_ptr->internals, internal_ptr); next: node_ptr = descriptor_node_ptr; descriptor_node_ptr = descriptor_node_ptr->next; free(node_ptr); } return true; } static void jackctl_server_free_internals( struct jackctl_server * server_ptr) { JSList * next_node_ptr; struct jackctl_internal * internal_ptr; while (server_ptr->internals) { next_node_ptr = server_ptr->internals->next; internal_ptr = (struct jackctl_internal *)server_ptr->internals->data; jackctl_free_driver_parameters((struct jackctl_driver *)internal_ptr); free(internal_ptr->desc_ptr->params); free(internal_ptr->desc_ptr); free(internal_ptr); free(server_ptr->internals); server_ptr->internals = next_node_ptr; } } static void jackctl_server_free_parameters( struct jackctl_server * server_ptr) { JSList * next_node_ptr; while (server_ptr->parameters) { next_node_ptr = server_ptr->parameters->next; jack_constraint_free(((jackctl_parameter *)server_ptr->parameters->data)->constraint_ptr); free(server_ptr->parameters->data); free(server_ptr->parameters); server_ptr->parameters = next_node_ptr; } } #ifdef WIN32 struct jackctl_sigmask { HANDLE wait_event; }; static jackctl_sigmask sigmask; static void signal_handler(int signum) { printf("Jack main caught signal %d\n", signum); (void) signal(SIGINT, SIG_DFL); SetEvent(sigmask.wait_event); } jackctl_sigmask_t * jackctl_setup_signals( unsigned int flags) { if ((sigmask.wait_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) { jack_error("CreateEvent fails err = %ld", GetLastError()); return 0; } (void) signal(SIGINT, signal_handler); (void) signal(SIGABRT, signal_handler); (void) signal(SIGTERM, signal_handler); return &sigmask; } void jackctl_wait_signals(jackctl_sigmask_t * signals) { if (WaitForSingleObject(signals->wait_event, INFINITE) != WAIT_OBJECT_0) { jack_error("WaitForSingleObject fails err = %ld", GetLastError()); } } #else struct jackctl_sigmask { sigset_t signals; }; static jackctl_sigmask sigmask; static void signal_handler(int sig) { /* this is used by the child (active) process, but it never gets called unless we are already shutting down after another signal. */ char buf[64]; snprintf(buf, sizeof(buf), "Received signal %d during shutdown (ignored)\n", sig); } SERVER_EXPORT jackctl_sigmask_t * jackctl_setup_signals( unsigned int flags) { sigset_t allsignals; struct sigaction action; int i; /* ensure that we are in our own process group so that kill (SIG, -pgrp) does the right thing. */ setsid(); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* what's this for? POSIX says that signals are delivered like this: * if a thread has blocked that signal, it is not a candidate to receive the signal. * of all threads not blocking the signal, pick one at random, and deliver the signal. this means that a simple-minded multi-threaded program can expect to get POSIX signals delivered randomly to any one of its threads, here, we block all signals that we think we might receive and want to catch. all "child" threads will inherit this setting. if we create a thread that calls sigwait() on the same set of signals, implicitly unblocking all those signals. any of those signals that are delivered to the process will be delivered to that thread, and that thread alone. this makes cleanup for a signal-driven exit much easier, since we know which thread is doing it and more importantly, we are free to call async-unsafe functions, because the code is executing in normal thread context after a return from sigwait(). */ sigemptyset(&sigmask.signals); sigaddset(&sigmask.signals, SIGHUP); sigaddset(&sigmask.signals, SIGINT); sigaddset(&sigmask.signals, SIGQUIT); sigaddset(&sigmask.signals, SIGPIPE); sigaddset(&sigmask.signals, SIGTERM); #ifndef __ANDROID__ /* android's bionic c doesn't provide pthread_cancel() and related functions. * to solve this issue, use pthread_kill() & SIGUSR1 instead. */ sigaddset(&sigmask.signals, SIGUSR1); #endif sigaddset(&sigmask.signals, SIGUSR2); /* all child threads will inherit this mask unless they * explicitly reset it */ pthread_sigmask(SIG_BLOCK, &sigmask.signals, 0); /* install a do-nothing handler because otherwise pthreads behaviour is undefined when we enter sigwait. */ sigfillset(&allsignals); action.sa_handler = signal_handler; action.sa_mask = allsignals; action.sa_flags = SA_RESTART|SA_RESETHAND; for (i = 1; i < NSIG; i++) { if (sigismember (&sigmask.signals, i)) { sigaction(i, &action, 0); } } return &sigmask; } SERVER_EXPORT void jackctl_wait_signals(jackctl_sigmask_t * sigmask) { int sig; bool waiting = true; while (waiting) { #if defined(sun) && !defined(__sun__) // SUN compiler only, to check sigwait(&sigmask->signals); #else sigwait(&sigmask->signals, &sig); #endif fprintf(stderr, "Jack main caught signal %d\n", sig); switch (sig) { case SIGUSR1: //jack_dump_configuration(engine, 1); break; case SIGUSR2: // driver exit waiting = false; break; case SIGTTOU: break; default: waiting = false; break; } } if (sig != SIGSEGV) { // unblock signals so we can see them during shutdown. // this will help prod developers not to lose sight of // bugs that cause segfaults etc. during shutdown. sigprocmask(SIG_UNBLOCK, &sigmask->signals, 0); } } #endif static jack_driver_param_constraint_desc_t * get_realtime_priority_constraint() { jack_driver_param_constraint_desc_t * constraint_ptr; int min, max; if (!jack_get_thread_realtime_priority_range(&min, &max)) { return NULL; } //jack_info("realtime priority range is (%d,%d)", min, max); constraint_ptr = (jack_driver_param_constraint_desc_t *)calloc(1, sizeof(jack_driver_param_constraint_desc_t)); if (constraint_ptr == NULL) { jack_error("Cannot allocate memory for jack_driver_param_constraint_desc_t structure."); return NULL; } constraint_ptr->flags = JACK_CONSTRAINT_FLAG_RANGE; constraint_ptr->constraint.range.min.i = min; constraint_ptr->constraint.range.max.i = max; return constraint_ptr; } SERVER_EXPORT jackctl_server_t * jackctl_server_create( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name)) { return jackctl_server_create2(on_device_acquire, on_device_release, NULL); } SERVER_EXPORT jackctl_server_t * jackctl_server_create2( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name), void (* on_device_reservation_loop)(void)) { struct jackctl_server * server_ptr; union jackctl_parameter_value value; server_ptr = (struct jackctl_server *)malloc(sizeof(struct jackctl_server)); if (server_ptr == NULL) { jack_error("Cannot allocate memory for jackctl_server structure."); goto fail; } server_ptr->drivers = NULL; server_ptr->internals = NULL; server_ptr->parameters = NULL; server_ptr->engine = NULL; strcpy(value.str, JackTools::DefaultServerName()); if (jackctl_add_parameter( &server_ptr->parameters, "name", "Server name to use.", "", JackParamString, &server_ptr->name, &server_ptr->default_name, value) == NULL) { goto fail_free_parameters; } value.b = true; if (jackctl_add_parameter( &server_ptr->parameters, "realtime", "Whether to use realtime mode.", "Use realtime scheduling. This is needed for reliable low-latency performance. On most systems, it requires JACK to run with special scheduler and memory allocation privileges, which may be obtained in several ways. On Linux you should use PAM.", JackParamBool, &server_ptr->realtime, &server_ptr->default_realtime, value) == NULL) { goto fail_free_parameters; } value.i = 10; if (jackctl_add_parameter( &server_ptr->parameters, "realtime-priority", "Scheduler priority when running in realtime mode.", "", JackParamInt, &server_ptr->realtime_priority, &server_ptr->default_realtime_priority, value, get_realtime_priority_constraint()) == NULL) { goto fail_free_parameters; } value.b = false; if (jackctl_add_parameter( &server_ptr->parameters, "temporary", "Exit once all clients have closed their connections.", "", JackParamBool, &server_ptr->temporary, &server_ptr->default_temporary, value) == NULL) { goto fail_free_parameters; } value.b = false; if (jackctl_add_parameter( &server_ptr->parameters, "verbose", "Verbose mode.", "", JackParamBool, &server_ptr->verbose, &server_ptr->default_verbose, value) == NULL) { goto fail_free_parameters; } value.i = 0; if (jackctl_add_parameter( &server_ptr->parameters, "client-timeout", "Client timeout limit in milliseconds.", "", JackParamInt, &server_ptr->client_timeout, &server_ptr->default_client_timeout, value) == NULL) { goto fail_free_parameters; } value.ui = 0; if (jackctl_add_parameter( &server_ptr->parameters, "clock-source", "Clocksource type : c(ycle) | h(pet) | s(ystem).", "", JackParamUInt, &server_ptr->clock_source, &server_ptr->default_clock_source, value) == NULL) { goto fail_free_parameters; } value.ui = PORT_NUM; if (jackctl_add_parameter( &server_ptr->parameters, "port-max", "Maximum number of ports.", "", JackParamUInt, &server_ptr->port_max, &server_ptr->default_port_max, value) == NULL) { goto fail_free_parameters; } value.b = false; if (jackctl_add_parameter( &server_ptr->parameters, "replace-registry", "Replace shared memory registry.", "", JackParamBool, &server_ptr->replace_registry, &server_ptr->default_replace_registry, value) == NULL) { goto fail_free_parameters; } value.b = false; if (jackctl_add_parameter( &server_ptr->parameters, "sync", "Use server synchronous mode.", "", JackParamBool, &server_ptr->sync, &server_ptr->default_sync, value) == NULL) { goto fail_free_parameters; } value.c = JACK_DEFAULT_SELF_CONNECT_MODE; if (jackctl_add_parameter( &server_ptr->parameters, "self-connect-mode", "Self connect mode.", "Whether JACK clients are allowed to connect their own ports", JackParamChar, &server_ptr->self_connect_mode, &server_ptr->default_self_connect_mode, value, jack_constraint_compose_enum_char( JACK_CONSTRAINT_FLAG_STRICT | JACK_CONSTRAINT_FLAG_FAKE_VALUE, self_connect_mode_constraint_descr_array)) == NULL) { goto fail_free_parameters; } JackServerGlobals::on_device_acquire = on_device_acquire; JackServerGlobals::on_device_release = on_device_release; JackServerGlobals::on_device_reservation_loop = on_device_reservation_loop; if (!jackctl_drivers_load(server_ptr)) { goto fail_free_parameters; } /* Allowed to fail */ jackctl_internals_load(server_ptr); return server_ptr; fail_free_parameters: jackctl_server_free_parameters(server_ptr); free(server_ptr); fail: return NULL; } SERVER_EXPORT void jackctl_server_destroy(jackctl_server *server_ptr) { if (server_ptr) { jackctl_server_free_drivers(server_ptr); jackctl_server_free_internals(server_ptr); jackctl_server_free_parameters(server_ptr); free(server_ptr); } } SERVER_EXPORT const JSList * jackctl_server_get_drivers_list(jackctl_server *server_ptr) { return (server_ptr) ? server_ptr->drivers : NULL; } SERVER_EXPORT bool jackctl_server_stop(jackctl_server *server_ptr) { if (server_ptr) { server_ptr->engine->Stop(); return true; } else { return false; } } SERVER_EXPORT bool jackctl_server_close(jackctl_server *server_ptr) { if (server_ptr) { server_ptr->engine->Close(); delete server_ptr->engine; /* clean up shared memory and files from this server instance */ jack_log("Cleaning up shared memory"); jack_cleanup_shm(); jack_log("Cleaning up files"); JackTools::CleanupFiles(server_ptr->name.str); jack_log("Unregistering server `%s'", server_ptr->name.str); jack_unregister_server(server_ptr->name.str); server_ptr->engine = NULL; return true; } else { return false; } } SERVER_EXPORT const JSList * jackctl_server_get_parameters(jackctl_server *server_ptr) { return (server_ptr) ? server_ptr->parameters : NULL; } SERVER_EXPORT bool jackctl_server_open( jackctl_server *server_ptr, jackctl_driver *driver_ptr) { JSList * paramlist = NULL; try { if (!server_ptr || !driver_ptr) { return false; } int rc = jack_register_server(server_ptr->name.str, server_ptr->replace_registry.b); switch (rc) { case EEXIST: jack_error("`%s' server already active", server_ptr->name.str); goto fail; case ENOSPC: jack_error("Too many servers already active"); goto fail; case ENOMEM: jack_error("No access to shm registry"); goto fail; } jack_log("Server `%s' registered", server_ptr->name.str); /* clean up shared memory and files from any previous * instance of this server name */ jack_cleanup_shm(); JackTools::CleanupFiles(server_ptr->name.str); if (!server_ptr->realtime.b && server_ptr->client_timeout.i == 0) { server_ptr->client_timeout.i = 500; /* 0.5 sec; usable when non realtime. */ } /* check port max value before allocating server */ if (server_ptr->port_max.ui > PORT_NUM_MAX) { jack_error("Jack server started with too much ports %d (when port max can be %d)", server_ptr->port_max.ui, PORT_NUM_MAX); goto fail; } /* get the engine/driver started */ server_ptr->engine = new JackServer( server_ptr->sync.b, server_ptr->temporary.b, server_ptr->client_timeout.i, server_ptr->realtime.b, server_ptr->realtime_priority.i, server_ptr->port_max.ui, server_ptr->verbose.b, (jack_timer_type_t)server_ptr->clock_source.ui, server_ptr->self_connect_mode.c, server_ptr->name.str); if (server_ptr->engine == NULL) { jack_error("Failed to create new JackServer object"); goto fail_unregister; } if (!jackctl_create_param_list(driver_ptr->parameters, ¶mlist)) goto fail_delete; rc = server_ptr->engine->Open(driver_ptr->desc_ptr, paramlist); jackctl_destroy_param_list(paramlist); if (rc < 0) { jack_error("JackServer::Open failed with %d", rc); goto fail_delete; } return true; } catch (std::exception&) { jack_error("jackctl_server_open error..."); jackctl_destroy_param_list(paramlist); } fail_delete: delete server_ptr->engine; server_ptr->engine = NULL; fail_unregister: jack_log("Cleaning up shared memory"); jack_cleanup_shm(); jack_log("Cleaning up files"); JackTools::CleanupFiles(server_ptr->name.str); jack_log("Unregistering server `%s'", server_ptr->name.str); jack_unregister_server(server_ptr->name.str); fail: return false; } SERVER_EXPORT bool jackctl_server_start( jackctl_server *server_ptr) { if (!server_ptr) { return false; } else { int rc = server_ptr->engine->Start(); bool result = rc >= 0; if (! result) { jack_error("JackServer::Start() failed with %d", rc); } return result; } } SERVER_EXPORT const char * jackctl_driver_get_name(jackctl_driver *driver_ptr) { return (driver_ptr) ? driver_ptr->desc_ptr->name : NULL; } SERVER_EXPORT jackctl_driver_type_t jackctl_driver_get_type(jackctl_driver *driver_ptr) { return (driver_ptr) ? (jackctl_driver_type_t)driver_ptr->desc_ptr->type : (jackctl_driver_type_t)0; } SERVER_EXPORT const JSList * jackctl_driver_get_parameters(jackctl_driver *driver_ptr) { return (driver_ptr) ? driver_ptr->parameters : NULL; } SERVER_EXPORT jack_driver_desc_t * jackctl_driver_get_desc(jackctl_driver *driver_ptr) { return (driver_ptr) ? driver_ptr->desc_ptr : NULL; } SERVER_EXPORT const char * jackctl_parameter_get_name(jackctl_parameter *parameter_ptr) { return (parameter_ptr) ? parameter_ptr->name : NULL; } SERVER_EXPORT const char * jackctl_parameter_get_short_description(jackctl_parameter *parameter_ptr) { return (parameter_ptr) ? parameter_ptr->short_description : NULL; } SERVER_EXPORT const char * jackctl_parameter_get_long_description(jackctl_parameter *parameter_ptr) { return (parameter_ptr) ? parameter_ptr->long_description : NULL; } SERVER_EXPORT bool jackctl_parameter_has_range_constraint(jackctl_parameter *parameter_ptr) { return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) != 0) : false; } SERVER_EXPORT bool jackctl_parameter_has_enum_constraint(jackctl_parameter *parameter_ptr) { return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) == 0): false; } SERVER_EXPORT uint32_t jackctl_parameter_get_enum_constraints_count(jackctl_parameter *parameter_ptr) { if (!parameter_ptr) { return 0; } if (!jackctl_parameter_has_enum_constraint(parameter_ptr)) { return 0; } return parameter_ptr->constraint_ptr->constraint.enumeration.count; } SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value(jackctl_parameter *parameter_ptr, uint32_t index) { jack_driver_param_value_t * value_ptr; union jackctl_parameter_value jackctl_value; if (!parameter_ptr) { memset(&jackctl_value, 0, sizeof(jackctl_value)); return jackctl_value; } value_ptr = ¶meter_ptr->constraint_ptr->constraint.enumeration.possible_values_array[index].value; switch (parameter_ptr->type) { case JackParamInt: jackctl_value.i = value_ptr->i; break; case JackParamUInt: jackctl_value.ui = value_ptr->ui; break; case JackParamChar: jackctl_value.c = value_ptr->c; break; case JackParamString: strcpy(jackctl_value.str, value_ptr->str); break; default: jack_error("Bad driver parameter type %i (enum constraint)", (int)parameter_ptr->type); assert(0); } return jackctl_value; } SERVER_EXPORT const char * jackctl_parameter_get_enum_constraint_description(jackctl_parameter *parameter_ptr, uint32_t index) { return (parameter_ptr) ? parameter_ptr->constraint_ptr->constraint.enumeration.possible_values_array[index].short_desc : NULL; } SERVER_EXPORT void jackctl_parameter_get_range_constraint(jackctl_parameter *parameter_ptr, union jackctl_parameter_value * min_ptr, union jackctl_parameter_value * max_ptr) { if (!parameter_ptr || !min_ptr || !max_ptr) { return; } switch (parameter_ptr->type) { case JackParamInt: min_ptr->i = parameter_ptr->constraint_ptr->constraint.range.min.i; max_ptr->i = parameter_ptr->constraint_ptr->constraint.range.max.i; return; case JackParamUInt: min_ptr->ui = parameter_ptr->constraint_ptr->constraint.range.min.ui; max_ptr->ui = parameter_ptr->constraint_ptr->constraint.range.max.ui; return; default: jack_error("Bad driver parameter type %i (range constraint)", (int)parameter_ptr->type); assert(0); } } SERVER_EXPORT bool jackctl_parameter_constraint_is_strict(jackctl_parameter_t * parameter_ptr) { return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_STRICT) != 0) : false; } SERVER_EXPORT bool jackctl_parameter_constraint_is_fake_value(jackctl_parameter_t * parameter_ptr) { return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_FAKE_VALUE) != 0) : false; } SERVER_EXPORT jackctl_param_type_t jackctl_parameter_get_type(jackctl_parameter *parameter_ptr) { return (parameter_ptr) ? parameter_ptr->type : (jackctl_param_type_t)0; } SERVER_EXPORT char jackctl_parameter_get_id(jackctl_parameter_t * parameter_ptr) { return (parameter_ptr) ? parameter_ptr->id : 0; } SERVER_EXPORT bool jackctl_parameter_is_set(jackctl_parameter *parameter_ptr) { return (parameter_ptr) ? parameter_ptr->is_set : false; } SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_value(jackctl_parameter *parameter_ptr) { if (parameter_ptr) { return *parameter_ptr->value_ptr; } else { union jackctl_parameter_value jackctl_value; memset(&jackctl_value, 0, sizeof(jackctl_value)); return jackctl_value; } } SERVER_EXPORT bool jackctl_parameter_reset(jackctl_parameter *parameter_ptr) { if (!parameter_ptr) { return false; } if (!parameter_ptr->is_set) { return true; } parameter_ptr->is_set = false; *parameter_ptr->value_ptr = *parameter_ptr->default_value_ptr; return true; } SERVER_EXPORT bool jackctl_parameter_set_value(jackctl_parameter *parameter_ptr, const union jackctl_parameter_value * value_ptr) { if (!parameter_ptr || !value_ptr) { return false; } parameter_ptr->is_set = true; *parameter_ptr->value_ptr = *value_ptr; return true; } SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value(jackctl_parameter *parameter_ptr) { if (parameter_ptr) { return *parameter_ptr->default_value_ptr; } else { union jackctl_parameter_value jackctl_value; memset(&jackctl_value, 0, sizeof(jackctl_value)); return jackctl_value; } } // Internals clients SERVER_EXPORT const JSList * jackctl_server_get_internals_list(jackctl_server *server_ptr) { return (server_ptr) ? server_ptr->internals : NULL; } SERVER_EXPORT const char * jackctl_internal_get_name(jackctl_internal *internal_ptr) { return (internal_ptr) ? internal_ptr->desc_ptr->name : NULL; } SERVER_EXPORT const JSList * jackctl_internal_get_parameters(jackctl_internal *internal_ptr) { return (internal_ptr) ? internal_ptr->parameters : NULL; } SERVER_EXPORT bool jackctl_server_load_internal( jackctl_server * server_ptr, jackctl_internal * internal) { if (!server_ptr || !internal) { return false; } int status; if (server_ptr->engine != NULL) { JSList * paramlist; if (!jackctl_create_param_list(internal->parameters, ¶mlist)) return false; server_ptr->engine->InternalClientLoad2(internal->desc_ptr->name, internal->desc_ptr->name, paramlist, JackNullOption, &internal->refnum, -1, &status); jackctl_destroy_param_list(paramlist); return (internal->refnum > 0); } else { return false; } } SERVER_EXPORT bool jackctl_server_unload_internal( jackctl_server * server_ptr, jackctl_internal * internal) { if (!server_ptr || !internal) { return false; } int status; if (server_ptr->engine != NULL && internal->refnum > 0) { // Client object is internally kept in JackEngine, and will be deallocated in InternalClientUnload return ((server_ptr->engine->GetEngine()->InternalClientUnload(internal->refnum, &status)) == 0); } else { return false; } } SERVER_EXPORT bool jackctl_server_load_session_file( jackctl_server * server_ptr, const char * file) { if (!server_ptr || !file || !server_ptr->engine) { return false; } return (server_ptr->engine->LoadInternalSessionFile(file) >= 0); } SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) { if (server_ptr && server_ptr->engine) { if (server_ptr->engine->IsRunning()) { jack_error("Cannot add a slave in a running server"); return false; } else { JSList * paramlist; if (!jackctl_create_param_list(driver_ptr->parameters, ¶mlist)) return false; JackDriverInfo* info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, paramlist); jackctl_destroy_param_list(paramlist); if (info) { driver_ptr->infos = jack_slist_append(driver_ptr->infos, info); return true; } else { return false; } } } else { return false; } } SERVER_EXPORT bool jackctl_server_remove_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) { if (server_ptr && server_ptr->engine) { if (server_ptr->engine->IsRunning()) { jack_error("Cannot remove a slave from a running server"); return false; } else { if (driver_ptr->infos) { JackDriverInfo* info = (JackDriverInfo*)driver_ptr->infos->data; assert(info); driver_ptr->infos = jack_slist_remove(driver_ptr->infos, info); server_ptr->engine->RemoveSlave(info); delete info; return true; } else { return false; } } } else { return false; } } SERVER_EXPORT bool jackctl_server_switch_master(jackctl_server * server_ptr, jackctl_driver * driver_ptr) { if (server_ptr && server_ptr->engine) { JSList * paramlist; if (!jackctl_create_param_list(driver_ptr->parameters, ¶mlist)) return false; bool ret = (server_ptr->engine->SwitchMaster(driver_ptr->desc_ptr, paramlist) == 0); jackctl_destroy_param_list(paramlist); return ret; } else { return false; } } jack2-1.9.22/common/JackControlAPI.h000066400000000000000000000164761436671425200170330ustar00rootroot00000000000000/* JACK control API Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackControlAPI__ #define __JackControlAPI__ #include "jslist.h" #include "JackCompilerDeps.h" /** Parameter types, intentionally similar to jack_driver_param_type_t */ typedef enum { JackParamInt = 1, /**< @brief value type is a signed integer */ JackParamUInt, /**< @brief value type is an unsigned integer */ JackParamChar, /**< @brief value type is a char */ JackParamString, /**< @brief value type is a string with max size of ::JACK_PARAM_STRING_MAX+1 chars */ JackParamBool, /**< @brief value type is a boolean */ } jackctl_param_type_t; /** Driver types, intentionally similar to jack_driver_type_t */ typedef enum { JackMaster = 1, /**< @brief master driver */ JackSlave, /**< @brief slave driver */ } jackctl_driver_type_t; /** @brief Max value that jackctl_param_type_t type can have */ #define JACK_PARAM_MAX (JackParamBool + 1) /** @brief Max length of string parameter value, excluding terminating nul char */ #define JACK_PARAM_STRING_MAX 127 /** @brief Type for parameter value */ /* intentionally similar to jack_driver_param_value_t */ union jackctl_parameter_value { uint32_t ui; /**< @brief member used for ::JackParamUInt */ int32_t i; /**< @brief member used for ::JackParamInt */ char c; /**< @brief member used for ::JackParamChar */ char str[JACK_PARAM_STRING_MAX + 1]; /**< @brief member used for ::JackParamString */ bool b; /**< @brief member used for ::JackParamBool */ }; /** opaque type for server object */ typedef struct jackctl_server jackctl_server_t; /** opaque type for driver object */ typedef struct jackctl_driver jackctl_driver_t; /** opaque type for internal client object */ typedef struct jackctl_internal jackctl_internal_t; /** opaque type for parameter object */ typedef struct jackctl_parameter jackctl_parameter_t; /** opaque type for sigmask object */ typedef struct jackctl_sigmask jackctl_sigmask_t; #ifdef __cplusplus extern "C" { #endif #if 0 } /* Adjust editor indent */ #endif SERVER_EXPORT jackctl_sigmask_t * jackctl_setup_signals( unsigned int flags); SERVER_EXPORT void jackctl_wait_signals( jackctl_sigmask_t * signals); SERVER_EXPORT jackctl_server_t * jackctl_server_create( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name)); SERVER_EXPORT jackctl_server_t * jackctl_server_create2( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name), void (* on_device_reservation_loop)(void)); SERVER_EXPORT void jackctl_server_destroy( jackctl_server_t * server); SERVER_EXPORT const JSList * jackctl_server_get_drivers_list( jackctl_server_t * server); SERVER_EXPORT bool jackctl_server_open( jackctl_server_t * server, jackctl_driver_t * driver); SERVER_EXPORT bool jackctl_server_start( jackctl_server_t * server); SERVER_EXPORT bool jackctl_server_stop( jackctl_server_t * server); SERVER_EXPORT bool jackctl_server_close( jackctl_server_t * server); SERVER_EXPORT const JSList * jackctl_server_get_parameters( jackctl_server_t * server); SERVER_EXPORT const char * jackctl_driver_get_name( jackctl_driver_t * driver); SERVER_EXPORT jackctl_driver_type_t jackctl_driver_get_type( jackctl_driver_t * driver); SERVER_EXPORT const JSList * jackctl_driver_get_parameters( jackctl_driver_t * driver); SERVER_EXPORT const char * jackctl_parameter_get_name( jackctl_parameter_t * parameter); SERVER_EXPORT const char * jackctl_parameter_get_short_description( jackctl_parameter_t * parameter); SERVER_EXPORT const char * jackctl_parameter_get_long_description( jackctl_parameter_t * parameter); SERVER_EXPORT jackctl_param_type_t jackctl_parameter_get_type( jackctl_parameter_t * parameter); SERVER_EXPORT char jackctl_parameter_get_id( jackctl_parameter_t * parameter); SERVER_EXPORT bool jackctl_parameter_is_set( jackctl_parameter_t * parameter); SERVER_EXPORT bool jackctl_parameter_reset( jackctl_parameter_t * parameter); SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_value( jackctl_parameter_t * parameter); SERVER_EXPORT bool jackctl_parameter_set_value( jackctl_parameter_t * parameter, const union jackctl_parameter_value * value_ptr); SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value( jackctl_parameter_t * parameter); SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value( jackctl_parameter *parameter_ptr); SERVER_EXPORT bool jackctl_parameter_has_range_constraint( jackctl_parameter_t * parameter_ptr); SERVER_EXPORT bool jackctl_parameter_has_enum_constraint( jackctl_parameter_t * parameter_ptr); SERVER_EXPORT uint32_t jackctl_parameter_get_enum_constraints_count( jackctl_parameter_t * parameter_ptr); SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value( jackctl_parameter_t * parameter_ptr, uint32_t index); SERVER_EXPORT const char * jackctl_parameter_get_enum_constraint_description( jackctl_parameter_t * parameter_ptr, uint32_t index); SERVER_EXPORT void jackctl_parameter_get_range_constraint( jackctl_parameter_t * parameter_ptr, union jackctl_parameter_value * min_ptr, union jackctl_parameter_value * max_ptr); SERVER_EXPORT bool jackctl_parameter_constraint_is_strict( jackctl_parameter_t * parameter_ptr); SERVER_EXPORT bool jackctl_parameter_constraint_is_fake_value( jackctl_parameter_t * parameter_ptr); SERVER_EXPORT const JSList * jackctl_server_get_internals_list( jackctl_server *server_ptr); SERVER_EXPORT const char * jackctl_internal_get_name( jackctl_internal *internal_ptr); SERVER_EXPORT const JSList * jackctl_internal_get_parameters( jackctl_internal *internal_ptr); SERVER_EXPORT bool jackctl_server_load_internal( jackctl_server * server, jackctl_internal * internal); SERVER_EXPORT bool jackctl_server_unload_internal( jackctl_server * server, jackctl_internal * internal); SERVER_EXPORT bool jackctl_server_load_session_file( jackctl_server * server_ptr, const char * file); SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server_t * server, jackctl_driver_t * driver); SERVER_EXPORT bool jackctl_server_remove_slave(jackctl_server_t * server, jackctl_driver_t * driver); SERVER_EXPORT bool jackctl_server_switch_master(jackctl_server_t * server, jackctl_driver_t * driver); SERVER_EXPORT int jackctl_parse_driver_params(jackctl_driver * driver_ptr, int argc, char* argv[]); #if 0 { /* Adjust editor indent */ #endif #ifdef __cplusplus } /* extern "C" */ #endif #endif jack2-1.9.22/common/JackDebugClient.cpp000066400000000000000000000552461436671425200175770ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackDebugClient.h" #include "JackEngineControl.h" #include "JackException.h" #include "JackError.h" #include "JackTime.h" #include #include #include #include #include #include using namespace std; namespace Jack { JackDebugClient::JackDebugClient(JackClient * client) : JackClient(client->fSynchroTable) { fTotalPortNumber = 1; // The total number of port opened and maybe closed. Historical view. fOpenPortNumber = 0; // The current number of opened port. fIsActivated = 0; fIsDeactivated = 0; fIsClosed = 0; fClient = client; fFreewheel = false; } JackDebugClient::~JackDebugClient() { fTotalPortNumber--; // fTotalPortNumber start at 1 *fStream << endl << endl << "----------------------------------- JackDebugClient summary ------------------------------- " << endl << endl; *fStream << "Client flags ( 1:yes / 0:no ) :" << endl; *fStream << setw(5) << "- Client call activated : " << fIsActivated << endl; *fStream << setw(5) << "- Client call deactivated : " << fIsDeactivated << endl; *fStream << setw(5) << "- Client call closed : " << fIsClosed << endl; *fStream << setw(5) << "- Total number of instantiated port : " << fTotalPortNumber << endl; *fStream << setw(5) << "- Number of port remaining open when exiting client : " << fOpenPortNumber << endl; if (fOpenPortNumber != 0) *fStream << "!!! WARNING !!! Some ports have not been unregistered ! Incorrect exiting !" << endl; if (fIsDeactivated != fIsActivated) *fStream << "!!! ERROR !!! Client seem to not perform symmetric activation-deactivation ! (not the same number of activate and deactivate)" << endl; if (fIsClosed == 0) *fStream << "!!! ERROR !!! Client have not been closed with jack_client_close() !" << endl; *fStream << endl << endl << "---------------------------- JackDebugClient detailed port summary ------------------------ " << endl << endl; //for (int i = 0; i < fTotalPortNumber ; i++) { for (int i = 1; i <= fTotalPortNumber ; i++) { *fStream << endl << "Port index (internal debug test value) : " << i << endl; *fStream << setw(5) << "- Name : " << fPortList[i].name << endl; *fStream << setw(5) << "- idport : " << fPortList[i].idport << endl; *fStream << setw(5) << "- IsConnected : " << fPortList[i].IsConnected << endl; *fStream << setw(5) << "- IsUnregistered : " << fPortList[i].IsUnregistered << endl; if (fPortList[i].IsUnregistered == 0) *fStream << "!!! WARNING !!! Port have not been unregistered ! Incorrect exiting !" << endl; } *fStream << "delete object JackDebugClient : end of tracing" << endl; delete fStream; delete fClient; } int JackDebugClient::Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status) { int res = fClient->Open(server_name, name, uuid, options, status); char provstr[256]; char buffer[256]; time_t curtime; struct tm *loctime; /* Get the current time. */ curtime = time (NULL); /* Convert it to local time representation. */ loctime = localtime (&curtime); strftime (buffer, 256, "%I-%M", loctime); snprintf(provstr, sizeof(provstr), "JackClientDebug-%s-%s.log", name, buffer); fStream = new ofstream(provstr, ios_base::ate); if (fStream->is_open()) { if (res == -1) { *fStream << "Trying to open client with name '" << name << "' with bad result (client not opened)." << res << endl; } else { *fStream << "Open client with name '" << name << "'." << endl; } } else { jack_log("JackDebugClient::Open : cannot open log file"); } strcpy(fClientName, name); return res; } int JackDebugClient::Close() { *fStream << "Client '" << fClientName << "' was closed" << endl; int res = fClient->Close(); fIsClosed++; return res; } void JackDebugClient::CheckClient(const char* function_name) const { #ifdef WIN32 *fStream << "CheckClient : " << function_name << ", calling thread : " << GetCurrentThread() << endl; #else *fStream << "CheckClient : " << function_name << ", calling thread : " << pthread_self() << endl; #endif if (fIsClosed > 0) { *fStream << "!!! ERROR !!! : Accessing a client '" << fClientName << "' already closed " << "from " << function_name << endl; *fStream << "This is likely to cause crash !'" << endl; #ifdef __APPLE__ // Debugger(); #endif } } jack_native_thread_t JackDebugClient::GetThreadID() { CheckClient("GetThreadID"); return fClient->GetThreadID(); } JackGraphManager* JackDebugClient::GetGraphManager() const { CheckClient("GetGraphManager"); return fClient->GetGraphManager(); } JackEngineControl* JackDebugClient::GetEngineControl() const { CheckClient("GetEngineControl"); return fClient->GetEngineControl(); } /*! \brief Notification received from the server. */ int JackDebugClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { CheckClient("ClientNotify"); return fClient->ClientNotify( refnum, name, notify, sync, message, value1, value2); } int JackDebugClient::Activate() { CheckClient("Activate"); int res = fClient->Activate(); fIsActivated++; if (fIsDeactivated) *fStream << "Client '" << fClientName << "' call activate a new time (it already call 'activate' previously)." << endl; *fStream << "Client '" << fClientName << "' Activated" << endl; if (res != 0) *fStream << "Client '" << fClientName << "' try to activate but server return " << res << " ." << endl; return res; } int JackDebugClient::Deactivate() { CheckClient("Deactivate"); int res = fClient->Deactivate(); fIsDeactivated++; if (fIsActivated == 0) *fStream << "Client '" << fClientName << "' deactivate while it hasn't been previously activated !" << endl; *fStream << "Client '" << fClientName << "' Deactivated" << endl; if (res != 0) *fStream << "Client '" << fClientName << "' try to deactivate but server return " << res << " ." << endl; return res; } //----------------- // Port management //----------------- int JackDebugClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) { CheckClient("PortRegister"); int res = fClient->PortRegister(port_name, port_type, flags, buffer_size); if (res <= 0) { *fStream << "Client '" << fClientName << "' try port register ('" << port_name << "') and server return error " << res << " ." << endl; } else { if (fTotalPortNumber < MAX_PORT_HISTORY) { fPortList[fTotalPortNumber].idport = res; strcpy(fPortList[fTotalPortNumber].name, port_name); fPortList[fTotalPortNumber].IsConnected = 0; fPortList[fTotalPortNumber].IsUnregistered = 0; } else { *fStream << "!!! WARNING !!! History is full : no more port history will be recorded." << endl; } fTotalPortNumber++; fOpenPortNumber++; *fStream << "Client '" << fClientName << "' port register with portname '" << port_name << " port " << res << "' ." << endl; } return res; } int JackDebugClient::PortUnRegister(jack_port_id_t port_index) { CheckClient("PortUnRegister"); int res = fClient->PortUnRegister(port_index); fOpenPortNumber--; int i; for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history if (fPortList[i].idport == port_index) { // We found the last record if (fPortList[i].IsUnregistered != 0) *fStream << "!!! ERROR !!! : '" << fClientName << "' id deregistering port '" << fPortList[i].name << "' that have already been unregistered !" << endl; fPortList[i].IsUnregistered++; break; } } if (i == 0) // Port is not found *fStream << "JackClientDebug : PortUnregister : port " << port_index << " was not previously registered !" << endl; if (res != 0) *fStream << "Client '" << fClientName << "' try to do PortUnregister and server return " << res << endl; *fStream << "Client '" << fClientName << "' unregister port '" << port_index << "'." << endl; return res; } int JackDebugClient::PortConnect(const char* src, const char* dst) { CheckClient("PortConnect"); if (!fIsActivated) *fStream << "!!! ERROR !!! Trying to connect a port ( " << src << " to " << dst << ") while the client has not been activated !" << endl; int i; int res = fClient->PortConnect( src, dst); for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history if (strcmp(fPortList[i].name, src) == 0) { // We found the last record in sources if (fPortList[i].IsUnregistered != 0) *fStream << "!!! ERROR !!! Connecting port " << src << " previously unregistered !" << endl; fPortList[i].IsConnected++; *fStream << "Connecting port " << src << " to " << dst << ". "; break; } else if (strcmp(fPortList[i].name, dst) == 0 ) { // We found the record in dest if (fPortList[i].IsUnregistered != 0) *fStream << "!!! ERROR !!! Connecting port " << dst << " previously unregistered !" << endl; fPortList[i].IsConnected++; *fStream << "Connecting port " << src << " to " << dst << ". "; break; } } if (i == 0) // Port is not found *fStream << "JackClientDebug : PortConnect : port was not found in debug database !" << endl; if (res != 0) *fStream << "Client '" << fClientName << "' try to do PortConnect but server return " << res << " ." << endl; //*fStream << "Client Port Connect done with names" << endl; return res; } int JackDebugClient::PortDisconnect(const char* src, const char* dst) { CheckClient("PortDisconnect"); if (!fIsActivated) *fStream << "!!! ERROR !!! Trying to disconnect a port ( " << src << " to " << dst << ") while the client has not been activated !" << endl; int res = fClient->PortDisconnect( src, dst); int i; for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history if (strcmp(fPortList[i].name, src) == 0) { // We found the record in sources if (fPortList[i].IsUnregistered != 0) *fStream << "!!! ERROR !!! : Disconnecting port " << src << " previously unregistered !" << endl; fPortList[i].IsConnected--; *fStream << "disconnecting port " << src << ". "; break; } else if (strcmp(fPortList[i].name, dst) == 0 ) { // We found the record in dest if (fPortList[i].IsUnregistered != 0) *fStream << "!!! ERROR !!! : Disonnecting port " << dst << " previously unregistered !" << endl; fPortList[i].IsConnected--; *fStream << "disconnecting port " << dst << ". "; break; } } if (i == 0) // Port is not found *fStream << "JackClientDebug : PortDisConnect : port was not found in debug database !" << endl; if (res != 0) *fStream << "Client '" << fClientName << "' try to do PortDisconnect but server return " << res << " ." << endl; //*fStream << "Client Port Disconnect done." << endl; return res; } int JackDebugClient::PortDisconnect(jack_port_id_t src) { CheckClient("PortDisconnect"); if (!fIsActivated) *fStream << "!!! ERROR !!! : Trying to disconnect port " << src << " while that client has not been activated !" << endl; int res = fClient->PortDisconnect(src); int i; for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history if (fPortList[i].idport == src) { // We found the record in sources if (fPortList[i].IsUnregistered != 0) *fStream << "!!! ERROR !!! : Disconnecting port " << src << " previously unregistered !" << endl; fPortList[i].IsConnected--; *fStream << "Disconnecting port " << src << ". " << endl; break; } } if (i == 0) // Port is not found *fStream << "JackClientDebug : PortDisconnect : port was not found in debug database !" << endl; if (res != 0) *fStream << "Client '" << fClientName << "' try to do PortDisconnect but server return " << res << " ." << endl; //*fStream << "Client Port Disconnect with ID done." << endl; return res; } int JackDebugClient::PortIsMine(jack_port_id_t port_index) { CheckClient("PortIsMine"); *fStream << "JackClientDebug : PortIsMine port_index " << port_index << endl; return fClient->PortIsMine(port_index); } int JackDebugClient::PortRename(jack_port_id_t port_index, const char* name) { CheckClient("PortRename"); *fStream << "JackClientDebug : PortRename port_index " << port_index << "name" << name << endl; return fClient->PortRename(port_index, name); } //-------------------- // Context management //-------------------- int JackDebugClient::SetBufferSize(jack_nframes_t buffer_size) { CheckClient("SetBufferSize"); *fStream << "JackClientDebug : SetBufferSize buffer_size " << buffer_size << endl; return fClient->SetBufferSize(buffer_size); } int JackDebugClient::SetFreeWheel(int onoff) { CheckClient("SetFreeWheel"); if (onoff && fFreewheel) *fStream << "!!! ERROR !!! : Freewheel setup seems incorrect : set = ON while FW is already ON " << endl; if (!onoff && !fFreewheel) *fStream << "!!! ERROR !!! : Freewheel setup seems incorrect : set = OFF while FW is already OFF " << endl; fFreewheel = onoff ? true : false; return fClient->SetFreeWheel(onoff); } int JackDebugClient::ComputeTotalLatencies() { CheckClient("ComputeTotalLatencies"); return fClient->ComputeTotalLatencies(); } /* ShutDown is called: - from the RT thread when Execute method fails - possibly from a "closed" notification channel (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown)) */ void JackDebugClient::ShutDown(jack_status_t code, const char* message) { CheckClient("ShutDown"); fClient->ShutDown(code, message); } //--------------------- // Transport management //--------------------- int JackDebugClient::ReleaseTimebase() { CheckClient("ReleaseTimebase"); return fClient->ReleaseTimebase(); } int JackDebugClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg) { CheckClient("SetSyncCallback"); return fClient->SetSyncCallback(sync_callback, arg); } int JackDebugClient::SetSyncTimeout(jack_time_t timeout) { CheckClient("SetSyncTimeout"); *fStream << "JackClientDebug : SetSyncTimeout timeout " << timeout << endl; return fClient->SetSyncTimeout(timeout); } int JackDebugClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg) { CheckClient("SetTimebaseCallback"); return fClient->SetTimebaseCallback( conditional, timebase_callback, arg); } void JackDebugClient::TransportLocate(jack_nframes_t frame) { CheckClient("TransportLocate"); *fStream << "JackClientDebug : TransportLocate frame " << frame << endl; fClient->TransportLocate(frame); } jack_transport_state_t JackDebugClient::TransportQuery(jack_position_t* pos) { CheckClient("TransportQuery"); return fClient->TransportQuery(pos); } jack_nframes_t JackDebugClient::GetCurrentTransportFrame() { CheckClient("GetCurrentTransportFrame"); return fClient->GetCurrentTransportFrame(); } int JackDebugClient::TransportReposition(const jack_position_t* pos) { CheckClient("TransportReposition"); return fClient->TransportReposition(pos); } void JackDebugClient::TransportStart() { CheckClient("TransportStart"); fClient->TransportStart(); } void JackDebugClient::TransportStop() { CheckClient("TransportStop"); fClient->TransportStop(); } //--------------------- // Callback management //--------------------- void JackDebugClient::OnShutdown(JackShutdownCallback callback, void *arg) { CheckClient("OnShutdown"); fClient->OnShutdown(callback, arg); } void JackDebugClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg) { CheckClient("OnInfoShutdown"); fClient->OnInfoShutdown(callback, arg); } int JackDebugClient::TimeCallback(jack_nframes_t nframes, void *arg) { JackDebugClient* client = (JackDebugClient*)arg; jack_time_t t1 = GetMicroSeconds(); int res = client->fProcessTimeCallback(nframes, client->fProcessTimeCallbackArg); if (res == 0) { jack_time_t t2 = GetMicroSeconds(); long delta = long((t2 - t1) - client->GetEngineControl()->fPeriodUsecs); if (delta > 0 && !client->fFreewheel) { *client->fStream << "!!! ERROR !!! : Process overload of " << delta << " us" << endl; } } return res; } int JackDebugClient::SetProcessCallback(JackProcessCallback callback, void *arg) { CheckClient("SetProcessCallback"); fProcessTimeCallback = callback; fProcessTimeCallbackArg = arg; if (callback == NULL) { // Clear the callback... return fClient->SetProcessCallback(callback, arg); } else { // Setup the measuring version... return fClient->SetProcessCallback(TimeCallback, this); } } int JackDebugClient::SetXRunCallback(JackXRunCallback callback, void *arg) { CheckClient("SetXRunCallback"); return fClient->SetXRunCallback(callback, arg); } int JackDebugClient::SetInitCallback(JackThreadInitCallback callback, void *arg) { CheckClient("SetInitCallback"); return fClient->SetInitCallback(callback, arg); } int JackDebugClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg) { CheckClient("SetGraphOrderCallback"); return fClient->SetGraphOrderCallback(callback, arg); } int JackDebugClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg) { CheckClient("SetBufferSizeCallback"); return fClient->SetBufferSizeCallback(callback, arg); } int JackDebugClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg) { CheckClient("SetClientRegistrationCallback"); return fClient->SetClientRegistrationCallback(callback, arg); } int JackDebugClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg) { CheckClient("SetFreewheelCallback"); return fClient->SetFreewheelCallback(callback, arg); } int JackDebugClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg) { CheckClient("SetPortRegistrationCallback"); return fClient->SetPortRegistrationCallback(callback, arg); } int JackDebugClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg) { CheckClient("SetPortConnectCallback"); return fClient->SetPortConnectCallback(callback, arg); } int JackDebugClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg) { CheckClient("SetPortRenameCallback"); return fClient->SetPortRenameCallback(callback, arg); } int JackDebugClient::SetSessionCallback(JackSessionCallback callback, void *arg) { CheckClient("SetSessionCallback"); return fClient->SetSessionCallback(callback, arg); } int JackDebugClient::SetLatencyCallback(JackLatencyCallback callback, void *arg) { CheckClient("SetLatencyCallback"); return fClient->SetLatencyCallback(callback, arg); } int JackDebugClient::SetProcessThread(JackThreadCallback fun, void *arg) { CheckClient("SetProcessThread"); return fClient->SetProcessThread(fun, arg); } jack_session_command_t* JackDebugClient::SessionNotify(const char* target, jack_session_event_type_t type, const char* path) { CheckClient("SessionNotify"); *fStream << "JackClientDebug : SessionNotify target " << target << "type " << type << "path " << path << endl; return fClient->SessionNotify(target, type, path); } int JackDebugClient::SessionReply(jack_session_event_t* ev) { CheckClient("SessionReply"); return fClient->SessionReply(ev); } char* JackDebugClient::GetUUIDForClientName(const char* client_name) { CheckClient("GetUUIDForClientName"); *fStream << "JackClientDebug : GetUUIDForClientName client_name " << client_name << endl; return fClient->GetUUIDForClientName(client_name); } char* JackDebugClient::GetClientNameByUUID(const char* uuid) { CheckClient("GetClientNameByUUID"); *fStream << "JackClientDebug : GetClientNameByUUID uuid " << uuid << endl; return fClient->GetClientNameByUUID(uuid); } int JackDebugClient::ReserveClientName(const char* client_name, const char* uuid) { CheckClient("ReserveClientName"); *fStream << "JackClientDebug : ReserveClientName client_name " << client_name << "uuid " << uuid << endl; return fClient->ReserveClientName(client_name, uuid); } int JackDebugClient::ClientHasSessionCallback(const char* client_name) { CheckClient("ClientHasSessionCallback"); *fStream << "JackClientDebug : ClientHasSessionCallback client_name " << client_name << endl; return fClient->ClientHasSessionCallback(client_name); } JackClientControl* JackDebugClient::GetClientControl() const { CheckClient("GetClientControl"); return fClient->GetClientControl(); } // Internal clients char* JackDebugClient::GetInternalClientName(int ref) { CheckClient("GetInternalClientName"); return fClient->GetInternalClientName(ref); } int JackDebugClient::InternalClientHandle(const char* client_name, jack_status_t* status) { CheckClient("InternalClientHandle"); return fClient->InternalClientHandle(client_name, status); } int JackDebugClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va) { CheckClient("InternalClientLoad"); return fClient->InternalClientLoad(client_name, options, status, va); } void JackDebugClient::InternalClientUnload(int ref, jack_status_t* status) { CheckClient("InternalClientUnload"); fClient->InternalClientUnload(ref, status); } } // end of namespace jack2-1.9.22/common/JackDebugClient.h000066400000000000000000000133561436671425200172400ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackDebugClient__ #define __JackDebugClient__ #define MAX_PORT_HISTORY 2048 #include "JackClient.h" #include #include namespace Jack { /*! \brief Follow a single port. */ typedef struct { jack_port_id_t idport; char name[JACK_PORT_NAME_SIZE]; //portname int IsConnected; int IsUnregistered; } PortFollower; /*! \brief A "decorator" debug client to validate API use. */ class JackDebugClient : public JackClient { protected: JackClient* fClient; std::ofstream* fStream; PortFollower fPortList[MAX_PORT_HISTORY]; // Arbitrary value... To be tuned... int fTotalPortNumber; // The total number of port opened and maybe closed. Historical view. int fOpenPortNumber; // The current number of opened port. int fIsActivated; int fIsDeactivated; int fIsClosed; bool fFreewheel; char fClientName[JACK_CLIENT_NAME_SIZE+1]; JackProcessCallback fProcessTimeCallback; void* fProcessTimeCallbackArg; public: JackDebugClient(JackClient* fTheClient); virtual ~JackDebugClient(); virtual int Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status); int Close(); virtual JackGraphManager* GetGraphManager() const; virtual JackEngineControl* GetEngineControl() const; // Notifications int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); int Activate(); int Deactivate(); // Context int SetBufferSize(jack_nframes_t buffer_size); int SetFreeWheel(int onoff); int ComputeTotalLatencies(); void ShutDown(jack_status_t code, const char* message); jack_native_thread_t GetThreadID(); // Port management int PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); int PortUnRegister(jack_port_id_t port); int PortConnect(const char* src, const char* dst); int PortDisconnect(const char* src, const char* dst); int PortDisconnect(jack_port_id_t src); int PortIsMine(jack_port_id_t port_index); int PortRename(jack_port_id_t port_index, const char* name); // Transport int ReleaseTimebase(); int SetSyncCallback(JackSyncCallback sync_callback, void* arg); int SetSyncTimeout(jack_time_t timeout); int SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg); void TransportLocate(jack_nframes_t frame); jack_transport_state_t TransportQuery(jack_position_t* pos); jack_nframes_t GetCurrentTransportFrame(); int TransportReposition(const jack_position_t* pos); void TransportStart(); void TransportStop(); // Callbacks void OnShutdown(JackShutdownCallback callback, void *arg); void OnInfoShutdown(JackInfoShutdownCallback callback, void *arg); int SetProcessCallback(JackProcessCallback callback, void* arg); int SetXRunCallback(JackXRunCallback callback, void* arg); int SetInitCallback(JackThreadInitCallback callback, void* arg); int SetGraphOrderCallback(JackGraphOrderCallback callback, void* arg); int SetBufferSizeCallback(JackBufferSizeCallback callback, void* arg); int SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg); int SetFreewheelCallback(JackFreewheelCallback callback, void* arg); int SetPortRegistrationCallback(JackPortRegistrationCallback callback, void* arg); int SetPortConnectCallback(JackPortConnectCallback callback, void *arg); int SetPortRenameCallback(JackPortRenameCallback callback, void *arg); int SetSessionCallback(JackSessionCallback callback, void *arg); int SetLatencyCallback(JackLatencyCallback callback, void *arg); // Internal clients char* GetInternalClientName(int ref); int InternalClientHandle(const char* client_name, jack_status_t* status); int InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va); void InternalClientUnload(int ref, jack_status_t* status); // RT Thread int SetProcessThread(JackThreadCallback fun, void *arg); // Session API jack_session_command_t* SessionNotify(const char* target, jack_session_event_type_t type, const char* path); int SessionReply(jack_session_event_t* ev); char* GetUUIDForClientName(const char* client_name); char* GetClientNameByUUID(const char* uuid); int ReserveClientName(const char* client_name, const char* uuid); int ClientHasSessionCallback(const char* client_name); JackClientControl* GetClientControl() const; void CheckClient(const char* function_name) const; static int TimeCallback(jack_nframes_t nframes, void *arg); }; } // end of namespace #endif jack2-1.9.22/common/JackDriver.cpp000066400000000000000000000426571436671425200166470ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackDriver.h" #include "JackTime.h" #include "JackError.h" #include "JackPort.h" #include "JackGraphManager.h" #include "JackGlobals.h" #include "JackEngineControl.h" #include "JackClientControl.h" #include "JackLockedEngine.h" #include "JackTime.h" #include #include using namespace std; namespace Jack { JackDriver::JackDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) :fCaptureChannels(0), fPlaybackChannels(0), fClientControl(name, jack_client_uuid_generate()), fWithMonitorPorts(false) { assert(strlen(name) < JACK_CLIENT_NAME_SIZE); fSynchroTable = table; strcpy(fAliasName, alias); fEngine = engine; fGraphManager = NULL; fBeginDateUst = 0; fEndDateUst = 0; fDelayedUsecs = 0.f; fIsMaster = true; fIsRunning = false; } JackDriver::~JackDriver() { jack_log("~JackDriver"); } int JackDriver::Open() { int refnum = -1; if (fEngine->ClientInternalOpen(fClientControl.fName, &refnum, &fEngineControl, &fGraphManager, this, false) != 0) { jack_error("Cannot allocate internal client for driver"); return -1; } fClientControl.fRefNum = refnum; fClientControl.fActive = true; fEngineControl->fDriverNum++; fGraphManager->DirectConnect(fClientControl.fRefNum, fClientControl.fRefNum); // Connect driver to itself for "sync" mode SetupDriverSync(fClientControl.fRefNum, false); return 0; } int JackDriver::Open(jack_nframes_t buffer_size, jack_nframes_t sample_rate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { jack_log("JackDriver::Open capture_driver_name = %s", capture_driver_name); jack_log("JackDriver::Open playback_driver_name = %s", playback_driver_name); int refnum = -1; char name_res[JACK_CLIENT_NAME_SIZE + 1]; int status; // Check name and possibly rename if (fEngine->ClientCheck(fClientControl.fName, -1, name_res, JACK_PROTOCOL_VERSION, (int)JackNullOption, (int*)&status) < 0) { jack_error("Client name = %s conflits with another running client", fClientControl.fName); return -1; } strcpy(fClientControl.fName, name_res); if (fEngine->ClientInternalOpen(fClientControl.fName, &refnum, &fEngineControl, &fGraphManager, this, false) != 0) { jack_error("Cannot allocate internal client for driver"); return -1; } fClientControl.fRefNum = refnum; fClientControl.fActive = true; fEngineControl->fDriverNum++; if (buffer_size > 0) { fEngineControl->fBufferSize = buffer_size; } if (sample_rate > 0) { fEngineControl->fSampleRate = sample_rate; } fCaptureLatency = capture_latency; fPlaybackLatency = playback_latency; assert(strlen(capture_driver_name) < JACK_CLIENT_NAME_SIZE); assert(strlen(playback_driver_name) < JACK_CLIENT_NAME_SIZE); strcpy(fCaptureDriverName, capture_driver_name); strcpy(fPlaybackDriverName, playback_driver_name); fEngineControl->UpdateTimeOut(); fGraphManager->SetBufferSize(fEngineControl->fBufferSize); fGraphManager->DirectConnect(fClientControl.fRefNum, fClientControl.fRefNum); // Connect driver to itself for "sync" mode SetupDriverSync(fClientControl.fRefNum, false); return 0; } int JackDriver::Close() { if (fClientControl.fRefNum >= 0) { jack_log("JackDriver::Close"); fGraphManager->DirectDisconnect(fClientControl.fRefNum, fClientControl.fRefNum); // Disconnect driver from itself for sync fClientControl.fActive = false; fEngineControl->fDriverNum--; return fEngine->ClientInternalClose(fClientControl.fRefNum, false); } else { return -1; } } /*! In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations. The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations. Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel. */ void JackDriver::SetupDriverSync(int ref, bool freewheel) { if (!freewheel && !fEngineControl->fSyncMode) { jack_log("JackDriver::SetupDriverSync driver sem in flush mode"); fSynchroTable[ref].SetFlush(true); } else { jack_log("JackDriver::SetupDriverSync driver sem in normal mode"); fSynchroTable[ref].SetFlush(false); } } int JackDriver::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { jack_log("JackDriver::ClientNotify ref = %ld driver = %s name = %s notify = %ld", refnum, fClientControl.fName, name, notify); switch (notify) { case kStartFreewheelCallback: jack_log("JackDriver::kStartFreewheel"); SetupDriverSync(fClientControl.fRefNum, true); break; case kStopFreewheelCallback: jack_log("JackDriver::kStopFreewheel"); SetupDriverSync(fClientControl.fRefNum, false); break; } return 0; } bool JackDriver::IsRealTime() const { return fEngineControl->fRealTime; } void JackDriver::CycleIncTime() { fEngineControl->CycleIncTime(fBeginDateUst); } void JackDriver::CycleTakeBeginTime() { fBeginDateUst = GetMicroSeconds(); // Take callback date here fEngineControl->CycleIncTime(fBeginDateUst); } void JackDriver::CycleTakeEndTime() { fEndDateUst = GetMicroSeconds(); // Take end date here } JackClientControl* JackDriver::GetClientControl() const { return (JackClientControl*)&fClientControl; } void JackDriver::NotifyXRun(jack_time_t cur_cycle_begin, float delayed_usecs) { fEngineControl->NotifyXRun(cur_cycle_begin, delayed_usecs); fEngine->NotifyDriverXRun(); } void JackDriver::NotifyBufferSize(jack_nframes_t buffer_size) { fEngine->NotifyBufferSize(buffer_size); fEngineControl->InitFrameTime(); } void JackDriver::NotifySampleRate(jack_nframes_t sample_rate) { fEngine->NotifySampleRate(sample_rate); fEngineControl->InitFrameTime(); } void JackDriver::NotifyFailure(int code, const char* reason) { fEngine->NotifyFailure(code, reason); } void JackDriver::SetMaster(bool onoff) { fIsMaster = onoff; } bool JackDriver::GetMaster() { return fIsMaster; } void JackDriver::AddSlave(JackDriverInterface* slave) { fSlaveList.push_back(slave); } void JackDriver::RemoveSlave(JackDriverInterface* slave) { fSlaveList.remove(slave); } int JackDriver::ProcessReadSlaves() { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; if (slave->IsRunning()) { if (slave->ProcessRead() < 0) { res = -1; } } } return res; } int JackDriver::ProcessWriteSlaves() { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; if (slave->IsRunning()) { if (slave->ProcessWrite() < 0) { res = -1; } } } return res; } int JackDriver::ProcessRead() { return (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); } int JackDriver::ProcessWrite() { return (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); } int JackDriver::ProcessReadSync() { return 0; } int JackDriver::ProcessWriteSync() { return 0; } int JackDriver::ProcessReadAsync() { return 0; } int JackDriver::ProcessWriteAsync() { return 0; } int JackDriver::Process() { return 0; } int JackDriver::Attach() { return 0; } int JackDriver::Detach() { return 0; } int JackDriver::Read() { return 0; } int JackDriver::Write() { return 0; } int JackDriver::Start() { if (fIsMaster) { fEngineControl->InitFrameTime(); } fIsRunning = true; return StartSlaves(); } int JackDriver::Stop() { fIsRunning = false; return StopSlaves(); } int JackDriver::StartSlaves() { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; if (slave->Start() < 0) { res = -1; // XXX: We should attempt to stop all of the slaves that we've // started here. break; } } return res; } int JackDriver::StopSlaves() { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; if (slave->Stop() < 0) { res = -1; } } return res; } bool JackDriver::IsFixedBufferSize() { return true; } int JackDriver::SetBufferSize(jack_nframes_t buffer_size) { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; if (slave->SetBufferSize(buffer_size) < 0) { res = -1; } } return res; } int JackDriver::SetSampleRate(jack_nframes_t sample_rate) { int res = 0; list::const_iterator it; for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) { JackDriverInterface* slave = *it; if (slave->SetSampleRate(sample_rate) < 0) { res = -1; } } return res; } bool JackDriver::Initialize() { return true; } static string RemoveLast(const string& name) { return name.substr(0, name.find_last_of(':')); // Remove end of name after last ":" } void JackDriver::SaveConnections(int alias) { const char** connections; char alias1[REAL_JACK_PORT_NAME_SIZE+1]; char alias2[REAL_JACK_PORT_NAME_SIZE+1]; char system_alias1[REAL_JACK_PORT_NAME_SIZE+1]; char system_alias2[REAL_JACK_PORT_NAME_SIZE+1]; char* aliases[2]; char* system_aliases[2]; aliases[0] = alias1; aliases[1] = alias2; system_aliases[0] = system_alias1; system_aliases[1] = system_alias2; fConnections.clear(); for (int i = 0; i < fCaptureChannels; ++i) { if (fCapturePortList[i] && (connections = fGraphManager->GetConnections(fCapturePortList[i])) != 0) { if (alias == 0) { for (int j = 0; connections[j]; j++) { JackPort* port_id = fGraphManager->GetPort(fCapturePortList[i]); fConnections.push_back(make_pair(port_id->GetType(), make_pair(port_id->GetName(), connections[j]))); jack_info("Save connection: %s %s", fGraphManager->GetPort(fCapturePortList[i])->GetName(), connections[j]); } } else { int res1 = fGraphManager->GetPort(fCapturePortList[i])->GetAliases(aliases); string sub_system_name; if (res1 >= alias) { sub_system_name = aliases[alias-1]; } else { sub_system_name = fGraphManager->GetPort(fCapturePortList[i])->GetName(); } for (int j = 0; connections[j]; j++) { JackPort* port_id = fGraphManager->GetPort(fGraphManager->GetPort(connections[j])); int res2 = port_id->GetAliases(system_aliases); string sub_system; if (res2 >= alias) { sub_system = system_aliases[alias-1]; } else { sub_system = connections[j]; } fConnections.push_back(make_pair(port_id->GetType(), make_pair(sub_system_name, sub_system))); jack_info("Save connection: %s %s", sub_system_name.c_str(), sub_system.c_str()); } } free(connections); } } for (int i = 0; i < fPlaybackChannels; ++i) { if (fPlaybackPortList[i] && (connections = fGraphManager->GetConnections(fPlaybackPortList[i])) != 0) { if (alias == 0) { for (int j = 0; connections[j]; j++) { JackPort* port_id = fGraphManager->GetPort(fPlaybackPortList[i]); fConnections.push_back(make_pair(port_id->GetType(), make_pair(connections[j], port_id->GetName()))); jack_info("Save connection: %s %s", connections[j], fGraphManager->GetPort(fPlaybackPortList[i])->GetName()); } } else { int res1 = fGraphManager->GetPort(fPlaybackPortList[i])->GetAliases(aliases); string sub_system_name; if (res1 >= alias) { sub_system_name = aliases[alias-1]; } else { sub_system_name = fGraphManager->GetPort(fPlaybackPortList[i])->GetName(); } for (int j = 0; connections[j]; j++) { JackPort* port_id = fGraphManager->GetPort(fGraphManager->GetPort(connections[j])); int res2 = port_id->GetAliases(system_aliases); string sub_name; if (res2 >= alias) { sub_name = system_aliases[alias-1]; } else { sub_name = connections[j]; } fConnections.push_back(make_pair(port_id->GetType(), make_pair(sub_name, sub_system_name))); jack_info("Save connection: %s %s", sub_name.c_str(), sub_system_name.c_str()); } } free(connections); } } } string JackDriver::MatchPortName(const char* name, const char** ports, int alias, const std::string& type) { char alias1[REAL_JACK_PORT_NAME_SIZE+1]; char alias2[REAL_JACK_PORT_NAME_SIZE+1]; char* aliases[2]; aliases[0] = alias1; aliases[1] = alias2; for (int i = 0; ports && ports[i]; ++i) { jack_port_id_t port_id2 = fGraphManager->GetPort(ports[i]); JackPort* port2 = (port_id2 != NO_PORT) ? fGraphManager->GetPort(port_id2) : NULL; if (port2) { int res = port2->GetAliases(aliases); string name_str; if (res >= alias) { name_str = string(aliases[alias-1]); } else { name_str = string(ports[i]); } string sub_name = RemoveLast(name); if ((name_str.find(sub_name) != string::npos) && (type == string(port2->GetType()))) { return name_str; } } } return ""; } void JackDriver::LoadConnections(int alias, bool full_name) { list > >::const_iterator it; if (full_name) { for (it = fConnections.begin(); it != fConnections.end(); it++) { pair connection = (*it).second; jack_info("Load connection: %s %s", connection.first.c_str(), connection.second.c_str()); fEngine->PortConnect(fClientControl.fRefNum, connection.first.c_str(), connection.second.c_str()); } } else { const char** inputs = fGraphManager->GetPorts(NULL, NULL, JackPortIsInput); const char** outputs = fGraphManager->GetPorts(NULL, NULL, JackPortIsOutput); for (it = fConnections.begin(); it != fConnections.end(); it++) { pair connection = (*it).second; string real_input = MatchPortName(connection.first.c_str(), outputs, alias, (*it).first); string real_output = MatchPortName(connection.second.c_str(), inputs, alias, (*it).first); if ((real_input != "") && (real_output != "")) { jack_info("Load connection: %s %s", real_input.c_str(), real_output.c_str()); fEngine->PortConnect(fClientControl.fRefNum, real_input.c_str(), real_output.c_str()); } } // Wait for connection change if (fGraphManager->IsPendingChange()) { JackSleep(int(fEngineControl->fPeriodUsecs * 1.1f)); } if (inputs) { free(inputs); } if (outputs) { free(outputs); } } } int JackDriver::ResumeRefNum() { return fGraphManager->ResumeRefNum(&fClientControl, fSynchroTable); } int JackDriver::SuspendRefNum() { return fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, DRIVER_TIMEOUT_FACTOR * fEngineControl->fTimeOutUsecs); } } // end of namespace jack2-1.9.22/common/JackDriver.h000066400000000000000000000204241436671425200163000ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackDriver__ #define __JackDriver__ #include "types.h" #include "JackClientInterface.h" #include "JackConstants.h" #include "JackPlatformPlug.h" #include "JackClientControl.h" #include namespace Jack { class JackLockedEngine; class JackGraphManager; struct JackEngineControl; class JackSlaveDriverInterface; /*! \brief The base interface for drivers. */ class SERVER_EXPORT JackDriverInterface { public: JackDriverInterface() {} virtual ~JackDriverInterface() {} virtual int Open() = 0; virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency) = 0; virtual int Attach() = 0; virtual int Detach() = 0; virtual int Read() = 0; virtual int Write() = 0; virtual int Start() = 0; virtual int Stop() = 0; virtual bool IsFixedBufferSize() = 0; virtual int SetBufferSize(jack_nframes_t buffer_size) = 0; virtual int SetSampleRate(jack_nframes_t sample_rate) = 0; virtual int Process() = 0; virtual void SetMaster(bool onoff) = 0; virtual bool GetMaster() = 0; virtual void AddSlave(JackDriverInterface* slave) = 0; virtual void RemoveSlave(JackDriverInterface* slave) = 0; virtual std::list GetSlaves() = 0; // For "master" driver virtual int ProcessReadSlaves() = 0; virtual int ProcessWriteSlaves() = 0; // For "slave" driver virtual int ProcessRead() = 0; virtual int ProcessWrite() = 0; // For "slave" driver in "synchronous" mode virtual int ProcessReadSync() = 0; virtual int ProcessWriteSync() = 0; // For "slave" driver in "asynchronous" mode virtual int ProcessReadAsync() = 0; virtual int ProcessWriteAsync() = 0; virtual bool IsRealTime() const = 0; virtual bool IsRunning() const = 0; }; /*! \brief The base interface for drivers clients. */ class SERVER_EXPORT JackDriverClientInterface : public JackDriverInterface, public JackClientInterface {}; /*! \brief The base class for drivers. */ #define CaptureDriverFlags static_cast(JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal) #define PlaybackDriverFlags static_cast(JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal) #define MonitorDriverFlags static_cast(JackPortIsOutput) typedef std::list > > driver_connections_list_t; // [type : (src, dst)] class SERVER_EXPORT JackDriver : public JackDriverClientInterface { protected: char fCaptureDriverName[JACK_CLIENT_NAME_SIZE+1]; char fPlaybackDriverName[JACK_CLIENT_NAME_SIZE+1]; char fAliasName[JACK_CLIENT_NAME_SIZE+1]; jack_nframes_t fCaptureLatency; jack_nframes_t fPlaybackLatency; int fCaptureChannels; int fPlaybackChannels; jack_time_t fBeginDateUst; jack_time_t fEndDateUst; float fDelayedUsecs; // Pointers to engine state JackLockedEngine* fEngine; JackGraphManager* fGraphManager; JackSynchro* fSynchroTable; JackEngineControl* fEngineControl; JackClientControl fClientControl; std::list fSlaveList; bool fIsMaster; bool fIsRunning; bool fWithMonitorPorts; // Static tables since the actual number of ports may be changed by the real driver // thus dynamic allocation is more difficult to handle jack_port_id_t fCapturePortList[DRIVER_PORT_NUM]; jack_port_id_t fPlaybackPortList[DRIVER_PORT_NUM]; jack_port_id_t fMonitorPortList[DRIVER_PORT_NUM]; driver_connections_list_t fConnections; // Connections list void CycleIncTime(); void CycleTakeBeginTime(); void CycleTakeEndTime(); void SetupDriverSync(int ref, bool freewheel); void NotifyXRun(jack_time_t callback_usecs, float delayed_usecs); // XRun notification sent by the driver void NotifyBufferSize(jack_nframes_t buffer_size); // BufferSize notification sent by the driver void NotifySampleRate(jack_nframes_t sample_rate); // SampleRate notification sent by the driver void NotifyFailure(int code, const char* reason); // Failure notification sent by the driver virtual void SaveConnections(int alias); virtual void LoadConnections(int alias, bool full_name = true); std::string MatchPortName(const char* name, const char** ports, int alias, const std::string& type); virtual int StartSlaves(); virtual int StopSlaves(); virtual int ResumeRefNum(); virtual int SuspendRefNum(); public: JackDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); virtual ~JackDriver(); void SetMaster(bool onoff); bool GetMaster(); void AddSlave(JackDriverInterface* slave); void RemoveSlave(JackDriverInterface* slave); std::list GetSlaves() { return fSlaveList; } virtual int Open(); virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); virtual int Close(); virtual int Process(); virtual int Attach(); virtual int Detach(); virtual int Read(); virtual int Write(); virtual int Start(); virtual int Stop(); // For "master" driver int ProcessReadSlaves(); int ProcessWriteSlaves(); // For "slave" driver with typically decompose a given cycle in separated Read and Write parts. virtual int ProcessRead(); virtual int ProcessWrite(); // For "slave" driver in "synchronous" mode virtual int ProcessReadSync(); virtual int ProcessWriteSync(); // For "slave" driver in "asynchronous" mode virtual int ProcessReadAsync(); virtual int ProcessWriteAsync(); virtual bool IsFixedBufferSize(); virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetSampleRate(jack_nframes_t sample_rate); virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); virtual JackClientControl* GetClientControl() const; virtual bool IsRealTime() const; virtual bool IsRunning() const { return fIsRunning; } virtual bool Initialize(); // To be called by the wrapping thread Init method when the driver is a "blocking" one }; } // end of namespace #endif jack2-1.9.22/common/JackDriverInfo.h000066400000000000000000000030161436671425200171120ustar00rootroot00000000000000/* Copyright (C) 2001-2005 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackDriverInfo__ #define __JackDriverInfo__ #include "driver_interface.h" #include "JackDriver.h" #include "JackSystemDeps.h" typedef Jack::JackDriverClientInterface* (*driverInitialize) (Jack::JackLockedEngine*, Jack::JackSynchro*, const JSList*); class SERVER_EXPORT JackDriverInfo { private: driverInitialize fInitialize; DRIVER_HANDLE fHandle; Jack::JackDriverClientInterface* fBackend; public: JackDriverInfo():fInitialize(NULL),fHandle(NULL),fBackend(NULL) {} ~JackDriverInfo(); Jack::JackDriverClientInterface* Open(jack_driver_desc_t* driver_desc, Jack::JackLockedEngine*, Jack::JackSynchro*, const JSList*); Jack::JackDriverClientInterface* GetBackend() { return fBackend; } }; #endif jack2-1.9.22/common/JackDriverLoader.cpp000066400000000000000000000766351436671425200200010ustar00rootroot00000000000000/* Copyright (C) 2001-2005 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackDriverLoader.h" #include "JackDriverInfo.h" #include "JackConstants.h" #include "JackError.h" #include #include #include #include #ifndef WIN32 #include #endif #ifdef WIN32 typedef wchar_t file_char_t; #else typedef char file_char_t; #endif #ifdef WIN32 static wchar_t* locate_dll_driver_dir() { HMODULE libjack_handle = NULL; GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(locate_dll_driver_dir), &libjack_handle); // For WIN32 ADDON_DIR is defined in JackConstants.h as relative path wchar_t driver_dir_storage[512]; if (3 < GetModuleFileNameW(libjack_handle, driver_dir_storage, 512)) { wchar_t *p = wcsrchr(driver_dir_storage, L'\\'); if (p && (p != driver_dir_storage)) { *p = 0; } jack_info("Drivers/internals found in : %S", driver_dir_storage); wcscat(driver_dir_storage, L"\\"); wcscat(driver_dir_storage, ADDON_DIRW); return wcsdup(driver_dir_storage); } else { jack_error("Cannot get JACK dll directory : %d", GetLastError()); return NULL; } } static wchar_t* locate_driver_dir(HANDLE& file, WIN32_FIND_DATAW& filedata) { // Search drivers/internals iin the same folder of "libjackserver.dll" wchar_t* driver_dir = locate_dll_driver_dir(); wchar_t dll_filename[512]; swprintf(dll_filename, 510, L"%S/*.dll", driver_dir); file = (HANDLE)FindFirstFileW(dll_filename, &filedata); if (file == INVALID_HANDLE_VALUE) { jack_error("Drivers not found in \"%S\": \"%S\"", driver_dir, dll_filename); free(driver_dir); return NULL; } else { return driver_dir; } } #endif jack_driver_desc_t* jackctl_driver_get_desc(jackctl_driver_t * driver); void jack_print_driver_options(jack_driver_desc_t* desc, FILE* file) { unsigned long i; char arg_default[JACK_DRIVER_PARAM_STRING_MAX + 1]; for (i = 0; i < desc->nparams; i++) { switch (desc->params[i].type) { case JackDriverParamInt: sprintf (arg_default, "%" "i", desc->params[i].value.i); break; case JackDriverParamUInt: sprintf (arg_default, "%" "u", desc->params[i].value.ui); break; case JackDriverParamChar: sprintf (arg_default, "%c", desc->params[i].value.c); break; case JackDriverParamString: if (desc->params[i].value.str && strcmp (desc->params[i].value.str, "") != 0) { sprintf (arg_default, "%s", desc->params[i].value.str); } else { sprintf (arg_default, "none"); } break; case JackDriverParamBool: sprintf (arg_default, "%s", desc->params[i].value.i ? "true" : "false"); break; } fprintf(file, "\t-%c, --%s \t%s (default: %s)\n", desc->params[i].character, desc->params[i].name, desc->params[i].long_desc, arg_default); } } static void jack_print_driver_param_usage (jack_driver_desc_t* desc, unsigned long param, FILE *file) { fprintf (file, "Usage information for the '%s' parameter for driver '%s':\n", desc->params[param].name, desc->name); fprintf (file, "%s\n", desc->params[param].long_desc); } void jack_free_driver_params(JSList * driver_params) { JSList*node_ptr = driver_params; JSList*next_node_ptr; while (node_ptr) { next_node_ptr = node_ptr->next; free(node_ptr->data); free(node_ptr); node_ptr = next_node_ptr; } } int jack_parse_driver_params(jack_driver_desc_t* desc, int argc, char* argv[], JSList** param_ptr) { struct option * long_options; char* options, * options_ptr; unsigned long i; int opt; unsigned int param_index; JSList* params = NULL; jack_driver_param_t * driver_param; if (argc <= 1) { *param_ptr = NULL; return 0; } /* check for help */ if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) { if (argc > 2) { for (i = 0; i < desc->nparams; i++) { if (strcmp (desc->params[i].name, argv[2]) == 0) { jack_print_driver_param_usage (desc, i, stdout); return 1; } } fprintf (stderr, "Jackd: unknown option '%s' " "for driver '%s'\n", argv[2], desc->name); } jack_log("Parameters for driver '%s' (all parameters are optional):", desc->name); jack_print_driver_options (desc, stdout); return 1; } /* set up the stuff for getopt */ options = (char*)calloc (desc->nparams * 3 + 1, sizeof (char)); long_options = (option*)calloc (desc->nparams + 1, sizeof (struct option)); options_ptr = options; for (i = 0; i < desc->nparams; i++) { sprintf (options_ptr, "%c::", desc->params[i].character); options_ptr += 3; long_options[i].name = desc->params[i].name; long_options[i].flag = NULL; long_options[i].val = desc->params[i].character; long_options[i].has_arg = optional_argument; } /* create the params */ optind = 0; opterr = 0; while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) { if (opt == ':' || opt == '?') { if (opt == ':') { fprintf (stderr, "Missing option to argument '%c'\n", optopt); } else { fprintf (stderr, "Unknownage with option '%c'\n", optopt); } fprintf (stderr, "Options for driver '%s':\n", desc->name); jack_print_driver_options (desc, stderr); return 1; } for (param_index = 0; param_index < desc->nparams; param_index++) { if (opt == desc->params[param_index].character) { break; } } driver_param = (jack_driver_param_t*)calloc (1, sizeof (jack_driver_param_t)); driver_param->character = desc->params[param_index].character; if (!optarg && optind < argc && strlen(argv[optind]) && argv[optind][0] != '-') { optarg = argv[optind]; } if (optarg) { switch (desc->params[param_index].type) { case JackDriverParamInt: driver_param->value.i = atoi(optarg); break; case JackDriverParamUInt: driver_param->value.ui = strtoul(optarg, NULL, 10); break; case JackDriverParamChar: driver_param->value.c = optarg[0]; break; case JackDriverParamString: strncpy (driver_param->value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX); break; case JackDriverParamBool: if (strcasecmp("false", optarg) == 0 || strcasecmp("off", optarg) == 0 || strcasecmp("no", optarg) == 0 || strcasecmp("0", optarg) == 0 || strcasecmp("(null)", optarg) == 0 ) { driver_param->value.i = false; } else { driver_param->value.i = true; } break; } } else { if (desc->params[param_index].type == JackDriverParamBool) { driver_param->value.i = true; } else { driver_param->value = desc->params[param_index].value; } } params = jack_slist_append (params, driver_param); } free (options); free (long_options); if (param_ptr) { *param_ptr = params; } return 0; } SERVER_EXPORT int jackctl_driver_params_parse(jackctl_driver *driver_ptr, int argc, char* argv[]) { struct option* long_options; char* options, * options_ptr; unsigned long i; int opt; JSList* node_ptr; jackctl_parameter_t * param = NULL; union jackctl_parameter_value value; if (argc <= 1) { return 0; } const JSList* driver_params = jackctl_driver_get_parameters(driver_ptr); if (driver_params == NULL) { return 1; } jack_driver_desc_t* desc = jackctl_driver_get_desc(driver_ptr); /* check for help */ if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) { if (argc > 2) { for (i = 0; i < desc->nparams; i++) { if (strcmp (desc->params[i].name, argv[2]) == 0) { jack_print_driver_param_usage (desc, i, stdout); return 1; } } fprintf (stderr, "Jackd: unknown option '%s' " "for driver '%s'\n", argv[2], desc->name); } jack_log("Parameters for driver '%s' (all parameters are optional):", desc->name); jack_print_driver_options (desc, stdout); return 1; } /* set up the stuff for getopt */ options = (char*)calloc (desc->nparams * 3 + 1, sizeof (char)); long_options = (option*)calloc (desc->nparams + 1, sizeof (struct option)); options_ptr = options; for (i = 0; i < desc->nparams; i++) { sprintf(options_ptr, "%c::", desc->params[i].character); options_ptr += 3; long_options[i].name = desc->params[i].name; long_options[i].flag = NULL; long_options[i].val = desc->params[i].character; long_options[i].has_arg = optional_argument; } /* create the params */ optind = 0; opterr = 0; while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) { if (opt == ':' || opt == '?') { if (opt == ':') { fprintf (stderr, "Missing option to argument '%c'\n", optopt); } else { fprintf (stderr, "Unknownage with option '%c'\n", optopt); } fprintf (stderr, "Options for driver '%s':\n", desc->name); jack_print_driver_options(desc, stderr); return 1; } node_ptr = (JSList *)driver_params; while (node_ptr) { param = (jackctl_parameter_t*)node_ptr->data; if (opt == jackctl_parameter_get_id(param)) { break; } node_ptr = node_ptr->next; } if (!optarg && optind < argc && strlen(argv[optind]) && argv[optind][0] != '-') { optarg = argv[optind]; } if (optarg) { switch (jackctl_parameter_get_type(param)) { case JackDriverParamInt: value.i = atoi(optarg); jackctl_parameter_set_value(param, &value); break; case JackDriverParamUInt: value.ui = strtoul(optarg, NULL, 10); jackctl_parameter_set_value(param, &value); break; case JackDriverParamChar: value.c = optarg[0]; jackctl_parameter_set_value(param, &value); break; case JackDriverParamString: strncpy(value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX); jackctl_parameter_set_value(param, &value); break; case JackDriverParamBool: if (strcasecmp("false", optarg) == 0 || strcasecmp("off", optarg) == 0 || strcasecmp("no", optarg) == 0 || strcasecmp("0", optarg) == 0 || strcasecmp("(null)", optarg) == 0 ) { value.i = false; } else { value.i = true; } jackctl_parameter_set_value(param, &value); break; } } else { if (jackctl_parameter_get_type(param) == JackParamBool) { value.i = true; } else { value = jackctl_parameter_get_default_value(param); } jackctl_parameter_set_value(param, &value); } } free(options); free(long_options); return 0; } jack_driver_desc_t* jack_find_driver_descriptor (JSList * drivers, const char* name) { jack_driver_desc_t* desc = 0; JSList* node; for (node = drivers; node; node = jack_slist_next (node)) { desc = (jack_driver_desc_t*) node->data; if (strcmp (desc->name, name) != 0) { desc = NULL; } else { break; } } return desc; } static void* check_symbol(const file_char_t* sofile, const char* symbol, const file_char_t* driver_dir, void** res_dllhandle = NULL) { void* dlhandle; void* res = NULL; file_char_t filename[1024]; #ifdef WIN32 swprintf(filename, 1022, L"%S/%S", driver_dir, sofile); #else snprintf(filename, 1022, "%s/%s", driver_dir, sofile); #endif if ((dlhandle = LoadDriverModule(filename)) == NULL) { #ifdef WIN32 jack_error ("Could not open component .dll '%S': %ld", filename, GetLastError()); #else jack_error ("Could not open component .so '%s': %s", filename, dlerror()); #endif } else { res = (void*)GetDriverProc(dlhandle, symbol); if (res_dllhandle) { *res_dllhandle = dlhandle; } else { UnloadDriverModule(dlhandle); } } return res; } static jack_driver_desc_t* jack_get_descriptor (JSList* drivers, const file_char_t* sofile, const char* symbol, const file_char_t* driver_dir) { jack_driver_desc_t* descriptor = NULL; jack_driver_desc_t* other_descriptor; JackDriverDescFunction so_get_descriptor = NULL; file_char_t filename[1024]; JSList* node; void* dlhandle = NULL; #ifdef WIN32 swprintf(filename, 1022, L"%S/%S", driver_dir, sofile); #else snprintf(filename, 1022, "%s/%s", driver_dir, sofile); #endif so_get_descriptor = (JackDriverDescFunction)check_symbol(sofile, symbol, driver_dir, &dlhandle); if (so_get_descriptor == NULL) { jack_error("jack_get_descriptor : dll %S is not a driver", sofile); goto error; } if ((descriptor = so_get_descriptor ()) == NULL) { jack_error("Driver from '%S' returned NULL descriptor", filename); goto error; } /* check it doesn't exist already */ for (node = drivers; node; node = jack_slist_next (node)) { other_descriptor = (jack_driver_desc_t*) node->data; if (strcmp(descriptor->name, other_descriptor->name) == 0) { jack_error("The drivers in '%S' and '%S' both have the name '%S'; using the first", other_descriptor->file, filename, other_descriptor->name); /* FIXME: delete the descriptor */ goto error; } } #ifdef WIN32 wcsncpy(descriptor->file, filename, JACK_PATH_MAX); #else strncpy(descriptor->file, filename, JACK_PATH_MAX); #endif error: if (dlhandle) { UnloadDriverModule(dlhandle); } return descriptor; } #ifdef WIN32 JSList * jack_drivers_load(JSList * drivers) { //char dll_filename[512]; WIN32_FIND_DATAW filedata; HANDLE file; const wchar_t* ptr = NULL; JSList* driver_list = NULL; jack_driver_desc_t* desc = NULL; wchar_t* driver_dir = locate_driver_dir(file, filedata); if (!driver_dir) { jack_error("Driver folder not found"); goto error; } do { /* check the filename is of the right format */ if (wcsncmp (L"jack_", filedata.cFileName, 5) != 0) { continue; } ptr = wcsrchr (filedata.cFileName, L'.'); if (!ptr) { continue; } ptr++; if (wcsncmp (L"dll", ptr, 3) != 0) { continue; } /* check if dll is an internal client */ if (check_symbol(filedata.cFileName, "jack_internal_initialize", driver_dir) != NULL) { continue; } desc = jack_get_descriptor (drivers, filedata.cFileName, "driver_get_descriptor", driver_dir); if (desc) { driver_list = jack_slist_append (driver_list, desc); } else { jack_error ("jack_get_descriptor returns null for \'%S\'", filedata.cFileName); } } while (FindNextFileW(file, &filedata)); if (!driver_list) { jack_error ("Could not find any drivers in %S!", driver_dir); } error: if (driver_dir) { free(driver_dir); } FindClose(file); return driver_list; } #else JSList* jack_drivers_load (JSList * drivers) { struct dirent * dir_entry; DIR * dir_stream; const char* ptr; int err; JSList* driver_list = NULL; jack_driver_desc_t* desc = NULL; const char* driver_dir; if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { driver_dir = ADDON_DIR; } /* search through the driver_dir and add get descriptors from the .so files in it */ dir_stream = opendir (driver_dir); if (!dir_stream) { jack_error ("Could not open driver directory %s: %s", driver_dir, strerror (errno)); return NULL; } while ((dir_entry = readdir(dir_stream))) { /* check the filename is of the right format */ if (strncmp ("jack_", dir_entry->d_name, 5) != 0) { continue; } ptr = strrchr (dir_entry->d_name, '.'); if (!ptr) { continue; } ptr++; if (strncmp ("so", ptr, 2) != 0) { continue; } /* check if dll is an internal client */ if (check_symbol(dir_entry->d_name, "jack_internal_initialize", driver_dir) != NULL) { continue; } desc = jack_get_descriptor (drivers, dir_entry->d_name, "driver_get_descriptor", driver_dir); if (desc) { driver_list = jack_slist_append (driver_list, desc); } else { jack_error ("jack_get_descriptor returns null for \'%s\'", dir_entry->d_name); } } err = closedir (dir_stream); if (err) { jack_error ("Error closing driver directory %s: %s", driver_dir, strerror (errno)); } if (!driver_list) { jack_error ("Could not find any drivers in %s!", driver_dir); return NULL; } return driver_list; } #endif #ifdef WIN32 JSList* jack_internals_load(JSList * internals) { ///char dll_filename[512]; WIN32_FIND_DATAW filedata; HANDLE file; const wchar_t* ptr = NULL; JSList* driver_list = NULL; jack_driver_desc_t* desc; wchar_t* driver_dir = locate_driver_dir(file, filedata); if (!driver_dir) { jack_error("Driver folder not found"); goto error; } do { ptr = wcsrchr (filedata.cFileName, L'.'); if (!ptr) { continue; } ptr++; if (wcsncmp (L"dll", ptr, 3) != 0) { continue; } /* check if dll is an internal client */ if (check_symbol(filedata.cFileName, "jack_internal_initialize", driver_dir) == NULL) { continue; } desc = jack_get_descriptor (internals, filedata.cFileName, "jack_get_descriptor", driver_dir); if (desc) { driver_list = jack_slist_append (driver_list, desc); } else { jack_error ("jack_get_descriptor returns null for \'%s\'", filedata.cFileName); } } while (FindNextFileW(file, &filedata)); if (!driver_list) { jack_error ("Could not find any internals in %s!", driver_dir); } error: if (driver_dir) { free(driver_dir); } FindClose(file); return driver_list; } #else JSList* jack_internals_load(JSList * internals) { struct dirent * dir_entry; DIR * dir_stream; const char* ptr; int err; JSList* driver_list = NULL; jack_driver_desc_t* desc; const char* driver_dir; if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) { driver_dir = ADDON_DIR; } /* search through the driver_dir and add get descriptors from the .so files in it */ dir_stream = opendir (driver_dir); if (!dir_stream) { jack_error ("Could not open driver directory %s: %s\n", driver_dir, strerror (errno)); return NULL; } while ((dir_entry = readdir(dir_stream))) { ptr = strrchr (dir_entry->d_name, '.'); if (!ptr) { continue; } ptr++; if (strncmp ("so", ptr, 2) != 0) { continue; } /* check if dll is an internal client */ if (check_symbol(dir_entry->d_name, "jack_internal_initialize", driver_dir) == NULL) { continue; } desc = jack_get_descriptor (internals, dir_entry->d_name, "jack_get_descriptor", driver_dir); if (desc) { driver_list = jack_slist_append (driver_list, desc); } else { jack_error ("jack_get_descriptor returns null for \'%s\'", dir_entry->d_name); } } err = closedir (dir_stream); if (err) { jack_error ("Error closing internal directory %s: %s\n", driver_dir, strerror (errno)); } if (!driver_list) { jack_error ("Could not find any internals in %s!", driver_dir); return NULL; } return driver_list; } #endif Jack::JackDriverClientInterface* JackDriverInfo::Open(jack_driver_desc_t* driver_desc, Jack::JackLockedEngine* engine, Jack::JackSynchro* synchro, const JSList* params) { #ifdef WIN32 int errstr; #else const char* errstr; #endif fHandle = LoadDriverModule (driver_desc->file); if (fHandle == NULL) { #ifdef WIN32 if ((errstr = GetLastError ()) != 0) { jack_error ("Can't load \"%S\": %ld", driver_desc->file, errstr); #else if ((errstr = dlerror ()) != 0) { jack_error ("Can't load \"%s\": %s", driver_desc->file, errstr); #endif } else { #ifdef WIN32 jack_error ("Error loading driver shared object %S", driver_desc->file); #else jack_error ("Error loading driver shared object %s", driver_desc->file); #endif } return NULL; } //jack_error (" --------------------------------- Successfully opened driver \"%S\"\n", driver_desc->file); fInitialize = (driverInitialize)GetDriverProc(fHandle, "driver_initialize"); #ifdef WIN32 if ((fInitialize == NULL) && (errstr = GetLastError ()) != 0) { #else if ((fInitialize == NULL) && (errstr = dlerror ()) != 0) { #endif #ifdef WIN32 jack_error("No initialize function in shared object %S\n", driver_desc->file); #else jack_error("No initialize function in shared object %s\n", driver_desc->file); #endif return NULL; } fBackend = fInitialize(engine, synchro, params); return fBackend; } JackDriverInfo::~JackDriverInfo() { delete fBackend; if (fHandle) { UnloadDriverModule(fHandle); } } SERVER_EXPORT jack_driver_desc_t* jack_driver_descriptor_construct( const char * name, jack_driver_type_t type, const char * description, jack_driver_desc_filler_t * filler_ptr) { size_t name_len; size_t description_len; jack_driver_desc_t* desc_ptr; name_len = strlen(name); description_len = strlen(description); if (name_len > sizeof(desc_ptr->name) - 1 || description_len > sizeof(desc_ptr->desc) - 1) { assert(false); return 0; } desc_ptr = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); if (desc_ptr == NULL) { jack_error("Error calloc() failed to allocate memory for driver descriptor struct"); return 0; } memcpy(desc_ptr->name, name, name_len + 1); memcpy(desc_ptr->desc, description, description_len + 1); desc_ptr->nparams = 0; desc_ptr->type = type; if (filler_ptr != NULL) { filler_ptr->size = 0; } return desc_ptr; } SERVER_EXPORT int jack_driver_descriptor_add_parameter( jack_driver_desc_t* desc_ptr, jack_driver_desc_filler_t * filler_ptr, const char* name, char character, jack_driver_param_type_t type, const jack_driver_param_value_t * value_ptr, jack_driver_param_constraint_desc_t * constraint, const char* short_desc, const char* long_desc) { size_t name_len; size_t short_desc_len; size_t long_desc_len; jack_driver_param_desc_t * param_ptr; size_t newsize; name_len = strlen(name); short_desc_len = strlen(short_desc); if (long_desc != NULL) { long_desc_len = strlen(long_desc); } else { long_desc = short_desc; long_desc_len = short_desc_len; } if (name_len > sizeof(param_ptr->name) - 1 || short_desc_len > sizeof(param_ptr->short_desc) - 1 || long_desc_len > sizeof(param_ptr->long_desc) - 1) { assert(false); return 0; } if (desc_ptr->nparams == filler_ptr->size) { newsize = filler_ptr->size + 20; // most drivers have less than 20 parameters param_ptr = (jack_driver_param_desc_t*)realloc (desc_ptr->params, newsize * sizeof (jack_driver_param_desc_t)); if (param_ptr == NULL) { jack_error("Error realloc() failed for parameter array of %zu elements", newsize); return false; } filler_ptr->size = newsize; desc_ptr->params = param_ptr; } assert(desc_ptr->nparams < filler_ptr->size); param_ptr = desc_ptr->params + desc_ptr->nparams; memcpy(param_ptr->name, name, name_len + 1); param_ptr->character = character; param_ptr->type = type; param_ptr->value = *value_ptr; param_ptr->constraint = constraint; memcpy(param_ptr->short_desc, short_desc, short_desc_len + 1); memcpy(param_ptr->long_desc, long_desc, long_desc_len + 1); desc_ptr->nparams++; return true; } SERVER_EXPORT int jack_constraint_add_enum( jack_driver_param_constraint_desc_t ** constraint_ptr_ptr, uint32_t * array_size_ptr, jack_driver_param_value_t * value_ptr, const char * short_desc) { jack_driver_param_constraint_desc_t * constraint_ptr; uint32_t array_size; jack_driver_param_value_enum_t * possible_value_ptr; size_t len; len = strlen(short_desc) + 1; if (len > sizeof(possible_value_ptr->short_desc)) { assert(false); return false; } constraint_ptr = *constraint_ptr_ptr; if (constraint_ptr == NULL) { constraint_ptr = (jack_driver_param_constraint_desc_t *)calloc(1, sizeof(jack_driver_param_constraint_desc_t)); if (constraint_ptr == NULL) { jack_error("calloc() failed to allocate memory for param constraint struct"); return false; } array_size = 0; } else { array_size = *array_size_ptr; } if (constraint_ptr->constraint.enumeration.count == array_size) { array_size += 10; possible_value_ptr = (jack_driver_param_value_enum_t *)realloc( constraint_ptr->constraint.enumeration.possible_values_array, sizeof(jack_driver_param_value_enum_t) * array_size); if (possible_value_ptr == NULL) { jack_error("realloc() failed to (re)allocate memory for possible values array"); return false; } constraint_ptr->constraint.enumeration.possible_values_array = possible_value_ptr; } else { possible_value_ptr = constraint_ptr->constraint.enumeration.possible_values_array; } possible_value_ptr += constraint_ptr->constraint.enumeration.count; constraint_ptr->constraint.enumeration.count++; possible_value_ptr->value = *value_ptr; memcpy(possible_value_ptr->short_desc, short_desc, len); *constraint_ptr_ptr = constraint_ptr; *array_size_ptr = array_size; return true; } SERVER_EXPORT void jack_constraint_free( jack_driver_param_constraint_desc_t * constraint_ptr) { if (constraint_ptr != NULL) { if ((constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) == 0) { free(constraint_ptr->constraint.enumeration.possible_values_array); } free(constraint_ptr); } } #define JACK_CONSTRAINT_COMPOSE_ENUM_IMPL(type, copy) \ JACK_CONSTRAINT_COMPOSE_ENUM(type) \ { \ jack_driver_param_constraint_desc_t * constraint_ptr; \ uint32_t array_size; \ jack_driver_param_value_t value; \ struct jack_constraint_enum_ ## type ## _descriptor * descr_ptr; \ \ constraint_ptr = NULL; \ for (descr_ptr = descr_array_ptr; \ descr_ptr->value; \ descr_ptr++) \ { \ copy; \ if (!jack_constraint_add_enum( \ &constraint_ptr, \ &array_size, \ &value, \ descr_ptr->short_desc)) \ { \ jack_constraint_free(constraint_ptr); \ return NULL; \ } \ } \ \ constraint_ptr->flags = flags; \ \ return constraint_ptr; \ } JACK_CONSTRAINT_COMPOSE_ENUM_IMPL(uint32, value.c = descr_ptr->value); JACK_CONSTRAINT_COMPOSE_ENUM_IMPL(sint32, value.c = descr_ptr->value); JACK_CONSTRAINT_COMPOSE_ENUM_IMPL(char, value.c = descr_ptr->value); JACK_CONSTRAINT_COMPOSE_ENUM_IMPL(str, strcpy(value.str, descr_ptr->value)); jack2-1.9.22/common/JackDriverLoader.h000066400000000000000000000025401436671425200174260ustar00rootroot00000000000000/* Copyright (C) 2001-2005 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackDriverLoader__ #define __JackDriverLoader__ #include "driver_interface.h" #include "JackControlAPI.h" #include "JackPlatformPlug.h" jack_driver_desc_t* jack_find_driver_descriptor(JSList* drivers, const char* name); JSList* jack_drivers_load(JSList* drivers); JSList* jack_internals_load(JSList* internals); void jack_free_driver_params(JSList * param_ptr); void jack_print_driver_options(jack_driver_desc_t* desc, FILE* file); // External control.h API extern "C" SERVER_EXPORT int jackctl_driver_params_parse(jackctl_driver * driver, int argc, char* argv[]); #endif jack2-1.9.22/common/JackDummyDriver.cpp000066400000000000000000000102631436671425200176470ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackDummyDriver.h" #include "JackDriverLoader.h" #include "JackThreadedDriver.h" #include "JackCompilerDeps.h" #include #include #include #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("dummy", JackDriverMaster, "Timer based backend", &filler); value.ui = 2U; jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamUInt, &value, NULL, "Number of capture ports", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamUInt, &value, NULL, "Number of playback ports", NULL); value.ui = 48000U; jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); value.ui = 1024U; jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); value.ui = 21333U; jack_driver_descriptor_add_parameter(desc, &filler, "wait", 'w', JackDriverParamUInt, &value, NULL, "Number of usecs to wait between engine processes", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { jack_nframes_t sample_rate = 48000; jack_nframes_t buffer_size = 1024; unsigned int capture_ports = 2; unsigned int playback_ports = 2; int wait_time = 0; const JSList * node; const jack_driver_param_t * param; bool monitor = false; for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; switch (param->character) { case 'C': capture_ports = param->value.ui; break; case 'P': playback_ports = param->value.ui; break; case 'r': sample_rate = param->value.ui; break; case 'p': buffer_size = param->value.ui; break; case 'w': wait_time = param->value.ui; break; case 'm': monitor = param->value.i; break; } } if (wait_time > 0) { buffer_size = lroundf((float(wait_time) * float(sample_rate)) / 1000000.0f); } if (buffer_size > BUFFER_SIZE_MAX) { buffer_size = BUFFER_SIZE_MAX; jack_error("Buffer size set to %d", BUFFER_SIZE_MAX); } Jack::JackDriverClientInterface* driver = new Jack::JackThreadedDriver(new Jack::JackDummyDriver("system", "dummy_pcm", engine, table)); if (driver->Open(buffer_size, sample_rate, 1, 1, capture_ports, playback_ports, monitor, "dummy", "dummy", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } #ifdef __cplusplus } #endif jack2-1.9.22/common/JackDummyDriver.h000066400000000000000000000026621436671425200173200ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackDummyDriver__ #define __JackDummyDriver__ #include "JackTimedDriver.h" namespace Jack { /*! \brief The dummy driver. */ class JackDummyDriver : public JackTimedDriver { public: JackDummyDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackTimedDriver(name, alias, engine, table) {} virtual ~JackDummyDriver() {} virtual int Process() { JackDriver::CycleTakeBeginTime(); if (JackAudioDriver::Process() < 0) { return -1; } else { ProcessWait(); return 0; } } }; } // end of namespace #endif jack2-1.9.22/common/JackEngine.cpp000066400000000000000000001175641436671425200166210ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "JackSystemDeps.h" #include "JackLockedEngine.h" #include "JackExternalClient.h" #include "JackInternalClient.h" #include "JackEngineControl.h" #include "JackClientControl.h" #include "JackServerGlobals.h" #include "JackGlobals.h" #include "JackChannel.h" #include "JackError.h" extern const char* JACK_METADATA_HARDWARE; extern const char* JACK_METADATA_PRETTY_NAME; namespace Jack { JackEngine::JackEngine(JackGraphManager* manager, JackSynchro* table, JackEngineControl* control, char self_connect_mode) : JackLockAble(control->fServerName), fSignal(control->fServerName), fMetadata(true) { fGraphManager = manager; fSynchroTable = table; fEngineControl = control; fSelfConnectMode = self_connect_mode; for (int i = 0; i < CLIENT_NUM; i++) { fClientTable[i] = NULL; } fLastSwitchUsecs = 0; fSessionPendingReplies = 0; fSessionTransaction = NULL; fSessionResult = NULL; } JackEngine::~JackEngine() {} int JackEngine::Open() { jack_log("JackEngine::Open"); // Open audio thread => request thread communication channel if (fChannel.Open(fEngineControl->fServerName) < 0) { jack_error("Cannot connect to server"); return -1; } else { return 0; } } int JackEngine::Close() { jack_log("JackEngine::Close"); fChannel.Close(); // Close remaining clients (RT is stopped) for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) { if (JackLoadableInternalClient* loadable_client = dynamic_cast(fClientTable[i])) { jack_log("JackEngine::Close loadable client = %s", loadable_client->GetClientControl()->fName); loadable_client->Close(); fClientTable[i] = NULL; delete loadable_client; } else if (JackExternalClient* external_client = dynamic_cast(fClientTable[i])) { jack_log("JackEngine::Close external client = %s", external_client->GetClientControl()->fName); external_client->Close(); fClientTable[i] = NULL; delete external_client; } } return 0; } void JackEngine::NotifyQuit() { fChannel.NotifyQuit(); } //----------------------------- // Client resource management //----------------------------- int JackEngine::AllocateRefnum() { for (int i = 0; i < CLIENT_NUM; i++) { if (!fClientTable[i]) { jack_log("JackEngine::AllocateRefNum ref = %ld", i); return i; } } return -1; } void JackEngine::ReleaseRefnum(int refnum) { fClientTable[refnum] = NULL; if (fEngineControl->fTemporary) { int i; for (i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) { if (fClientTable[i]) { break; } } if (i == CLIENT_NUM) { // Last client and temporary case: quit the server jack_log("JackEngine::ReleaseRefnum server quit"); fEngineControl->fTemporary = false; throw JackTemporaryException(); } } } //------------------ // Graph management //------------------ void JackEngine::ProcessNext(jack_time_t cur_cycle_begin) { fLastSwitchUsecs = cur_cycle_begin; if (fGraphManager->RunNextGraph()) { // True if the graph actually switched to a new state fChannel.Notify(ALL_CLIENTS, kGraphOrderCallback, 0); } fSignal.Signal(); // Signal for threads waiting for next cycle } void JackEngine::ProcessCurrent(jack_time_t cur_cycle_begin) { if (cur_cycle_begin < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) { // Signal XRun only for the first failing cycle CheckXRun(cur_cycle_begin); } fGraphManager->RunCurrentGraph(); } bool JackEngine::Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end) { bool res = true; // Cycle begin fEngineControl->CycleBegin(fClientTable, fGraphManager, cur_cycle_begin, prev_cycle_end); // Graph if (fGraphManager->IsFinishedGraph()) { ProcessNext(cur_cycle_begin); res = true; } else { jack_log("Process: graph not finished!"); if (cur_cycle_begin > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) { jack_log("Process: switch to next state delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs)); ProcessNext(cur_cycle_begin); res = true; } else { jack_log("Process: waiting to switch delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs)); ProcessCurrent(cur_cycle_begin); res = false; } } // Cycle end fEngineControl->CycleEnd(fClientTable); return res; } /* Client that finish *after* the callback date are considered late even if their output buffers may have been correctly mixed in the time window: callbackUsecs <==> Read <==> Write. */ static const char* State2String(jack_client_state_t state) { switch (state) { case NotTriggered: return "NotTriggered"; case Triggered: return "Triggered"; case Running: return "Running"; case Finished: return "Finished"; default: return ""; } } void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions de fin { for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && client->GetClientControl()->fActive) { JackClientTiming* timing = fGraphManager->GetClientTiming(i); jack_client_state_t status = timing->fStatus; jack_time_t finished_date = timing->fFinishedAt; if (status != NotTriggered && status != Finished) { jack_error("JackEngine::XRun: client = %s was not finished, state = %s", client->GetClientControl()->fName, State2String(status)); fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients } if (status == Finished && (long)(finished_date - callback_usecs) > 0) { jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName); fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients } } } } int JackEngine::ComputeTotalLatencies() { std::vector sorted; std::vector::iterator it; std::vector::reverse_iterator rit; fGraphManager->TopologicalSort(sorted); /* iterate over all clients in graph order, and emit * capture latency callback. */ for (it = sorted.begin(); it != sorted.end(); it++) { NotifyClient(*it, kLatencyCallback, true, "", 0, 0); } /* now issue playback latency callbacks in reverse graph order. */ for (rit = sorted.rbegin(); rit != sorted.rend(); rit++) { NotifyClient(*rit, kLatencyCallback, true, "", 1, 0); } return 0; } //-------------- // Metadata API //-------------- int JackEngine::PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change) { jack_log("JackEngine::PropertyChangeNotify: subject = %x key = %s change = %x", subject, key, change); for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client) { char buf[JACK_UUID_STRING_SIZE]; jack_uuid_unparse(subject, buf); client->ClientNotify(i, buf, kPropertyChangeCallback, false, key, change, 0); } } return 0; } //--------------- // Notifications //--------------- int JackEngine::ClientNotify(JackClientInterface* client, int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { // Check if notification is needed if (!client->GetClientControl()->fCallback[notify]) { jack_log("JackEngine::ClientNotify: no callback for notification = %ld", notify); return 0; } int res1; // External client if (dynamic_cast(client)) { res1 = client->ClientNotify(refnum, name, notify, sync, message, value1, value2); // Important for internal client : unlock before calling the notification callbacks } else { bool res2 = Unlock(); res1 = client->ClientNotify(refnum, name, notify, sync, message, value1, value2); if (res2) { Lock(); } } if (res1 < 0) { jack_error("ClientNotify fails name = %s notification = %ld val1 = %ld val2 = %ld", name, notify, value1, value2); } return res1; } void JackEngine::NotifyClient(int refnum, int event, int sync, const char* message, int value1, int value2) { JackClientInterface* client = fClientTable[refnum]; if (client) { ClientNotify(client, refnum, client->GetClientControl()->fName, event, sync, message, value1, value2); } } void JackEngine::NotifyClients(int event, int sync, const char* message, int value1, int value2) { for (int i = 0; i < CLIENT_NUM; i++) { NotifyClient(i, event, sync, message, value1, value2); } } int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* new_name, int refnum) { jack_log("JackEngine::NotifyAddClient: name = %s", new_name); // Notify existing clients of the new client and new client of existing clients. for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* old_client = fClientTable[i]; if (old_client && old_client != new_client) { char* old_name = old_client->GetClientControl()->fName; if (ClientNotify(old_client, refnum, new_name, kAddClient, false, "", 0, 0) < 0) { jack_error("NotifyAddClient old_client fails name = %s", old_name); // Not considered as a failure... } if (ClientNotify(new_client, i, old_name, kAddClient, true, "", 0, 0) < 0) { jack_error("NotifyAddClient new_client fails name = %s", new_name); return -1; } } } return 0; } void JackEngine::NotifyRemoveClient(const char* name, int refnum) { // Notify existing clients (including the one being suppressed) of the removed client for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client) { ClientNotify(client, refnum, name, kRemoveClient, false, "", 0, 0); } } } // Coming from the driver void JackEngine::NotifyDriverXRun() { // Use the audio thread => request thread communication channel fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); } void JackEngine::NotifyClientXRun(int refnum) { if (refnum == ALL_CLIENTS) { NotifyClients(kXRunCallback, false, "", 0, 0); } else { NotifyClient(refnum, kXRunCallback, false, "", 0, 0); } } void JackEngine::NotifyGraphReorder() { ComputeTotalLatencies(); NotifyClients(kGraphOrderCallback, false, "", 0, 0); } void JackEngine::NotifyBufferSize(jack_nframes_t buffer_size) { NotifyClients(kBufferSizeCallback, true, "", buffer_size, 0); } void JackEngine::NotifySampleRate(jack_nframes_t sample_rate) { NotifyClients(kSampleRateCallback, true, "", sample_rate, 0); } void JackEngine::NotifyFailure(int code, const char* reason) { NotifyClients(kShutDownCallback, false, reason, code, 0); } void JackEngine::NotifyFreewheel(bool onoff) { if (onoff) { // Save RT state fEngineControl->fSavedRealTime = fEngineControl->fRealTime; fEngineControl->fRealTime = false; } else { // Restore RT state fEngineControl->fRealTime = fEngineControl->fSavedRealTime; fEngineControl->fSavedRealTime = false; } NotifyClients((onoff ? kStartFreewheelCallback : kStopFreewheelCallback), true, "", 0, 0); } void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff) { NotifyClients((onoff ? kPortRegistrationOnCallback : kPortRegistrationOffCallback), false, "", port_index, 0); } void JackEngine::NotifyPortRename(jack_port_id_t port, const char* old_name) { NotifyClients(kPortRenameCallback, false, old_name, port, 0); } void JackEngine::NotifyPortConnect(jack_port_id_t src, jack_port_id_t dst, bool onoff) { NotifyClients((onoff ? kPortConnectCallback : kPortDisconnectCallback), false, "", src, dst); } void JackEngine::NotifyActivate(int refnum) { NotifyClient(refnum, kActivateClient, true, "", 0, 0); } //---------------------------- // Loadable client management //---------------------------- int JackEngine::GetInternalClientName(int refnum, char* name_res) { JackClientInterface* client = fClientTable[refnum]; assert(client); strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE); return 0; } int JackEngine::InternalClientHandle(const char* client_name, int* status, int* int_ref) { // Clear status *status = 0; for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && dynamic_cast(client) && (strcmp(client->GetClientControl()->fName, client_name) == 0)) { jack_log("InternalClientHandle found client name = %s ref = %ld", client_name, i); *int_ref = i; return 0; } } *status |= (JackNoSuchClient | JackFailure); return -1; } int JackEngine::InternalClientUnload(int refnum, int* status) { JackClientInterface* client = fClientTable[refnum]; if (client) { int res = client->Close(); delete client; *status = 0; return res; } else { *status = (JackNoSuchClient | JackFailure); return -1; } } //------------------- // Client management //------------------- int JackEngine::ClientCheck(const char* name, jack_uuid_t uuid, char* name_res, int protocol, int options, int* status) { // Clear status *status = 0; strcpy(name_res, name); jack_log("Check protocol client = %ld server = %ld", protocol, JACK_PROTOCOL_VERSION); if (protocol != JACK_PROTOCOL_VERSION) { *status |= (JackFailure | JackVersionError); jack_error("JACK protocol mismatch (%d vs %d)", protocol, JACK_PROTOCOL_VERSION); return -1; } std::map::iterator res = fReservationMap.find(uuid); if (res != fReservationMap.end()) { strncpy(name_res, res->second.c_str(), JACK_CLIENT_NAME_SIZE); } else if (ClientCheckName(name)) { *status |= JackNameNotUnique; if (options & JackUseExactName) { jack_error("cannot create new client; %s already exists", name); *status |= JackFailure; return -1; } if (GenerateUniqueName(name_res)) { *status |= JackFailure; return -1; } } return 0; } bool JackEngine::GenerateUniqueName(char* name) { int tens, ones; int length = strlen(name); if (length > JACK_CLIENT_NAME_SIZE - 4) { jack_error("%s exists and is too long to make unique", name); return true; /* failure */ } /* generate a unique name by appending "-01".."-99" */ name[length++] = '-'; tens = length++; ones = length++; name[tens] = '0'; name[ones] = '1'; name[length] = '\0'; while (ClientCheckName(name)) { if (name[ones] == '9') { if (name[tens] == '9') { jack_error("client %s has 99 extra instances already", name); return true; /* give up */ } name[tens]++; name[ones] = '0'; } else { name[ones]++; } } return false; } bool JackEngine::ClientCheckName(const char* name) { for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && (strcmp(client->GetClientControl()->fName, name) == 0)) { return true; } } for (std::map::iterator i = fReservationMap.begin(); i != fReservationMap.end(); i++) { if (i->second == name) { return true; } } return false; } void JackEngine::EnsureUUID(jack_uuid_t uuid) { if (jack_uuid_empty(uuid)) return; for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && jack_uuid_compare(client->GetClientControl()->fSessionID, uuid) == 0) { // FIXME? this code does nothing, but jack1 has it like this too.. jack_uuid_clear (&uuid); // client->GetClientControl()->fSessionID = jack_client_uuid_generate(); } } } int JackEngine::GetClientPID(const char* name) { for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && (strcmp(client->GetClientControl()->fName, name) == 0)) { return client->GetClientControl()->fPID; } } return 0; } int JackEngine::GetClientRefNum(const char* name) { for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && (strcmp(client->GetClientControl()->fName, name) == 0)) { return client->GetClientControl()->fRefNum; } } return -1; } // Used for external clients int JackEngine::ClientExternalOpen(const char* name, int pid, jack_uuid_t uuid, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager) { char real_name[JACK_CLIENT_NAME_SIZE + 1]; if (jack_uuid_empty(uuid)) { uuid = jack_client_uuid_generate(); strncpy(real_name, name, JACK_CLIENT_NAME_SIZE); } else { std::map::iterator res = fReservationMap.find(uuid); if (res != fReservationMap.end()) { strncpy(real_name, res->second.c_str(), JACK_CLIENT_NAME_SIZE); fReservationMap.erase(uuid); } else { strncpy(real_name, name, JACK_CLIENT_NAME_SIZE); } EnsureUUID(uuid); } jack_log("JackEngine::ClientExternalOpen: uuid = %d, name = %s", uuid, real_name); int refnum = AllocateRefnum(); if (refnum < 0) { jack_error("No more refnum available"); return -1; } JackExternalClient* client = new JackExternalClient(); if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0)) { jack_error("Cannot allocate synchro"); goto error; } if (client->Open(real_name, pid, refnum, uuid, shared_client) < 0) { jack_error("Cannot open client"); goto error; } if (!fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) { // Failure if RT thread is not running (problem with the driver...) jack_error("Driver is not running"); goto error; } fClientTable[refnum] = client; if (NotifyAddClient(client, real_name, refnum) < 0) { jack_error("Cannot notify add client"); goto error; } fGraphManager->InitRefNum(refnum); fEngineControl->ResetRollingUsecs(); *shared_engine = fEngineControl->GetShmIndex(); *shared_graph_manager = fGraphManager->GetShmIndex(); *ref = refnum; return 0; error: // Cleanup... fSynchroTable[refnum].Destroy(); fClientTable[refnum] = 0; client->Close(); delete client; return -1; } // Used for server driver clients int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait) { jack_log("JackEngine::ClientInternalOpen: name = %s", name); int refnum = AllocateRefnum(); if (refnum < 0) { jack_error("No more refnum available"); goto error; } if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) { jack_error("Cannot allocate synchro"); goto error; } if (wait && !fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) { // Failure if RT thread is not running (problem with the driver...) jack_error("Driver is not running"); goto error; } fClientTable[refnum] = client; if (NotifyAddClient(client, name, refnum) < 0) { jack_error("Cannot notify add client"); goto error; } fGraphManager->InitRefNum(refnum); fEngineControl->ResetRollingUsecs(); *shared_engine = fEngineControl; *shared_manager = fGraphManager; *ref = refnum; return 0; error: // Cleanup... fSynchroTable[refnum].Destroy(); fClientTable[refnum] = 0; return -1; } // Used for external clients int JackEngine::ClientExternalClose(int refnum) { jack_log("JackEngine::ClientExternalClose ref = %ld", refnum); JackClientInterface* client = fClientTable[refnum]; assert(client); int res = ClientCloseAux(refnum, true); client->Close(); delete client; return res; } // Used for server internal clients or drivers when the RT thread is stopped int JackEngine::ClientInternalClose(int refnum, bool wait) { jack_log("JackEngine::ClientInternalClose ref = %ld", refnum); return ClientCloseAux(refnum, wait); } int JackEngine::ClientCloseAux(int refnum, bool wait) { jack_log("JackEngine::ClientCloseAux ref = %ld", refnum); JackClientInterface* client = fClientTable[refnum]; fEngineControl->fTransport.ResetTimebase(refnum); jack_uuid_t uuid = JACK_UUID_EMPTY_INITIALIZER; jack_uuid_copy (&uuid, client->GetClientControl()->fSessionID); // Unregister all ports ==> notifications are sent jack_int_t ports[PORT_NUM_FOR_CLIENT]; int i; fGraphManager->GetInputPorts(refnum, ports); for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY); i++) { PortUnRegister(refnum, ports[i]); } fGraphManager->GetOutputPorts(refnum, ports); for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY); i++) { PortUnRegister(refnum, ports[i]); } // Remove the client from the table ReleaseRefnum(refnum); // Remove all ports fGraphManager->RemoveAllPorts(refnum); // Wait until next cycle to be sure client is not used anymore if (wait) { if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum); } } if (fMetadata.RemoveProperties(NULL, uuid) > 0) { /* have to do the notification ourselves, since the client argument to fMetadata->RemoveProperties() was NULL */ PropertyChangeNotify(uuid, NULL, PropertyDeleted); } // Notify running clients NotifyRemoveClient(client->GetClientControl()->fName, refnum); // Cleanup... fSynchroTable[refnum].Destroy(); fEngineControl->ResetRollingUsecs(); return 0; } int JackEngine::ClientActivate(int refnum, bool is_real_time) { JackClientInterface* client = fClientTable[refnum]; jack_log("JackEngine::ClientActivate ref = %ld name = %s", refnum, client->GetClientControl()->fName); if (is_real_time) { fGraphManager->Activate(refnum); } // Wait for graph state change to be effective if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) { jack_error("JackEngine::ClientActivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName); return -1; } else { jack_int_t input_ports[PORT_NUM_FOR_CLIENT]; jack_int_t output_ports[PORT_NUM_FOR_CLIENT]; fGraphManager->GetInputPorts(refnum, input_ports); fGraphManager->GetOutputPorts(refnum, output_ports); // Notify client NotifyActivate(refnum); // Then issue port registration notification for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) { NotifyPortRegistation(input_ports[i], true); } for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) { NotifyPortRegistation(output_ports[i], true); } return 0; } } // May be called without client int JackEngine::ClientDeactivate(int refnum) { JackClientInterface* client = fClientTable[refnum]; jack_log("JackEngine::ClientDeactivate ref = %ld name = %s", refnum, client->GetClientControl()->fName); jack_int_t input_ports[PORT_NUM_FOR_CLIENT]; jack_int_t output_ports[PORT_NUM_FOR_CLIENT]; fGraphManager->GetInputPorts(refnum, input_ports); fGraphManager->GetOutputPorts(refnum, output_ports); // First disconnect all ports for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) { PortDisconnect(-1, input_ports[i], ALL_PORTS); } for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) { PortDisconnect(-1, output_ports[i], ALL_PORTS); } // Then issue port registration notification for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (input_ports[i] != EMPTY); i++) { NotifyPortRegistation(input_ports[i], false); } for (int i = 0; (i < PORT_NUM_FOR_CLIENT) && (output_ports[i] != EMPTY); i++) { NotifyPortRegistation(output_ports[i], false); } fGraphManager->Deactivate(refnum); fLastSwitchUsecs = 0; // Force switch to occur next cycle, even when called with "dead" clients // Wait for graph state change to be effective if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) { jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName); return -1; } else { return 0; } } void JackEngine::ClientKill(int refnum) { jack_log("JackEngine::ClientKill ref = %ld", refnum); if (ClientDeactivate(refnum) < 0) { jack_error("JackEngine::ClientKill ref = %ld cannot be removed from the graph !!", refnum); } if (ClientExternalClose(refnum) < 0) { jack_error("JackEngine::ClientKill ref = %ld cannot be closed", refnum); } } //----------------- // Port management //----------------- int JackEngine::PortRegister(int refnum, const char* name, const char *type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index) { jack_log("JackEngine::PortRegister ref = %ld name = %s type = %s flags = %d buffer_size = %d", refnum, name, type, flags, buffer_size); JackClientInterface* client = fClientTable[refnum]; // Check if port name already exists if (fGraphManager->GetPort(name) != NO_PORT) { jack_error("port_name \"%s\" already exists", name); return -1; } // buffer_size is actually ignored... *port_index = fGraphManager->AllocatePort(refnum, name, type, (JackPortFlags)flags, fEngineControl->fBufferSize); if (*port_index != NO_PORT) { if (client->GetClientControl()->fActive) { NotifyPortRegistation(*port_index, true); } return 0; } else { return -1; } } int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index) { jack_log("JackEngine::PortUnRegister ref = %ld port_index = %ld", refnum, port_index); JackClientInterface* client = fClientTable[refnum]; assert(client); // Disconnect port ==> notification is sent PortDisconnect(-1, port_index, ALL_PORTS); if (fGraphManager->ReleasePort(refnum, port_index) == 0) { const jack_uuid_t uuid = jack_port_uuid_generate(port_index); if (!jack_uuid_empty(uuid)) { if (fMetadata.RemoveProperties(NULL, uuid) > 0) { /* have to do the notification ourselves, since the client argument to fMetadata->RemoveProperties() was NULL */ PropertyChangeNotify(uuid, NULL, PropertyDeleted); } } if (client->GetClientControl()->fActive) { NotifyPortRegistation(port_index, false); } return 0; } else { return -1; } } // this check is to prevent apps to self connect to other apps // TODO: make this work with multiple clients per app int JackEngine::CheckPortsConnect(int refnum, jack_port_id_t src, jack_port_id_t dst) { if (fSelfConnectMode == ' ') return 1; JackPort* src_port = fGraphManager->GetPort(src); JackPort* dst_port = fGraphManager->GetPort(dst); jack_log("JackEngine::CheckPortsConnect(ref = %d, src = %d, dst = %d)", refnum, src_port->GetRefNum(), dst_port->GetRefNum()); //jack_log("%s -> %s", src_port->GetName(), dst_port->GetName()); //jack_log("mode = '%c'", fSelfConnectMode); int src_self = src_port->GetRefNum() == refnum ? 1 : 0; int dst_self = dst_port->GetRefNum() == refnum ? 1 : 0; //jack_log("src_self is %s", src_self ? "true" : "false"); //jack_log("dst_self is %s", dst_self ? "true" : "false"); // 0 means client is connecting other client ports (control app patchbay functionality) // 1 means client is connecting its own port to port of other client (e.g. self connecting into "system" client) // 2 means client is connecting its own ports (for app internal functionality) int sum = src_self + dst_self; //jack_log("sum = %d", sum); if (sum == 0) return 1; char lmode = tolower(fSelfConnectMode); //jack_log("lmode = '%c'", lmode); if (sum == 2 && lmode == 'e') return 1; bool fail = lmode != fSelfConnectMode; // fail modes are upper case //jack_log("fail = %d", (int)fail); jack_info( "%s port self connect request%s (%s -> %s)", fail ? "rejecting" : "ignoring", sum == 1 ? " to external port" : "", src_port->GetName(), dst_port->GetName()); return fail ? -1 : 0; } int JackEngine::PortConnect(int refnum, const char* src, const char* dst) { jack_log("JackEngine::PortConnect ref = %d src = %s dst = %s", refnum, src, dst); jack_port_id_t port_src, port_dst; return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0) ? -1 : PortConnect(refnum, port_src, port_dst); } int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst) { jack_log("JackEngine::PortConnect ref = %d src = %d dst = %d", refnum, src, dst); JackClientInterface* client; int ref; if (fGraphManager->CheckPorts(src, dst) < 0) { return -1; } ref = fGraphManager->GetOutputRefNum(src); assert(ref >= 0); client = fClientTable[ref]; assert(client); if (!client->GetClientControl()->fActive) { jack_error("Cannot connect ports owned by inactive clients:" " \"%s\" is not active", client->GetClientControl()->fName); return -1; } ref = fGraphManager->GetInputRefNum(dst); assert(ref >= 0); client = fClientTable[ref]; assert(client); if (!client->GetClientControl()->fActive) { jack_error("Cannot connect ports owned by inactive clients:" " \"%s\" is not active", client->GetClientControl()->fName); return -1; } int res = CheckPortsConnect(refnum, src, dst); if (res != 1) { return res; } res = fGraphManager->Connect(src, dst); if (res == 0) { NotifyPortConnect(src, dst, true); } return res; } int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst) { jack_log("JackEngine::PortDisconnect ref = %d src = %s dst = %s", refnum, src, dst); jack_port_id_t port_src, port_dst; return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0) ? -1 : PortDisconnect(refnum, port_src, port_dst); } int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst) { jack_log("JackEngine::PortDisconnect ref = %d src = %d dst = %d", refnum, src, dst); if (dst == ALL_PORTS) { jack_int_t connections[CONNECTION_NUM_FOR_PORT]; fGraphManager->GetConnections(src, connections); JackPort* port = fGraphManager->GetPort(src); int res = 0; if (port->GetFlags() & JackPortIsOutput) { for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) { if (PortDisconnect(refnum, src, connections[i]) != 0) { res = -1; } } } else { for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) { if (PortDisconnect(refnum, connections[i], src) != 0) { res = -1; } } } return res; } if (fGraphManager->CheckPorts(src, dst) < 0) { return -1; } int res = CheckPortsConnect(refnum, src, dst); if (res != 1) { return res; } res = fGraphManager->Disconnect(src, dst); if (res == 0) NotifyPortConnect(src, dst, false); return res; } int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name) { char old_name[REAL_JACK_PORT_NAME_SIZE+1]; strcpy(old_name, fGraphManager->GetPort(port)->GetName()); fGraphManager->GetPort(port)->SetName(name); NotifyPortRename(port, old_name); return 0; } int JackEngine::PortSetDefaultMetadata(jack_port_id_t port, const char* pretty_name) { static const char* type = "text/plain"; jack_uuid_t uuid = jack_port_uuid_generate(port); int res = fMetadata.SetProperty(NULL, uuid, JACK_METADATA_HARDWARE, pretty_name, type); if (res == -1) { return -1; } char *v, *t; res = fMetadata.GetProperty(uuid, JACK_METADATA_PRETTY_NAME, &v, &t); if (res == -1) { res = fMetadata.SetProperty(NULL, uuid, JACK_METADATA_PRETTY_NAME, pretty_name, type); } return res; } //-------------------- // Session management //-------------------- void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, detail::JackChannelTransactionInterface *socket, JackSessionNotifyResult** result) { if (fSessionPendingReplies != 0) { JackSessionNotifyResult res(-1); res.Write(socket); jack_log("JackEngine::SessionNotify ... busy"); if (result != NULL) *result = NULL; return; } for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && jack_uuid_empty(client->GetClientControl()->fSessionID)) { client->GetClientControl()->fSessionID = jack_client_uuid_generate(); } } fSessionResult = new JackSessionNotifyResult(); for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && client->GetClientControl()->fCallback[kSessionCallback]) { // check if this is a notification to a specific client. if (target != NULL && strlen(target) != 0) { if (strcmp(target, client->GetClientControl()->fName)) { continue; } } char path_buf[JACK_PORT_NAME_SIZE]; if (path[strlen(path) - 1] == DIR_SEPARATOR) { snprintf(path_buf, sizeof path_buf, "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR); } else { snprintf(path_buf, sizeof path_buf, "%s%c%s%c", path, DIR_SEPARATOR, client->GetClientControl()->fName, DIR_SEPARATOR); } int res = JackTools::MkDir(path_buf); if (res) jack_error("JackEngine::SessionNotify: can not create session directory '%s'", path_buf); int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int)type, 0); if (result == kPendingSessionReply) { fSessionPendingReplies += 1; } else if (result == kImmediateSessionReply) { char uuid_buf[JACK_UUID_STRING_SIZE]; jack_uuid_unparse(client->GetClientControl()->fSessionID, uuid_buf); fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf, client->GetClientControl()->fName, client->GetClientControl()->fSessionCommand, client->GetClientControl()->fSessionFlags)); } } } if (result != NULL) *result = fSessionResult; if (fSessionPendingReplies == 0) { fSessionResult->Write(socket); if (result == NULL) delete fSessionResult; fSessionResult = NULL; } else { fSessionTransaction = socket; } } int JackEngine::SessionReply(int refnum) { JackClientInterface* client = fClientTable[refnum]; assert(client); char uuid_buf[JACK_UUID_STRING_SIZE]; jack_uuid_unparse(client->GetClientControl()->fSessionID, uuid_buf); fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf, client->GetClientControl()->fName, client->GetClientControl()->fSessionCommand, client->GetClientControl()->fSessionFlags)); fSessionPendingReplies -= 1; if (fSessionPendingReplies == 0) { fSessionResult->Write(fSessionTransaction); if (fSessionTransaction != NULL) { delete fSessionResult; } fSessionResult = NULL; } return 0; } int JackEngine::GetUUIDForClientName(const char *client_name, char *uuid_res) { for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (client && (strcmp(client_name, client->GetClientControl()->fName) == 0)) { jack_uuid_unparse(client->GetClientControl()->fSessionID, uuid_res); return 0; } } // Did not find name. return -1; } int JackEngine::GetClientNameForUUID(const char *uuid_buf, char *name_res) { jack_uuid_t uuid; if (jack_uuid_parse(uuid_buf, &uuid) != 0) return -1; for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* client = fClientTable[i]; if (!client) { continue; } if (jack_uuid_compare(client->GetClientControl()->fSessionID, uuid) == 0) { strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE); return 0; } } // Did not find uuid. return -1; } int JackEngine::ReserveClientName(const char *name, const char *uuidstr) { jack_log("JackEngine::ReserveClientName ( name = %s, uuid = %s )", name, uuidstr); if (ClientCheckName(name)) { jack_log("name already taken"); return -1; } jack_uuid_t uuid; if (jack_uuid_parse(uuidstr, &uuid) != 0) { jack_error("JackEngine::ReserveClientName invalid uuid %s", uuidstr); return -1; } EnsureUUID(uuid); fReservationMap[uuid] = name; return 0; } int JackEngine::ClientHasSessionCallback(const char *name) { JackClientInterface* client = NULL; for (int i = 0; i < CLIENT_NUM; i++) { client = fClientTable[i]; if (client && (strcmp(client->GetClientControl()->fName, name) == 0)) { break; } } if (client) { return client->GetClientControl()->fCallback[kSessionCallback]; } else { return -1; } } } // end of namespace jack2-1.9.22/common/JackEngine.h000066400000000000000000000144271436671425200162600ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackEngine__ #define __JackEngine__ #include "JackConstants.h" #include "JackGraphManager.h" #include "JackSynchro.h" #include "JackMutex.h" #include "JackTransportEngine.h" #include "JackPlatformPlug.h" #include "JackRequest.h" #include "JackChannel.h" #include namespace Jack { class JackClientInterface; struct JackEngineControl; class JackExternalClient; /*! \brief Engine description. */ class SERVER_EXPORT JackEngine : public JackLockAble { friend class JackLockedEngine; private: JackGraphManager* fGraphManager; JackEngineControl* fEngineControl; char fSelfConnectMode; JackClientInterface* fClientTable[CLIENT_NUM]; JackSynchro* fSynchroTable; JackServerNotifyChannel fChannel; /*! To communicate between the RT thread and server */ JackProcessSync fSignal; jack_time_t fLastSwitchUsecs; JackMetadata fMetadata; int fSessionPendingReplies; detail::JackChannelTransactionInterface* fSessionTransaction; JackSessionNotifyResult* fSessionResult; std::map fReservationMap; int ClientCloseAux(int refnum, bool wait); void CheckXRun(jack_time_t callback_usecs); int NotifyAddClient(JackClientInterface* new_client, const char* new_name, int refnum); void NotifyRemoveClient(const char* name, int refnum); void ProcessNext(jack_time_t callback_usecs); void ProcessCurrent(jack_time_t callback_usecs); bool ClientCheckName(const char* name); bool GenerateUniqueName(char* name); int AllocateRefnum(); void ReleaseRefnum(int refnum); int ClientNotify(JackClientInterface* client, int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); void NotifyClient(int refnum, int event, int sync, const char* message, int value1, int value2); void NotifyClients(int event, int sync, const char* message, int value1, int value2); void NotifyPortRegistation(jack_port_id_t port_index, bool onoff); void NotifyPortConnect(jack_port_id_t src, jack_port_id_t dst, bool onoff); void NotifyPortRename(jack_port_id_t src, const char* old_name); void NotifyActivate(int refnum); void EnsureUUID(jack_uuid_t uuid); bool CheckClient(int refnum) { return (refnum >= 0 && refnum < CLIENT_NUM && fClientTable[refnum] != NULL); } int CheckPortsConnect(int refnum, jack_port_id_t src, jack_port_id_t dst); public: JackEngine(JackGraphManager* manager, JackSynchro* table, JackEngineControl* controler, char self_connect_mode); ~JackEngine(); int Open(); int Close(); // Client management int ClientCheck(const char* name, jack_uuid_t uuid, char* name_res, int protocol, int options, int* status); int ClientExternalOpen(const char* name, int pid, jack_uuid_t uuid, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager); int ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait); int ClientExternalClose(int refnum); int ClientInternalClose(int refnum, bool wait); int ClientActivate(int refnum, bool is_real_time); int ClientDeactivate(int refnum); void ClientKill(int refnum); int GetClientPID(const char* name); int GetClientRefNum(const char* name); // Internal client management int GetInternalClientName(int int_ref, char* name_res); int InternalClientHandle(const char* client_name, int* status, int* int_ref); int InternalClientUnload(int refnum, int* status); // Port management int PortRegister(int refnum, const char* name, const char *type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port); int PortUnRegister(int refnum, jack_port_id_t port); int PortConnect(int refnum, const char* src, const char* dst); int PortDisconnect(int refnum, const char* src, const char* dst); int PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst); int PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst); int PortRename(int refnum, jack_port_id_t port, const char* name); int PortSetDefaultMetadata(jack_port_id_t port, const char* pretty_name); int ComputeTotalLatencies(); int PropertyChangeNotify(jack_uuid_t subject, const char* key,jack_property_change_t change); // Graph bool Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end); // Notifications void NotifyDriverXRun(); void NotifyClientXRun(int refnum); void NotifyFailure(int code, const char* reason); void NotifyGraphReorder(); void NotifyBufferSize(jack_nframes_t buffer_size); void NotifySampleRate(jack_nframes_t sample_rate); void NotifyFreewheel(bool onoff); void NotifyQuit(); // Session management void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, detail::JackChannelTransactionInterface *socket, JackSessionNotifyResult** result); int SessionReply(int refnum); int GetUUIDForClientName(const char *client_name, char *uuid_res); int GetClientNameForUUID(const char *uuid, char *name_res); int ReserveClientName(const char *name, const char *uuid); int ClientHasSessionCallback(const char *name); }; } // end of namespace #endif jack2-1.9.22/common/JackEngineControl.cpp000066400000000000000000000075331436671425200201540ustar00rootroot00000000000000/* Copyright (C) 2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackClientInterface.h" #include "JackEngineControl.h" #include "JackGraphManager.h" #include "JackClientControl.h" #include #include namespace Jack { static inline jack_time_t JACK_MAX(jack_time_t a, jack_time_t b) { return (a < b) ? b : a; } void JackEngineControl::CalcCPULoad(JackClientInterface** table, JackGraphManager* manager, jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end) { fPrevCycleTime = fCurCycleTime; fCurCycleTime = cur_cycle_begin; jack_time_t last_cycle_end = prev_cycle_end; // In Asynchronous mode, last cycle end is the max of client end dates if (!fSyncMode) { for (int i = fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; JackClientTiming* timing = manager->GetClientTiming(i); if (client && client->GetClientControl()->fActive && timing->fStatus == Finished) { last_cycle_end = JACK_MAX(last_cycle_end, timing->fFinishedAt); } } } // Store the execution time for later averaging if (last_cycle_end > 0) { fRollingClientUsecs[fRollingClientUsecsIndex++] = last_cycle_end - fPrevCycleTime; } if (fRollingClientUsecsIndex >= JACK_ENGINE_ROLLING_COUNT) { fRollingClientUsecsIndex = 0; } // Each time we have a full set of iterations, recompute the current // usage from the latest JACK_ENGINE_ROLLING_COUNT client entries. if (fRollingClientUsecsCnt && (fRollingClientUsecsIndex == 0)) { jack_time_t avg_usecs = 0; jack_time_t max_usecs = 0; for (int i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) { avg_usecs += fRollingClientUsecs[i]; // This is really a running total to be averaged later max_usecs = JACK_MAX(fRollingClientUsecs[i], max_usecs); } fMaxUsecs = JACK_MAX(fMaxUsecs, max_usecs); if (max_usecs < ((fPeriodUsecs * 95) / 100)) { // Average the values from our JACK_ENGINE_ROLLING_COUNT array fSpareUsecs = (jack_time_t)(fPeriodUsecs - (avg_usecs / JACK_ENGINE_ROLLING_COUNT)); } else { // Use the 'worst case' value (or zero if we exceeded 'fPeriodUsecs') fSpareUsecs = jack_time_t((max_usecs < fPeriodUsecs) ? fPeriodUsecs - max_usecs : 0); } fCPULoad = ((1.f - (float(fSpareUsecs) / float(fPeriodUsecs))) * 50.f + (fCPULoad * 0.5f)); } fRollingClientUsecsCnt++; } void JackEngineControl::ResetRollingUsecs() { memset(fRollingClientUsecs, 0, sizeof(fRollingClientUsecs)); fRollingClientUsecsIndex = 0; fRollingClientUsecsCnt = 0; fSpareUsecs = 0; fRollingInterval = int(floor((JACK_ENGINE_ROLLING_INTERVAL * 1000.f) / fPeriodUsecs)); } void JackEngineControl::NotifyXRun(jack_time_t callback_usecs, float delayed_usecs) { ResetFrameTime(callback_usecs); fXrunDelayedUsecs = delayed_usecs; if (delayed_usecs > fMaxDelayedUsecs) { fMaxDelayedUsecs = delayed_usecs; } } } // end of namespace jack2-1.9.22/common/JackEngineControl.h000066400000000000000000000131201436671425200176060ustar00rootroot00000000000000/* Copyright (C) 2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackEngineControl__ #define __JackEngineControl__ #include "JackShmMem.h" #include "JackFrameTimer.h" #include "JackTransportEngine.h" #include "JackConstants.h" #include "types.h" #include #ifdef JACK_MONITOR #include "JackEngineProfiling.h" #endif namespace Jack { class JackClientInterface; class JackGraphManager; #define JACK_ENGINE_ROLLING_COUNT 32 #define JACK_ENGINE_ROLLING_INTERVAL 1024 /*! \brief Engine control in shared memory. */ PRE_PACKED_STRUCTURE struct SERVER_EXPORT JackEngineControl : public JackShmMem { // Shared state jack_nframes_t fBufferSize; jack_nframes_t fSampleRate; bool fSyncMode; bool fTemporary; jack_time_t fPeriodUsecs; jack_time_t fTimeOutUsecs; float fMaxDelayedUsecs; float fXrunDelayedUsecs; bool fTimeOut; bool fRealTime; bool fSavedRealTime; // RT state saved and restored during Freewheel mode int fServerPriority; int fClientPriority; int fMaxClientPriority; char fServerName[JACK_SERVER_NAME_SIZE+1]; alignas(UInt32) alignas(JackTransportEngine) JackTransportEngine fTransport; jack_timer_type_t fClockSource; int fDriverNum; bool fVerbose; // CPU Load jack_time_t fPrevCycleTime; jack_time_t fCurCycleTime; jack_time_t fSpareUsecs; jack_time_t fMaxUsecs; jack_time_t fRollingClientUsecs[JACK_ENGINE_ROLLING_COUNT]; unsigned int fRollingClientUsecsCnt; int fRollingClientUsecsIndex; int fRollingInterval; float fCPULoad; // For OSX thread UInt64 fPeriod; UInt64 fComputation; UInt64 fConstraint; // Timer alignas(UInt32) alignas(JackFrameTimer) JackFrameTimer fFrameTimer; #ifdef JACK_MONITOR JackEngineProfiling fProfiler; #endif JackEngineControl(bool sync, bool temporary, long timeout, bool rt, long priority, bool verbose, jack_timer_type_t clock, const char* server_name) { static_assert(offsetof(JackEngineControl, fTransport) % sizeof(UInt32) == 0, "fTransport must be aligned within JackEngineControl"); static_assert(offsetof(JackEngineControl, fFrameTimer) % sizeof(UInt32) == 0, "fFrameTimer must be aligned within JackEngineControl"); fBufferSize = 512; fSampleRate = 48000; fPeriodUsecs = jack_time_t(1000000.f / fSampleRate * fBufferSize); fSyncMode = sync; fTemporary = temporary; fTimeOut = (timeout > 0); fTimeOutUsecs = timeout * 1000; fRealTime = rt; fSavedRealTime = false; fServerPriority = priority; #ifdef WIN32 fClientPriority = (rt) ? priority - 3 : 0; #else fClientPriority = (rt) ? priority - 5 : 0; #endif fMaxClientPriority = (rt) ? priority - 1 : 0; fVerbose = verbose; fPrevCycleTime = 0; fCurCycleTime = 0; fSpareUsecs = 0; fMaxUsecs = 0; ResetRollingUsecs(); strncpy(fServerName, server_name, sizeof(fServerName)); fServerName[sizeof(fServerName) - 1] = 0; fCPULoad = 0.f; fPeriod = 0; fComputation = 0; fConstraint = 0; fMaxDelayedUsecs = 0.f; fXrunDelayedUsecs = 0.f; fClockSource = clock; fDriverNum = 0; } ~JackEngineControl() {} void UpdateTimeOut() { fPeriodUsecs = jack_time_t(1000000.f / fSampleRate * fBufferSize); // In microsec if (!(fTimeOut && fTimeOutUsecs > 2 * fPeriodUsecs)) { fTimeOutUsecs = 2 * fPeriodUsecs; } } // Cycle void CycleIncTime(jack_time_t callback_usecs) { // Timer fFrameTimer.IncFrameTime(fBufferSize, callback_usecs, fPeriodUsecs); } void CycleBegin(JackClientInterface** table, JackGraphManager* manager, jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end) { fTransport.CycleBegin(fSampleRate, cur_cycle_begin); CalcCPULoad(table, manager, cur_cycle_begin, prev_cycle_end); #ifdef JACK_MONITOR fProfiler.Profile(table, manager, fPeriodUsecs, cur_cycle_begin, prev_cycle_end); #endif } void CycleEnd(JackClientInterface** table) { fTransport.CycleEnd(table, fSampleRate, fBufferSize); } // Timer void InitFrameTime() { fFrameTimer.InitFrameTime(); } void ResetFrameTime(jack_time_t callback_usecs) { fFrameTimer.ResetFrameTime(callback_usecs); } void ReadFrameTime(JackTimer* timer) { fFrameTimer.ReadFrameTime(timer); } // XRun void NotifyXRun(jack_time_t callback_usecs, float delayed_usecs); void ResetXRun() { fMaxDelayedUsecs = 0.f; } // Private void CalcCPULoad(JackClientInterface** table, JackGraphManager* manager, jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end); void ResetRollingUsecs(); } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackEngineProfiling.cpp000066400000000000000000000423241436671425200204620ustar00rootroot00000000000000/* Copyright (C) 2008 Grame & RTL This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackEngineProfiling.h" #include "JackGraphManager.h" #include "JackClientControl.h" #include "JackEngineControl.h" #include "JackClientInterface.h" #include "JackGlobals.h" #include "JackTime.h" #include #include namespace Jack { JackEngineProfiling::JackEngineProfiling():fAudioCycle(0),fMeasuredClient(0) { jack_info("Engine profiling activated, beware %ld MBytes are needed to record profiling points...", sizeof(fProfileTable) / (1024 * 1024)); // Force memory page in memset(fProfileTable, 0, sizeof(fProfileTable)); } JackEngineProfiling::~JackEngineProfiling() { std::ofstream fStream("JackEngineProfiling.log", std::ios_base::ate); jack_info("Write server and clients timing data..."); if (!fStream.is_open()) { jack_error("JackEngineProfiling::Save cannot open JackEngineProfiling.log file"); } else { // For each measured point for (int i = 2; i < TIME_POINTS; i++) { // Driver timing values long d1 = long(fProfileTable[i].fCurCycleBegin - fProfileTable[i - 1].fCurCycleBegin); long d2 = long(fProfileTable[i].fPrevCycleEnd - fProfileTable[i - 1].fCurCycleBegin); if (d1 <= 0 || fProfileTable[i].fAudioCycle <= 0) continue; // Skip non valid cycles // Print driver delta and end cycle fStream << d1 << "\t" << d2 << "\t"; // For each measured client for (unsigned int j = 0; j < fMeasuredClient; j++) { int ref = fIntervalTable[j].fRefNum; // Is valid client cycle if (fProfileTable[i].fClientTable[ref].fStatus != NotTriggered) { long d5 = long(fProfileTable[i].fClientTable[ref].fSignaledAt - fProfileTable[i - 1].fCurCycleBegin); long d6 = long(fProfileTable[i].fClientTable[ref].fAwakeAt - fProfileTable[i - 1].fCurCycleBegin); long d7 = long(fProfileTable[i].fClientTable[ref].fFinishedAt - fProfileTable[i - 1].fCurCycleBegin); fStream << ref << "\t" ; fStream << ((d5 > 0) ? d5 : 0) << "\t"; fStream << ((d6 > 0) ? d6 : 0) << "\t" ; fStream << ((d7 > 0) ? d7 : 0) << "\t"; fStream << ((d6 > 0 && d5 > 0) ? (d6 - d5) : 0) << "\t" ; fStream << ((d7 > 0 && d6 > 0) ? (d7 - d6) : 0) << "\t" ; fStream << fProfileTable[i].fClientTable[ref].fStatus << "\t" ;; } else { // Print tabs fStream << "\t \t \t \t \t \t \t"; } } // Terminate line fStream << std::endl; } } // Driver period std::ofstream fStream1("Timing1.plot", std::ios_base::ate); if (!fStream1.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing1.plot file"); } else { fStream1 << "set grid\n"; fStream1 << "set title \"Audio driver timing\"\n"; fStream1 << "set xlabel \"audio cycles\"\n"; fStream1 << "set ylabel \"usec\"\n"; fStream1 << "plot \"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines \n"; fStream1 << "set output 'Timing1.svg\n"; fStream1 << "set terminal svg\n"; fStream1 << "set grid\n"; fStream1 << "set title \"Audio driver timing\"\n"; fStream1 << "set xlabel \"audio cycles\"\n"; fStream1 << "set ylabel \"usec\"\n"; fStream1 << "plot \"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines \n"; fStream1 << "unset output\n"; } // Driver end date std::ofstream fStream2("Timing2.plot", std::ios_base::ate); if (!fStream2.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing2.plot file"); } else { fStream2 << "set grid\n"; fStream2 << "set title \"Driver end date\"\n"; fStream2 << "set xlabel \"audio cycles\"\n"; fStream2 << "set ylabel \"usec\"\n"; fStream2 << "plot \"JackEngineProfiling.log\" using 2 title \"Driver end date\" with lines \n"; fStream2 << "set output 'Timing2.svg\n"; fStream2 << "set terminal svg\n"; fStream2 << "set grid\n"; fStream2 << "set title \"Driver end date\"\n"; fStream2 << "set xlabel \"audio cycles\"\n"; fStream2 << "set ylabel \"usec\"\n"; fStream2 << "plot \"JackEngineProfiling.log\" using 2 title \"Driver end date\" with lines \n"; fStream2 << "unset output\n"; } // Clients end date if (fMeasuredClient > 0) { std::ofstream fStream3("Timing3.plot", std::ios_base::ate); if (!fStream3.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing3.plot file"); } else { fStream3 << "set multiplot\n"; fStream3 << "set grid\n"; fStream3 << "set title \"Clients end date\"\n"; fStream3 << "set xlabel \"audio cycles\"\n"; fStream3 << "set ylabel \"usec\"\n"; fStream3 << "plot "; for (unsigned int i = 0; i < fMeasuredClient; i++) { if (i == 0) { if (i + 1 == fMeasuredClient) { // Last client fStream3 << "\"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines,\"JackEngineProfiling.log\" using "; fStream3 << ((i + 1) * 7) - 1; fStream3 << " title \"" << fIntervalTable[i].fName << "\"with lines"; } else { fStream3 << "\"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines,\"JackEngineProfiling.log\" using "; fStream3 << ((i + 1) * 7) - 1; fStream3 << " title \"" << fIntervalTable[i].fName << "\"with lines,"; } } else if (i + 1 == fMeasuredClient) { // Last client fStream3 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) - 1 << " title \"" << fIntervalTable[i].fName << "\" with lines"; } else { fStream3 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) - 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } fStream3 << "\n unset multiplot\n"; fStream3 << "set output 'Timing3.svg\n"; fStream3 << "set terminal svg\n"; fStream3 << "set multiplot\n"; fStream3 << "set grid\n"; fStream3 << "set title \"Clients end date\"\n"; fStream3 << "set xlabel \"audio cycles\"\n"; fStream3 << "set ylabel \"usec\"\n"; fStream3 << "plot "; for (unsigned int i = 0; i < fMeasuredClient; i++) { if (i == 0) { if ((i + 1) == fMeasuredClient) { // Last client fStream3 << "\"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines,\"JackEngineProfiling.log\" using "; fStream3 << ((i + 1) * 7) - 1; fStream3 << " title \"" << fIntervalTable[i].fName << "\"with lines"; } else { fStream3 << "\"JackEngineProfiling.log\" using 1 title \"Audio period\" with lines,\"JackEngineProfiling.log\" using "; fStream3 << ((i + 1) * 7) - 1; fStream3 << " title \"" << fIntervalTable[i].fName << "\"with lines,"; } } else if ((i + 1) == fMeasuredClient) { // Last client fStream3 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) - 1 << " title \"" << fIntervalTable[i].fName << "\" with lines"; } else { fStream3 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) - 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } fStream3 << "\nunset multiplot\n"; fStream3 << "unset output\n"; } } // Clients scheduling if (fMeasuredClient > 0) { std::ofstream fStream4("Timing4.plot", std::ios_base::ate); if (!fStream4.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing4.plot file"); } else { fStream4 << "set multiplot\n"; fStream4 << "set grid\n"; fStream4 << "set title \"Clients scheduling latency\"\n"; fStream4 << "set xlabel \"audio cycles\"\n"; fStream4 << "set ylabel \"usec\"\n"; fStream4 << "plot "; for (unsigned int i = 0; i < fMeasuredClient; i++) { if ((i + 1) == fMeasuredClient) { // Last client fStream4 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) << " title \"" << fIntervalTable[i].fName << "\" with lines"; } else { fStream4 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } fStream4 << "\n unset multiplot\n"; fStream4 << "set output 'Timing4.svg\n"; fStream4 << "set terminal svg\n"; fStream4 << "set multiplot\n"; fStream4 << "set grid\n"; fStream4 << "set title \"Clients scheduling latency\"\n"; fStream4 << "set xlabel \"audio cycles\"\n"; fStream4 << "set ylabel \"usec\"\n"; fStream4 << "plot "; for (unsigned int i = 0; i < fMeasuredClient; i++) { if ((i + 1) == fMeasuredClient) { // Last client fStream4 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) << " title \"" << fIntervalTable[i].fName << "\" with lines"; } else { fStream4 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } fStream4 << "\nunset multiplot\n"; fStream4 << "unset output\n"; } } // Clients duration if (fMeasuredClient > 0) { std::ofstream fStream5("Timing5.plot", std::ios_base::ate); if (!fStream5.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timing5.plot file"); } else { fStream5 << "set multiplot\n"; fStream5 << "set grid\n"; fStream5 << "set title \"Clients duration\"\n"; fStream5 << "set xlabel \"audio cycles\"\n"; fStream5 << "set ylabel \"usec\"\n"; fStream5 << "plot "; for (unsigned int i = 0; i < fMeasuredClient; i++) { if ((i + 1) == fMeasuredClient) { // Last client fStream5 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) + 1 << " title \"" << fIntervalTable[i].fName << "\" with lines"; } else { fStream5 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) + 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } fStream5 << "\n unset multiplot\n"; fStream5 << "set output 'Timing5.svg\n"; fStream5 << "set terminal svg\n"; fStream5 << "set multiplot\n"; fStream5 << "set grid\n"; fStream5 << "set title \"Clients duration\"\n"; fStream5 << "set xlabel \"audio cycles\"\n"; fStream5 << "set ylabel \"usec\"\n"; fStream5 << "plot "; for (unsigned int i = 0; i < fMeasuredClient; i++) { if ((i + 1) == fMeasuredClient) {// Last client fStream5 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) + 1 << " title \"" << fIntervalTable[i].fName << "\" with lines"; } else { fStream5 << "\"JackEngineProfiling.log\" using " << ((i + 1) * 7) + 1 << " title \"" << fIntervalTable[i].fName << "\" with lines,"; } } fStream5 << "\nunset multiplot\n"; fStream5 << "unset output\n"; } } std::ofstream fStream6("Timings.html", std::ios_base::ate); if (!fStream6.is_open()) { jack_error("JackEngineProfiling::Save cannot open Timings.html file"); } else { fStream6 << "\n"; fStream6 << "\n"; fStream6 << "\n"; fStream6 << " \n"; fStream6 << " JACK engine profiling\n"; fStream6 << " \n"; fStream6 << " \n"; fStream6 << " \n"; fStream6 << " \n"; fStream6 << "

JACK engine profiling

\n"; fStream6 << "
Timing1
"; fStream6 << "
Timing2
"; fStream6 << "
Timing3
"; fStream6 << "
Timing4
"; fStream6 << "
Timing5
"; fStream6 << " \n"; fStream6 << "\n"; } std::ofstream fStream7("generate_timings", std::ios_base::ate); if (!fStream7.is_open()) { jack_error("JackEngineProfiling::Save cannot open generate_timings file"); } else { fStream7 << "gnuplot -persist Timing1.plot \n"; fStream7 << "gnuplot -persist Timing2.plot\n"; fStream7 << "gnuplot -persist Timing3.plot\n"; fStream7 << "gnuplot -persist Timing4.plot\n"; fStream7 << "gnuplot -persist Timing5.plot\n"; } } bool JackEngineProfiling::CheckClient(const char* name, int cur_point) { for (int i = 0; i < MEASURED_CLIENTS; i++) { if (strcmp(fIntervalTable[i].fName, name) == 0) { fIntervalTable[i].fEndInterval = cur_point; return true; } } return false; } void JackEngineProfiling::Profile(JackClientInterface** table, JackGraphManager* manager, jack_time_t period_usecs, jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end) { fAudioCycle = (fAudioCycle + 1) % TIME_POINTS; // Keeps cycle data fProfileTable[fAudioCycle].fPeriodUsecs = period_usecs; fProfileTable[fAudioCycle].fCurCycleBegin = cur_cycle_begin; fProfileTable[fAudioCycle].fPrevCycleEnd = prev_cycle_end; fProfileTable[fAudioCycle].fAudioCycle = fAudioCycle; for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; JackClientTiming* timing = manager->GetClientTiming(i); if (client && client->GetClientControl()->fActive && client->GetClientControl()->fCallback[kRealTimeCallback]) { if (!CheckClient(client->GetClientControl()->fName, fAudioCycle)) { // Keep new measured client fIntervalTable[fMeasuredClient].fRefNum = i; strcpy(fIntervalTable[fMeasuredClient].fName, client->GetClientControl()->fName); fIntervalTable[fMeasuredClient].fBeginInterval = fAudioCycle; fIntervalTable[fMeasuredClient].fEndInterval = fAudioCycle; fMeasuredClient++; } fProfileTable[fAudioCycle].fClientTable[i].fRefNum = i; fProfileTable[fAudioCycle].fClientTable[i].fSignaledAt = timing->fSignaledAt; fProfileTable[fAudioCycle].fClientTable[i].fAwakeAt = timing->fAwakeAt; fProfileTable[fAudioCycle].fClientTable[i].fFinishedAt = timing->fFinishedAt; fProfileTable[fAudioCycle].fClientTable[i].fStatus = timing->fStatus; } } } JackTimingMeasure* JackEngineProfiling::GetCurMeasure() { return &fProfileTable[fAudioCycle]; } } // end of namespace jack2-1.9.22/common/JackEngineProfiling.h000066400000000000000000000061201436671425200201210ustar00rootroot00000000000000/* Copyright (C) 2008 Grame & RTL This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackEngineProfiling__ #define __JackEngineProfiling__ #include "types.h" #include "JackTypes.h" #include "JackConstants.h" #include "JackShmMem.h" namespace Jack { #define TIME_POINTS 100000 #define FAILURE_TIME_POINTS 10000 #define FAILURE_WINDOW 10 #define MEASURED_CLIENTS 32 /*! \brief Timing structure for a client. */ PRE_PACKED_STRUCTURE struct JackTimingMeasureClient { int fRefNum; jack_time_t fSignaledAt; jack_time_t fAwakeAt; jack_time_t fFinishedAt; jack_client_state_t fStatus; JackTimingMeasureClient() :fRefNum(-1), fSignaledAt(0), fAwakeAt(0), fFinishedAt(0), fStatus((jack_client_state_t)0) {} } POST_PACKED_STRUCTURE; /*! \brief Timing interval in the global table for a given client */ PRE_PACKED_STRUCTURE struct JackTimingClientInterval { int fRefNum; char fName[JACK_CLIENT_NAME_SIZE + 1]; int fBeginInterval; int fEndInterval; JackTimingClientInterval() :fRefNum(-1), fBeginInterval(-1), fEndInterval(-1) {} } POST_PACKED_STRUCTURE; /*! \brief Timing structure for a table of clients. */ PRE_PACKED_STRUCTURE struct JackTimingMeasure { unsigned int fAudioCycle; jack_time_t fPeriodUsecs; jack_time_t fCurCycleBegin; jack_time_t fPrevCycleEnd; JackTimingMeasureClient fClientTable[CLIENT_NUM]; JackTimingMeasure() :fAudioCycle(0), fPeriodUsecs(0), fCurCycleBegin(0), fPrevCycleEnd(0) {} } POST_PACKED_STRUCTURE; /*! \brief Client timing monitoring. */ class JackClientInterface; class JackGraphManager; PRE_PACKED_STRUCTURE class SERVER_EXPORT JackEngineProfiling { private: JackTimingMeasure fProfileTable[TIME_POINTS]; JackTimingClientInterval fIntervalTable[MEASURED_CLIENTS]; unsigned int fAudioCycle; unsigned int fMeasuredClient; bool CheckClient(const char* name, int cur_point); public: JackEngineProfiling(); ~JackEngineProfiling(); void Profile(JackClientInterface** table, JackGraphManager* manager, jack_time_t period_usecs, jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end); JackTimingMeasure* GetCurMeasure(); } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackError.cpp000066400000000000000000000067661436671425200165060ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2008 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "JackError.h" #include "JackGlobals.h" #include "JackMessageBuffer.h" using namespace Jack; static bool change_thread_log_function(jack_log_function_t log_function) { return (jack_tls_get(JackGlobals::fKeyLogFunction) == NULL && jack_tls_set(JackGlobals::fKeyLogFunction, (void*)log_function)); } SERVER_EXPORT int set_threaded_log_function() { return change_thread_log_function(JackMessageBufferAdd); } void jack_log_function(int level, const char *message) { void (* log_callback)(const char *); switch (level) { case LOG_LEVEL_INFO: log_callback = jack_info_callback; break; case LOG_LEVEL_ERROR: log_callback = jack_error_callback; break; default: return; } log_callback(message); } static void jack_format_and_log(int level, const char *prefix, const char *fmt, va_list ap) { char buffer[256]; size_t len; jack_log_function_t log_function; if (prefix != NULL) { len = strlen(prefix); assert(len < 256); memcpy(buffer, prefix, len); } else { len = 0; } vsnprintf(buffer + len, sizeof(buffer) - len, fmt, ap); log_function = (jack_log_function_t)jack_tls_get(JackGlobals::fKeyLogFunction); /* if log function is not overridden for thread, use default one */ if (log_function == NULL) { log_function = jack_log_function; //log_function(LOG_LEVEL_INFO, "------ Using default log function"); } else { //log_function(LOG_LEVEL_INFO, "++++++ Using thread-specific log function"); } log_function(level, buffer); } SERVER_EXPORT void jack_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_ERROR, NULL, fmt, ap); va_end(ap); } SERVER_EXPORT void jack_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, NULL, fmt, ap); va_end(ap); } SERVER_EXPORT void jack_log(const char *fmt,...) { if (JackGlobals::fVerbose) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); va_end(ap); } } SERVER_EXPORT void default_jack_error_callback(const char *desc) { fprintf(stderr, "%s\n", desc); fflush(stderr); } SERVER_EXPORT void default_jack_info_callback(const char *desc) { fprintf(stdout, "%s\n", desc); fflush(stdout); } SERVER_EXPORT void silent_jack_error_callback(const char *desc) {} SERVER_EXPORT void silent_jack_info_callback(const char *desc) {} LIB_EXPORT void (*jack_error_callback)(const char *desc) = &default_jack_error_callback; LIB_EXPORT void (*jack_info_callback)(const char *desc) = &default_jack_info_callback; jack2-1.9.22/common/JackError.h000066400000000000000000000035011436671425200161330ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame Copyright (C) 2008 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackError__ #define __JackError__ #include #include #include "JackCompilerDeps.h" #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT void jack_error(const char *fmt, ...); SERVER_EXPORT void jack_info(const char *fmt, ...); SERVER_EXPORT void jack_log(const char *fmt, ...); LIB_EXPORT extern void (*jack_error_callback)(const char *desc); LIB_EXPORT extern void (*jack_info_callback)(const char *desc); SERVER_EXPORT extern void default_jack_error_callback(const char *desc); SERVER_EXPORT extern void default_jack_info_callback(const char *desc); SERVER_EXPORT void silent_jack_error_callback(const char *desc); SERVER_EXPORT void silent_jack_info_callback(const char *desc); SERVER_EXPORT int set_threaded_log_function(); #define LOG_LEVEL_INFO 1 #define LOG_LEVEL_ERROR 2 void jack_log_function(int level, const char *message); typedef void (* jack_log_function_t)(int level, const char *message); #ifdef __cplusplus } #endif #endif jack2-1.9.22/common/JackException.cpp000066400000000000000000000017211436671425200173350ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackException.h" #include "JackError.h" namespace Jack { void JackException::PrintMessage() { std::string str = what(); if (str != "") { jack_info(str.c_str()); } } } jack2-1.9.22/common/JackException.h000066400000000000000000000054211436671425200170030ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackException__ #define __JackException__ #include "JackCompilerDeps.h" #include #include #include namespace Jack { #define ThrowIf(inCondition, inException) \ if(inCondition) \ { \ throw(inException); \ } /*! \brief Exception base class. */ class SERVER_EXPORT JackException : public std::runtime_error { public: JackException(const std::string& msg) : std::runtime_error(msg) {} JackException(char* msg) : std::runtime_error(msg) {} JackException(const char* msg) : std::runtime_error(msg) {} std::string Message() { return what(); } void PrintMessage(); }; /*! \brief Exception thrown by JackEngine in temporary mode. */ class SERVER_EXPORT JackTemporaryException : public JackException { public: JackTemporaryException(const std::string& msg) : JackException(msg) {} JackTemporaryException(char* msg) : JackException(msg) {} JackTemporaryException(const char* msg) : JackException(msg) {} JackTemporaryException() : JackException("") {} }; /*! \brief */ class SERVER_EXPORT JackQuitException : public JackException { public: JackQuitException(const std::string& msg) : JackException(msg) {} JackQuitException(char* msg) : JackException(msg) {} JackQuitException(const char* msg) : JackException(msg) {} JackQuitException() : JackException("") {} }; /*! \brief Exception possibly thrown by Net slaves. */ class SERVER_EXPORT JackNetException : public JackException { public: JackNetException(const std::string& msg) : JackException(msg) {} JackNetException(char* msg) : JackException(msg) {} JackNetException(const char* msg) : JackException(msg) {} JackNetException() : JackException("") {} }; } #endif jack2-1.9.22/common/JackExternalClient.cpp000066400000000000000000000052741436671425200203270ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackExternalClient.h" #include "JackClientControl.h" #include "JackGlobals.h" #include "JackChannel.h" #include "JackError.h" namespace Jack { JackExternalClient::JackExternalClient(): fClientControl(NULL) {} JackExternalClient::~JackExternalClient() {} int JackExternalClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { int result = -1; jack_log("JackExternalClient::ClientNotify ref = %ld client = %s name = %s notify = %ld", refnum, fClientControl->fName, name, notify); fChannel.ClientNotify(refnum, name, notify, sync, message, value1, value2, &result); return result; } int JackExternalClient::Open(const char* name, int pid, int refnum, jack_uuid_t uuid, int* shared_client) { try { if (fChannel.Open(name) < 0) { jack_error("Cannot connect to client name = %s\n", name); return -1; } // Use "placement new" to allocate object in shared memory JackShmMemAble* shared_mem = static_cast(JackShmMem::operator new(sizeof(JackClientControl))); shared_mem->Init(); fClientControl = new(shared_mem) JackClientControl(name, pid, refnum, uuid); if (!fClientControl) { jack_error("Cannot allocate client shared memory segment"); return -1; } *shared_client = shared_mem->GetShmIndex(); jack_log("JackExternalClient::Open name = %s index = %ld base = %x", name, shared_mem->GetShmIndex(), shared_mem->GetShmAddress()); return 0; } catch (std::exception&) { return -1; } } int JackExternalClient::Close() { jack_log("JackExternalClient::Close"); fChannel.Close(); if (fClientControl) { fClientControl->~JackClientControl(); JackShmMem::operator delete(fClientControl); } return 0; } JackClientControl* JackExternalClient::GetClientControl() const { return fClientControl; } } // end of namespace jack2-1.9.22/common/JackExternalClient.h000066400000000000000000000031311436671425200177620ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackExternalClient__ #define __JackExternalClient__ #include "JackClientInterface.h" #include "JackPlatformPlug.h" namespace Jack { struct JackClientControl; /*! \brief Server side implementation of library clients. */ class JackExternalClient : public JackClientInterface { private: JackNotifyChannel fChannel; /*! Server/client communication channel */ JackClientControl* fClientControl; /*! Client control in shared memory */ public: JackExternalClient(); virtual ~JackExternalClient(); int Open(const char* name, int pid, int refnum, jack_uuid_t uuid, int* shared_client); int Close(); int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); JackClientControl* GetClientControl() const; }; } // end of namespace #endif jack2-1.9.22/common/JackFilters.h000066400000000000000000000263611436671425200164630ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackFilters__ #define __JackFilters__ #ifdef __APPLE__ #include #endif #include "jack.h" #ifndef MY_TARGET_OS_IPHONE #include "JackAtomicState.h" #endif #include #include namespace Jack { #ifndef TARGET_OS_IPHONE #define MAX_SIZE 64 PRE_PACKED_STRUCTURE struct JackFilter { jack_time_t fTable[MAX_SIZE]; JackFilter() { for (int i = 0; i < MAX_SIZE; i++) { fTable[i] = 0; } } void AddValue(jack_time_t val) { memcpy(&fTable[1], &fTable[0], sizeof(jack_time_t) * (MAX_SIZE - 1)); fTable[0] = val; } jack_time_t GetVal() { jack_time_t mean = 0; for (int i = 0; i < MAX_SIZE; i++) { mean += fTable[i]; } return mean / MAX_SIZE; } } POST_PACKED_STRUCTURE; PRE_PACKED_STRUCTURE class JackDelayLockedLoop { private: jack_nframes_t fFrames; jack_time_t fCurrentWakeup; jack_time_t fCurrentCallback; jack_time_t fNextWakeUp; float fSecondOrderIntegrator; jack_nframes_t fBufferSize; jack_nframes_t fSampleRate; jack_time_t fPeriodUsecs; float fFilterCoefficient; /* set once, never altered */ bool fUpdating; public: JackDelayLockedLoop() {} JackDelayLockedLoop(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { Init(buffer_size, sample_rate); } void Init(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { fFrames = 0; fCurrentWakeup = 0; fCurrentCallback = 0; fNextWakeUp = 0; fFilterCoefficient = 0.01f; fSecondOrderIntegrator = 0.0f; fBufferSize = buffer_size; fSampleRate = sample_rate; fPeriodUsecs = jack_time_t(1000000.f / fSampleRate * fBufferSize); // in microsec } void Init(jack_time_t callback_usecs) { fFrames = 0; fCurrentWakeup = 0; fSecondOrderIntegrator = 0.0f; fCurrentCallback = callback_usecs; fNextWakeUp = callback_usecs + fPeriodUsecs; } void IncFrame(jack_time_t callback_usecs) { float delta = (int64_t)callback_usecs - (int64_t)fNextWakeUp; fCurrentWakeup = fNextWakeUp; fCurrentCallback = callback_usecs; fFrames += fBufferSize; fSecondOrderIntegrator += 0.5f * fFilterCoefficient * delta; fNextWakeUp = fCurrentWakeup + fPeriodUsecs + (int64_t) floorf((fFilterCoefficient * (delta + fSecondOrderIntegrator))); } jack_nframes_t Time2Frames(jack_time_t time) { long delta = (long) rint(((double) ((long long)(time - fCurrentWakeup)) / ((long long)(fNextWakeUp - fCurrentWakeup))) * fBufferSize); return (delta < 0) ? ((fFrames > 0) ? fFrames : 1) : (fFrames + delta); } jack_time_t Frames2Time(jack_nframes_t frames) { long delta = (long) rint(((double) ((long long)(frames - fFrames)) * ((long long)(fNextWakeUp - fCurrentWakeup))) / fBufferSize); return (delta < 0) ? ((fCurrentWakeup > 0) ? fCurrentWakeup : 1) : (fCurrentWakeup + delta); } jack_nframes_t CurFrame() { return fFrames; } jack_time_t CurTime() { return fCurrentWakeup; } } POST_PACKED_STRUCTURE; PRE_PACKED_STRUCTURE class JackAtomicDelayLockedLoop : public JackAtomicState { public: JackAtomicDelayLockedLoop(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { fState[0].Init(buffer_size, sample_rate); fState[1].Init(buffer_size, sample_rate); } void Init(jack_time_t callback_usecs) { JackDelayLockedLoop* dll = WriteNextStateStart(); dll->Init(callback_usecs); WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } void Init(jack_nframes_t buffer_size, jack_nframes_t sample_rate) { JackDelayLockedLoop* dll = WriteNextStateStart(); dll->Init(buffer_size, sample_rate); WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } void IncFrame(jack_time_t callback_usecs) { JackDelayLockedLoop* dll = WriteNextStateStart(); dll->IncFrame(callback_usecs); WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } jack_nframes_t Time2Frames(jack_time_t time) { UInt16 next_index = GetCurrentIndex(); UInt16 cur_index; jack_nframes_t res; do { cur_index = next_index; res = ReadCurrentState()->Time2Frames(time); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read return res; } jack_time_t Frames2Time(jack_nframes_t frames) { UInt16 next_index = GetCurrentIndex(); UInt16 cur_index; jack_time_t res; do { cur_index = next_index; res = ReadCurrentState()->Frames2Time(frames); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read return res; } } POST_PACKED_STRUCTURE; #endif /* Torben Hohn PI controller from JACK1 */ struct JackPIControler { double resample_mean; double static_resample_factor; double* offset_array; double* window_array; int offset_differential_index; double offset_integral; double catch_factor; double catch_factor2; double pclamp; double controlquant; int smooth_size; double hann(double x) { return 0.5 * (1.0 - cos(2 * M_PI * x)); } JackPIControler(double resample_factor, int fir_size) { resample_mean = resample_factor; static_resample_factor = resample_factor; offset_array = new double[fir_size]; window_array = new double[fir_size]; offset_differential_index = 0; offset_integral = 0.0; smooth_size = fir_size; for (int i = 0; i < fir_size; i++) { offset_array[i] = 0.0; window_array[i] = hann(double(i) / (double(fir_size) - 1.0)); } // These values could be configurable catch_factor = 100000; catch_factor2 = 10000; pclamp = 15.0; controlquant = 10000.0; } ~JackPIControler() { delete[] offset_array; delete[] window_array; } void Init(double resample_factor) { resample_mean = resample_factor; static_resample_factor = resample_factor; } /* double GetRatio(int fill_level) { double offset = fill_level; // Save offset. offset_array[(offset_differential_index++) % smooth_size] = offset; // Build the mean of the windowed offset array basically fir lowpassing. double smooth_offset = 0.0; for (int i = 0; i < smooth_size; i++) { smooth_offset += offset_array[(i + offset_differential_index - 1) % smooth_size] * window_array[i]; } smooth_offset /= double(smooth_size); // This is the integral of the smoothed_offset offset_integral += smooth_offset; // Clamp offset : the smooth offset still contains unwanted noise which would go straight onto the resample coeff. // It only used in the P component and the I component is used for the fine tuning anyways. if (fabs(smooth_offset) < pclamp) smooth_offset = 0.0; // Ok, now this is the PI controller. // u(t) = K * (e(t) + 1/T \int e(t') dt') // Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T double current_resample_factor = static_resample_factor - smooth_offset / catch_factor - offset_integral / catch_factor / catch_factor2; // Now quantize this value around resample_mean, so that the noise which is in the integral component doesn't hurt. current_resample_factor = floor((current_resample_factor - resample_mean) * controlquant + 0.5) / controlquant + resample_mean; // Calculate resample_mean so we can init ourselves to saner values. resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; return current_resample_factor; } */ double GetRatio(int error) { double smooth_offset = error; // This is the integral of the smoothed_offset offset_integral += smooth_offset; // Ok, now this is the PI controller. // u(t) = K * (e(t) + 1/T \int e(t') dt') // Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T return static_resample_factor - smooth_offset/catch_factor - offset_integral/catch_factor/catch_factor2; } void OurOfBounds() { int i; // Set the resample_rate... we need to adjust the offset integral, to do this. // first look at the PI controller, this code is just a special case, which should never execute once // everything is swung in. offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; // Also clear the array. we are beginning a new control cycle. for (i = 0; i < smooth_size; i++) { offset_array[i] = 0.0; } } }; } #endif jack2-1.9.22/common/JackFrameTimer.cpp000066400000000000000000000210311436671425200174260ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackFrameTimer.h" #include "JackError.h" #include #include namespace Jack { #if defined(WIN32) && !defined(__MINGW32__) /* missing on Windows : see http://bugs.mysql.com/bug.php?id=15936 */ inline double rint(double nr) { double f = floor(nr); double c = ceil(nr); return (((c -nr) >= (nr - f)) ? f : c); } #endif JackTimer::JackTimer() { fInitialized = false; fFrames = 0; fCurrentWakeup = 0; fCurrentCallback = 0; fNextWakeUp = 0; fPeriodUsecs = 0.0f; fFilterOmega = 0.0f; /* Initialised later */ } jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size) { if (fInitialized) { /* Make sure we have signed differences. It would make a lot of sense to use the standard signed intNN_t types everywhere instead of e.g. jack_nframes_t and jack_time_t. This would at least ensure that the types used below are the correct ones. There is no way to get a type that would be 'a signed version of jack_time_t' for example - the types below are inherently fragile and there is no automatic way to check they are the correct ones. The only way is to check manually against jack/types.h. FA - 16/02/2012 */ int64_t du = usecs - fCurrentWakeup; int64_t dp = fNextWakeUp - fCurrentWakeup; return fFrames + (int32_t)rint((double)du / (double)dp * buffer_size); } else { return 0; } } jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size) { if (fInitialized) { /* Make sure we have signed differences. It would make a lot of sense to use the standard signed intNN_t types everywhere instead of e.g. jack_nframes_t and jack_time_t. This would at least ensure that the types used below are the correct ones. There is no way to get a type that would be 'a signed version of jack_time_t' for example - the types below are inherently fragile and there is no automatic way to check they are the correct ones. The only way is to check manually against jack/types.h. FA - 16/02/2012 */ int32_t df = frames - fFrames; int64_t dp = fNextWakeUp - fCurrentWakeup; return fCurrentWakeup + (int64_t)rint((double) df * (double) dp / buffer_size); } else { return 0; } } int JackTimer::GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs) { if (fInitialized) { *current_frames = fFrames; *current_usecs = fCurrentWakeup; *next_usecs = fNextWakeUp; *period_usecs = fPeriodUsecs; return 0; } else { return -1; } } jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate) { return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback)); } void JackFrameTimer::InitFrameTime() { fFirstWakeUp = true; } void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs) { if (fFirstWakeUp) { InitFrameTimeAux(callback_usecs, period_usecs); fFirstWakeUp = false; } IncFrameTimeAux(buffer_size, callback_usecs, period_usecs); } void JackFrameTimer::ResetFrameTime(jack_time_t callback_usecs) { if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle JackTimer* timer = WriteNextStateStart(); timer->fCurrentWakeup = callback_usecs; timer->fCurrentCallback = callback_usecs; WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } } /* Use the state returned by ReadCurrentState and check that the state was not changed during the read operation. The operation is lock-free since there is no intermediate state in the write operation that could cause the read to loop forever. */ void JackFrameTimer::ReadFrameTime(JackTimer* timer) { UInt16 next_index = GetCurrentIndex(); UInt16 cur_index; do { cur_index = next_index; memcpy(timer, ReadCurrentState(), sizeof(JackTimer)); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read } // Internal void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs) { /* the first wakeup or post-freewheeling or post-xrun */ /* There seems to be no significant difference between the two conditions OR-ed above. Incrementing the frame_time after an xrun shouldn't harm, as there will be a discontinuity anyway. So the two are combined in this version. FA 16/03/2012 */ /* Since the DLL *will* be run, next_wakeup should be the current wakeup time *without* adding the period time, as if it were computed in the previous period. FA 16/03/2012 */ /* Added initialisation of timer->period_usecs, required due to the modified implementation of the DLL itself. OTOH, this should maybe not be repeated after e.g. freewheeling or an xrun, as the current value would be more accurate than the nominal one. But it doesn't really harm either. Implementing this would require a new flag in the engine structure, to be used after freewheeling or an xrun instead of first_wakeup. I don't know if this can be done without breaking compatibility, so I did not add this FA 13/02/2012 */ /* Added initialisation of timer->filter_omega. This makes the DLL bandwidth independent of the actual period time. The bandwidth is now 1/8 Hz in all cases. The value of timer->filter_omega is 2 * pi * BW * Tperiod. FA 13/02/2012 */ JackTimer* timer = WriteNextStateStart(); timer->fPeriodUsecs = (float)period_usecs; timer->fCurrentCallback = callback_usecs; timer->fNextWakeUp = callback_usecs; timer->fFilterOmega = period_usecs * 7.854e-7f; WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs) { JackTimer* timer = WriteNextStateStart(); /* Modified implementation (the actual result is the same). 'fSecondOrderIntegrator' is renamed to 'fPeriodUsecs' and now represents the DLL's best estimate of the period time in microseconds (before it was a scaled version of the difference w.r.t. the nominal value). This allows this value to be made available to clients that are interested in it (see jack_get_cycle_times). This change also means that 'fPeriodUsecs' must be initialised to the nominal period time instead of zero. This is done in the first cycle in jack_run_cycle(). 'fFilterCoefficient' is renamed to 'fFilterOmega'. It is now equal to the 'omega' value as defined in the 'Using a DLL to filter time' paper (before it was a scaled version of this value). It is computed once in jack_run_cycle() rather than set to a fixed value. This makes the DLL bandwidth independent of the period time. FA 13/02/2012 */ float delta = (float)((int64_t)callback_usecs - (int64_t)timer->fNextWakeUp); delta *= timer->fFilterOmega; timer->fCurrentWakeup = timer->fNextWakeUp; timer->fCurrentCallback = callback_usecs; timer->fFrames += buffer_size; timer->fPeriodUsecs += timer->fFilterOmega * delta; timer->fNextWakeUp += (int64_t)floorf(timer->fPeriodUsecs + 1.41f * delta + 0.5f); timer->fInitialized = true; WriteNextStateStop(); TrySwitchState(); // always succeed since there is only one writer } } // end of namespace jack2-1.9.22/common/JackFrameTimer.h000066400000000000000000000053561436671425200171070ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackFrameTimer__ #define __JackFrameTimer__ #include "JackAtomicState.h" #include "JackCompilerDeps.h" #include "types.h" namespace Jack { /*! \brief A structure used for time management. */ PRE_PACKED_STRUCTURE class SERVER_EXPORT JackTimer { friend class JackFrameTimer; private: jack_nframes_t fFrames; jack_time_t fCurrentWakeup; jack_time_t fCurrentCallback; jack_time_t fNextWakeUp; float fPeriodUsecs; float fFilterOmega; /* set once, never altered */ bool fInitialized; public: JackTimer(); ~JackTimer() {} jack_nframes_t Time2Frames(jack_time_t time, jack_nframes_t buffer_size); jack_time_t Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size); jack_nframes_t FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate); int GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs); jack_nframes_t CurFrame() { return fFrames; } jack_time_t CurTime() { return fCurrentWakeup; } } POST_PACKED_STRUCTURE; /*! \brief A class using the JackAtomicState to manage jack time. */ PRE_PACKED_STRUCTURE class SERVER_EXPORT JackFrameTimer : public JackAtomicState { private: bool fFirstWakeUp; void IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs); void InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs); public: JackFrameTimer(): fFirstWakeUp(true) {} ~JackFrameTimer() {} void InitFrameTime(); void ResetFrameTime(jack_time_t callback_usecs); void IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs); void ReadFrameTime(JackTimer* timer); } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackFreewheelDriver.cpp000066400000000000000000000052721436671425200204660ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackFreewheelDriver.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" namespace Jack { // When used in "master" mode int JackFreewheelDriver::Process() { jack_log("JackFreewheelDriver::Process master %lld", fEngineControl->fTimeOutUsecs); JackDriver::CycleTakeBeginTime(); if (fEngine->Process(fBeginDateUst, fEndDateUst)) { // Resume connected clients in the graph if (ResumeRefNum() < 0) { jack_error("JackFreewheelDriver::Process: ResumeRefNum error"); } // Special "SuspendRefNum" with longer timeout if (SuspendRefNum() < 0) { // Wait for all clients to finish for FREEWHEEL_DRIVER_TIMEOUT sec jack_error("JackFreewheelDriver::Process: SuspendRefNum error"); } } else { // Graph not finished: do not activate it jack_error("JackFreewheelDriver::Process: Process error"); } return 0; } // When used in "slave" mode int JackFreewheelDriver::ProcessReadSync() { // Resume connected clients in the graph if (ResumeRefNum() < 0) { // Signal all clients jack_error("JackFreewheelDriver::ProcessReadSync: ResumeRefNum error"); return -1; } return 0; } int JackFreewheelDriver::ProcessWriteSync() { // Generic "SuspendRefNum" here if (JackDriver::SuspendRefNum() < 0) { jack_error("JackFreewheelDriver::ProcessSync: SuspendRefNum error"); return -1; } return 0; } int JackFreewheelDriver::ProcessReadAsync() { // Resume connected clients in the graph if (ResumeRefNum() < 0) { // Signal all clients jack_error("JackFreewheelDriver::ProcessReadAsync: ResumeRefNum error"); return -1; } return 0; } int JackFreewheelDriver::ProcessWriteAsync() { return 0; } int JackFreewheelDriver::SuspendRefNum() { return fGraphManager->SuspendRefNum(&fClientControl, fSynchroTable, FREEWHEEL_DRIVER_TIMEOUT * 1000000); } } // end of namespace jack2-1.9.22/common/JackFreewheelDriver.h000066400000000000000000000027021436671425200201260ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackFreewheelDriver__ #define __JackFreewheelDriver__ #include "JackDriver.h" namespace Jack { /*! \brief The FreeWheel driver : run Jack engine at full speed. */ class JackFreewheelDriver : public JackDriver { protected: int SuspendRefNum(); public: JackFreewheelDriver(JackLockedEngine* engine, JackSynchro* table): JackDriver("freewheel", "", engine, table) {} virtual ~JackFreewheelDriver() {} bool IsRealTime() const { return false; } int Process(); int ProcessReadSync(); int ProcessWriteSync(); int ProcessReadAsync(); int ProcessWriteAsync(); }; } // end of namespace #endif jack2-1.9.22/common/JackGenericClientChannel.cpp000066400000000000000000000232001436671425200213770ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackGenericClientChannel.h" #include "JackClient.h" #include "JackGlobals.h" #include "JackError.h" namespace Jack { JackGenericClientChannel::JackGenericClientChannel() {} JackGenericClientChannel::~JackGenericClientChannel() {} int JackGenericClientChannel::ServerCheck(const char* server_name) { jack_log("JackGenericClientChannel::ServerCheck = %s", server_name); // Connect to server if (fRequest->Connect(jack_server_dir, server_name, 0) < 0) { jack_error("Cannot connect to server request channel"); return -1; } else { return 0; } } void JackGenericClientChannel::ServerSyncCall(JackRequest* req, JackResult* res, int* result) { // Check call context if (jack_tls_get(JackGlobals::fNotificationThread)) { jack_error("Cannot callback the server in notification thread!"); *result = -1; return; } if (!JackGlobals::fServerRunning) { jack_error("Server is not running"); *result = -1; return; } if (req->Write(fRequest) < 0) { jack_error("Could not write request type = %ld", req->fType); *result = -1; return; } if (res->Read(fRequest) < 0) { jack_error("Could not read result type = %ld", req->fType); *result = -1; return; } *result = res->fResult; } void JackGenericClientChannel::ServerAsyncCall(JackRequest* req, JackResult* res, int* result) { // Check call context if (jack_tls_get(JackGlobals::fNotificationThread)) { jack_error("Cannot callback the server in notification thread!"); *result = -1; return; } if (!JackGlobals::fServerRunning) { jack_error("Server is not running"); *result = -1; return; } if (req->Write(fRequest) < 0) { jack_error("Could not write request type = %ld", req->fType); *result = -1; } else { *result = 0; } } void JackGenericClientChannel::ClientCheck(const char* name, jack_uuid_t uuid, char* name_res, int protocol, int options, int* status, int* result, int open) { JackClientCheckRequest req(name, protocol, options, uuid, open); JackClientCheckResult res; ServerSyncCall(&req, &res, result); *status |= res.fStatus; strcpy(name_res, res.fName); } void JackGenericClientChannel::ClientOpen(const char* name, int pid, jack_uuid_t uuid, int* shared_engine, int* shared_client, int* shared_graph, int* result) { JackClientOpenRequest req(name, pid, uuid); JackClientOpenResult res; ServerSyncCall(&req, &res, result); *shared_engine = res.fSharedEngine; *shared_client = res.fSharedClient; *shared_graph = res.fSharedGraph; } void JackGenericClientChannel::ClientClose(int refnum, int* result) { JackClientCloseRequest req(refnum); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::ClientActivate(int refnum, int is_real_time, int* result) { JackActivateRequest req(refnum, is_real_time); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::ClientDeactivate(int refnum, int* result) { JackDeactivateRequest req(refnum); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) { JackPortRegisterRequest req(refnum, name, type, flags, buffer_size); JackPortRegisterResult res; ServerSyncCall(&req, &res, result); *port_index = res.fPortIndex; } void JackGenericClientChannel::PortUnRegister(int refnum, jack_port_id_t port_index, int* result) { JackPortUnRegisterRequest req(refnum, port_index); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::PortConnect(int refnum, const char* src, const char* dst, int* result) { JackPortConnectNameRequest req(refnum, src, dst); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::PortDisconnect(int refnum, const char* src, const char* dst, int* result) { JackPortDisconnectNameRequest req(refnum, src, dst); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) { JackPortConnectRequest req(refnum, src, dst); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) { JackPortDisconnectRequest req(refnum, src, dst); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::PortRename(int refnum, jack_port_id_t port, const char* name, int* result) { JackPortRenameRequest req(refnum, port, name); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::SetBufferSize(jack_nframes_t buffer_size, int* result) { JackSetBufferSizeRequest req(buffer_size); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::SetFreewheel(int onoff, int* result) { JackSetFreeWheelRequest req(onoff); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::ComputeTotalLatencies(int* result) { JackComputeTotalLatenciesRequest req; JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char* path, jack_session_command_t** result) { JackSessionNotifyRequest req(refnum, path, type, target); JackSessionNotifyResult res; int intresult; ServerSyncCall(&req, &res, &intresult); *result = res.GetCommands(); } void JackGenericClientChannel::SessionReply(int refnum, int* result) { JackSessionReplyRequest req(refnum); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::GetUUIDForClientName(int refnum, const char* client_name, char* uuid_res, int* result) { JackGetUUIDRequest req(client_name); JackUUIDResult res; ServerSyncCall(&req, &res, result); strncpy(uuid_res, res.fUUID, JACK_UUID_SIZE); } void JackGenericClientChannel::GetClientNameForUUID(int refnum, const char* uuid, char* name_res, int* result) { JackGetClientNameRequest req(uuid); JackClientNameResult res; ServerSyncCall(&req, &res, result); strncpy(name_res, res.fName, JACK_CLIENT_NAME_SIZE); } void JackGenericClientChannel::ClientHasSessionCallback(const char* client_name, int* result) { JackClientHasSessionCallbackRequest req(client_name); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::ReserveClientName(int refnum, const char* client_name, const char* uuid, int* result) { JackReserveNameRequest req(refnum, client_name, uuid); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::ReleaseTimebase(int refnum, int* result) { JackReleaseTimebaseRequest req(refnum); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::SetTimebaseCallback(int refnum, int conditional, int* result) { JackSetTimebaseCallbackRequest req(refnum, conditional); JackResult res; ServerSyncCall(&req, &res, result); } void JackGenericClientChannel::GetInternalClientName(int refnum, int int_ref, char* name_res, int* result) { JackGetInternalClientNameRequest req(refnum, int_ref); JackGetInternalClientNameResult res; ServerSyncCall(&req, &res, result); strcpy(name_res, res.fName); } void JackGenericClientChannel::InternalClientHandle(int refnum, const char* client_name, int* status, int* int_ref, int* result) { JackInternalClientHandleRequest req(refnum, client_name); JackInternalClientHandleResult res; ServerSyncCall(&req, &res, result); *int_ref = res.fIntRefNum; *status = res.fStatus; } void JackGenericClientChannel::InternalClientLoad(int refnum, const char* client_name, const char* so_name, const char* objet_data, int options, int* status, int* int_ref, jack_uuid_t uuid, int* result) { JackInternalClientLoadRequest req(refnum, client_name, so_name, objet_data, options, uuid); JackInternalClientLoadResult res; ServerSyncCall(&req, &res, result); *int_ref = res.fIntRefNum; *status = res.fStatus; } void JackGenericClientChannel::InternalClientUnload(int refnum, int int_ref, int* status, int* result) { JackInternalClientUnloadRequest req(refnum, int_ref); JackInternalClientUnloadResult res; ServerSyncCall(&req, &res, result); *status = res.fStatus; } void JackGenericClientChannel::PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change, int* result) { JackPropertyChangeNotifyRequest req(subject, key, change); JackResult res; ServerAsyncCall(&req, &res, result); } } // end of namespace jack2-1.9.22/common/JackGenericClientChannel.h000066400000000000000000000104151436671425200210500ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackGenericClientChannel__ #define __JackGenericClientChannel__ #include "JackChannel.h" namespace Jack { struct JackRequest; struct JackResult; /*! \brief Generic JackClientChannel class. */ class JackGenericClientChannel : public detail::JackClientChannelInterface { protected: detail::JackClientRequestInterface* fRequest; void ServerSyncCall(JackRequest* req, JackResult* res, int* result); void ServerAsyncCall(JackRequest* req, JackResult* res, int* result); public: JackGenericClientChannel(); virtual ~JackGenericClientChannel(); virtual int Open(const char* server_name, const char* name, jack_uuid_t uuid, char* name_res, JackClient* obj, jack_options_t options, jack_status_t* status) { return -1; } virtual void Close() {} virtual int Start() { return -1; } virtual void Stop() {} int ServerCheck(const char* server_name); void ClientCheck(const char* name, jack_uuid_t uuid, char* name_res, int protocol, int options, int* status, int* result, int open); void ClientOpen(const char* name, int pid, jack_uuid_t uuid, int* shared_engine, int* shared_client, int* shared_graph, int* result); void ClientClose(int refnum, int* result); void ClientActivate(int refnum, int is_real_time, int* result); void ClientDeactivate(int refnum, int* result); void PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result); void PortUnRegister(int refnum, jack_port_id_t port_index, int* result); void PortConnect(int refnum, const char* src, const char* dst, int* result); void PortDisconnect(int refnum, const char* src, const char* dst, int* result); void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result); void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result); void PortRename(int refnum, jack_port_id_t port, const char* name, int* result); void SetBufferSize(jack_nframes_t buffer_size, int* result); void SetFreewheel(int onoff, int* result); void ComputeTotalLatencies(int* result); void ReleaseTimebase(int refnum, int* result); void SetTimebaseCallback(int refnum, int conditional, int* result); void GetInternalClientName(int refnum, int int_ref, char* name_res, int* result); void InternalClientHandle(int refnum, const char* client_name, int* status, int* int_ref, int* result); void InternalClientLoad(int refnum, const char* client_name, const char* so_name, const char* objet_data, int options, int* status, int* int_ref, jack_uuid_t uuid, int* result); void InternalClientUnload(int refnum, int int_ref, int* status, int* result); // Session API void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char* path, jack_session_command_t** result); void SessionReply(int refnum, int* result); void GetUUIDForClientName(int refnum, const char* client_name, char* uuid_res, int* result); void GetClientNameForUUID(int refnum, const char* uuid, char* name_res, int* result); void ReserveClientName(int refnum, const char* client_name, const char *uuid, int* result); void ClientHasSessionCallback(const char* client_name, int* result); void PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change, int* result); }; } // end of namespace #endif jack2-1.9.22/common/JackGlobals.cpp000066400000000000000000000053031436671425200167620ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackGlobals.h" namespace Jack { bool JackGlobals::fVerbose = 0; jack_tls_key JackGlobals::fRealTimeThread; static bool gKeyRealtimeThreadInitialized = jack_tls_allocate_key(&JackGlobals::fRealTimeThread); jack_tls_key JackGlobals::fNotificationThread; static bool gKeyNotificationThreadInitialized = jack_tls_allocate_key(&JackGlobals::fNotificationThread); jack_tls_key JackGlobals::fKeyLogFunction; static bool fKeyLogFunctionInitialized = jack_tls_allocate_key(&JackGlobals::fKeyLogFunction); JackMutex* JackGlobals::fOpenMutex = new JackMutex(); JackMutex* JackGlobals::fSynchroMutex = new JackMutex(); volatile bool JackGlobals::fServerRunning = false; JackClient* JackGlobals::fClientTable[CLIENT_NUM] = {}; #ifndef WIN32 jack_thread_creator_t JackGlobals::fJackThreadCreator = pthread_create; #endif #ifdef __CLIENTDEBUG__ std::ofstream* JackGlobals::fStream = NULL; void JackGlobals::CheckContext(const char* name) { if (JackGlobals::fStream == NULL) { char provstr[256]; char buffer[256]; time_t curtime; struct tm *loctime; /* Get the current time. */ curtime = time (NULL); /* Convert it to local time representation. */ loctime = localtime (&curtime); strftime(buffer, 256, "%I-%M", loctime); snprintf(provstr, sizeof(provstr), "JackAPICall-%s.log", buffer); JackGlobals::fStream = new std::ofstream(provstr, std::ios_base::ate); JackGlobals::fStream->is_open(); } #ifdef PTHREAD_WIN32 /* Added by JE - 10-10-2011 */ (*fStream) << "JACK API call : " << name << ", calling thread : " << pthread_self().p << std::endl; #elif defined(WIN32) && !defined(__CYGWIN__) (*fStream) << "JACK API call : " << name << ", calling thread : " << GetCurrentThread() << std::endl; #else (*fStream) << "JACK API call : " << name << ", calling thread : " << pthread_self() << std::endl; #endif } #else void JackGlobals::CheckContext(const char* name) {} #endif } // end of namespace jack2-1.9.22/common/JackGlobals.h000066400000000000000000000036101436671425200164260ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackGlobals__ #define __JackGlobals__ #include "JackPlatformPlug.h" #include "JackSystemDeps.h" #include "JackConstants.h" #ifdef __CLIENTDEBUG__ #include #include #include #include #endif namespace Jack { // Globals used for client management on server or library side. struct JackGlobals { static jack_tls_key fRealTimeThread; static jack_tls_key fNotificationThread; static jack_tls_key fKeyLogFunction; static JackMutex* fOpenMutex; static JackMutex* fSynchroMutex; static volatile bool fServerRunning; static JackClient* fClientTable[CLIENT_NUM]; static bool fVerbose; #ifndef WIN32 static jack_thread_creator_t fJackThreadCreator; #endif #ifdef __CLIENTDEBUG__ static std::ofstream* fStream; #endif static void CheckContext(const char* name); }; // Each "side" server and client will implement this to get the shared graph manager, engine control and inter-process synchro table. extern SERVER_EXPORT JackGraphManager* GetGraphManager(); extern SERVER_EXPORT JackEngineControl* GetEngineControl(); extern SERVER_EXPORT JackSynchro* GetSynchroTable(); } // end of namespace #endif jack2-1.9.22/common/JackGraphManager.cpp000066400000000000000000000705721436671425200177450ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackGraphManager.h" #include "JackConstants.h" #include "JackError.h" #include #include #include #ifdef HAVE_TRE_REGEX_H #include #else #include #endif namespace Jack { static void AssertBufferSize(jack_nframes_t buffer_size) { if (buffer_size > BUFFER_SIZE_MAX) { jack_log("JackGraphManager::AssertBufferSize frames = %ld", buffer_size); assert(buffer_size <= BUFFER_SIZE_MAX); } } void JackGraphManager::AssertPort(jack_port_id_t port_index) { if (port_index >= fPortMax) { jack_log("JackGraphManager::AssertPort port_index = %ld", port_index); assert(port_index < fPortMax); } } JackGraphManager* JackGraphManager::Allocate(int port_max) { // Using "Placement" new void* shared_ptr = JackShmMem::operator new(sizeof(JackGraphManager) + port_max * sizeof(JackPort)); return new(shared_ptr) JackGraphManager(port_max); } void JackGraphManager::Destroy(JackGraphManager* manager) { // "Placement" new was used manager->~JackGraphManager(); JackShmMem::operator delete(manager); } JackGraphManager::JackGraphManager(int port_max) { assert(port_max <= PORT_NUM_MAX); for (int i = 0; i < port_max; i++) { fPortArray[i].Release(); } fPortMax = port_max; } JackPort* JackGraphManager::GetPort(jack_port_id_t port_index) { AssertPort(port_index); return &fPortArray[port_index]; } jack_default_audio_sample_t* JackGraphManager::GetBuffer(jack_port_id_t port_index) { return fPortArray[port_index].GetBuffer(); } // Server void JackGraphManager::InitRefNum(int refnum) { JackConnectionManager* manager = WriteNextStateStart(); manager->InitRefNum(refnum); WriteNextStateStop(); } // RT void JackGraphManager::RunCurrentGraph() { JackConnectionManager* manager = ReadCurrentState(); manager->ResetGraph(fClientTiming); } // RT bool JackGraphManager::RunNextGraph() { bool res; JackConnectionManager* manager = TrySwitchState(&res); manager->ResetGraph(fClientTiming); return res; } // RT bool JackGraphManager::IsFinishedGraph() { JackConnectionManager* manager = ReadCurrentState(); return (manager->GetActivation(FREEWHEEL_DRIVER_REFNUM) == 0); } // RT int JackGraphManager::ResumeRefNum(JackClientControl* control, JackSynchro* table) { JackConnectionManager* manager = ReadCurrentState(); return manager->ResumeRefNum(control, table, fClientTiming); } // RT int JackGraphManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, long usec) { JackConnectionManager* manager = ReadCurrentState(); return manager->SuspendRefNum(control, table, fClientTiming, usec); } void JackGraphManager::TopologicalSort(std::vector& sorted) { UInt16 cur_index; UInt16 next_index; do { cur_index = GetCurrentIndex(); sorted.clear(); ReadCurrentState()->TopologicalSort(sorted); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read } // Server void JackGraphManager::DirectConnect(int ref1, int ref2) { JackConnectionManager* manager = WriteNextStateStart(); manager->DirectConnect(ref1, ref2); jack_log("JackGraphManager::ConnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld", CurIndex(fCounter), ref1, ref2); WriteNextStateStop(); } // Server void JackGraphManager::DirectDisconnect(int ref1, int ref2) { JackConnectionManager* manager = WriteNextStateStart(); manager->DirectDisconnect(ref1, ref2); jack_log("JackGraphManager::DisconnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld", CurIndex(fCounter), ref1, ref2); WriteNextStateStop(); } // Server bool JackGraphManager::IsDirectConnection(int ref1, int ref2) { JackConnectionManager* manager = ReadCurrentState(); return manager->IsDirectConnection(ref1, ref2); } // RT void* JackGraphManager::GetBuffer(jack_port_id_t port_index, jack_nframes_t buffer_size) { AssertPort(port_index); AssertBufferSize(buffer_size); JackConnectionManager* manager = ReadCurrentState(); JackPort* port = GetPort(port_index); // This happens when a port has just been unregistered and is still used by the RT code if (!port->IsUsed()) { jack_log("JackGraphManager::GetBuffer : port = %ld is released state", port_index); return GetBuffer(0); // port_index 0 is not used } jack_int_t len = manager->Connections(port_index); // Output port if (port->fFlags & JackPortIsOutput) { return (port->fTied != NO_PORT) ? GetBuffer(port->fTied, buffer_size) : GetBuffer(port_index); } // No connections : return a zero-filled buffer if (len == 0) { port->ClearBuffer(buffer_size); return port->GetBuffer(); // One connection } else if (len == 1) { jack_port_id_t src_index = manager->GetPort(port_index, 0); // Ports in same client : copy the buffer if (GetPort(src_index)->GetRefNum() == port->GetRefNum()) { void* buffers[1]; buffers[0] = GetBuffer(src_index, buffer_size); port->MixBuffers(buffers, 1, buffer_size); return port->GetBuffer(); // Otherwise, use zero-copy mode, just pass the buffer of the connected (output) port. } else { return GetBuffer(src_index, buffer_size); } // Multiple connections : mix all buffers } else { const jack_int_t* connections = manager->GetConnections(port_index); void* buffers[CONNECTION_NUM_FOR_PORT]; jack_port_id_t src_index; int i; for (i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((src_index = connections[i]) != EMPTY); i++) { AssertPort(src_index); buffers[i] = GetBuffer(src_index, buffer_size); } port->MixBuffers(buffers, i, buffer_size); return port->GetBuffer(); } } // Server int JackGraphManager::RequestMonitor(jack_port_id_t port_index, bool onoff) // Client { AssertPort(port_index); JackPort* port = GetPort(port_index); /** jackd.h * If @ref JackPortCanMonitor is set for this @a port, turn input * monitoring on or off. Otherwise, do nothing. if (!(fFlags & JackPortCanMonitor)) return -1; */ port->RequestMonitor(onoff); const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index); if ((port->fFlags & JackPortIsOutput) == 0) { // ?? Taken from jack, why not (port->fFlags & JackPortIsInput) ? jack_port_id_t src_index; for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((src_index = connections[i]) != EMPTY); i++) { // XXX much worse things will happen if there is a feedback loop !!! RequestMonitor(src_index, onoff); } } return 0; } // Client jack_nframes_t JackGraphManager::ComputeTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count) { const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index); jack_nframes_t max_latency = 0; jack_port_id_t dst_index; if (hop_count > 8) return GetPort(port_index)->GetLatency(); for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((dst_index = connections[i]) != EMPTY); i++) { if (src_port_index != dst_index) { AssertPort(dst_index); JackPort* dst_port = GetPort(dst_index); jack_nframes_t this_latency = (dst_port->fFlags & JackPortIsTerminal) ? dst_port->GetLatency() : ComputeTotalLatencyAux(dst_index, port_index, manager, hop_count + 1); max_latency = ((max_latency > this_latency) ? max_latency : this_latency); } } return max_latency + GetPort(port_index)->GetLatency(); } // Client int JackGraphManager::ComputeTotalLatency(jack_port_id_t port_index) { UInt16 cur_index; UInt16 next_index; JackPort* port = GetPort(port_index); AssertPort(port_index); do { cur_index = GetCurrentIndex(); port->fTotalLatency = ComputeTotalLatencyAux(port_index, port_index, ReadCurrentState(), 0); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read jack_log("JackGraphManager::GetTotalLatency port_index = %ld total latency = %ld", port_index, port->fTotalLatency); return 0; } // Client int JackGraphManager::ComputeTotalLatencies() { jack_port_id_t port_index; for (port_index = FIRST_AVAILABLE_PORT; port_index < fPortMax; port_index++) { JackPort* port = GetPort(port_index); if (port->IsUsed()) { ComputeTotalLatency(port_index); } } return 0; } void JackGraphManager::RecalculateLatencyAux(jack_port_id_t port_index, jack_latency_callback_mode_t mode) { const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index); JackPort* port = GetPort(port_index); jack_latency_range_t latency = { UINT32_MAX, 0 }; jack_port_id_t dst_index; for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((dst_index = connections[i]) != EMPTY); i++) { AssertPort(dst_index); JackPort* dst_port = GetPort(dst_index); jack_latency_range_t other_latency; dst_port->GetLatencyRange(mode, &other_latency); if (other_latency.max > latency.max) { latency.max = other_latency.max; } if (other_latency.min < latency.min) { latency.min = other_latency.min; } } if (latency.min == UINT32_MAX) { latency.min = 0; } port->SetLatencyRange(mode, &latency); } void JackGraphManager::RecalculateLatency(jack_port_id_t port_index, jack_latency_callback_mode_t mode) { UInt16 cur_index; UInt16 next_index; do { cur_index = GetCurrentIndex(); RecalculateLatencyAux(port_index, mode); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read //jack_log("JackGraphManager::RecalculateLatency port_index = %ld", port_index); } // Server void JackGraphManager::SetBufferSize(jack_nframes_t buffer_size) { jack_log("JackGraphManager::SetBufferSize size = %ld", buffer_size); jack_port_id_t port_index; for (port_index = FIRST_AVAILABLE_PORT; port_index < fPortMax; port_index++) { JackPort* port = GetPort(port_index); if (port->IsUsed()) { port->ClearBuffer(buffer_size); } } } // Server jack_port_id_t JackGraphManager::AllocatePortAux(int refnum, const char* port_name, const char* port_type, JackPortFlags flags) { jack_port_id_t port_index; // Available ports start at FIRST_AVAILABLE_PORT (= 1), otherwise a port_index of 0 is "seen" as a NULL port by the external API... for (port_index = FIRST_AVAILABLE_PORT; port_index < fPortMax; port_index++) { JackPort* port = GetPort(port_index); if (!port->IsUsed()) { jack_log("JackGraphManager::AllocatePortAux port_index = %ld name = %s type = %s", port_index, port_name, port_type); if (!port->Allocate(refnum, port_name, port_type, flags)) { return NO_PORT; } break; } } return (port_index < fPortMax) ? port_index : NO_PORT; } // Server jack_port_id_t JackGraphManager::AllocatePort(int refnum, const char* port_name, const char* port_type, JackPortFlags flags, jack_nframes_t buffer_size) { JackConnectionManager* manager = WriteNextStateStart(); jack_port_id_t port_index = AllocatePortAux(refnum, port_name, port_type, flags); if (port_index != NO_PORT) { JackPort* port = GetPort(port_index); assert(port); port->ClearBuffer(buffer_size); int res; if (flags & JackPortIsOutput) { res = manager->AddOutputPort(refnum, port_index); } else { res = manager->AddInputPort(refnum, port_index); } // Insertion failure if (res < 0) { port->Release(); port_index = NO_PORT; } } WriteNextStateStop(); return port_index; } // Server int JackGraphManager::ReleasePort(int refnum, jack_port_id_t port_index) { JackConnectionManager* manager = WriteNextStateStart(); JackPort* port = GetPort(port_index); int res; if (port->fFlags & JackPortIsOutput) { DisconnectAllOutput(port_index); res = manager->RemoveOutputPort(refnum, port_index); } else { DisconnectAllInput(port_index); res = manager->RemoveInputPort(refnum, port_index); } port->Release(); WriteNextStateStop(); return res; } void JackGraphManager::GetInputPorts(int refnum, jack_int_t* res) { JackConnectionManager* manager = WriteNextStateStart(); const jack_int_t* input = manager->GetInputPorts(refnum); memcpy(res, input, sizeof(jack_int_t) * PORT_NUM_FOR_CLIENT); WriteNextStateStop(); } void JackGraphManager::GetOutputPorts(int refnum, jack_int_t* res) { JackConnectionManager* manager = WriteNextStateStart(); const jack_int_t* output = manager->GetOutputPorts(refnum); memcpy(res, output, sizeof(jack_int_t) * PORT_NUM_FOR_CLIENT); WriteNextStateStop(); } // Server void JackGraphManager::RemoveAllPorts(int refnum) { jack_log("JackGraphManager::RemoveAllPorts ref = %ld", refnum); JackConnectionManager* manager = WriteNextStateStart(); jack_port_id_t port_index; // Warning : ReleasePort shift port to left, thus we always remove the first port until the "input" table is empty const jack_int_t* input = manager->GetInputPorts(refnum); while ((port_index = input[0]) != EMPTY) { int res = ReleasePort(refnum, port_index); if (res < 0) { jack_error("JackGraphManager::RemoveAllPorts failure ref = %ld port_index = %ld", refnum, port_index); assert(true); break; } } // Warning : ReleasePort shift port to left, thus we always remove the first port until the "output" table is empty const jack_int_t* output = manager->GetOutputPorts(refnum); while ((port_index = output[0]) != EMPTY) { int res = ReleasePort(refnum, port_index); if (res < 0) { jack_error("JackGraphManager::RemoveAllPorts failure ref = %ld port_index = %ld", refnum, port_index); assert(true); break; } } WriteNextStateStop(); } // Server void JackGraphManager::DisconnectAllPorts(int refnum) { int i; jack_log("JackGraphManager::DisconnectAllPorts ref = %ld", refnum); JackConnectionManager* manager = WriteNextStateStart(); const jack_int_t* input = manager->GetInputPorts(refnum); for (i = 0; i < PORT_NUM_FOR_CLIENT && input[i] != EMPTY ; i++) { DisconnectAllInput(input[i]); } const jack_int_t* output = manager->GetOutputPorts(refnum); for (i = 0; i < PORT_NUM_FOR_CLIENT && output[i] != EMPTY; i++) { DisconnectAllOutput(output[i]); } WriteNextStateStop(); } // Server void JackGraphManager::DisconnectAllInput(jack_port_id_t port_index) { jack_log("JackGraphManager::DisconnectAllInput port_index = %ld", port_index); JackConnectionManager* manager = WriteNextStateStart(); for (unsigned int i = 0; i < fPortMax; i++) { if (manager->IsConnected(i, port_index)) { jack_log("JackGraphManager::Disconnect i = %ld port_index = %ld", i, port_index); Disconnect(i, port_index); } } WriteNextStateStop(); } // Server void JackGraphManager::DisconnectAllOutput(jack_port_id_t port_index) { jack_log("JackGraphManager::DisconnectAllOutput port_index = %ld ", port_index); JackConnectionManager* manager = WriteNextStateStart(); const jack_int_t* connections = manager->GetConnections(port_index); while (connections[0] != EMPTY) { Disconnect(port_index, connections[0]); // Warning : Disconnect shift port to left } WriteNextStateStop(); } // Server int JackGraphManager::DisconnectAll(jack_port_id_t port_index) { AssertPort(port_index); JackPort* port = GetPort(port_index); if (port->fFlags & JackPortIsOutput) { DisconnectAllOutput(port_index); } else { DisconnectAllInput(port_index); } return 0; } // Server void JackGraphManager::GetConnections(jack_port_id_t port_index, jack_int_t* res) { JackConnectionManager* manager = WriteNextStateStart(); const jack_int_t* connections = manager->GetConnections(port_index); memcpy(res, connections, sizeof(jack_int_t) * CONNECTION_NUM_FOR_PORT); WriteNextStateStop(); } // Server void JackGraphManager::Activate(int refnum) { DirectConnect(FREEWHEEL_DRIVER_REFNUM, refnum); DirectConnect(refnum, FREEWHEEL_DRIVER_REFNUM); } /* Disconnection from the FW must be done in last otherwise an intermediate "unconnected" (thus unactivated) state may happen where the client is still checked for its end. */ // Server void JackGraphManager::Deactivate(int refnum) { // Disconnect only when needed if (IsDirectConnection(refnum, FREEWHEEL_DRIVER_REFNUM)) { DirectDisconnect(refnum, FREEWHEEL_DRIVER_REFNUM); } else { jack_log("JackServer::Deactivate client = %ld was not activated", refnum); } // Disconnect only when needed if (IsDirectConnection(FREEWHEEL_DRIVER_REFNUM, refnum)) { DirectDisconnect(FREEWHEEL_DRIVER_REFNUM, refnum); } else { jack_log("JackServer::Deactivate client = %ld was not activated", refnum); } } // Server int JackGraphManager::GetInputRefNum(jack_port_id_t port_index) { AssertPort(port_index); JackConnectionManager* manager = WriteNextStateStart(); int res = manager->GetInputRefNum(port_index); WriteNextStateStop(); return res; } // Server int JackGraphManager::GetOutputRefNum(jack_port_id_t port_index) { AssertPort(port_index); JackConnectionManager* manager = WriteNextStateStart(); int res = manager->GetOutputRefNum(port_index); WriteNextStateStop(); return res; } int JackGraphManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst) { JackConnectionManager* manager = WriteNextStateStart(); jack_log("JackGraphManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst); JackPort* src = GetPort(port_src); JackPort* dst = GetPort(port_dst); int res = 0; if (!src->fInUse || !dst->fInUse) { if (!src->fInUse) jack_error("JackGraphManager::Connect port_src = %ld not used name = %s", port_src, GetPort(port_src)->fName); if (!dst->fInUse) jack_error("JackGraphManager::Connect port_dst = %ld not used name = %s", port_dst, GetPort(port_dst)->fName); res = -1; goto end; } if (src->fTypeId != dst->fTypeId) { jack_error("JackGraphManager::Connect different port types port_src = %ld port_dst = %ld", port_src, port_dst); res = -1; goto end; } if (manager->IsConnected(port_src, port_dst)) { jack_error("JackGraphManager::Connect already connected port_src = %ld port_dst = %ld", port_src, port_dst); res = EEXIST; goto end; } res = manager->Connect(port_src, port_dst); if (res < 0) { jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_src, port_dst); goto end; } res = manager->Connect(port_dst, port_src); if (res < 0) { jack_error("JackGraphManager::Connect failed port_dst = %ld port_src = %ld", port_dst, port_src); goto end; } if (manager->IsLoopPath(port_src, port_dst)) { jack_log("JackGraphManager::Connect: LOOP detected"); manager->IncFeedbackConnection(port_src, port_dst); } else { manager->IncDirectConnection(port_src, port_dst); } end: WriteNextStateStop(); return res; } // Server int JackGraphManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst) { JackConnectionManager* manager = WriteNextStateStart(); jack_log("JackGraphManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst); bool in_use_src = GetPort(port_src)->fInUse; bool in_use_dst = GetPort(port_dst)->fInUse; int res = 0; if (!in_use_src || !in_use_dst) { if (!in_use_src) jack_error("JackGraphManager::Disconnect: port_src = %ld not used name = %s", port_src, GetPort(port_src)->fName); if (!in_use_dst) jack_error("JackGraphManager::Disconnect: port_src = %ld not used name = %s", port_dst, GetPort(port_dst)->fName); res = -1; goto end; } if (!manager->IsConnected(port_src, port_dst)) { jack_error("JackGraphManager::Disconnect not connected port_src = %ld port_dst = %ld", port_src, port_dst); res = -1; goto end; } res = manager->Disconnect(port_src, port_dst); if (res < 0) { jack_error("JackGraphManager::Disconnect failed port_src = %ld port_dst = %ld", port_src, port_dst); goto end; } res = manager->Disconnect(port_dst, port_src); if (res < 0) { jack_error("JackGraphManager::Disconnect failed port_dst = %ld port_src = %ld", port_dst, port_src); goto end; } if (manager->IsFeedbackConnection(port_src, port_dst)) { jack_log("JackGraphManager::Disconnect: FEEDBACK removed"); manager->DecFeedbackConnection(port_src, port_dst); } else { manager->DecDirectConnection(port_src, port_dst); } end: WriteNextStateStop(); return res; } // Client int JackGraphManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) { JackConnectionManager* manager = ReadCurrentState(); return manager->IsConnected(port_src, port_dst); } // Server int JackGraphManager::CheckPorts(jack_port_id_t port_src, jack_port_id_t port_dst) { JackPort* src = GetPort(port_src); JackPort* dst = GetPort(port_dst); if ((dst->fFlags & JackPortIsInput) == 0) { jack_error("Destination port in attempted (dis)connection of %s and %s is not an input port", src->fName, dst->fName); return -1; } if ((src->fFlags & JackPortIsOutput) == 0) { jack_error("Source port in attempted (dis)connection of %s and %s is not an output port", src->fName, dst->fName); return -1; } return 0; } int JackGraphManager::GetTwoPorts(const char* src_name, const char* dst_name, jack_port_id_t* port_src, jack_port_id_t* port_dst) { jack_log("JackGraphManager::CheckConnect src_name = %s dst_name = %s", src_name, dst_name); if ((*port_src = GetPort(src_name)) == NO_PORT) { jack_error("Unknown source port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name); return -1; } if ((*port_dst = GetPort(dst_name)) == NO_PORT) { jack_error("Unknown destination port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name); return -1; } return 0; } // Client : port array jack_port_id_t JackGraphManager::GetPort(const char* name) { for (unsigned int i = 0; i < fPortMax; i++) { JackPort* port = GetPort(i); if (port->IsUsed() && port->NameEquals(name)) { return i; } } return NO_PORT; } /*! \brief Get the connection port name array. */ // Client void JackGraphManager::GetConnectionsAux(JackConnectionManager* manager, const char** res, jack_port_id_t port_index) { const jack_int_t* connections = manager->GetConnections(port_index); jack_int_t index; int i; // Cleanup connection array memset(res, 0, sizeof(char*) * CONNECTION_NUM_FOR_PORT); for (i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((index = connections[i]) != EMPTY); i++) { JackPort* port = GetPort(index); res[i] = port->fName; } res[i] = NULL; } /* Use the state returned by ReadCurrentState and check that the state was not changed during the read operation. The operation is lock-free since there is no intermediate state in the write operation that could cause the read to loop forever. */ // Client const char** JackGraphManager::GetConnections(jack_port_id_t port_index) { const char** res = (const char**)malloc(sizeof(char*) * CONNECTION_NUM_FOR_PORT); UInt16 cur_index, next_index; if (!res) return NULL; do { cur_index = GetCurrentIndex(); GetConnectionsAux(ReadCurrentState(), res, port_index); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read if (res[0]) { // At least one connection return res; } else { // Empty array, should return NULL free(res); return NULL; } } // Client void JackGraphManager::GetPortsAux(const char** matching_ports, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) { // Cleanup port array memset(matching_ports, 0, sizeof(char*) * fPortMax); int match_cnt = 0; regex_t port_regex, type_regex; if (port_name_pattern && port_name_pattern[0]) { if (regcomp(&port_regex, port_name_pattern, REG_EXTENDED | REG_NOSUB)!=0) { jack_log("JackGraphManager::GetPortsAux could not compile regex for port_name_pattern '%s'", port_name_pattern); return; } } if (type_name_pattern && type_name_pattern[0]) { if (regcomp(&type_regex, type_name_pattern, REG_EXTENDED | REG_NOSUB)!=0) { jack_log("JackGraphManager::GetPortsAux could not compile regex for type_name_pattern '%s'", type_name_pattern); return; } } for (unsigned int i = 0; i < fPortMax; i++) { bool matching = true; JackPort* port = GetPort(i); if (port->IsUsed()) { if (flags) { if ((port->fFlags & flags) != flags) { matching = false; } } if (matching && port_name_pattern && port_name_pattern[0]) { if (regexec(&port_regex, port->GetName(), 0, NULL, 0)) { matching = false; } } if (matching && type_name_pattern && type_name_pattern[0]) { if (regexec(&type_regex, port->GetType(), 0, NULL, 0)) { matching = false; } } if (matching) { matching_ports[match_cnt++] = port->fName; } } } matching_ports[match_cnt] = 0; if (port_name_pattern && port_name_pattern[0]) { regfree(&port_regex); } if (type_name_pattern && type_name_pattern[0]) { regfree(&type_regex); } } // Client /* Check that the state was not changed during the read operation. The operation is lock-free since there is no intermediate state in the write operation that could cause the read to loop forever. */ const char** JackGraphManager::GetPorts(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags) { const char** res = (const char**)malloc(sizeof(char*) * fPortMax); UInt16 cur_index, next_index; if (!res) return NULL; do { cur_index = GetCurrentIndex(); GetPortsAux(res, port_name_pattern, type_name_pattern, flags); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read if (res[0]) { // At least one port return res; } else { free(res); // Empty array, should return NULL return NULL; } } // Server void JackGraphManager::Save(JackConnectionManager* dst) { JackConnectionManager* manager = WriteNextStateStart(); memcpy(dst, manager, sizeof(JackConnectionManager)); WriteNextStateStop(); } // Server void JackGraphManager::Restore(JackConnectionManager* src) { JackConnectionManager* manager = WriteNextStateStart(); memcpy(manager, src, sizeof(JackConnectionManager)); WriteNextStateStop(); } } // end of namespace jack2-1.9.22/common/JackGraphManager.h000066400000000000000000000127721436671425200174100ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackGraphManager__ #define __JackGraphManager__ #include "JackShmMem.h" #include "JackPort.h" #include "JackConstants.h" #include "JackConnectionManager.h" #include "JackAtomicState.h" #include "JackPlatformPlug.h" #include "JackSystemDeps.h" namespace Jack { /*! \brief Graph manager: contains the connection manager and the port array. */ PRE_PACKED_STRUCTURE class SERVER_EXPORT JackGraphManager : public JackShmMem, public JackAtomicState { private: unsigned int fPortMax; JackClientTiming fClientTiming[CLIENT_NUM]; JackPort fPortArray[0]; // The actual size depends of port_max, it will be dynamically computed and allocated using "placement" new void AssertPort(jack_port_id_t port_index); jack_port_id_t AllocatePortAux(int refnum, const char* port_name, const char* port_type, JackPortFlags flags); void GetConnectionsAux(JackConnectionManager* manager, const char** res, jack_port_id_t port_index); void GetPortsAux(const char** matching_ports, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); jack_default_audio_sample_t* GetBuffer(jack_port_id_t port_index); void* GetBufferAux(JackConnectionManager* manager, jack_port_id_t port_index, jack_nframes_t frames); jack_nframes_t ComputeTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count); void RecalculateLatencyAux(jack_port_id_t port_index, jack_latency_callback_mode_t mode); public: JackGraphManager(int port_max); ~JackGraphManager() {} void SetBufferSize(jack_nframes_t buffer_size); // Ports management jack_port_id_t AllocatePort(int refnum, const char* port_name, const char* port_type, JackPortFlags flags, jack_nframes_t buffer_size); int ReleasePort(int refnum, jack_port_id_t port_index); void GetInputPorts(int refnum, jack_int_t* res); void GetOutputPorts(int refnum, jack_int_t* res); void RemoveAllPorts(int refnum); void DisconnectAllPorts(int refnum); JackPort* GetPort(jack_port_id_t index); jack_port_id_t GetPort(const char* name); int ComputeTotalLatency(jack_port_id_t port_index); int ComputeTotalLatencies(); void RecalculateLatency(jack_port_id_t port_index, jack_latency_callback_mode_t mode); int RequestMonitor(jack_port_id_t port_index, bool onoff); // Connections management int Connect(jack_port_id_t src_index, jack_port_id_t dst_index); int Disconnect(jack_port_id_t src_index, jack_port_id_t dst_index); int IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst); // RT, client int GetConnectionsNum(jack_port_id_t port_index) { JackConnectionManager* manager = ReadCurrentState(); return manager->Connections(port_index); } const char** GetConnections(jack_port_id_t port_index); void GetConnections(jack_port_id_t port_index, jack_int_t* connections); // TODO const char** GetPorts(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); int GetTwoPorts(const char* src, const char* dst, jack_port_id_t* src_index, jack_port_id_t* dst_index); int CheckPorts(jack_port_id_t port_src, jack_port_id_t port_dst); void DisconnectAllInput(jack_port_id_t port_index); void DisconnectAllOutput(jack_port_id_t port_index); int DisconnectAll(jack_port_id_t port_index); bool IsDirectConnection(int ref1, int ref2); void DirectConnect(int ref1, int ref2); void DirectDisconnect(int ref1, int ref2); void Activate(int refnum); void Deactivate(int refnum); int GetInputRefNum(jack_port_id_t port_index); int GetOutputRefNum(jack_port_id_t port_index); // Buffer management void* GetBuffer(jack_port_id_t port_index, jack_nframes_t frames); // Activation management void RunCurrentGraph(); bool RunNextGraph(); bool IsFinishedGraph(); void InitRefNum(int refnum); int ResumeRefNum(JackClientControl* control, JackSynchro* table); int SuspendRefNum(JackClientControl* control, JackSynchro* table, long usecs); void TopologicalSort(std::vector& sorted); JackClientTiming* GetClientTiming(int refnum) { return &fClientTiming[refnum]; } void Save(JackConnectionManager* dst); void Restore(JackConnectionManager* src); static JackGraphManager* Allocate(int port_max); static void Destroy(JackGraphManager* manager); } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackInternalClient.cpp000066400000000000000000000164061436671425200203200ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackServerGlobals.h" #include "JackGraphManager.h" #include "JackConstants.h" #include "JackInternalClient.h" #include "JackLockedEngine.h" #include "JackServer.h" #include "JackEngineControl.h" #include "JackClientControl.h" #include "JackInternalClientChannel.h" #include "JackTools.h" #include namespace Jack { JackGraphManager* JackInternalClient::fGraphManager = NULL; JackEngineControl* JackInternalClient::fEngineControl = NULL; // Used for external C API (JackAPI.cpp) SERVER_EXPORT JackGraphManager* GetGraphManager() { return JackServerGlobals::fInstance->GetGraphManager(); } SERVER_EXPORT JackEngineControl* GetEngineControl() { return JackServerGlobals::fInstance->GetEngineControl(); } SERVER_EXPORT JackSynchro* GetSynchroTable() { return JackServerGlobals::fInstance->GetSynchroTable(); } JackInternalClient::JackInternalClient(JackServer* server, JackSynchro* table): JackClient(table) { fChannel = new JackInternalClientChannel(server); } JackInternalClient::~JackInternalClient() { delete fChannel; } int JackInternalClient::Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status) { int result; jack_log("JackInternalClient::Open name = %s", name); if (strlen(name) >= JACK_CLIENT_NAME_SIZE) { jack_error("\"%s\" is too long to be used as a JACK client name.\n" "Please use %lu characters or less", name, JACK_CLIENT_NAME_SIZE - 1); return -1; } strncpy(fServerName, server_name, sizeof(fServerName)); fServerName[sizeof(fServerName) - 1] = 0; // Open server/client direct channel char name_res[JACK_CLIENT_NAME_SIZE + 1]; fChannel->ClientCheck(name, uuid, name_res, JACK_PROTOCOL_VERSION, (int)options, (int*)status, &result, false); if (result < 0) { int status1 = *status; if (status1 & JackVersionError) { jack_error("JACK protocol mismatch %d", JACK_PROTOCOL_VERSION); } else { jack_error("Client name = %s conflits with another running client", name); } goto error; } strcpy(fClientControl.fName, name_res); // Require new client fChannel->ClientOpen(name_res, &fClientControl.fRefNum, &fEngineControl, &fGraphManager, this, &result); if (result < 0) { jack_error("Cannot open client name = %s", name_res); goto error; } SetupDriverSync(false); JackGlobals::fClientTable[fClientControl.fRefNum] = this; JackGlobals::fServerRunning = true; jack_log("JackInternalClient::Open name = %s refnum = %ld", name_res, fClientControl.fRefNum); return 0; error: fChannel->Close(); return -1; } void JackInternalClient::ShutDown(jack_status_t code, const char* message) { jack_log("JackInternalClient::ShutDown"); JackClient::ShutDown(code, message); } JackGraphManager* JackInternalClient::GetGraphManager() const { assert(fGraphManager); return fGraphManager; } JackEngineControl* JackInternalClient::GetEngineControl() const { assert(fEngineControl); return fEngineControl; } JackClientControl* JackInternalClient::GetClientControl() const { return const_cast(&fClientControl); } int JackLoadableInternalClient::Init(const char* so_name) { char path_to_so[JACK_PATH_MAX + 1]; BuildClientPath(path_to_so, sizeof(path_to_so), so_name); fHandle = LoadJackModule(path_to_so); jack_log("JackLoadableInternalClient::JackLoadableInternalClient path_to_so = %s", path_to_so); if (fHandle == NULL) { PrintLoadError(so_name); return -1; } fFinish = (FinishCallback)GetJackProc(fHandle, "jack_finish"); if (fFinish == NULL) { UnloadJackModule(fHandle); jack_error("symbol jack_finish cannot be found in %s", so_name); return -1; } fDescriptor = (JackDriverDescFunction)GetJackProc(fHandle, "jack_get_descriptor"); if (fDescriptor == NULL) { jack_info("No jack_get_descriptor entry-point for %s", so_name); } return 0; } int JackLoadableInternalClient1::Init(const char* so_name) { if (JackLoadableInternalClient::Init(so_name) < 0) { return -1; } fInitialize = (InitializeCallback)GetJackProc(fHandle, "jack_initialize"); if (fInitialize == NULL) { UnloadJackModule(fHandle); jack_error("symbol jack_initialize cannot be found in %s", so_name); return -1; } return 0; } int JackLoadableInternalClient2::Init(const char* so_name) { if (JackLoadableInternalClient::Init(so_name) < 0) { return -1; } fInitialize = (InternalInitializeCallback)GetJackProc(fHandle, "jack_internal_initialize"); if (fInitialize == NULL) { UnloadJackModule(fHandle); jack_error("symbol jack_internal_initialize cannot be found in %s", so_name); return -1; } return 0; } JackLoadableInternalClient1::JackLoadableInternalClient1(JackServer* server, JackSynchro* table, const char* object_data) : JackLoadableInternalClient(server, table) { if (object_data != NULL) strncpy(fObjectData, object_data, JACK_LOAD_INIT_LIMIT); else memset(fObjectData, 0, sizeof(fObjectData)); } JackLoadableInternalClient2::JackLoadableInternalClient2(JackServer* server, JackSynchro* table, const JSList* parameters) : JackLoadableInternalClient(server, table) { fParameters = parameters; } JackLoadableInternalClient::~JackLoadableInternalClient() { if (fFinish != NULL) { fFinish(fProcessArg); } if (fHandle != NULL) { UnloadJackModule(fHandle); } } int JackLoadableInternalClient1::Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status) { int res = -1; if (JackInternalClient::Open(server_name, name, uuid, options, status) == 0) { if (fInitialize((jack_client_t*)this, fObjectData) == 0) { res = 0; } else { JackInternalClient::Close(); fFinish = NULL; } } return res; } int JackLoadableInternalClient2::Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status) { int res = -1; if (JackInternalClient::Open(server_name, name, uuid, options, status) == 0) { if (fInitialize((jack_client_t*)this, fParameters) == 0) { res = 0; } else { JackInternalClient::Close(); fFinish = NULL; } } return res; } } // end of namespace jack2-1.9.22/common/JackInternalClient.h000066400000000000000000000067251436671425200177700ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackInternalClient__ #define __JackInternalClient__ #include "JackClient.h" #include "JackClientControl.h" #include "driver_interface.h" namespace Jack { struct JackEngineControl; /*! \brief Internal clients in the server. */ class JackInternalClient : public JackClient { private: JackClientControl fClientControl; /*! Client control */ public: JackInternalClient(JackServer* server, JackSynchro* table); virtual ~JackInternalClient(); int Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status); void ShutDown(jack_status_t code, const char* message); JackGraphManager* GetGraphManager() const; JackEngineControl* GetEngineControl() const; JackClientControl* GetClientControl() const; static JackGraphManager* fGraphManager; /*! Shared memory Port manager */ static JackEngineControl* fEngineControl; /*! Shared engine control */ }; /*! \brief Loadable internal clients in the server. */ typedef int (*InitializeCallback)(jack_client_t*, const char*); typedef int (*InternalInitializeCallback)(jack_client_t*, const JSList* params); typedef void (*FinishCallback)(void *); class JackLoadableInternalClient : public JackInternalClient { protected: JACK_HANDLE fHandle; FinishCallback fFinish; JackDriverDescFunction fDescriptor; public: JackLoadableInternalClient(JackServer* server, JackSynchro* table) :JackInternalClient(server, table), fHandle(NULL), fFinish(NULL), fDescriptor(NULL) {} virtual ~JackLoadableInternalClient(); virtual int Init(const char* so_name); }; class JackLoadableInternalClient1 : public JackLoadableInternalClient { private: InitializeCallback fInitialize; char fObjectData[JACK_LOAD_INIT_LIMIT]; public: JackLoadableInternalClient1(JackServer* server, JackSynchro* table, const char* object_data); virtual ~JackLoadableInternalClient1() {} int Init(const char* so_name); int Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status); }; class JackLoadableInternalClient2 : public JackLoadableInternalClient { private: InternalInitializeCallback fInitialize; const JSList* fParameters; public: JackLoadableInternalClient2(JackServer* server, JackSynchro* table, const JSList* parameters); virtual ~JackLoadableInternalClient2() {} int Init(const char* so_name); int Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status); }; } // end of namespace #endif jack2-1.9.22/common/JackInternalClientChannel.h000066400000000000000000000144461436671425200212600ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackInternalClientChannel__ #define __JackInternalClientChannel__ #include "JackChannel.h" namespace Jack { /*! \brief JackClientChannel for server internal clients. */ class JackInternalClientChannel : public detail::JackClientChannelInterface { private: JackServer* fServer; JackLockedEngine* fEngine; public: JackInternalClientChannel(JackServer* server): fServer(server), fEngine(server->GetEngine()) {} virtual ~JackInternalClientChannel() {} void ClientCheck(const char* name, jack_uuid_t uuid, char* name_res, int protocol, int options, int* status, int* result, int open) { *result = fEngine->ClientCheck(name, uuid, name_res, protocol, options, status); } void ClientOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, int* result) { *result = fEngine->ClientInternalOpen(name, ref, shared_engine, shared_manager, client, true); } void ClientClose(int refnum, int* result) { *result = fEngine->ClientInternalClose(refnum, true); } void ClientActivate(int refnum, int is_real_time, int* result) { *result = fEngine->ClientActivate(refnum, is_real_time); } void ClientDeactivate(int refnum, int* result) { *result = fEngine->ClientDeactivate(refnum); } void PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, unsigned int* port_index, int* result) { *result = fEngine->PortRegister(refnum, name, type, flags, buffer_size, port_index); } void PortUnRegister(int refnum, jack_port_id_t port_index, int* result) { *result = fEngine->PortUnRegister(refnum, port_index); } void PortConnect(int refnum, const char* src, const char* dst, int* result) { *result = fEngine->PortConnect(refnum, src, dst); } void PortDisconnect(int refnum, const char* src, const char* dst, int* result) { *result = fEngine->PortDisconnect(refnum, src, dst); } void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) { *result = fEngine->PortConnect(refnum, src, dst); } void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result) { *result = fEngine->PortDisconnect(refnum, src, dst); } void PortRename(int refnum, jack_port_id_t port, const char* name, int* result) { *result = fEngine->PortRename(refnum, port, name); } void SetBufferSize(jack_nframes_t buffer_size, int* result) { *result = fServer->SetBufferSize(buffer_size); } void SetFreewheel(int onoff, int* result) { *result = fServer->SetFreewheel(onoff); } void ComputeTotalLatencies(int* result) { *result = fEngine->ComputeTotalLatencies(); } void ReleaseTimebase(int refnum, int* result) { *result = fServer->ReleaseTimebase(refnum); } void SetTimebaseCallback(int refnum, int conditional, int* result) { *result = fServer->SetTimebaseCallback(refnum, conditional); } void GetInternalClientName(int refnum, int int_ref, char* name_res, int* result) { *result = fEngine->GetInternalClientName(int_ref, name_res); } void InternalClientHandle(int refnum, const char* client_name, int* status, int* int_ref, int* result) { *result = fEngine->InternalClientHandle(client_name, status, int_ref); } void InternalClientLoad(int refnum, const char* client_name, const char* so_name, const char* objet_data, int options, int* status, int* int_ref, jack_uuid_t uuid, int* result) { *result = fServer->InternalClientLoad1(client_name, so_name, objet_data, options, int_ref, uuid, status); } void InternalClientUnload(int refnum, int int_ref, int* status, int* result) { *result = fEngine->InternalClientUnload(int_ref, status); } void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, jack_session_command_t** result) { JackSessionNotifyResult* res; fEngine->SessionNotify(refnum, target, type, path, NULL, &res); if (res == NULL) { *result = NULL; return; } *result = res->GetCommands(); delete(res); } void SessionReply(int refnum, int* result) { *result = fEngine->SessionReply(refnum); } void GetUUIDForClientName(int refnum, const char* client_name, char* uuid_res, int* result) { *result = fEngine->GetUUIDForClientName(client_name, uuid_res); } void GetClientNameForUUID(int refnum, const char* uuid, char* name_res, int* result) { *result = fEngine->GetClientNameForUUID(uuid, name_res); } void ReserveClientName(int refnum, const char* client_name, const char *uuid, int* result) { *result = fEngine->ReserveClientName(client_name, uuid); } void ClientHasSessionCallback(const char* client_name, int* result) { *result = fEngine->ClientHasSessionCallback(client_name); } }; } // end of namespace #endif jack2-1.9.22/common/JackInternalSessionLoader.cpp000066400000000000000000000140041436671425200216440ustar00rootroot00000000000000/* Copyright (C) 2017 Timo Wischer This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "JackInternalSessionLoader.h" #include "JackLockedEngine.h" namespace Jack { JackInternalSessionLoader::JackInternalSessionLoader(JackServer* const server) : fServer(server) { } int JackInternalSessionLoader::Load(const char* file) { std::ifstream infile(file); if (!infile.is_open()) { jack_error("JACK internal session file %s does not exist or cannot be opened for reading.", file); return -1; } std::string line; int linenr = -1; while (std::getline(infile, line)) { linenr++; std::istringstream iss(line); std::string command; if ( !(iss >> command) ) { /* ignoring empty line or line only filled with spaces */ continue; } /* convert command to lower case to accept any case of the letters in the command */ std::transform(command.begin(), command.end(), command.begin(), ::tolower); if ( (command.compare("c") == 0) || (command.compare("con") == 0) ) { ConnectPorts(iss, linenr); } else if ( (command.compare("l") == 0) || (command.compare("load") == 0) ) { LoadClient(iss, linenr); #if 0 /* NOTE: c++11 only */ } else if (command.front() == '#') { #else } else if (command[0] == '#') { #endif /* ignoring commented lines. * The # can be followed by non spaces. * Therefore only compare the first letter of the command. */ } else { jack_error("JACK internal session file %s line %u contains unknown command '%s'. Ignoring the line!", file, linenr, line.c_str()); } } return 0; } void JackInternalSessionLoader::LoadClient(std::istringstream& iss, const int linenr) { std::string client_name; if ( !(iss >> client_name) ) { jack_error("Cannot read client name from internal session file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); return; } std::string lib_name; if ( !(iss >> lib_name) ) { jack_error("Cannot read client library name from internal session file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); return; } /* get the rest of the line */ std::string parameters; if ( std::getline(iss, parameters) ) { /* remove the leading spaces */ const std::size_t start = parameters.find_first_not_of(" \t"); if (start == std::string::npos) { /* Parameters containing only spaces. * Use empty parameter string. */ parameters = ""; } else { parameters = parameters.substr(start); } } /* jackctl_server_load_internal() can not be used * because it calls jack_internal_initialize() * instead of jack_initialize() */ int status = 0; int refnum = 0; if (fServer->InternalClientLoad1(client_name.c_str(), lib_name.c_str(), parameters.c_str(), (JackLoadName|JackUseExactName|JackLoadInit), &refnum, -1, &status) < 0) { /* Due to the JackUseExactName option JackNameNotUnique will always handled as a failure. * See JackEngine::ClientCheck(). */ if (status & JackNameNotUnique) { jack_error("Internal client name `%s' not unique", client_name.c_str()); } /* An error message for JackVersionError will already * be printed by JackInternalClient::Open(). * Therefore no need to handle it here. */ jack_error("Cannot load client %s from internal session file line %u. Ignoring the line!", client_name.c_str(), linenr); return; } /* status has not to be checked for JackFailure * because JackServer::InternalClientLoad1() will return a value < 0 * and this is handled by the previous if-clause. */ jack_info("Internal client %s successfully loaded", client_name.c_str()); } void JackInternalSessionLoader::ConnectPorts(std::istringstream& iss, const int linenr) { std::string src_port; if ( !(iss >> src_port) ) { jack_error("Cannot read first port from internal session file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); return; } std::string dst_port; if ( !(iss >> dst_port) ) { jack_error("Cannot read second port from internal session file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); return; } /* use the client reference of the source port */ const jack_port_id_t src_port_index = fServer->GetGraphManager()->GetPort(src_port.c_str()); if (src_port_index >= NO_PORT) { jack_error("Source port %s does not exist! Ignoring internal session file line %u '%s'.", src_port.c_str(), linenr, iss.str().c_str()); return; } const int src_refnum = fServer->GetGraphManager()->GetOutputRefNum(src_port_index); if (fServer->GetEngine()->PortConnect(src_refnum, src_port.c_str(), dst_port.c_str()) < 0) { jack_error("Cannot connect ports of internal session file line %u '%s'.\n" "Possibly the destination port does not exist. Ignoring the line!", linenr, iss.str().c_str()); return; } jack_info("Ports connected: %s -> %s", src_port.c_str(), dst_port.c_str()); } } jack2-1.9.22/common/JackInternalSessionLoader.h000066400000000000000000000023241436671425200213130ustar00rootroot00000000000000/* Copyright (C) 2017 Timo Wischer This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackInternalSessionLoader__ #define __JackInternalSessionLoader__ #include #include #include "JackServer.h" namespace Jack { class JackInternalSessionLoader { public: JackInternalSessionLoader(JackServer* const server); int Load(const char* file); private: void LoadClient(std::istringstream& iss, const int linenr); void ConnectPorts(std::istringstream& iss, const int linenr); JackServer* const fServer; }; } // end of namespace #endif jack2-1.9.22/common/JackLibAPI.cpp000066400000000000000000000261661436671425200164510ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackDebugClient.h" #include "JackLibClient.h" #include "JackChannel.h" #include "JackLibGlobals.h" #include "JackGlobals.h" #include "JackCompilerDeps.h" #include "JackTools.h" #include "JackSystemDeps.h" #include "JackServerLaunch.h" #include "JackMetadata.h" #include using namespace Jack; #ifdef __cplusplus extern "C" { #endif jack_client_t * jack_client_new_aux (const char *client_name, jack_options_t options, jack_status_t *status); LIB_EXPORT jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...); LIB_EXPORT int jack_client_close (jack_client_t *client); LIB_EXPORT int jack_get_client_pid (const char *name); LIB_EXPORT int jack_set_property(jack_client_t*, jack_uuid_t subject, const char* key, const char* value, const char* type); LIB_EXPORT int jack_get_property(jack_uuid_t subject, const char* key, char** value, char** type); LIB_EXPORT void jack_free_description(jack_description_t* desc, int free_description_itself); LIB_EXPORT int jack_get_properties(jack_uuid_t subject, jack_description_t* desc); LIB_EXPORT int jack_get_all_properties(jack_description_t** descs); LIB_EXPORT int jack_remove_property(jack_client_t* client, jack_uuid_t subject, const char* key); LIB_EXPORT int jack_remove_properties(jack_client_t* client, jack_uuid_t subject); LIB_EXPORT int jack_remove_all_properties(jack_client_t* client); LIB_EXPORT int jack_set_property_change_callback(jack_client_t* client, JackPropertyChangeCallback callback, void* arg); #ifdef __cplusplus } #endif static jack_client_t * jack_client_open_aux (const char *client_name, jack_options_t options, jack_status_t *status, va_list ap); JackLibGlobals* JackLibGlobals::fGlobals = NULL; int JackLibGlobals::fClientCount = 0; jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t options, jack_status_t* status) { jack_varargs_t va; /* variable arguments */ jack_status_t my_status; JackClient* client; if (client_name == NULL) { jack_error("jack_client_new called with a NULL client_name"); return NULL; } jack_log("jack_client_new %s", client_name); if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; /* validate parameters */ if ((options & ~JackOpenOptions)) { int my_status1 = *status | (JackFailure | JackInvalidOption); *status = (jack_status_t)my_status1; return NULL; } /* parse variable arguments */ jack_varargs_init(&va); JackLibGlobals::Init(); // jack library initialisation if (try_start_server(&va, options, status)) { jack_error("jack server is not running or cannot be started"); JackLibGlobals::Destroy(); // jack library destruction return 0; } if (JACK_DEBUG) { client = new JackDebugClient(new JackLibClient(GetSynchroTable())); // Debug mode } else { client = new JackLibClient(GetSynchroTable()); } int res = client->Open(va.server_name, client_name, va.session_id, options, status); if (res < 0) { delete client; JackLibGlobals::Destroy(); // jack library destruction int my_status1 = (JackFailure | JackServerError); *status = (jack_status_t)my_status1; return NULL; } else { return (jack_client_t*)client; } } static jack_client_t* jack_client_open_aux(const char* client_name, jack_options_t options, jack_status_t* status, va_list ap) { jack_varargs_t va; /* variable arguments */ jack_status_t my_status; JackClient* client; if (client_name == NULL) { jack_error("jack_client_open called with a NULL client_name"); return NULL; } jack_log("jack_client_open %s", client_name); if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; /* validate parameters */ if ((options & ~JackOpenOptions)) { int my_status1 = *status | (JackFailure | JackInvalidOption); *status = (jack_status_t)my_status1; return NULL; } /* parse variable arguments */ jack_varargs_parse(options, ap, &va); JackLibGlobals::Init(); // jack library initialisation if (try_start_server(&va, options, status)) { jack_error("jack server is not running or cannot be started"); JackLibGlobals::Destroy(); // jack library destruction return 0; } if (JACK_DEBUG) { client = new JackDebugClient(new JackLibClient(GetSynchroTable())); // Debug mode } else { client = new JackLibClient(GetSynchroTable()); } int res = client->Open(va.server_name, client_name, va.session_id, options, status); if (res < 0) { delete client; JackLibGlobals::Destroy(); // jack library destruction int my_status1 = (JackFailure | JackServerError); *status = (jack_status_t)my_status1; return NULL; } else { return (jack_client_t*)client; } } LIB_EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options_t options, jack_status_t* status, ...) { JackGlobals::CheckContext("jack_client_open"); try { assert(JackGlobals::fOpenMutex); JackGlobals::fOpenMutex->Lock(); va_list ap; va_start(ap, status); jack_client_t* res = jack_client_open_aux(ext_client_name, options, status, ap); va_end(ap); JackGlobals::fOpenMutex->Unlock(); return res; } catch (std::bad_alloc& e) { jack_error("Memory allocation error..."); return NULL; } catch (...) { jack_error("Unknown error..."); return NULL; } } LIB_EXPORT int jack_client_close(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_client_close"); assert(JackGlobals::fOpenMutex); JackGlobals::fOpenMutex->Lock(); int res = -1; jack_log("jack_client_close"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_client_close called with a NULL client"); } else { res = client->Close(); delete client; JackLibGlobals::Destroy(); // jack library destruction jack_log("jack_client_close res = %d", res); } JackGlobals::fOpenMutex->Unlock(); return res; } LIB_EXPORT int jack_get_client_pid(const char *name) { jack_error("jack_get_client_pid : not implemented on library side"); return 0; } // Metadata API LIB_EXPORT int jack_set_property(jack_client_t* ext_client, jack_uuid_t subject, const char* key, const char* value, const char* type) { JackGlobals::CheckContext("jack_set_property"); JackClient* client = (JackClient*)ext_client; jack_log("jack_set_property ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_set_property called with a NULL client"); return -1; } else { JackMetadata* metadata = GetMetadata(); return (metadata ? metadata->SetProperty(client, subject, key, value, type) : -1); } } LIB_EXPORT int jack_get_property(jack_uuid_t subject, const char* key, char** value, char** type) { JackGlobals::CheckContext("jack_get_property"); JackMetadata* metadata = GetMetadata(); return (metadata ? metadata->GetProperty(subject, key, value, type) : -1); } LIB_EXPORT void jack_free_description(jack_description_t* desc, int free_actual_description_too) { JackGlobals::CheckContext("jack_free_description"); JackMetadata* metadata = GetMetadata(); if (metadata) metadata->FreeDescription(desc, free_actual_description_too); } LIB_EXPORT int jack_get_properties(jack_uuid_t subject, jack_description_t* desc) { JackGlobals::CheckContext("jack_get_properties"); JackMetadata* metadata = GetMetadata(); return (metadata ? metadata->GetProperties(subject, desc) : -1); } LIB_EXPORT int jack_get_all_properties(jack_description_t** descriptions) { JackGlobals::CheckContext("jack_get_all_properties"); JackMetadata* metadata = GetMetadata(); return (metadata ? metadata->GetAllProperties(descriptions) : -1); } LIB_EXPORT int jack_remove_property(jack_client_t* ext_client, jack_uuid_t subject, const char* key) { JackGlobals::CheckContext("jack_remove_property"); JackClient* client = (JackClient*)ext_client; jack_log("jack_remove_property ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_remove_property called with a NULL client"); return -1; } else { JackMetadata* metadata = GetMetadata(); return (metadata ? metadata->RemoveProperty(client, subject, key) : -1); } } LIB_EXPORT int jack_remove_properties(jack_client_t* ext_client, jack_uuid_t subject) { JackGlobals::CheckContext("jack_remove_properties"); JackClient* client = (JackClient*)ext_client; jack_log("jack_remove_properties ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_remove_properties called with a NULL client"); return -1; } else { JackMetadata* metadata = GetMetadata(); return (metadata ? metadata->RemoveProperties(client, subject) : -1); } } LIB_EXPORT int jack_remove_all_properties(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_remove_all_properties"); JackClient* client = (JackClient*)ext_client; jack_log("jack_remove_all_properties ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_remove_all_properties called with a NULL client"); return -1; } else { JackMetadata* metadata = GetMetadata(); return (metadata ? metadata->RemoveAllProperties(client) : -1); } } LIB_EXPORT int jack_set_property_change_callback(jack_client_t* ext_client, JackPropertyChangeCallback callback, void* arg) { JackGlobals::CheckContext("jack_set_property_change_callback"); JackClient* client = (JackClient*)ext_client; jack_log("jack_set_property_change_callback ext_client %x client %x ", ext_client, client); if (client == NULL) { jack_error("jack_set_property_change_callback called with a NULL client"); return -1; } else { return client->SetPropertyChangeCallback(callback, arg); } } jack2-1.9.22/common/JackLibClient.cpp000066400000000000000000000145541436671425200172540ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackLibClient.h" #include "JackTime.h" #include "JackLibGlobals.h" #include "JackGlobals.h" #include "JackPlatformPlug.h" #include "JackTools.h" namespace Jack { // Used for external C API (JackAPI.cpp) JackGraphManager* GetGraphManager() { if (JackLibGlobals::fGlobals) { return JackLibGlobals::fGlobals->fGraphManager; } else { return NULL; } } JackEngineControl* GetEngineControl() { if (JackLibGlobals::fGlobals) { return JackLibGlobals::fGlobals->fEngineControl; } else { return NULL; } } JackSynchro* GetSynchroTable() { return (JackLibGlobals::fGlobals ? JackLibGlobals::fGlobals->fSynchroTable : 0); } // Used for client-side Metadata API (JackLibAPI.cpp) JackMetadata* GetMetadata() { if (JackLibGlobals::fGlobals) { return JackLibGlobals::fGlobals->fMetadata; } else { return NULL; } } //------------------- // Client management //------------------- /* ShutDown is called: - from the RT thread when Execute method fails - possibly from a "closed" notification channel (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown)) */ void JackLibClient::ShutDown(jack_status_t code, const char* message) { jack_log("JackLibClient::ShutDown"); JackGlobals::fServerRunning = false; JackClient::ShutDown(code, message); } JackLibClient::JackLibClient(JackSynchro* table): JackClient(table) { jack_log("JackLibClient::JackLibClient table = %x", table); fChannel = new JackClientChannel(); } JackLibClient::~JackLibClient() { jack_log("JackLibClient::~JackLibClient"); delete fChannel; } int JackLibClient::Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status) { int shared_engine, shared_client, shared_graph, result; bool res; jack_log("JackLibClient::Open name = %s", name); if (strlen(name) >= JACK_CLIENT_NAME_SIZE) { jack_error("\"%s\" is too long to be used as a JACK client name.\n" "Please use %lu characters or less", name, JACK_CLIENT_NAME_SIZE - 1); return -1; } strncpy(fServerName, server_name, sizeof(fServerName)); fServerName[sizeof(fServerName) - 1] = 0; // Open server/client channel char name_res[JACK_CLIENT_NAME_SIZE+1]; if (fChannel->Open(server_name, name, uuid, name_res, this, options, status) < 0) { jack_error("Cannot connect to the server"); goto error; } // Start receiving notifications if (fChannel->Start() < 0) { jack_error("Cannot start channel"); goto error; } // Require new client fChannel->ClientOpen(name_res, JackTools::GetPID(), uuid, &shared_engine, &shared_client, &shared_graph, &result); if (result < 0) { jack_error("Cannot open %s client", name_res); goto error; } try { // Map shared memory segments JackLibGlobals::fGlobals->fEngineControl.SetShmIndex(shared_engine, fServerName); JackLibGlobals::fGlobals->fGraphManager.SetShmIndex(shared_graph, fServerName); fClientControl.SetShmIndex(shared_client, fServerName); JackGlobals::fVerbose = GetEngineControl()->fVerbose; } catch (...) { jack_error("Map shared memory segments exception"); goto error; } SetupDriverSync(false); // Connect shared synchro : the synchro must be usable in I/O mode when several clients live in the same process assert(JackGlobals::fSynchroMutex); JackGlobals::fSynchroMutex->Lock(); res = fSynchroTable[GetClientControl()->fRefNum].Connect(name_res, fServerName); JackGlobals::fSynchroMutex->Unlock(); if (!res) { jack_error("Cannot ConnectSemaphore %s client", name_res); goto error; } JackGlobals::fClientTable[GetClientControl()->fRefNum] = this; SetClockSource(GetEngineControl()->fClockSource); jack_log("JackLibClient::Open name = %s refnum = %ld", name_res, GetClientControl()->fRefNum); return 0; error: fChannel->Stop(); fChannel->Close(); return -1; } // Notifications received from the server // TODO this should be done once for all clients in the process, when a shared notification channel // will be shared by all clients... int JackLibClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { int res = 0; assert(JackGlobals::fSynchroMutex); JackGlobals::fSynchroMutex->Lock(); // Done all time switch (notify) { case kAddClient: jack_log("JackClient::AddClient name = %s, ref = %ld ", name, refnum); // the synchro must be usable in I/O mode when several clients live in the same process res = fSynchroTable[refnum].Connect(name, fServerName) ? 0 : -1; break; case kRemoveClient: jack_log("JackClient::RemoveClient name = %s, ref = %ld ", name, refnum); if (GetClientControl() && strcmp(GetClientControl()->fName, name) != 0) { res = fSynchroTable[refnum].Disconnect() ? 0 : -1; } break; } JackGlobals::fSynchroMutex->Unlock(); return res; } JackGraphManager* JackLibClient::GetGraphManager() const { assert(JackLibGlobals::fGlobals->fGraphManager); return JackLibGlobals::fGlobals->fGraphManager; } JackEngineControl* JackLibClient::GetEngineControl() const { assert(JackLibGlobals::fGlobals->fEngineControl); return JackLibGlobals::fGlobals->fEngineControl; } JackClientControl* JackLibClient::GetClientControl() const { return fClientControl; } } // end of namespace jack2-1.9.22/common/JackLibClient.h000066400000000000000000000034101436671425200167060ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackLibClient__ #define __JackLibClient__ #include "JackClient.h" #include "JackShmMem.h" #include "JackClientControl.h" #include "JackEngineControl.h" namespace Jack { /*! \brief Client on the library side. */ class JackLibClient : public JackClient { private: JackShmReadWritePtr1 fClientControl; /*! Shared client control */ public: JackLibClient(JackSynchro* table); virtual ~JackLibClient(); int Open(const char* server_name, const char* name, jack_uuid_t uuid, jack_options_t options, jack_status_t* status); void ShutDown(jack_status_t code, const char* message); int ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); JackGraphManager* GetGraphManager() const; JackEngineControl* GetEngineControl() const; JackClientControl* GetClientControl() const; }; // Used for client-side Metadata API (JackLibAPI.cpp) JackMetadata* GetMetadata(); } // end of namespace #endif jack2-1.9.22/common/JackLibGlobals.h000066400000000000000000000076021436671425200170620ustar00rootroot00000000000000/* Copyright (C) 2005 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackLibGlobals__ #define __JackLibGlobals__ #include "JackShmMem.h" #include "JackEngineControl.h" #include "JackGlobals.h" #include "JackPlatformPlug.h" #include "JackGraphManager.h" #include "JackMessageBuffer.h" #include "JackTime.h" #include "JackClient.h" #include "JackError.h" #include #include #ifdef WIN32 #ifdef __MINGW32__ #include typedef _sigset_t sigset_t; #else typedef HANDLE sigset_t; #endif #endif namespace Jack { class JackClient; /*! \brief Global library static structure: singleton kind of pattern. */ struct JackLibGlobals { JackShmReadWritePtr fGraphManager; /*! Shared memory Port manager */ JackShmReadWritePtr fEngineControl; /*! Shared engine control */ // transport engine has to be writable JackSynchro fSynchroTable[CLIENT_NUM]; /*! Shared synchro table */ JackMetadata *fMetadata; /*! Shared metadata base */ sigset_t fProcessSignals; static int fClientCount; static JackLibGlobals* fGlobals; JackLibGlobals() { jack_log("JackLibGlobals"); if (!JackMessageBuffer::Create()) { jack_error("Cannot create message buffer"); } fGraphManager = -1; fEngineControl = -1; fMetadata = new JackMetadata(false); // Filter SIGPIPE to avoid having client get a SIGPIPE when trying to access a died server. #ifdef WIN32 // TODO #else sigset_t signals; sigemptyset(&signals); sigaddset(&signals, SIGPIPE); sigprocmask(SIG_BLOCK, &signals, &fProcessSignals); #endif } ~JackLibGlobals() { jack_log("~JackLibGlobals"); for (int i = 0; i < CLIENT_NUM; i++) { fSynchroTable[i].Disconnect(); } JackMessageBuffer::Destroy(); delete fMetadata; fMetadata = NULL; // Restore old signal mask #ifdef WIN32 // TODO #else sigprocmask(SIG_BLOCK, &fProcessSignals, 0); #endif } static void Init() { if (!JackGlobals::fServerRunning && fClientCount > 0) { // Cleanup remaining clients jack_error("Jack server was closed but clients are still allocated, cleanup..."); for (int i = 0; i < CLIENT_NUM; i++) { JackClient* client = JackGlobals::fClientTable[i]; if (client) { jack_error("Cleanup client ref = %d", i); client->Close(); delete client; } } // Cleanup global context fClientCount = 0; delete fGlobals; fGlobals = NULL; } if (fClientCount++ == 0 && !fGlobals) { jack_log("JackLibGlobals Init %x", fGlobals); InitTime(); fGlobals = new JackLibGlobals(); } } static void Destroy() { if (--fClientCount == 0 && fGlobals) { jack_log("JackLibGlobals Destroy %x", fGlobals); EndTime(); delete fGlobals; fGlobals = NULL; } } }; } // end of namespace #endif jack2-1.9.22/common/JackLibSampleRateResampler.cpp000066400000000000000000000146721436671425200217470ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackLibSampleRateResampler.h" #include "JackError.h" namespace Jack { JackLibSampleRateResampler::JackLibSampleRateResampler() :JackResampler() { int error; fResampler = src_new(SRC_LINEAR, 1, &error); if (error != 0) { jack_error("JackLibSampleRateResampler::JackLibSampleRateResampler err = %s", src_strerror(error)); } } JackLibSampleRateResampler::JackLibSampleRateResampler(unsigned int quality) :JackResampler() { switch (quality) { case 0: quality = SRC_LINEAR; break; case 1: quality = SRC_ZERO_ORDER_HOLD; break; case 2: quality = SRC_SINC_FASTEST; break; case 3: quality = SRC_SINC_MEDIUM_QUALITY; break; case 4: quality = SRC_SINC_BEST_QUALITY; break; default: quality = SRC_LINEAR; jack_error("Out of range resample quality"); break; } int error; fResampler = src_new(quality, 1, &error); if (error != 0) { jack_error("JackLibSampleRateResampler::JackLibSampleRateResampler err = %s", src_strerror(error)); } } JackLibSampleRateResampler::~JackLibSampleRateResampler() { src_delete(fResampler); } void JackLibSampleRateResampler::Reset(unsigned int new_size) { JackResampler::Reset(new_size); src_reset(fResampler); } unsigned int JackLibSampleRateResampler::ReadResample(jack_default_audio_sample_t* buffer, unsigned int frames) { jack_ringbuffer_data_t ring_buffer_data[2]; SRC_DATA src_data; unsigned int frames_to_write = frames; unsigned int written_frames = 0; int res; jack_ringbuffer_get_read_vector(fRingBuffer, ring_buffer_data); unsigned int available_frames = (ring_buffer_data[0].len + ring_buffer_data[1].len) / sizeof(jack_default_audio_sample_t); jack_log("Output available = %ld", available_frames); for (int j = 0; j < 2; j++) { if (ring_buffer_data[j].len > 0) { src_data.data_in = (jack_default_audio_sample_t*)ring_buffer_data[j].buf; src_data.data_out = &buffer[written_frames]; src_data.input_frames = ring_buffer_data[j].len / sizeof(jack_default_audio_sample_t); src_data.output_frames = frames_to_write; src_data.end_of_input = 0; src_data.src_ratio = fRatio; res = src_process(fResampler, &src_data); if (res != 0) { jack_error("JackLibSampleRateResampler::ReadResample ratio = %f err = %s", fRatio, src_strerror(res)); return 0; } frames_to_write -= src_data.output_frames_gen; written_frames += src_data.output_frames_gen; if ((src_data.input_frames_used == 0 || src_data.output_frames_gen == 0) && j == 0) { jack_log("Output : j = %d input_frames_used = %ld output_frames_gen = %ld frames1 = %lu frames2 = %lu" , j, src_data.input_frames_used, src_data.output_frames_gen, ring_buffer_data[0].len, ring_buffer_data[1].len); } jack_log("Output : j = %d input_frames_used = %ld output_frames_gen = %ld", j, src_data.input_frames_used, src_data.output_frames_gen); jack_ringbuffer_read_advance(fRingBuffer, src_data.input_frames_used * sizeof(jack_default_audio_sample_t)); } } if (written_frames < frames) { jack_error("Output available = %ld", available_frames); jack_error("JackLibSampleRateResampler::ReadResample error written_frames = %ld", written_frames); } return written_frames; } unsigned int JackLibSampleRateResampler::WriteResample(jack_default_audio_sample_t* buffer, unsigned int frames) { jack_ringbuffer_data_t ring_buffer_data[2]; SRC_DATA src_data; unsigned int frames_to_read = frames; unsigned int read_frames = 0; int res; jack_ringbuffer_get_write_vector(fRingBuffer, ring_buffer_data); unsigned int available_frames = (ring_buffer_data[0].len + ring_buffer_data[1].len) / sizeof(jack_default_audio_sample_t); jack_log("Input available = %ld", available_frames); for (int j = 0; j < 2; j++) { if (ring_buffer_data[j].len > 0) { src_data.data_in = &buffer[read_frames]; src_data.data_out = (jack_default_audio_sample_t*)ring_buffer_data[j].buf; src_data.input_frames = frames_to_read; src_data.output_frames = (ring_buffer_data[j].len / sizeof(jack_default_audio_sample_t)); src_data.end_of_input = 0; src_data.src_ratio = fRatio; res = src_process(fResampler, &src_data); if (res != 0) { jack_error("JackLibSampleRateResampler::WriteResample ratio = %f err = %s", fRatio, src_strerror(res)); return 0; } frames_to_read -= src_data.input_frames_used; read_frames += src_data.input_frames_used; if ((src_data.input_frames_used == 0 || src_data.output_frames_gen == 0) && j == 0) { jack_log("Input : j = %d input_frames_used = %ld output_frames_gen = %ld frames1 = %lu frames2 = %lu" , j, src_data.input_frames_used, src_data.output_frames_gen, ring_buffer_data[0].len, ring_buffer_data[1].len); } jack_log("Input : j = %d input_frames_used = %ld output_frames_gen = %ld", j, src_data.input_frames_used, src_data.output_frames_gen); jack_ringbuffer_write_advance(fRingBuffer, src_data.output_frames_gen * sizeof(jack_default_audio_sample_t)); } } if (read_frames < frames) { jack_error("Input available = %ld", available_frames); jack_error("JackLibSampleRateResampler::WriteResample error read_frames = %ld", read_frames); } return read_frames; } } jack2-1.9.22/common/JackLibSampleRateResampler.h000066400000000000000000000026321436671425200214050ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackLibSampleRateResampler__ #define __JackLibSampleRateResampler__ #include "JackResampler.h" #include namespace Jack { /*! \brief Resampler using "libsamplerate" (http://www.mega-nerd.com/SRC/). */ class JackLibSampleRateResampler : public JackResampler { private: SRC_STATE* fResampler; public: JackLibSampleRateResampler(); JackLibSampleRateResampler(unsigned int quality); virtual ~JackLibSampleRateResampler(); unsigned int ReadResample(jack_default_audio_sample_t* buffer, unsigned int frames); unsigned int WriteResample(jack_default_audio_sample_t* buffer, unsigned int frames); void Reset(unsigned int new_size); }; } #endif jack2-1.9.22/common/JackLockedEngine.h000066400000000000000000000320061436671425200173730ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackLockedEngine__ #define __JackLockedEngine__ #include "JackEngine.h" #include "JackMutex.h" #include "JackTools.h" #include "JackException.h" namespace Jack { #define TRY_CALL \ try { \ /* See : http://groups.google.com/group/comp.programming.threads/browse_thread/thread/652bcf186fbbf697/f63757846514e5e5 catch (...) { // Assuming thread cancellation, must rethrow throw; } */ #define CATCH_EXCEPTION_RETURN \ } catch (std::bad_alloc& e) { \ jack_error("Memory allocation error..."); \ return -1; \ } catch (...) { \ jack_error("Unknown error..."); \ throw; \ } \ #define CATCH_CLOSE_EXCEPTION_RETURN \ } catch (std::bad_alloc& e) { \ jack_error("Memory allocation error..."); \ return -1; \ } catch (JackTemporaryException& e) { \ jack_error("JackTemporaryException : now quits..."); \ JackTools::KillServer(); \ return 0; \ } catch (...) { \ jack_error("Unknown error..."); \ throw; \ } #define CATCH_EXCEPTION \ } catch (std::bad_alloc& e) { \ jack_error("Memory allocation error..."); \ } catch (...) { \ jack_error("Unknown error..."); \ throw; \ } \ /*! \brief Locked Engine, access to methods is serialized using a mutex. */ class SERVER_EXPORT JackLockedEngine { private: JackEngine fEngine; public: JackLockedEngine(JackGraphManager* manager, JackSynchro* table, JackEngineControl* controler, char self_connect_mode): fEngine(manager, table, controler, self_connect_mode) {} ~JackLockedEngine() {} bool Lock() { return fEngine.Lock(); } bool Unlock() { return fEngine.Unlock(); } bool Trylock() { return fEngine.Trylock(); } int Open() { // No lock needed TRY_CALL return fEngine.Open(); CATCH_EXCEPTION_RETURN } int Close() { // No lock needed TRY_CALL return fEngine.Close(); CATCH_EXCEPTION_RETURN } // Client management int ClientCheck(const char* name, jack_uuid_t uuid, char* name_res, int protocol, int options, int* status) { TRY_CALL JackLock lock(&fEngine); return fEngine.ClientCheck(name, uuid, name_res, protocol, options, status); CATCH_EXCEPTION_RETURN } int ClientExternalOpen(const char* name, int pid, jack_uuid_t uuid, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager) { TRY_CALL JackLock lock(&fEngine); return fEngine.ClientExternalOpen(name, pid, uuid, ref, shared_engine, shared_client, shared_graph_manager); CATCH_EXCEPTION_RETURN } int ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait) { TRY_CALL JackLock lock(&fEngine); return fEngine.ClientInternalOpen(name, ref, shared_engine, shared_manager, client, wait); CATCH_EXCEPTION_RETURN } int ClientExternalClose(int refnum) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.ClientExternalClose(refnum) : -1; CATCH_CLOSE_EXCEPTION_RETURN } int ClientInternalClose(int refnum, bool wait) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.ClientInternalClose(refnum, wait) : -1; CATCH_CLOSE_EXCEPTION_RETURN } int ClientActivate(int refnum, bool is_real_time) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.ClientActivate(refnum, is_real_time) : -1; CATCH_EXCEPTION_RETURN } int ClientDeactivate(int refnum) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.ClientDeactivate(refnum) : -1; CATCH_EXCEPTION_RETURN } void ClientKill(int refnum) { TRY_CALL JackLock lock(&fEngine); fEngine.ClientKill(refnum); CATCH_EXCEPTION } // Internal client management int GetInternalClientName(int int_ref, char* name_res) { TRY_CALL JackLock lock(&fEngine); return fEngine.GetInternalClientName(int_ref, name_res); CATCH_EXCEPTION_RETURN } int InternalClientHandle(const char* client_name, int* status, int* int_ref) { TRY_CALL JackLock lock(&fEngine); return fEngine.InternalClientHandle(client_name, status, int_ref); CATCH_EXCEPTION_RETURN } int InternalClientUnload(int refnum, int* status) { TRY_CALL JackLock lock(&fEngine); // Client is tested in fEngine.InternalClientUnload return fEngine.InternalClientUnload(refnum, status); CATCH_EXCEPTION_RETURN } // Port management int PortRegister(int refnum, const char* name, const char *type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortRegister(refnum, name, type, flags, buffer_size, port) : -1; CATCH_EXCEPTION_RETURN } int PortUnRegister(int refnum, jack_port_id_t port) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortUnRegister(refnum, port) : -1; CATCH_EXCEPTION_RETURN } int PortConnect(int refnum, const char* src, const char* dst) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortConnect(refnum, src, dst) : -1; CATCH_EXCEPTION_RETURN } int PortDisconnect(int refnum, const char* src, const char* dst) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortDisconnect(refnum, src, dst) : -1; CATCH_EXCEPTION_RETURN } int PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortConnect(refnum, src, dst) : -1; CATCH_EXCEPTION_RETURN } int PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortDisconnect(refnum, src, dst) : -1; CATCH_EXCEPTION_RETURN } int PortRename(int refnum, jack_port_id_t port, const char* name) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortRename(refnum, port, name) : -1; CATCH_EXCEPTION_RETURN } int PortSetDefaultMetadata(int refnum, jack_port_id_t port, const char* pretty_name) { TRY_CALL JackLock lock(&fEngine); return (fEngine.CheckClient(refnum)) ? fEngine.PortSetDefaultMetadata(port, pretty_name) : -1; CATCH_EXCEPTION_RETURN } int ComputeTotalLatencies() { TRY_CALL JackLock lock(&fEngine); return fEngine.ComputeTotalLatencies(); CATCH_EXCEPTION_RETURN } // Graph bool Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end) { // RT : no lock return fEngine.Process(cur_cycle_begin, prev_cycle_end); } // Notifications void NotifyDriverXRun() { // Coming from the driver in RT : no lock fEngine.NotifyDriverXRun(); } void NotifyClientXRun(int refnum) { TRY_CALL JackLock lock(&fEngine); fEngine.NotifyClientXRun(refnum); CATCH_EXCEPTION } void NotifyGraphReorder() { TRY_CALL JackLock lock(&fEngine); fEngine.NotifyGraphReorder(); CATCH_EXCEPTION } void NotifyBufferSize(jack_nframes_t buffer_size) { TRY_CALL JackLock lock(&fEngine); fEngine.NotifyBufferSize(buffer_size); CATCH_EXCEPTION } void NotifySampleRate(jack_nframes_t sample_rate) { TRY_CALL JackLock lock(&fEngine); fEngine.NotifySampleRate(sample_rate); CATCH_EXCEPTION } void NotifyFreewheel(bool onoff) { TRY_CALL JackLock lock(&fEngine); fEngine.NotifyFreewheel(onoff); CATCH_EXCEPTION } void NotifyFailure(int code, const char* reason) { TRY_CALL JackLock lock(&fEngine); fEngine.NotifyFailure(code, reason); CATCH_EXCEPTION } int GetClientPID(const char* name) { TRY_CALL JackLock lock(&fEngine); return fEngine.GetClientPID(name); CATCH_EXCEPTION_RETURN } int GetClientRefNum(const char* name) { TRY_CALL JackLock lock(&fEngine); return fEngine.GetClientRefNum(name); CATCH_EXCEPTION_RETURN } void NotifyQuit() { // No lock needed TRY_CALL return fEngine.NotifyQuit(); CATCH_EXCEPTION } void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, detail::JackChannelTransactionInterface *socket, JackSessionNotifyResult** result) { TRY_CALL JackLock lock(&fEngine); fEngine.SessionNotify(refnum, target, type, path, socket, result); CATCH_EXCEPTION } int SessionReply(int refnum) { TRY_CALL JackLock lock(&fEngine); return fEngine.SessionReply(refnum); CATCH_EXCEPTION_RETURN } int GetUUIDForClientName(const char *client_name, char *uuid_res) { TRY_CALL JackLock lock(&fEngine); return fEngine.GetUUIDForClientName(client_name, uuid_res); CATCH_EXCEPTION_RETURN } int GetClientNameForUUID(const char *uuid, char *name_res) { TRY_CALL JackLock lock(&fEngine); return fEngine.GetClientNameForUUID(uuid, name_res); CATCH_EXCEPTION_RETURN } int ReserveClientName(const char *name, const char *uuid) { TRY_CALL JackLock lock(&fEngine); return fEngine.ReserveClientName(name, uuid); CATCH_EXCEPTION_RETURN } int ClientHasSessionCallback(const char *name) { TRY_CALL JackLock lock(&fEngine); return fEngine.ClientHasSessionCallback(name); CATCH_EXCEPTION_RETURN } int PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change) { TRY_CALL JackLock lock(&fEngine); return fEngine.PropertyChangeNotify(subject, key, change); CATCH_EXCEPTION_RETURN } }; } // end of namespace #endif jack2-1.9.22/common/JackLoopbackDriver.cpp000066400000000000000000000071421436671425200203100ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackLoopbackDriver.h" #include "JackDriverLoader.h" #include "JackEngineControl.h" #include "JackGraphManager.h" #include "JackError.h" #include #include namespace Jack { // When used in "slave" mode int JackLoopbackDriver::ProcessReadSync() { int res = 0; // Loopback copy for (int i = 0; i < fCaptureChannels; i++) { memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); } // Resume connected clients in the graph if (ResumeRefNum() < 0) { jack_error("JackLoopbackDriver::ProcessReadSync - ResumeRefNum error"); res = -1; } return res; } int JackLoopbackDriver::ProcessWriteSync() { // Suspend on connected clients in the graph if (SuspendRefNum() < 0) { jack_error("JackLoopbackDriver::ProcessWriteSync - SuspendRefNum error"); return -1; } return 0; } int JackLoopbackDriver::ProcessReadAsync() { int res = 0; // Loopback copy for (int i = 0; i < fCaptureChannels; i++) { memcpy(GetInputBuffer(i), GetOutputBuffer(i), sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); } // Resume connected clients in the graph if (ResumeRefNum() < 0) { jack_error("JackLoopbackDriver::ProcessReadAsync - ResumeRefNum error"); res = -1; } return res; } int JackLoopbackDriver::ProcessWriteAsync() { return 0; } } // end of namespace #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("loopback", JackDriverSlave, "Loopback backend", &filler); value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "channels", 'c', JackDriverParamInt, &value, NULL, "Maximum number of loopback ports", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { const JSList * node; const jack_driver_param_t * param; int channels = 2; for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; switch (param->character) { case 'c': channels = param->value.ui; break; } } Jack::JackDriverClientInterface* driver = new Jack::JackLoopbackDriver(engine, table); if (driver->Open(0, 0, 1, 1, channels, channels, false, "loopback", "loopback", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } #ifdef __cplusplus } #endif jack2-1.9.22/common/JackLoopbackDriver.h000066400000000000000000000026141436671425200177540ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackLoopbackDriver__ #define __JackLoopbackDriver__ #include "JackAudioDriver.h" namespace Jack { /*! \brief The loopback driver : to be used to "pipeline" applications connected in sequence. */ class JackLoopbackDriver : public JackAudioDriver { private: virtual int ProcessReadSync(); virtual int ProcessWriteSync(); virtual int ProcessReadAsync(); virtual int ProcessWriteAsync(); public: JackLoopbackDriver(JackLockedEngine* engine, JackSynchro* table) : JackAudioDriver("loopback", "loopback", engine, table) {} virtual ~JackLoopbackDriver() {} }; } // end of namespace #endif jack2-1.9.22/common/JackMessageBuffer.cpp000066400000000000000000000125741436671425200201250ustar00rootroot00000000000000/* * Copyright (C) 2004 Rui Nuno Capela, Steve Harris * Copyright (C) 2008 Nedko Arnaudov * Copyright (C) 2008 Grame * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "JackMessageBuffer.h" #include "JackGlobals.h" #include "JackError.h" #include "JackTime.h" namespace Jack { JackMessageBuffer* JackMessageBuffer::fInstance = NULL; JackMessageBuffer::JackMessageBuffer() :fInit(NULL), fInitArg(NULL), fThread(this), fGuard(), fInBuffer(0), fOutBuffer(0), fOverruns(0), fRunning(false) { static_assert(offsetof(JackMessageBuffer, fOverruns) % sizeof(fOverruns) == 0, "fOverruns must be aligned within JackMessageBuffer"); } JackMessageBuffer::~JackMessageBuffer() {} bool JackMessageBuffer::Start() { // Before StartSync()... fRunning = true; if (fThread.StartSync() == 0) { return true; } else { fRunning = false; return false; } } bool JackMessageBuffer::Stop() { if (fOverruns > 0) { jack_error("WARNING: %d message buffer overruns!", fOverruns); } else { jack_log("no message buffer overruns"); } if (fGuard.Lock()) { fRunning = false; fGuard.Signal(); fGuard.Unlock(); fThread.Stop(); } else { fThread.Kill(); } Flush(); return true; } void JackMessageBuffer::Flush() { while (fOutBuffer != fInBuffer) { jack_log_function(fBuffers[fOutBuffer].level, fBuffers[fOutBuffer].message); fOutBuffer = MB_NEXT(fOutBuffer); } } void JackMessageBuffer::AddMessage(int level, const char *message) { if (fGuard.Trylock()) { fBuffers[fInBuffer].level = level; strncpy(fBuffers[fInBuffer].message, message, MB_BUFFERSIZE); fInBuffer = MB_NEXT(fInBuffer); fGuard.Signal(); fGuard.Unlock(); } else { /* lock collision */ INC_ATOMIC(&fOverruns); } } bool JackMessageBuffer::Execute() { if (fGuard.Lock()) { while (fRunning) { fGuard.Wait(); /* the client asked for all threads to run a thread initialization callback, which includes us. */ if (fInit) { fInit(fInitArg); fInit = NULL; /* and we're done */ fGuard.Signal(); } /* releasing the mutex reduces contention */ fGuard.Unlock(); Flush(); fGuard.Lock(); } fGuard.Unlock(); } else { jack_error("JackMessageBuffer::Execute lock cannot be taken"); } return false; } bool JackMessageBuffer::Create() { if (fInstance == NULL) { fInstance = new JackMessageBuffer(); if (!fInstance->Start()) { jack_error("JackMessageBuffer::Create cannot start thread"); delete fInstance; fInstance = NULL; return false; } } return true; } bool JackMessageBuffer::Destroy() { if (fInstance != NULL) { fInstance->Stop(); delete fInstance; fInstance = NULL; return true; } else { return false; } } void JackMessageBufferAdd(int level, const char *message) { if (Jack::JackMessageBuffer::fInstance == NULL) { /* Unable to print message with realtime safety. Complain and print it anyway. */ jack_log_function(LOG_LEVEL_ERROR, "messagebuffer not initialized, skip message"); } else { Jack::JackMessageBuffer::fInstance->AddMessage(level, message); } } int JackMessageBuffer::SetInitCallback(JackThreadInitCallback callback, void *arg) { if (fInstance && callback && fRunning && fGuard.Lock()) { /* set up the callback */ fInitArg = arg; fInit = callback; #ifndef WIN32 // wake msg buffer thread fGuard.Signal(); // wait for it to be done fGuard.Wait(); // and we're done fGuard.Unlock(); #else /* The condition variable emulation code does not work reliably on Windows (lost signal). So use a "hackish" way to signal/wait for the result. Probably better in the long term : use pthread-win32 (http://sourceware.org/pthreads-win32/` */ fGuard.Unlock(); int count = 0; while (fInit && ++count < 1000) { /* wake msg buffer thread */ fGuard.Signal(); JackSleep(1000); } if (count == 1000) { jack_error("JackMessageBuffer::SetInitCallback : signal lost"); return -1; } #endif return 0; } jack_error("JackMessageBuffer::SetInitCallback : callback could not be executed"); return -1; } }; jack2-1.9.22/common/JackMessageBuffer.h000066400000000000000000000050461436671425200175660ustar00rootroot00000000000000/* * messagebuffer.h -- realtime-safe message interface for jackd. * * This function is included in libjack so backend drivers can use * it, *not* for external client processes. The VERBOSE() and * MESSAGE() macros are realtime-safe. */ /* * Copyright (C) 2004 Rui Nuno Capela, Steve Harris * Copyright (C) 2008 Nedko Arnaudov * Copyright (C) 2008 Grame * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifndef __JackMessageBuffer__ #define __JackMessageBuffer__ #include "JackPlatformPlug.h" #include "JackMutex.h" #include "JackAtomic.h" namespace Jack { /* MB_NEXT() relies on the fact that MB_BUFFERS is a power of two */ #define MB_BUFFERS 128 #define MB_NEXT(index) ((index+1) & (MB_BUFFERS-1)) #define MB_BUFFERSIZE 256 /* message length limit */ struct JackMessage { int level; char message[MB_BUFFERSIZE]; }; /*! \brief Message buffer to be used from RT threads. */ class JackMessageBuffer : public JackRunnableInterface { private: volatile JackThreadInitCallback fInit; void* fInitArg; JackMessage fBuffers[MB_BUFFERS]; JackThread fThread; JackProcessSync fGuard; volatile unsigned int fInBuffer; volatile unsigned int fOutBuffer; alignas(SInt32) SInt32 fOverruns; bool fRunning; void Flush(); bool Start(); bool Stop(); public: JackMessageBuffer(); ~JackMessageBuffer(); // JackRunnableInterface interface bool Execute(); bool static Create(); bool static Destroy(); void AddMessage(int level, const char *message); int SetInitCallback(JackThreadInitCallback callback, void *arg); static JackMessageBuffer* fInstance; }; #ifdef __cplusplus extern "C" { #endif void JackMessageBufferAdd(int level, const char *message); #ifdef __cplusplus } #endif }; #endif jack2-1.9.22/common/JackMetadata.cpp000066400000000000000000000520731436671425200171250ustar00rootroot00000000000000/* Copyright (C) 2011 David Robillard Copyright (C) 2013 Paul Davis Copyright (C) 2019 Filipe Coelho This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackMetadata.h" #include "JackClient.h" #include "JackTools.h" #include #include #include #define JACK_METADATA_PREFIX "http://jackaudio.org/metadata/" LIB_EXPORT const char* JACK_METADATA_CONNECTED = JACK_METADATA_PREFIX "connected"; LIB_EXPORT const char* JACK_METADATA_EVENT_TYPES = JACK_METADATA_PREFIX "event-types"; LIB_EXPORT const char* JACK_METADATA_HARDWARE = JACK_METADATA_PREFIX "hardware"; LIB_EXPORT const char* JACK_METADATA_ICON_LARGE = JACK_METADATA_PREFIX "icon-large"; LIB_EXPORT const char* JACK_METADATA_ICON_NAME = JACK_METADATA_PREFIX "icon-name"; LIB_EXPORT const char* JACK_METADATA_ICON_SMALL = JACK_METADATA_PREFIX "icon-small"; LIB_EXPORT const char* JACK_METADATA_ORDER = JACK_METADATA_PREFIX "order"; LIB_EXPORT const char* JACK_METADATA_PORT_GROUP = JACK_METADATA_PREFIX "port-group"; LIB_EXPORT const char* JACK_METADATA_PRETTY_NAME = JACK_METADATA_PREFIX "pretty-name"; LIB_EXPORT const char* JACK_METADATA_SIGNAL_TYPE = JACK_METADATA_PREFIX "signal-type"; #undef JACK_METADATA_PREFIX namespace Jack { JackMetadata::JackMetadata(bool isEngine) #if HAVE_DB : fDB(NULL), fDBenv(NULL), fIsEngine(isEngine) #endif { PropertyInit(); } JackMetadata::~JackMetadata() { #if HAVE_DB char dbpath[PATH_MAX + 1]; if (fDB) { fDB->close (fDB, 0); fDB = NULL; } if (fDBenv) { fDBenv->close (fDBenv, 0); fDBenv = NULL; } if (fIsEngine) { // cleanup after libdb, nasty! snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/metadata.db", fDBFilesDir, JackTools::GetUID()); remove (dbpath); snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.001", fDBFilesDir, JackTools::GetUID()); remove (dbpath); snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.002", fDBFilesDir, JackTools::GetUID()); remove (dbpath); snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.003", fDBFilesDir, JackTools::GetUID()); remove (dbpath); // remove our custom dir snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d", fDBFilesDir, JackTools::GetUID()); rmdir (dbpath); } #endif } int JackMetadata::PropertyInit() { #if HAVE_DB int ret; char dbpath[PATH_MAX + 1]; #ifdef WIN32 ret = GetTempPathA (PATH_MAX, fDBFilesDir); if ((ret > PATH_MAX) || (ret == 0)) { jack_error ("cannot get path for temp files"); return -1; } #else strncpy (fDBFilesDir, jack_server_dir, PATH_MAX); #endif /* idempotent */ if (fDBenv) { return 0; } if ((ret = db_env_create (&fDBenv, 0)) != 0) { jack_error ("cannot initialize DB environment: %s\n", db_strerror (ret)); return -1; } snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d", fDBFilesDir, JackTools::GetUID()); #ifdef WIN32 mkdir (dbpath); #else mkdir (dbpath, S_IRWXU | S_IRWXG); #endif if ((ret = fDBenv->open (fDBenv, dbpath, DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD, 0)) != 0) { #if defined(WIN32) || defined(__APPLE__) // new versions of jack2 are built with HAVE_MIXED_SIZE_ADDRESSING, which induces this error, this is expected if (ret == DB_VERSION_MISMATCH) { jack_error ("Failed to open previous DB environment, trying again clean..."); // cleanup old stuff snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/metadata.db", fDBFilesDir, JackTools::GetUID()); remove (dbpath); snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.001", fDBFilesDir, JackTools::GetUID()); remove (dbpath); snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.002", fDBFilesDir, JackTools::GetUID()); remove (dbpath); snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.003", fDBFilesDir, JackTools::GetUID()); remove (dbpath); // try again fresh ret = fDBenv->open (fDBenv, dbpath, DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD, 0); } if (ret != 0) #endif { jack_error ("Cannot open DB environment: %s", db_strerror (ret)); fDBenv = NULL; return -1; } } if ((ret = db_create (&fDB, fDBenv, 0)) != 0) { jack_error ("Cannot initialize metadata DB (%s)", db_strerror (ret)); fDBenv->close (fDBenv, 0); fDBenv = NULL; return -1; } snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/metadata.db", fDBFilesDir, JackTools::GetUID()); if ((ret = fDB->open (fDB, NULL, dbpath, NULL, DB_HASH, DB_CREATE | DB_THREAD, 0666)) != 0) { jack_error ("Cannot open metadata DB at %s: %s", dbpath, db_strerror (ret)); fDB->close (fDB, 0); fDB = NULL; fDBenv->close (fDBenv, 0); fDBenv = NULL; return -1; } return 0; #else // !HAVE_DB return -1; #endif } int JackMetadata::PropertyChangeNotify(JackClient* client, jack_uuid_t subject, const char* key, jack_property_change_t change) { /* the engine passes in a NULL client when it removes metadata during port or client removal */ if (client == NULL) { return 0; } return client->PropertyChangeNotify(subject, key, change); } #if HAVE_DB void JackMetadata::MakeKeyDbt(DBT* dbt, jack_uuid_t subject, const char* key) { char ustr[JACK_UUID_STRING_SIZE]; size_t len1, len2; memset (dbt, 0, sizeof(DBT)); memset (ustr, 0, JACK_UUID_STRING_SIZE); jack_uuid_unparse (subject, ustr); len1 = JACK_UUID_STRING_SIZE; len2 = strlen (key) + 1; dbt->size = len1 + len2; dbt->data = malloc (dbt->size); memcpy (dbt->data, ustr, len1); // copy subject+null memcpy ((char *)dbt->data + len1, key, len2); // copy key+null } #endif int JackMetadata::SetProperty(JackClient* client, jack_uuid_t subject, const char* key, const char* value, const char* type) { #if HAVE_DB DBT d_key; DBT data; int ret; size_t len1, len2; jack_property_change_t change; if (!key || key[0] == '\0') { jack_error ("empty key string for metadata not allowed"); return -1; } if (!value || value[0] == '\0') { jack_error ("empty value string for metadata not allowed"); return -1; } if (PropertyInit()) { return -1; } /* build a key */ MakeKeyDbt(&d_key, subject, key); /* build data */ memset (&data, 0, sizeof(data)); len1 = strlen (value) + 1; if (type && type[0] != '\0') { len2 = strlen (type) + 1; } else { len2 = 0; } data.size = len1 + len2; data.data = malloc (data.size); memcpy (data.data, value, len1); if (len2) { memcpy ((char *)data.data + len1, type, len2); } if (fDB->exists (fDB, NULL, &d_key, 0) == DB_NOTFOUND) { change = PropertyCreated; } else { change = PropertyChanged; } if ((ret = fDB->put (fDB, NULL, &d_key, &data, 0)) != 0) { char ustr[JACK_UUID_STRING_SIZE]; jack_uuid_unparse (subject, ustr); jack_error ("Cannot store metadata for %s/%s (%s)", ustr, key, db_strerror (ret)); if (d_key.size > 0) { free (d_key.data); } if (data.size > 0) { free (data.data); } return -1; } PropertyChangeNotify(client, subject, key, change); if (d_key.size > 0) { free (d_key.data); } if (data.size > 0) { free (data.data); } return 0; #else // !HAVE_DB return -1; #endif } int JackMetadata::GetProperty(jack_uuid_t subject, const char* key, char** value, char** type) { #if HAVE_DB DBT d_key; DBT data; int ret; size_t len1, len2; if (key == NULL || key[0] == '\0') { return -1; } if (PropertyInit()) { return -1; } /* build a key */ MakeKeyDbt(&d_key, subject, key); /* setup data DBT */ memset (&data, 0, sizeof(data)); data.flags = DB_DBT_MALLOC; if ((ret = fDB->get (fDB, NULL, &d_key, &data, 0)) != 0) { if (ret != DB_NOTFOUND) { char ustr[JACK_UUID_STRING_SIZE]; jack_uuid_unparse (subject, ustr); jack_error ("Cannot retrieve metadata for %s/%s (%s)", ustr, key, db_strerror (ret)); } if (d_key.size > 0) { free (d_key.data); } if (data.size > 0) { free (data.data); } return -1; } /* result must have at least 1 char plus 1 null to be valid (old rule was: result must have at least 2 chars plus 2 nulls to be valid ) */ if (data.size < 2) { if (d_key.size > 0) { free (d_key.data); } if (data.size > 0) { free (data.data); } return -1; } len1 = strlen ((const char*)data.data) + 1; (*value) = (char*)malloc (len1); memcpy (*value, data.data, len1); if (len1 < data.size) { len2 = strlen ((const char*)data.data + len1) + 1; (*type) = (char*)malloc (len2); memcpy (*type, (const char *)data.data + len1, len2); } else { /* no type specified, assume default */ *type = NULL; } if (d_key.size > 0) { free (d_key.data); } if (data.size > 0) { free (data.data); } return 0; #else // !HAVE_DB return -1; #endif } int JackMetadata::GetProperties(jack_uuid_t subject, jack_description_t* desc) { #if HAVE_DB DBT key; DBT data; DBC* cursor; int ret; size_t len1, len2; size_t cnt = 0; char ustr[JACK_UUID_STRING_SIZE]; size_t props_size = 0; jack_property_t* prop; desc->properties = NULL; desc->property_cnt = 0; memset (ustr, 0, JACK_UUID_STRING_SIZE); jack_uuid_unparse (subject, ustr); if (PropertyInit()) { return -1; } if ((ret = fDB->cursor (fDB, NULL, &cursor, 0)) != 0) { jack_error ("Cannot create cursor for metadata search (%s)", db_strerror (ret)); return -1; } memset (&key, 0, sizeof(key)); memset (&data, 0, sizeof(data)); data.flags = DB_DBT_MALLOC; while ((ret = cursor->get (cursor, &key, &data, DB_NEXT)) == 0) { /* require 2 extra chars (data+null) for key, which is composed of UUID str plus a key name */ if (key.size < JACK_UUID_STRING_SIZE + 2) { /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } continue; } if (memcmp (ustr, key.data, JACK_UUID_STRING_SIZE) != 0) { /* not relevant */ /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } continue; } /* result must have at least 1 char plus 1 null to be valid (old rule was: result must have at least 2 chars plus 2 nulls to be valid ) */ if (data.size < 2) { /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } continue; } /* realloc array if necessary */ if (cnt == props_size) { if (props_size == 0) { props_size = 8; /* a rough guess at a likely upper bound for the number of properties */ } else { props_size *= 2; } desc->properties = (jack_property_t*)realloc (desc->properties, sizeof(jack_property_t) * props_size); } prop = &desc->properties[cnt]; /* store UUID/subject */ jack_uuid_copy (&desc->subject, subject); /* copy key (without leading UUID as subject */ len1 = key.size - JACK_UUID_STRING_SIZE; prop->key = (char*)malloc (len1); memcpy ((char*)prop->key, (const char *)key.data + JACK_UUID_STRING_SIZE, len1); /* copy data (which contains 1 or 2 null terminated strings, the value and optionally a MIME type. */ len1 = strlen ((const char*)data.data) + 1; prop->data = (char*)malloc (len1); memcpy ((char*)prop->data, data.data, len1); if (len1 < data.size) { len2 = strlen ((const char *)data.data + len1) + 1; prop->type = (char*)malloc (len2); memcpy ((char*)prop->type, (const char *)data.data + len1, len2); } else { /* no type specified, assume default */ prop->type = NULL; } /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } ++cnt; } cursor->close (cursor); desc->property_cnt = cnt; return cnt; #else // !HAVE_DB return -1; #endif } int JackMetadata::GetAllProperties(jack_description_t** descriptions) { #if HAVE_DB DBT key; DBT data; DBC* cursor; int ret; size_t dcnt = 0; size_t dsize = 0; size_t n = 0; jack_description_t* desc = NULL; jack_uuid_t uuid = JACK_UUID_EMPTY_INITIALIZER; jack_description_t* current_desc = NULL; jack_property_t* current_prop = NULL; size_t len1, len2; if (PropertyInit()) { return -1; } if ((ret = fDB->cursor (fDB, NULL, &cursor, 0)) != 0) { jack_error ("Cannot create cursor for metadata search (%s)", db_strerror (ret)); return -1; } memset (&key, 0, sizeof(key)); memset (&data, 0, sizeof(data)); data.flags = DB_DBT_MALLOC; dsize = 8; /* initial guess at number of descriptions we need */ dcnt = 0; desc = (jack_description_t*)malloc (dsize * sizeof(jack_description_t)); while ((ret = cursor->get (cursor, &key, &data, DB_NEXT)) == 0) { /* require 2 extra chars (data+null) for key, which is composed of UUID str plus a key name */ if (key.size < JACK_UUID_STRING_SIZE + 2) { /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } continue; } if (jack_uuid_parse ((const char *)key.data, &uuid) != 0) { continue; } /* do we have an existing description for this UUID */ for (n = 0; n < dcnt; ++n) { if (jack_uuid_compare (uuid, desc[n].subject) == 0) { break; } } if (n == dcnt) { /* we do not have an existing description, so grow the array */ if (dcnt == dsize) { dsize *= 2; desc = (jack_description_t*)realloc (desc, sizeof(jack_description_t) * dsize); } /* initialize */ desc[n].property_size = 0; desc[n].property_cnt = 0; desc[n].properties = NULL; /* set up UUID */ jack_uuid_copy (&desc[n].subject, uuid); dcnt++; } current_desc = &desc[n]; /* see if there is room for the new property or if we need to realloc */ if (current_desc->property_cnt == current_desc->property_size) { if (current_desc->property_size == 0) { current_desc->property_size = 8; } else { current_desc->property_size *= 2; } current_desc->properties = (jack_property_t*)realloc (current_desc->properties, sizeof(jack_property_t) * current_desc->property_size); } current_prop = ¤t_desc->properties[current_desc->property_cnt++]; /* copy key (without leading UUID) */ len1 = key.size - JACK_UUID_STRING_SIZE; current_prop->key = (char*)malloc (len1); memcpy ((char*)current_prop->key, (const char *)key.data + JACK_UUID_STRING_SIZE, len1); /* copy data (which contains 1 or 2 null terminated strings, the value and optionally a MIME type. */ len1 = strlen ((const char *)data.data) + 1; current_prop->data = (char*)malloc (len1); memcpy ((char*)current_prop->data, data.data, len1); if (len1 < data.size) { len2 = strlen ((const char *)data.data + len1) + 1; current_prop->type = (char*)malloc (len2); memcpy ((char*)current_prop->type, (const char *)data.data + len1, len2); } else { /* no type specified, assume default */ current_prop->type = NULL; } /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } } cursor->close (cursor); (*descriptions) = desc; return dcnt; #else // !HAVE_DB return -1; #endif } int JackMetadata::GetDescription(jack_uuid_t subject, jack_description_t* desc) { return 0; } int JackMetadata::GetAllDescriptions(jack_description_t** descs) { return 0; } void JackMetadata::FreeDescription(jack_description_t* desc, int free_actual_description_too) { uint32_t n; for (n = 0; n < desc->property_cnt; ++n) { free ((char*)desc->properties[n].key); free ((char*)desc->properties[n].data); if (desc->properties[n].type) { free ((char*)desc->properties[n].type); } } free (desc->properties); if (free_actual_description_too) { free (desc); } } int JackMetadata::RemoveProperty(JackClient* client, jack_uuid_t subject, const char* key) { #if HAVE_DB DBT d_key; int ret; if (PropertyInit()) { return -1; } MakeKeyDbt(&d_key, subject, key); if ((ret = fDB->del (fDB, NULL, &d_key, 0)) != 0) { jack_error ("Cannot delete key %s (%s)", key, db_strerror (ret)); if (d_key.size > 0) { free (d_key.data); } return -1; } PropertyChangeNotify(client, subject, key, PropertyDeleted); if (d_key.size > 0) { free (d_key.data); } return 0; #else // !HAVE_DB return -1; #endif } int JackMetadata::RemoveProperties(JackClient* client, jack_uuid_t subject) { #if HAVE_DB DBT key; DBT data; DBC* cursor; int ret; char ustr[JACK_UUID_STRING_SIZE]; int retval = 0; uint32_t cnt = 0; memset (ustr, 0, JACK_UUID_STRING_SIZE); jack_uuid_unparse (subject, ustr); if (PropertyInit() || fDB == NULL) { return -1; } if ((ret = fDB->cursor (fDB, NULL, &cursor, 0)) != 0) { jack_error ("Cannot create cursor for metadata search (%s)", db_strerror (ret)); return -1; } memset (&key, 0, sizeof(key)); memset (&data, 0, sizeof(data)); data.flags = DB_DBT_MALLOC; while ((ret = cursor->get (cursor, &key, &data, DB_NEXT)) == 0) { /* require 2 extra chars (data+null) for key, which is composed of UUID str plus a key name */ if (key.size < JACK_UUID_STRING_SIZE + 2) { /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } continue; } if (memcmp (ustr, key.data, JACK_UUID_STRING_SIZE) != 0) { /* not relevant */ /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } continue; } if ((ret = cursor->del (cursor, 0)) != 0) { jack_error ("cannot delete property (%s)", db_strerror (ret)); /* don't return -1 here since this would leave things even more inconsistent. wait till the cursor is finished */ retval = -1; } cnt++; /* if (key.size > 0) free(key.data); */ if (data.size > 0) { free (data.data); } } cursor->close (cursor); if (cnt) { PropertyChangeNotify(client, subject, NULL, PropertyDeleted); } if (retval) { return -1; } return cnt; #else // !HAVE_DB return -1; #endif } int JackMetadata::RemoveAllProperties(JackClient* client) { #if HAVE_DB int ret; jack_uuid_t empty_uuid = JACK_UUID_EMPTY_INITIALIZER; if (PropertyInit()) { return -1; } if ((ret = fDB->truncate (fDB, NULL, NULL, 0)) != 0) { jack_error ("Cannot clear properties (%s)", db_strerror (ret)); return -1; } PropertyChangeNotify(client, empty_uuid, NULL, PropertyDeleted); return 0; #else // !HAVE_DB return -1; #endif } } // end of namespace jack2-1.9.22/common/JackMetadata.h000066400000000000000000000062531436671425200165710ustar00rootroot00000000000000/* Copyright (C) 2011 David Robillard Copyright (C) 2013 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMetadata__ #define __JackMetadata__ #ifdef HAVE_CONFIG_H #include "config.h" #endif // libdb does not work in 32bit mixed mode #ifdef BUILD_WITH_32_64 #undef HAVE_DB #define HAVE_DB 0 #endif #include #include #if HAVE_DB #include #endif #include #ifdef __cplusplus extern "C" { #endif typedef struct { const char* key; const char* data; const char* type; } jack_property_t; typedef struct { jack_uuid_t subject; uint32_t property_cnt; jack_property_t* properties; uint32_t property_size; } jack_description_t; typedef enum { PropertyCreated, PropertyChanged, PropertyDeleted } jack_property_change_t; typedef void (*JackPropertyChangeCallback)(jack_uuid_t subject, const char* key, jack_property_change_t change, void* arg); #ifdef __cplusplus } #endif namespace Jack { class JackClient; /*! \brief Metadata base. */ class JackMetadata { private: #if HAVE_DB DB* fDB; DB_ENV* fDBenv; const bool fIsEngine; char fDBFilesDir[PATH_MAX + 1]; #endif int PropertyInit(); int PropertyChangeNotify(JackClient* client, jack_uuid_t subject, const char* key, jack_property_change_t change); #if HAVE_DB void MakeKeyDbt(DBT* dbt, jack_uuid_t subject, const char* key); #endif public: JackMetadata(bool isEngine); ~JackMetadata(); int GetProperty(jack_uuid_t subject, const char* key, char** value, char** type); int GetProperties(jack_uuid_t subject, jack_description_t* desc); int GetAllProperties(jack_description_t** descriptions); int GetDescription(jack_uuid_t subject, jack_description_t* desc); int GetAllDescriptions(jack_description_t** descs); void FreeDescription(jack_description_t* desc, int free_actual_description_too); int SetProperty(JackClient* client, jack_uuid_t subject, const char* key, const char* value, const char* type); int RemoveProperty(JackClient* client, jack_uuid_t subject, const char* key); int RemoveProperties(JackClient* client, jack_uuid_t subject); int RemoveAllProperties(JackClient* client); }; } // end of namespace #endif jack2-1.9.22/common/JackMidiAPI.cpp000066400000000000000000000110501436671425200166070ustar00rootroot00000000000000/* Copyright (C) 2007 Dmitry Baikov Original JACK MIDI implementation Copyright (C) 2004 Ian Esten This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackError.h" #include "JackMidiPort.h" #include #include #ifdef __cplusplus extern "C" { #endif LIB_EXPORT uint32_t jack_midi_get_event_count(void* port_buffer); LIB_EXPORT int jack_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint32_t event_index); LIB_EXPORT void jack_midi_clear_buffer(void* port_buffer); LIB_EXPORT void jack_midi_reset_buffer(void* port_buffer); LIB_EXPORT size_t jack_midi_max_event_size(void* port_buffer); LIB_EXPORT jack_midi_data_t* jack_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size); LIB_EXPORT int jack_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size); LIB_EXPORT jack_nframes_t jack_midi_get_lost_event_count(void* port_buffer); #ifdef __cplusplus } #endif using namespace Jack; LIB_EXPORT uint32_t jack_midi_get_event_count(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (!buf || !buf->IsValid()) { return 0; } return buf->event_count; } LIB_EXPORT int jack_midi_event_get(jack_midi_event_t *event, void* port_buffer, uint32_t event_index) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (!buf || !buf->IsValid()) { return -EINVAL; } if (event_index >= buf->event_count) { return -ENOBUFS; } JackMidiEvent* ev = &buf->events[event_index]; event->time = ev->time; event->size = ev->size; event->buffer = ev->GetData(buf); return 0; } LIB_EXPORT void jack_midi_clear_buffer(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (buf && buf->IsValid()) { buf->Reset(buf->nframes); } } LIB_EXPORT void jack_midi_reset_buffer(void* port_buffer) { MidiBufferInit(port_buffer, BUFFER_SIZE_MAX, BUFFER_SIZE_MAX); } LIB_EXPORT size_t jack_midi_max_event_size(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (buf && buf->IsValid()) { return buf->MaxEventSize(); } return 0; } LIB_EXPORT jack_midi_data_t* jack_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (! buf) { jack_error("jack_midi_event_reserve: port buffer is set to NULL"); return 0; } if (! buf->IsValid()) { jack_error("jack_midi_event_reserve: port buffer is invalid"); return 0; } if (time >= buf->nframes) { jack_error("jack_midi_event_reserve: time parameter is out of range " "(%lu >= %lu)", time, buf->nframes); return 0; } if (buf->event_count && (buf->events[buf->event_count - 1].time > time)) { jack_error("jack_midi_event_reserve: time parameter is earlier than " "last reserved event"); return 0; } return buf->ReserveEvent(time, data_size); } LIB_EXPORT int jack_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (!buf || !buf->IsValid()) { return -EINVAL; } if (time >= buf->nframes || (buf->event_count && buf->events[buf->event_count - 1].time > time)) { return -EINVAL; } jack_midi_data_t* dest = buf->ReserveEvent(time, data_size); if (!dest) { return -ENOBUFS; } memcpy(dest, data, data_size); return 0; } LIB_EXPORT uint32_t jack_midi_get_lost_event_count(void* port_buffer) { JackMidiBuffer *buf = (JackMidiBuffer*)port_buffer; if (buf && buf->IsValid()) { return buf->lost_events; } return 0; } jack2-1.9.22/common/JackMidiAsyncQueue.cpp000066400000000000000000000062071436671425200202700ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "JackMidiAsyncQueue.h" using Jack::JackMidiAsyncQueue; JackMidiAsyncQueue::JackMidiAsyncQueue(size_t max_bytes, size_t max_messages) { data_buffer = new jack_midi_data_t[max_bytes]; byte_ring = jack_ringbuffer_create((max_bytes * sizeof(jack_midi_data_t)) + 1); if (byte_ring) { info_ring = jack_ringbuffer_create((max_messages * INFO_SIZE) + 1); if (info_ring) { jack_ringbuffer_mlock(byte_ring); jack_ringbuffer_mlock(info_ring); this->max_bytes = max_bytes; return; } jack_ringbuffer_free(byte_ring); } delete[] data_buffer; throw std::bad_alloc(); } JackMidiAsyncQueue::~JackMidiAsyncQueue() { jack_ringbuffer_free(byte_ring); jack_ringbuffer_free(info_ring); delete[] data_buffer; } jack_midi_event_t * JackMidiAsyncQueue::DequeueEvent() { jack_midi_event_t *event = 0; if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) { size_t size; event = &dequeue_event; jack_ringbuffer_read(info_ring, (char *) &(event->time), sizeof(jack_nframes_t)); jack_ringbuffer_read(info_ring, (char *) &size, sizeof(size_t)); jack_ringbuffer_read(byte_ring, (char *) data_buffer, size * sizeof(jack_midi_data_t)); event->buffer = data_buffer; event->size = size; } return event; } Jack::JackMidiWriteQueue::EnqueueResult JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer) { if (size > max_bytes) { return BUFFER_TOO_SMALL; } if (! ((jack_ringbuffer_write_space(info_ring) >= INFO_SIZE) && (jack_ringbuffer_write_space(byte_ring) >= (size * sizeof(jack_midi_data_t))))) { return BUFFER_FULL; } jack_ringbuffer_write(byte_ring, (const char *) buffer, size * sizeof(jack_midi_data_t)); jack_ringbuffer_write(info_ring, (const char *) (&time), sizeof(jack_nframes_t)); jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t)); return OK; } size_t JackMidiAsyncQueue::GetAvailableSpace() { return jack_ringbuffer_write_space(info_ring) < INFO_SIZE ? 0 : max_bytes - jack_ringbuffer_read_space(byte_ring); } jack2-1.9.22/common/JackMidiAsyncQueue.h000066400000000000000000000057611436671425200177410ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiAsyncQueue__ #define __JackMidiAsyncQueue__ #include "JackMidiPort.h" #include "JackMidiReadQueue.h" #include "JackMidiWriteQueue.h" #include "ringbuffer.h" namespace Jack { /** * This is a MIDI message queue designed to allow one thread to pass MIDI * messages to another thread (though it can also be used to buffer events * internally). This is especially useful if the MIDI API you're * attempting to interface with doesn't provide the ability to schedule * MIDI events ahead of time and/or has blocking send/receive calls, as it * allows a separate thread to handle input/output while the JACK process * thread copies events from a MIDI buffer to this queue, or vice versa. */ class SERVER_EXPORT JackMidiAsyncQueue: public JackMidiReadQueue, public JackMidiWriteQueue { private: static const size_t INFO_SIZE = sizeof(jack_nframes_t) + sizeof(size_t); jack_ringbuffer_t *byte_ring; jack_midi_data_t *data_buffer; jack_midi_event_t dequeue_event; jack_ringbuffer_t *info_ring; size_t max_bytes; public: using JackMidiWriteQueue::EnqueueEvent; /** * Creates a new asynchronous MIDI message queue. The queue can store * up to `max_messages` MIDI messages and up to `max_bytes` of MIDI * data before it starts rejecting messages. */ JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024); virtual ~JackMidiAsyncQueue(); /** * Dequeues and returns a MIDI event. Returns '0' if there are no MIDI * events available. This method may be overridden. */ virtual jack_midi_event_t * DequeueEvent(); /** * Enqueues the MIDI event specified by the arguments. The return * value indicates whether or not the event was successfully enqueued. * This method may be overridden. */ virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer); /** * Returns the maximum size event that can be enqueued right *now*. */ size_t GetAvailableSpace(); }; } #endif jack2-1.9.22/common/JackMidiAsyncWaitQueue.cpp000066400000000000000000000055401436671425200211140ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "JackMidiAsyncWaitQueue.h" #include "JackMidiUtil.h" #include "JackTime.h" using Jack::JackMidiAsyncWaitQueue; JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes, size_t max_messages): JackMidiAsyncQueue(max_bytes, max_messages) { if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) { throw std::bad_alloc(); } } JackMidiAsyncWaitQueue::~JackMidiAsyncWaitQueue() { semaphore.Destroy(); } jack_midi_event_t * JackMidiAsyncWaitQueue::DequeueEvent() { return DequeueEvent((long) 0); } jack_midi_event_t * JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame) { // XXX: I worry about timer resolution on Solaris and Windows. When the // resolution for the `JackSynchro` object is milliseconds, the worst-case // scenario for processor objects is that the wait time becomes less than a // millisecond, and the processor object continually calls this method, // expecting to wait a certain amount of microseconds, and ends up not // waiting at all each time, essentially busy-waiting until the current // frame is reached. Perhaps there should be a #define that indicates the // wait time resolution for `JackSynchro` objects so that we can wait a // little longer if necessary. jack_time_t frame_time = GetTimeFromFrames(frame); jack_time_t current_time = GetMicroSeconds(); return DequeueEvent((frame_time < current_time) ? 0 : (long) (frame_time - current_time)); } jack_midi_event_t * JackMidiAsyncWaitQueue::DequeueEvent(long usec) { return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ? JackMidiAsyncQueue::DequeueEvent() : 0; } Jack::JackMidiWriteQueue::EnqueueResult JackMidiAsyncWaitQueue::EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer) { EnqueueResult result = JackMidiAsyncQueue::EnqueueEvent(time, size, buffer); if (result == OK) { semaphore.Signal(); } return result; } jack2-1.9.22/common/JackMidiAsyncWaitQueue.h000066400000000000000000000057461436671425200205710ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiAsyncWaitQueue__ #define __JackMidiAsyncWaitQueue__ #include "JackMidiAsyncQueue.h" namespace Jack { /** * This is an asynchronous wait queue that allows a thread to wait for a * message, either indefinitely or for a specified time. This is one * example of a way that the `JackMidiAsyncQueue` class can be extended so * that process threads can interact with non-process threads to send MIDI * events. * * XXX: As of right now, this code hasn't been tested. Also, note the * warning in the JackMidiAsyncWaitQueue.cpp about semaphore wait * resolution. */ class SERVER_EXPORT JackMidiAsyncWaitQueue: public JackMidiAsyncQueue { private: JackSynchro semaphore; public: using JackMidiAsyncQueue::EnqueueEvent; /** * Creates a new asynchronous MIDI wait message queue. The queue can * store up to `max_messages` MIDI messages and up to `max_bytes` of * MIDI data before it starts rejecting messages. */ JackMidiAsyncWaitQueue(size_t max_bytes=4096, size_t max_messages=1024); ~JackMidiAsyncWaitQueue(); /** * Dequeues and returns a MIDI event. Returns '0' if there are no MIDI * events available right now. */ jack_midi_event_t * DequeueEvent(); /** * Waits a specified time for a MIDI event to be available, or * indefinitely if the time is negative. Returns the MIDI event, or * '0' if time runs out and no MIDI event is available. */ jack_midi_event_t * DequeueEvent(long usecs); /** * Waits until the specified frame for a MIDI event to be available. * Returns the MIDI event, or '0' if time runs out and no MIDI event is * available. */ jack_midi_event_t * DequeueEvent(jack_nframes_t frame); /** * Enqueues the MIDI event specified by the arguments. The return * value indicates whether or not the event was successfully enqueued. */ EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer); }; } #endif jack2-1.9.22/common/JackMidiBufferReadQueue.cpp000066400000000000000000000041001436671425200212060ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackMidiBufferReadQueue.h" #include "JackMidiUtil.h" #include "JackError.h" using Jack::JackMidiBufferReadQueue; JackMidiBufferReadQueue::JackMidiBufferReadQueue() { event_count = 0; index = 0; } jack_midi_event_t * JackMidiBufferReadQueue::DequeueEvent() { jack_midi_event_t *e = 0; if (index < event_count) { JackMidiEvent *event = &(buffer->events[index]); midi_event.buffer = event->GetData(buffer); midi_event.size = event->size; midi_event.time = last_frame_time + event->time; e = &midi_event; index++; } return e; } void JackMidiBufferReadQueue::ResetMidiBuffer(JackMidiBuffer *buffer) { event_count = 0; index = 0; if (! buffer) { jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " "to NULL"); } else if (! buffer->IsValid()) { jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - buffer reset " "to invalid buffer"); } else { uint32_t lost_events = buffer->lost_events; if (lost_events) { jack_error("JackMidiBufferReadQueue::ResetMidiBuffer - %d events " "lost during mixdown", lost_events); } this->buffer = buffer; event_count = buffer->event_count; last_frame_time = GetLastFrame(); } } jack2-1.9.22/common/JackMidiBufferReadQueue.h000066400000000000000000000030011436671425200206520ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiBufferReadQueue__ #define __JackMidiBufferReadQueue__ #include "JackMidiReadQueue.h" namespace Jack { /** * Wrapper class to present a JackMidiBuffer in a read queue interface. */ class SERVER_EXPORT JackMidiBufferReadQueue: public JackMidiReadQueue { private: JackMidiBuffer *buffer; jack_nframes_t event_count; jack_nframes_t index; jack_nframes_t last_frame_time; jack_midi_event_t midi_event; public: JackMidiBufferReadQueue(); jack_midi_event_t * DequeueEvent(); /** * This method must be called each period to reset the MIDI buffer for * processing. */ void ResetMidiBuffer(JackMidiBuffer *buffer); }; } #endif jack2-1.9.22/common/JackMidiBufferWriteQueue.cpp000066400000000000000000000041131436671425200214310ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackMidiBufferWriteQueue.h" #include "JackMidiUtil.h" #include "JackError.h" using Jack::JackMidiBufferWriteQueue; JackMidiBufferWriteQueue::JackMidiBufferWriteQueue() { // Empty } Jack::JackMidiWriteQueue::EnqueueResult JackMidiBufferWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *data) { if (time >= next_frame_time) { return EVENT_EARLY; } if (time < last_frame_time) { time = last_frame_time; } jack_midi_data_t *dst = buffer->ReserveEvent(time - last_frame_time, size); if (! dst) { return size > max_bytes ? BUFFER_TOO_SMALL : BUFFER_FULL; } memcpy(dst, data, size); return OK; } void JackMidiBufferWriteQueue::ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames) { if (! buffer) { jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " "to NULL"); } else if (! buffer->IsValid()) { jack_error("JackMidiBufferWriteQueue::ResetMidiBuffer - buffer reset " "to invalid buffer"); } else { this->buffer = buffer; buffer->Reset(frames); last_frame_time = GetLastFrame(); max_bytes = buffer->MaxEventSize(); next_frame_time = last_frame_time + frames; } } jack2-1.9.22/common/JackMidiBufferWriteQueue.h000066400000000000000000000031631436671425200211020ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiBufferWriteQueue__ #define __JackMidiBufferWriteQueue__ #include "JackMidiWriteQueue.h" namespace Jack { /** * Wrapper class to present a JackMidiBuffer in a write queue interface. */ class SERVER_EXPORT JackMidiBufferWriteQueue: public JackMidiWriteQueue { private: JackMidiBuffer *buffer; jack_nframes_t last_frame_time; size_t max_bytes; jack_nframes_t next_frame_time; public: using JackMidiWriteQueue::EnqueueEvent; JackMidiBufferWriteQueue(); EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer); /** * This method must be called each period to reset the MIDI buffer for * processing. */ void ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames); }; } #endif jack2-1.9.22/common/JackMidiDriver.cpp000066400000000000000000000145111436671425200174360ustar00rootroot00000000000000/* Copyright (C) 2009 Grame. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the (at your option) any later version. GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackMidiDriver.h" #include "JackTime.h" #include "JackError.h" #include "JackEngineControl.h" #include "JackPort.h" #include "JackGraphManager.h" #include "JackException.h" #include using namespace std; namespace Jack { JackMidiDriver::JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackDriver(name, alias, engine, table) {} JackMidiDriver::~JackMidiDriver() {} int JackMidiDriver::Open(bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { fCaptureChannels = inchannels; fPlaybackChannels = outchannels; return JackDriver::Open(0, 0, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } int JackMidiDriver::Attach() { JackPort* port; jack_port_id_t port_index; char name[REAL_JACK_PORT_NAME_SIZE+1]; char alias[REAL_JACK_PORT_NAME_SIZE+1]; int i; jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); for (i = 0; i < fCaptureChannels; i++) { snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); fCapturePortList[i] = port_index; jack_log("JackMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } for (i = 0; i < fPlaybackChannels; i++) { snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); fPlaybackPortList[i] = port_index; jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } UpdateLatencies(); return 0; } int JackMidiDriver::Detach() { int i; jack_log("JackMidiDriver::Detach"); for (i = 0; i < fCaptureChannels; i++) { fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[i]); } for (i = 0; i < fPlaybackChannels; i++) { fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[i]); } return 0; } void JackMidiDriver::UpdateLatencies() { jack_latency_range_t range; for (int i = 0; i < fCaptureChannels; i++) { range.max = range.min = fEngineControl->fBufferSize; fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &range); } for (int i = 0; i < fPlaybackChannels; i++) { if (! fEngineControl->fSyncMode) { range.max = range.min = fEngineControl->fBufferSize * 2; } fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &range); } } int JackMidiDriver::SetBufferSize(jack_nframes_t buffer_size) { UpdateLatencies(); return 0; } int JackMidiDriver::ProcessReadSync() { int res = 0; // Read input buffers for the current cycle if (Read() < 0) { jack_error("JackMidiDriver::ProcessReadSync: read error"); res = -1; } if (ResumeRefNum() < 0) { jack_error("JackMidiDriver::ProcessReadSync: ResumeRefNum error"); res = -1; } return res; } int JackMidiDriver::ProcessWriteSync() { int res = 0; if (SuspendRefNum() < 0) { jack_error("JackMidiDriver::ProcessWriteSync: SuspendRefNum error"); res = -1; } // Write output buffers from the current cycle if (Write() < 0) { jack_error("JackMidiDriver::ProcessWriteSync: write error"); res = -1; } return res; } int JackMidiDriver::ProcessReadAsync() { int res = 0; // Read input buffers for the current cycle if (Read() < 0) { jack_error("JackMidiDriver::ProcessReadAsync: read error"); res = -1; } // Write output buffers from the previous cycle if (Write() < 0) { jack_error("JackMidiDriver::ProcessReadAsync: write error"); res = -1; } if (ResumeRefNum() < 0) { jack_error("JackMidiDriver::ProcessReadAsync: ResumeRefNum error"); res = -1; } return res; } int JackMidiDriver::ProcessWriteAsync() { return 0; } JackMidiBuffer* JackMidiDriver::GetInputBuffer(int port_index) { assert(fCapturePortList[port_index]); return (JackMidiBuffer*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize); } JackMidiBuffer* JackMidiDriver::GetOutputBuffer(int port_index) { assert(fPlaybackPortList[port_index]); return (JackMidiBuffer*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize); } } // end of namespace jack2-1.9.22/common/JackMidiDriver.h000066400000000000000000000043051436671425200171030ustar00rootroot00000000000000/* Copyright (C) 2009 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackMidiDriver__ #define __JackMidiDriver__ #include "JackDriver.h" #include "JackMidiPort.h" #include "JackLockedEngine.h" #include "ringbuffer.h" namespace Jack { /*! \brief The base class for MIDI drivers: drivers with MIDI ports. A concrete derived class will have to be defined with a real MIDI driver API, either callback based one (like CoreMIDI..) ones or blocking ones (like ALSA MIDI). */ class SERVER_EXPORT JackMidiDriver : public JackDriver { protected: JackMidiBuffer* GetInputBuffer(int port_index); JackMidiBuffer* GetOutputBuffer(int port_index); virtual int ProcessReadSync(); virtual int ProcessWriteSync(); virtual int ProcessReadAsync(); virtual int ProcessWriteAsync(); virtual void UpdateLatencies(); public: JackMidiDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); virtual ~JackMidiDriver(); virtual int Open(bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int Attach(); virtual int Detach(); }; } // end of namespace #endif jack2-1.9.22/common/JackMidiPort.cpp000066400000000000000000000123571436671425200171350ustar00rootroot00000000000000/* Copyright (C) 2007 Dmitry Baikov Copyright (C) 2018 Filipe Coelho Original JACK MIDI implementation Copyright (C) 2004 Ian Esten This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackError.h" #include "JackPortType.h" #include "JackMidiPort.h" #include #include namespace Jack { SERVER_EXPORT void JackMidiBuffer::Reset(jack_nframes_t nframes) { /* This line ate 1 hour of my life... dsbaikov */ this->nframes = nframes; write_pos = 0; event_count = 0; lost_events = 0; } SERVER_EXPORT jack_shmsize_t JackMidiBuffer::MaxEventSize() const { assert (((jack_shmsize_t) - 1) < 0); // jack_shmsize_t should be signed jack_shmsize_t left = buffer_size - (sizeof(JackMidiBuffer) + sizeof(JackMidiEvent) * (event_count + 1) + write_pos); if (left < 0) { return 0; } if (left <= JackMidiEvent::INLINE_SIZE_MAX) { return JackMidiEvent::INLINE_SIZE_MAX; } return left; } SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time, jack_shmsize_t size) { jack_shmsize_t space = MaxEventSize(); if (space == 0 || size > space) { jack_error("JackMidiBuffer::ReserveEvent - the buffer does not have " "enough room to enqueue a %lu byte event", size); lost_events++; return 0; } JackMidiEvent* event = &events[event_count++]; event->time = time; event->size = size; if (size <= JackMidiEvent::INLINE_SIZE_MAX) { return event->data; } write_pos += size; event->offset = buffer_size - write_pos; return (jack_midi_data_t*)this + event->offset; } void MidiBufferInit(void* buffer, size_t buffer_size, jack_nframes_t nframes) { JackMidiBuffer* midi = (JackMidiBuffer*)buffer; midi->magic = JackMidiBuffer::MAGIC; /* Since port buffer has actually always BUFFER_SIZE_MAX frames, we can safely use all the size */ midi->buffer_size = BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t); midi->Reset(nframes); } /* * The mixdown function below, is a simplest (read slowest) implementation possible. * But, since it is unlikely that it will mix many buffers with many events, * it should perform quite good. * More efficient (and possibly, fastest possible) implementation (it exists), * using calendar queue algorithm is about 3 times bigger, and uses alloca(). * So, let's listen to D.Knuth about premature optimisation, a leave the current * implementation as is, until it is proved to be a bottleneck. * Dmitry Baikov. */ static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count, jack_nframes_t nframes) { JackMidiBuffer* mix = static_cast(mixbuffer); if (!mix->IsValid()) { jack_error("Jack::MidiBufferMixdown - invalid mix buffer"); return; } mix->Reset(nframes); uint32_t mix_index[src_count]; int event_count = 0; for (int i = 0; i < src_count; ++i) { JackMidiBuffer* buf = static_cast(src_buffers[i]); if (!buf->IsValid()) { jack_error("Jack::MidiBufferMixdown - invalid source buffer"); return; } mix_index[i] = 0; event_count += buf->event_count; mix->lost_events += buf->lost_events; } int events_done; for (events_done = 0; events_done < event_count; ++events_done) { JackMidiBuffer* next_buf = 0; JackMidiEvent* next_event = 0; uint32_t next_buf_index = 0; // find the earliest event for (int i = 0; i < src_count; ++i) { JackMidiBuffer* buf = static_cast(src_buffers[i]); if (mix_index[i] >= buf->event_count) continue; JackMidiEvent* e = &buf->events[mix_index[i]]; if (!next_event || e->time < next_event->time) { next_event = e; next_buf = buf; next_buf_index = i; } } if (next_event == 0) { jack_error("Jack::MidiBufferMixdown - got invalid next event"); break; } // write the event jack_midi_data_t* dest = mix->ReserveEvent(next_event->time, next_event->size); if (!dest) break; memcpy(dest, next_event->GetData(next_buf), next_event->size); mix_index[next_buf_index]++; } mix->lost_events += event_count - events_done; } static size_t MidiBufferSize() { return BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t); } const JackPortType gMidiPortType = { JACK_DEFAULT_MIDI_TYPE, MidiBufferSize, MidiBufferInit, MidiBufferMixdown }; } // namespace Jack jack2-1.9.22/common/JackMidiPort.h000066400000000000000000000056601436671425200166010ustar00rootroot00000000000000/* Copyright (C) 2007 Dmitry Baikov Original JACK MIDI API implementation Copyright (C) 2004 Ian Esten This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiPort__ #define __JackMidiPort__ #include "types.h" #include "JackConstants.h" #include "JackPlatformPlug.h" #include /** Type for raw event data contained in @ref jack_midi_event_t. */ typedef unsigned char jack_midi_data_t; /** A Jack MIDI event. */ struct jack_midi_event_t { jack_nframes_t time; /**< Sample index at which event is valid */ size_t size; /**< Number of bytes of data in \a buffer */ jack_midi_data_t *buffer; /**< Raw MIDI data */ }; /** A Jack MIDI port type. */ #define JACK_DEFAULT_MIDI_TYPE "8 bit raw midi" namespace Jack { struct SERVER_EXPORT JackMidiEvent { // Most MIDI events are < 4 bytes in size, so we can save a lot, storing them inplace. enum { INLINE_SIZE_MAX = sizeof(jack_shmsize_t) }; uint32_t time; jack_shmsize_t size; union { jack_shmsize_t offset; jack_midi_data_t data[INLINE_SIZE_MAX]; }; jack_midi_data_t* GetData(void* buffer) { if (size <= INLINE_SIZE_MAX) { return data; } else { return (jack_midi_data_t*)buffer + offset; } } }; /* * To store events with arbitrarily sized payload, but still have O(1) indexed access * we use a trick here: * Events are stored in an linear array from the beginning of the buffer, * but their data (if not inlined) is stored from the end of the same buffer. */ struct SERVER_EXPORT JackMidiBuffer { enum { MAGIC = 0x900df00d }; uint32_t magic; jack_shmsize_t buffer_size; jack_nframes_t nframes; jack_shmsize_t write_pos; //!< data write position from the end of the buffer. uint32_t event_count; uint32_t lost_events; JackMidiEvent events[1]; // Using 0 size does not compile with older GCC versions, so use 1 here. int IsValid() const { return magic == MAGIC; } void Reset(jack_nframes_t nframes); jack_shmsize_t MaxEventSize() const; // checks only size constraints. jack_midi_data_t* ReserveEvent(jack_nframes_t time, jack_shmsize_t size); }; void MidiBufferInit(void* buffer, size_t buffer_size, jack_nframes_t nframes); } // namespace Jack #endif jack2-1.9.22/common/JackMidiRawInputWriteQueue.cpp000066400000000000000000000207331436671425200217770ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "JackMidiRawInputWriteQueue.h" #include "JackError.h" using Jack::JackMidiRawInputWriteQueue; JackMidiRawInputWriteQueue:: JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, size_t max_packet_data, size_t max_packets) { packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets); std::unique_ptr packet_queue_ptr(packet_queue); input_buffer = new jack_midi_data_t[max_packet_data]; Clear(); expected_bytes = 0; event_pending = false; input_buffer_size = max_packet_data; packet = 0; status_byte = 0; this->write_queue = write_queue; packet_queue_ptr.release(); } JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue() { delete[] input_buffer; delete packet_queue; } void JackMidiRawInputWriteQueue::Clear() { total_bytes = 0; unbuffered_bytes = 0; } Jack::JackMidiWriteQueue::EnqueueResult JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer) { return packet_queue->EnqueueEvent(time, size, buffer); } size_t JackMidiRawInputWriteQueue::GetAvailableSpace() { return packet_queue->GetAvailableSpace(); } void JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes) { jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI " "byte(s) of a %d byte message could not be buffered. The " "message has been dropped.", unbuffered_bytes, total_bytes); } void JackMidiRawInputWriteQueue::HandleEventLoss(jack_midi_event_t *event) { jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI " "event scheduled for frame '%d' could not be processed because " "the write queue cannot accommodate an event of that size. The " "event has been discarded.", event->size, event->time); } void JackMidiRawInputWriteQueue::HandleIncompleteMessage(size_t total_bytes) { jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - " "Discarding %d MIDI byte(s) of an incomplete message. The " "MIDI cable may have been unplugged.", total_bytes); } void JackMidiRawInputWriteQueue::HandleInvalidStatusByte(jack_midi_data_t byte) { jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - " "Dropping invalid MIDI status byte '%x'.", (unsigned int) byte); } void JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd(size_t total_bytes) { jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - " "Received a sysex end byte without first receiving a sysex " "start byte. Discarding %d MIDI byte(s). The cable may have " "been unplugged.", total_bytes); } bool JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time) { bool result = ! unbuffered_bytes; if (! result) { HandleBufferFailure(unbuffered_bytes, total_bytes); } else { PrepareEvent(time, total_bytes, input_buffer); } Clear(); if (status_byte >= 0xf0) { expected_bytes = 0; status_byte = 0; } return result; } bool JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time, jack_midi_data_t byte) { event_byte = byte; PrepareEvent(time, 1, &event_byte); return true; } void JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer) { event.buffer = buffer; event.size = size; event.time = time; event_pending = true; } jack_nframes_t JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame) { if (event_pending) { if (! WriteEvent(boundary_frame)) { return event.time; } } if (! packet) { packet = packet_queue->DequeueEvent(); } for (; packet; packet = packet_queue->DequeueEvent()) { for (; packet->size; (packet->buffer)++, (packet->size)--) { if (ProcessByte(packet->time, *(packet->buffer))) { if (! WriteEvent(boundary_frame)) { (packet->buffer)++; (packet->size)--; return event.time; } } } } return 0; } bool JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time, jack_midi_data_t byte) { if (byte >= 0xf8) { // Realtime if (byte == 0xfd) { HandleInvalidStatusByte(byte); return false; } return PrepareByteEvent(time, byte); } if (byte == 0xf7) { // Sysex end if (status_byte == 0xf0) { RecordByte(byte); return PrepareBufferedEvent(time); } HandleUnexpectedSysexEnd(total_bytes); Clear(); expected_bytes = 0; status_byte = 0; return false; } if (byte >= 0x80) { // Non-realtime status byte if (total_bytes) { HandleIncompleteMessage(total_bytes); Clear(); } status_byte = byte; switch (byte & 0xf0) { case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel expected_bytes = 3; break; case 0xc0: case 0xd0: // Program Change, Channel Pressure expected_bytes = 2; break; case 0xf0: switch (byte) { case 0xf0: // Sysex expected_bytes = 0; break; case 0xf1: case 0xf3: // MTC Quarter Frame, Song Select expected_bytes = 2; break; case 0xf2: // Song Position expected_bytes = 3; break; case 0xf4: case 0xf5: // Undefined HandleInvalidStatusByte(byte); expected_bytes = 0; status_byte = 0; return false; case 0xf6: // Tune Request bool result = PrepareByteEvent(time, byte); if (result) { expected_bytes = 0; status_byte = 0; } return result; } } RecordByte(byte); return false; } // Data byte if (! status_byte) { // Data bytes without a status will be discarded. total_bytes++; unbuffered_bytes++; return false; } if (! total_bytes) { // Apply running status. RecordByte(status_byte); } RecordByte(byte); return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) : false; } void JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte) { if (total_bytes < input_buffer_size) { input_buffer[total_bytes] = byte; } else { unbuffered_bytes++; } total_bytes++; } bool JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame) { if ((! boundary_frame) || (event.time < boundary_frame)) { switch (write_queue->EnqueueEvent(&event)) { case BUFFER_TOO_SMALL: HandleEventLoss(&event); // Fallthrough on purpose case OK: event_pending = false; return true; default: // This is here to stop compilers from warning us about not // handling enumeration values. ; } } return false; } jack2-1.9.22/common/JackMidiRawInputWriteQueue.h000066400000000000000000000132571436671425200214470ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiRawInputWriteQueue__ #define __JackMidiRawInputWriteQueue__ #include "JackMidiAsyncQueue.h" #include "JackMidiWriteQueue.h" namespace Jack { /** * This queue enqueues raw, unparsed MIDI packets, and outputs complete * MIDI messages to a write queue. * * Use this queue if the MIDI API you're interfacing with gives you raw * MIDI bytes that must be parsed. */ class SERVER_EXPORT JackMidiRawInputWriteQueue: public JackMidiWriteQueue { private: jack_midi_event_t event; jack_midi_data_t event_byte; bool event_pending; size_t expected_bytes; jack_midi_data_t *input_buffer; size_t input_buffer_size; jack_midi_event_t *packet; JackMidiAsyncQueue *packet_queue; jack_midi_data_t status_byte; size_t total_bytes; size_t unbuffered_bytes; JackMidiWriteQueue *write_queue; void Clear(); bool PrepareBufferedEvent(jack_nframes_t time); bool PrepareByteEvent(jack_nframes_t time, jack_midi_data_t byte); void PrepareEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer); bool ProcessByte(jack_nframes_t time, jack_midi_data_t byte); void RecordByte(jack_midi_data_t byte); bool WriteEvent(jack_nframes_t boundary_frame); protected: /** * Override this method to specify what happens when there isn't enough * room in the ringbuffer to contain a parsed event. The default * method outputs an error message. */ virtual void HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes); /** * Override this method to specify what happens when a parsed event * can't be written to the write queue because the event's size exceeds * the total possible space in the write queue. The default method * outputs an error message. */ virtual void HandleEventLoss(jack_midi_event_t *event); /** * Override this method to specify what happens when an incomplete MIDI * message is parsed. The default method outputs an error message. */ virtual void HandleIncompleteMessage(size_t total_bytes); /** * Override this method to specify what happens when an invalid MIDI * status byte is parsed. The default method outputs an error message. */ virtual void HandleInvalidStatusByte(jack_midi_data_t byte); /** * Override this method to specify what happens when a sysex end byte * is parsed without first parsing a sysex begin byte. The default * method outputs an error message. */ virtual void HandleUnexpectedSysexEnd(size_t total_bytes); public: using JackMidiWriteQueue::EnqueueEvent; /** * Called to create a new raw input write queue. The `write_queue` * argument is the queue to write parsed messages to. The optional * `max_packets` argument specifies the number of packets that can be * enqueued in the internal queue. The optional `max_packet_data` * argument specifies the total number of MIDI bytes that can be put in * the internal queue, AND the maximum size for an event that can be * written to the write queue. */ JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue, size_t max_packet_data=4096, size_t max_packets=1024); ~JackMidiRawInputWriteQueue(); EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer); /** * Returns the maximum size event that can be enqueued right *now*. */ size_t GetAvailableSpace(); /** * The `Process()` method should be called each time the * `EnqueueEvent()` method returns `OK`. The `Process()` method will * return the next frame at which an event should be sent. The return * value from `Process()` depends upon the result of writing bytes to * the write queue: * * -If the return value is '0', then all *complete* events have been * sent successfully to the write queue. Don't call `Process()` again * until another event has been enqueued. * * -If the return value is a non-zero value, then it specifies the * frame that a pending event is scheduled to sent at. If the frame is * in the future, then `Process()` should be called again at that time; * otherwise, `Process()` should be called as soon as the write queue * will accept events again. */ jack_nframes_t Process(jack_nframes_t boundary_frame=0); }; } #endif jack2-1.9.22/common/JackMidiRawOutputWriteQueue.cpp000066400000000000000000000117271436671425200222030ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "JackError.h" #include "JackMidiRawOutputWriteQueue.h" #include "JackMidiUtil.h" using Jack::JackMidiRawOutputWriteQueue; #define STILL_TIME(c, b) ((! (b)) || ((c) < (b))) JackMidiRawOutputWriteQueue:: JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size, size_t max_non_rt_messages, size_t max_rt_messages) { non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages); std::unique_ptr non_rt_ptr(non_rt_queue); rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages); std::unique_ptr rt_ptr(rt_queue); non_rt_event = 0; rt_event = 0; running_status = 0; this->send_queue = send_queue; rt_ptr.release(); non_rt_ptr.release(); } JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue() { delete non_rt_queue; delete rt_queue; } void JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent() { non_rt_event = non_rt_queue->DequeueEvent(); if (non_rt_event) { non_rt_event_time = non_rt_event->time; running_status = ApplyRunningStatus(non_rt_event, running_status); } } void JackMidiRawOutputWriteQueue::DequeueRealtimeEvent() { rt_event = rt_queue->DequeueEvent(); if (rt_event) { rt_event_time = rt_event->time; } } Jack::JackMidiWriteQueue::EnqueueResult JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer) { JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue : non_rt_queue; return queue->EnqueueEvent(time, size, buffer); } void JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte) { jack_error("JackMidiRawOutputWriteQueue::HandleWriteQueueBug - **BUG** " "The write queue told us that it couldn't enqueue a 1-byte " "MIDI event scheduled for frame '%d'. This is probably a bug " "in the write queue implementation.", time); } jack_nframes_t JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame) { if (! non_rt_event) { DequeueNonRealtimeEvent(); } if (! rt_event) { DequeueRealtimeEvent(); } while (rt_event) { jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); if ((rt_event_time > current_frame) && non_rt_event && (non_rt_event_time < rt_event_time)) { if (! SendNonRTBytes(rt_event_time < boundary_frame ? rt_event_time : boundary_frame)) { return non_rt_event_time; } current_frame = send_queue->GetNextScheduleFrame(); } if (! STILL_TIME(current_frame, boundary_frame)) { return (! non_rt_event) ? rt_event_time : non_rt_event_time < rt_event_time ? non_rt_event_time : rt_event_time; } if (! SendByte(rt_event_time, *(rt_event->buffer))) { return rt_event_time; } DequeueRealtimeEvent(); } SendNonRTBytes(boundary_frame); return non_rt_event ? non_rt_event_time : 0; } bool JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time, jack_midi_data_t byte) { switch (send_queue->EnqueueEvent(time, 1, &byte)) { case BUFFER_TOO_SMALL: HandleWriteQueueBug(time, byte); case OK: return true; default: // This is here to stop compilers from warning us about not handling // enumeration values. ; } return false; } bool JackMidiRawOutputWriteQueue::SendNonRTBytes(jack_nframes_t boundary_frame) { while (non_rt_event) { for (; non_rt_event->size; (non_rt_event->size)--, (non_rt_event->buffer)++) { jack_nframes_t current_frame = send_queue->GetNextScheduleFrame(); if (! STILL_TIME(current_frame, boundary_frame)) { return true; } if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) { return false; } } DequeueNonRealtimeEvent(); } return true; } jack2-1.9.22/common/JackMidiRawOutputWriteQueue.h000066400000000000000000000120111436671425200216330ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiRawOutputWriteQueue__ #define __JackMidiRawOutputWriteQueue__ #include "JackMidiAsyncQueue.h" #include "JackMidiSendQueue.h" namespace Jack { /** * This queue enqueues valid MIDI events and modifies them for raw output * to a write queue. It has a couple of advantages over straight MIDI * event copying: * * -Running status: Status bytes can be omitted when the status byte of the * current MIDI message is the same as the status byte of the last sent * MIDI message. * * -Realtime messages: Realtime messages are given priority over * non-realtime messages. Realtime bytes are interspersed with * non-realtime bytes so that realtime messages can be sent as close as * possible to the time they're scheduled for sending. * * Use this queue if the MIDI API you're interfacing with allows you to * send raw MIDI bytes. */ class SERVER_EXPORT JackMidiRawOutputWriteQueue: public JackMidiWriteQueue { private: jack_midi_event_t *non_rt_event; jack_nframes_t non_rt_event_time; JackMidiAsyncQueue *non_rt_queue; jack_midi_event_t *rt_event; jack_nframes_t rt_event_time; JackMidiAsyncQueue *rt_queue; jack_midi_data_t running_status; JackMidiSendQueue *send_queue; void DequeueNonRealtimeEvent(); void DequeueRealtimeEvent(); bool SendByte(jack_nframes_t time, jack_midi_data_t byte); bool SendNonRTBytes(jack_nframes_t boundary_frame); protected: /** * Override this method to specify what happens when the write queue * says that a 1-byte event is too large for its buffer. Basically, * this should never happen. */ virtual void HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte); public: using JackMidiWriteQueue::EnqueueEvent; /** * Called to create a new raw write queue. The `send_queue` argument * is the queue to write raw bytes to. The optional `max_rt_messages` * argument specifies the number of messages that can be enqueued in * the internal realtime queue. The optional `max_non_rt_messages` * argument specifies the number of messages that can be enqueued in * the internal non-realtime queue. The optional `non_rt_size` * argument specifies the total number of MIDI bytes that can be put in * the non-realtime queue. */ JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size=4096, size_t max_non_rt_messages=1024, size_t max_rt_messages=128); ~JackMidiRawOutputWriteQueue(); EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer); /** * The `Process()` method should be called each time the * `EnqueueEvent()` method returns 'OK'. The `Process()` method will * return the next frame at which an event should be sent. The return * value from `Process()` depends upon the result of writing bytes to * the write queue: * * -If the return value is '0', then all events that have been enqueued * in this queue have been sent successfully to the write queue. Don't * call `Process()` again until another event has been enqueued. * * -If the return value is an earlier frame or the current frame, it * means that the write queue returned 'BUFFER_FULL', 'ERROR', or * 'EVENT_EARLY' when this queue attempted to send the next byte, and * that the byte should have already been sent, or is scheduled to be * sent *now*. `Process()` should be called again when the write queue * can enqueue events again successfully. How to determine when this * will happen is left up to the caller. * * -If the return value is in the future, then `Process()` should be * called again at that time, or after another event is enqueued. */ jack_nframes_t Process(jack_nframes_t boundary_frame=0); }; } #endif jack2-1.9.22/common/JackMidiReadQueue.cpp000066400000000000000000000015401436671425200200610ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackMidiReadQueue.h" using Jack::JackMidiReadQueue; JackMidiReadQueue::~JackMidiReadQueue() { // Empty } jack2-1.9.22/common/JackMidiReadQueue.h000066400000000000000000000027721436671425200175360ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiReadQueue__ #define __JackMidiReadQueue__ #include "JackMidiPort.h" namespace Jack { /** * Interface for objects that MIDI events can be read from. */ class SERVER_EXPORT JackMidiReadQueue { public: virtual ~JackMidiReadQueue(); /** * Dequeues an event from the queue. Returns the event, or 0 if no * events are available for reading. * * An event dequeued from the read queue is guaranteed to be valid up * until another event is dequeued, at which all bets are off. Make * sure that you handle each event you dequeue before dequeueing the * next event. */ virtual jack_midi_event_t * DequeueEvent() = 0; }; } #endif jack2-1.9.22/common/JackMidiReceiveQueue.cpp000066400000000000000000000015541436671425200205750ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackMidiReceiveQueue.h" using Jack::JackMidiReceiveQueue; JackMidiReceiveQueue::~JackMidiReceiveQueue() { // Empty } jack2-1.9.22/common/JackMidiReceiveQueue.h000066400000000000000000000020661436671425200202410ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiReceiveQueue__ #define __JackMidiReceiveQueue__ #include "JackMidiReadQueue.h" namespace Jack { /** * Implemented by MIDI input connections. */ class SERVER_EXPORT JackMidiReceiveQueue: public JackMidiReadQueue { public: virtual ~JackMidiReceiveQueue(); }; } #endif jack2-1.9.22/common/JackMidiSendQueue.cpp000066400000000000000000000017261436671425200201050ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackMidiSendQueue.h" #include "JackMidiUtil.h" using Jack::JackMidiSendQueue; JackMidiSendQueue::~JackMidiSendQueue() { // Empty } jack_nframes_t JackMidiSendQueue::GetNextScheduleFrame() { return GetCurrentFrame(); } jack2-1.9.22/common/JackMidiSendQueue.h000066400000000000000000000024671436671425200175550ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiSendQueue__ #define __JackMidiSendQueue__ #include "JackMidiWriteQueue.h" namespace Jack { /** * Implemented by MIDI output connections. */ class SERVER_EXPORT JackMidiSendQueue: public JackMidiWriteQueue { public: using JackMidiWriteQueue::EnqueueEvent; virtual ~JackMidiSendQueue(); /** * Returns the next frame that a MIDI message can be sent at. The * default method returns the current frame. */ virtual jack_nframes_t GetNextScheduleFrame(); }; } #endif jack2-1.9.22/common/JackMidiUtil.cpp000066400000000000000000000057761436671425200171350ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackEngineControl.h" #include "JackFrameTimer.h" #include "JackGlobals.h" #include "JackMidiUtil.h" #include "JackTime.h" jack_midi_data_t Jack::ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, jack_midi_data_t running_status) { // Stolen and modified from alsa/midi_pack.h jack_midi_data_t status = **buffer; if ((status >= 0x80) && (status < 0xf0)) { if (status == running_status) { (*buffer)++; (*size)--; } else { running_status = status; } } else if (status < 0xf8) { running_status = 0; } return running_status; } jack_midi_data_t Jack::ApplyRunningStatus(jack_midi_event_t *event, jack_midi_data_t running_status) { return ApplyRunningStatus(&(event->size), &(event->buffer), running_status); } jack_nframes_t Jack::GetCurrentFrame() { jack_time_t time = GetMicroSeconds(); JackEngineControl *control = GetEngineControl(); JackTimer timer; control->ReadFrameTime(&timer); return timer.Time2Frames(time, control->fBufferSize); } jack_nframes_t Jack::GetFramesFromTime(jack_time_t time) { JackEngineControl* control = GetEngineControl(); JackTimer timer; control->ReadFrameTime(&timer); return timer.Time2Frames(time, control->fBufferSize); } jack_nframes_t Jack::GetLastFrame() { return GetEngineControl()->fFrameTimer.ReadCurrentState()->CurFrame(); } int Jack::GetMessageLength(jack_midi_data_t status_byte) { switch (status_byte & 0xf0) { case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: return 3; case 0xc0: case 0xd0: return 2; case 0xf0: switch (status_byte) { case 0xf0: return 0; case 0xf1: case 0xf3: return 2; case 0xf2: return 3; case 0xf4: case 0xf5: case 0xf7: case 0xfd: break; default: return 1; } } return -1; } jack_time_t Jack::GetTimeFromFrames(jack_nframes_t frames) { JackEngineControl* control = GetEngineControl(); JackTimer timer; control->ReadFrameTime(&timer); return timer.Frames2Time(frames, control->fBufferSize); } jack2-1.9.22/common/JackMidiUtil.h000066400000000000000000000064711436671425200165730ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiUtil__ #define __JackMidiUtil__ #include "JackMidiPort.h" namespace Jack { /** * Use this function to optimize MIDI output by omitting unnecessary status * bytes. This can't be used with all MIDI APIs, so before using this * function, make sure that your MIDI API doesn't require complete MIDI * messages to be sent. * * To start using this function, call this method with pointers to the * `size` and `buffer` arguments of the MIDI message you want to send, and * set the `running_status` argument to '0'. For each subsequent MIDI * message, call this method with pointers to its `size` and `buffer` * arguments, and set the `running_status` argument to the return value of * the previous call to this function. * * Note: This function will alter the `size` and `buffer` of your MIDI * message for each message that can be optimized. */ SERVER_EXPORT jack_midi_data_t ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer, jack_midi_data_t running_status=0); /** * A wrapper function for the above `ApplyRunningStatus` function. */ SERVER_EXPORT jack_midi_data_t ApplyRunningStatus(jack_midi_event_t *event, jack_midi_data_t running_status); /** * Gets the estimated current time in frames. This function has the same * functionality as the JACK client API function `jack_frame_time`. */ SERVER_EXPORT jack_nframes_t GetCurrentFrame(); /** * Gets the estimated frame that will be occurring at the given time. This * function has the same functionality as the JACK client API function * `jack_time_to_frames`. */ SERVER_EXPORT jack_nframes_t GetFramesFromTime(jack_time_t time); /** * Gets the precise time at the start of the current process cycle. This * function has the same functionality as the JACK client API function * `jack_last_frame_time`. */ SERVER_EXPORT jack_nframes_t GetLastFrame(); /** * Returns the expected message length for the status byte. Returns 0 if * the status byte is a system exclusive status byte, or -1 if the status * byte is invalid. */ SERVER_EXPORT int GetMessageLength(jack_midi_data_t status_byte); /** * Gets the estimated time at which the given frame will occur. This * function has the same functionality as the JACK client API function * `jack_frames_to_time`. */ SERVER_EXPORT jack_time_t GetTimeFromFrames(jack_nframes_t frames); }; #endif jack2-1.9.22/common/JackMidiWriteQueue.cpp000066400000000000000000000015441436671425200203040ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackMidiWriteQueue.h" using Jack::JackMidiWriteQueue; JackMidiWriteQueue::~JackMidiWriteQueue() { // Empty } jack2-1.9.22/common/JackMidiWriteQueue.h000066400000000000000000000054231436671425200177510ustar00rootroot00000000000000/* Copyright (C) 2010 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackMidiWriteQueue__ #define __JackMidiWriteQueue__ #include "JackMidiPort.h" namespace Jack { /** * Interface for classes that act as write queues for MIDI messages. Write * queues are used by processors to transfer data to the next processor. */ class SERVER_EXPORT JackMidiWriteQueue { public: enum EnqueueResult { BUFFER_FULL, BUFFER_TOO_SMALL, EVENT_EARLY, EN_ERROR, OK }; virtual ~JackMidiWriteQueue(); /** * Enqueues a data packet in the write queue of `size` bytes contained * in `buffer` that will be sent the absolute time specified by `time`. * This method should not block unless 1.) this write queue represents * the actual outbound MIDI connection, 2.) the MIDI event is being * sent *now*, meaning that `time` is less than or equal to *now*, and * 3.) the method is *not* being called in the process thread. The * method should return `OK` if the event was enqueued, `BUFFER_FULL` * if the write queue isn't able to accept the event right now, * `BUFFER_TOO_SMALL` if this write queue will never be able to accept * the event because the event is too large, `EVENT_EARLY` if this * queue cannot schedule events ahead of time, and `EN_ERROR` if an error * occurs that cannot be specified by another return code. */ virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer) = 0; /** * A wrapper method for the `EnqueueEvent` method above. The optional * 'frame_offset' argument is an amount of frames to add to the event's * time. */ inline EnqueueResult EnqueueEvent(jack_midi_event_t *event, jack_nframes_t frame_offset=0) { return EnqueueEvent(event->time + frame_offset, event->size, event->buffer); } }; } #endif jack2-1.9.22/common/JackMutex.h000066400000000000000000000034421436671425200161500ustar00rootroot00000000000000/* Copyright (C) 2006 Grame This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France grame@grame.fr */ #ifndef __JackMutex__ #define __JackMutex__ #include #include "JackError.h" #include "JackPlatformPlug.h" namespace Jack { /*! \brief Base class for "lockable" objects. */ class JackLockAble { protected: JackMutex fMutex; JackLockAble(const char* name = NULL) :fMutex(name) {} ~JackLockAble() {} public: bool Lock() { return fMutex.Lock(); } bool Trylock() { return fMutex.Trylock(); } bool Unlock() { return fMutex.Unlock(); } }; class JackLock { private: JackLockAble* fObj; public: JackLock(JackLockAble* obj): fObj(obj) { fObj->Lock(); } JackLock(const JackLockAble* obj): fObj((JackLockAble*)obj) { fObj->Lock(); } ~JackLock() { fObj->Unlock(); } }; } // namespace #endif jack2-1.9.22/common/JackNetAPI.cpp000066400000000000000000001247321436671425200164670ustar00rootroot00000000000000/* Copyright (C) 2009-2013 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "JackNetInterface.h" #include "JackAudioAdapterInterface.h" #ifdef __cplusplus extern "C" { #endif // NetJack common API #define MASTER_NAME_SIZE 256 enum JackNetEncoder { JackFloatEncoder = 0, JackIntEncoder = 1, JackCeltEncoder = 2, JackOpusEncoder = 3 }; typedef struct { int audio_input; int audio_output; int midi_input; int midi_output; int mtu; int time_out; // in millisecond, -1 means in infinite int encoder; // one of JackNetEncoder int kbps; // KB per second for CELT encoder int latency; // network cycles } jack_slave_t; typedef struct { int audio_input; int audio_output; int midi_input; int midi_output; jack_nframes_t buffer_size; jack_nframes_t sample_rate; char master_name[MASTER_NAME_SIZE]; int time_out; int partial_cycle; } jack_master_t; // NetJack slave API typedef struct _jack_net_slave jack_net_slave_t; typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, void* data); typedef int (*JackNetSlaveBufferSizeCallback) (jack_nframes_t nframes, void *arg); typedef int (*JackNetSlaveSampleRateCallback) (jack_nframes_t nframes, void *arg); typedef void (*JackNetSlaveShutdownCallback) (void* arg); typedef int (*JackNetSlaveRestartCallback) (void* arg); typedef void (*JackNetSlaveErrorCallback) (int error_code, void* arg); LIB_EXPORT jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result); LIB_EXPORT int jack_net_slave_close(jack_net_slave_t* net); LIB_EXPORT int jack_net_slave_activate(jack_net_slave_t* net); LIB_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net); LIB_EXPORT int jack_net_slave_is_active(jack_net_slave_t* net); LIB_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg); LIB_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t* net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg); LIB_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t* net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg); LIB_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t* net, JackNetSlaveShutdownCallback shutdown_callback, void *arg); LIB_EXPORT int jack_set_net_slave_restart_callback(jack_net_slave_t* net, JackNetSlaveRestartCallback restart_callback, void *arg); LIB_EXPORT int jack_set_net_slave_error_callback(jack_net_slave_t* net, JackNetSlaveErrorCallback error_callback, void *arg); // NetJack master API typedef struct _jack_net_master jack_net_master_t; LIB_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result); LIB_EXPORT int jack_net_master_close(jack_net_master_t* net); LIB_EXPORT int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer); LIB_EXPORT int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer); LIB_EXPORT int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames); LIB_EXPORT int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames); // NetJack adapter API typedef struct _jack_adapter jack_adapter_t; LIB_EXPORT jack_adapter_t* jack_create_adapter(int input, int output, jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate); LIB_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter); LIB_EXPORT void jack_flush_adapter(jack_adapter_t* adapter); LIB_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); LIB_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); #define LOG_LEVEL_INFO 1 #define LOG_LEVEL_ERROR 2 LIB_EXPORT void jack_error(const char *fmt, ...); LIB_EXPORT void jack_info(const char *fmt, ...); LIB_EXPORT void jack_log(const char *fmt, ...); #ifdef __cplusplus } #endif namespace Jack { struct JackNetExtMaster : public JackNetMasterInterface { jack_master_t fRequest; JackRingBuffer** fRingBuffer; JackNetExtMaster(const char* ip, int port, jack_master_t* request) { fRunning = true; assert(strlen(ip) < 32); strcpy(fMulticastIP, ip); fSocket.SetPort(port); fRequest.buffer_size = request->buffer_size; fRequest.sample_rate = request->sample_rate; fRequest.audio_input = request->audio_input; fRequest.audio_output = request->audio_output; fRequest.time_out = request->time_out; fRequest.partial_cycle = request->partial_cycle; fRingBuffer = NULL; } virtual ~JackNetExtMaster() { if (fRingBuffer) { for (int i = 0; i < fParams.fReturnAudioChannels; i++) { delete fRingBuffer[i]; } delete [] fRingBuffer; } } int Open(jack_slave_t* result) { // Check buffer_size if (fRequest.buffer_size == 0) { jack_error("Incorrect buffer_size..."); return -1; } // Check sample_rate if (fRequest.sample_rate == 0) { jack_error("Incorrect sample_rate..."); return -1; } // Init socket API (win32) if (SocketAPIInit() < 0) { jack_error("Can't init Socket API, exiting..."); return -1; } // Request socket if (fSocket.NewSocket() == SOCKET_ERROR) { jack_error("Can't create the network manager input socket : %s", StrError(NET_ERROR_CODE)); return -1; } // Bind the socket to the local port if (fSocket.Bind() == SOCKET_ERROR) { jack_error("Can't bind the network manager socket : %s", StrError(NET_ERROR_CODE)); fSocket.Close(); return -1; } // Join multicast group if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) { jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE)); } // Local loop if (fSocket.SetLocalLoop() == SOCKET_ERROR) { jack_error("Can't set local loop : %s", StrError(NET_ERROR_CODE)); } // Set a timeout on the multicast receive (the thread can now be cancelled) if (fSocket.SetTimeOut(MANAGER_INIT_TIMEOUT) == SOCKET_ERROR) { jack_error("Can't set timeout : %s", StrError(NET_ERROR_CODE)); } // Main loop, wait for data, deal with it and wait again int attempt = 0; int rx_bytes = 0; int try_count = (fRequest.time_out > 0) ? int((1000000.f * float(fRequest.time_out)) / float(MANAGER_INIT_TIMEOUT)) : INT_MAX; do { session_params_t net_params; rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0); SessionParamsNToH(&net_params, &fParams); if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { jack_error("Error in receive : %s", StrError(NET_ERROR_CODE)); if (++attempt == 10) { jack_error("Can't receive on the socket, exiting net manager" ); goto error; } } if (rx_bytes == sizeof(session_params_t)) { switch (GetPacketType(&fParams)) { case SLAVE_AVAILABLE: if (InitMaster(result) == 0) { SessionParamsDisplay(&fParams); fRunning = false; } else { jack_error("Can't init new net master..."); goto error; } jack_info("Waiting for a slave..."); break; case KILL_MASTER: break; default: break; } } } while (fRunning && (--try_count > 0)); if (try_count == 0) { jack_error("Time out error in connect"); return -1; } // Set result parameters result->audio_input = fParams.fSendAudioChannels; result->audio_output = fParams.fReturnAudioChannels; result->midi_input = fParams.fSendMidiChannels; result->midi_output = fParams.fReturnMidiChannels; result->mtu = fParams.fMtu; result->latency = fParams.fNetworkLatency; // Use ringbuffer in case of partial cycle and latency > 0 if (fRequest.partial_cycle && result->latency > 0) { fRingBuffer = new JackRingBuffer*[fParams.fReturnAudioChannels]; for (int i = 0; i < fParams.fReturnAudioChannels; i++) { fRingBuffer[i] = new JackRingBuffer(fRequest.buffer_size * result->latency * 2); } } return 0; error: fSocket.Close(); return -1; } int InitMaster(jack_slave_t* result) { // Check MASTER <==> SLAVE network protocol coherency if (fParams.fProtocolVersion != NETWORK_PROTOCOL) { jack_error("Error : slave '%s' is running with a different protocol %d != %d", fParams.fName, fParams.fProtocolVersion, NETWORK_PROTOCOL); return -1; } // Settings fSocket.GetName(fParams.fMasterNetName); fParams.fID = 1; fParams.fPeriodSize = fRequest.buffer_size; fParams.fSampleRate = fRequest.sample_rate; if (fRequest.audio_input == -1) { if (fParams.fSendAudioChannels == -1) { jack_error("Error : master and slave use -1 for wanted inputs..."); return -1; } else { result->audio_input = fParams.fSendAudioChannels; jack_info("Takes slave %d inputs", fParams.fSendAudioChannels); } } else if (fParams.fSendAudioChannels == -1) { fParams.fSendAudioChannels = fRequest.audio_input; jack_info("Takes master %d inputs", fRequest.audio_input); } else if (fParams.fSendAudioChannels != fRequest.audio_input) { jack_error("Error : master wants %d inputs and slave wants %d inputs...", fRequest.audio_input, fParams.fSendAudioChannels); return -1; } if (fRequest.audio_output == -1) { if (fParams.fReturnAudioChannels == -1) { jack_error("Error : master and slave use -1 for wanted outputs..."); return -1; } else { result->audio_output = fParams.fReturnAudioChannels; jack_info("Takes slave %d outputs", fParams.fReturnAudioChannels); } } else if (fParams.fReturnAudioChannels == -1) { fParams.fReturnAudioChannels = fRequest.audio_output; jack_info("Takes master %d outputs", fRequest.audio_output); } else if (fParams.fReturnAudioChannels != fRequest.audio_output) { jack_error("Error : master wants %d outputs and slave wants %d outputs...", fRequest.audio_output, fParams.fReturnAudioChannels); return -1; } // Close request socket fSocket.Close(); /// Network init if (!JackNetMasterInterface::Init()) { return -1; } // Set global parameters if (!SetParams()) { return -1; } return 0; } int Close() { fSocket.Close(); return 0; } void UseRingBuffer(int audio_input, float** audio_input_buffer, int write, int read) { // Possibly use ringbuffer... if (fRingBuffer) { for (int i = 0; i < audio_input; i++) { fRingBuffer[i]->Write(audio_input_buffer[i], write); fRingBuffer[i]->Read(audio_input_buffer[i], read); } } } int Read(int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames) { try { // frames = -1 means : entire buffer if (frames < 0) frames = fParams.fPeriodSize; int read_frames = 0; assert(audio_input == fParams.fReturnAudioChannels); for (int audio_port_index = 0; audio_port_index < audio_input; audio_port_index++) { assert(audio_input_buffer[audio_port_index]); fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, audio_input_buffer[audio_port_index]); } for (int midi_port_index = 0; midi_port_index < midi_input; midi_port_index++) { assert(((JackMidiBuffer**)midi_input_buffer)[midi_port_index]); fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_input_buffer)[midi_port_index]); } int res1 = SyncRecv(); switch (res1) { case NET_SYNCHING: // Data will not be received, so cleanup buffers... for (int audio_port_index = 0; audio_port_index < audio_input; audio_port_index++) { memset(audio_input_buffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize); } UseRingBuffer(audio_input, audio_input_buffer, fParams.fPeriodSize, frames); return res1; case SOCKET_ERROR: return res1; case SYNC_PACKET_ERROR: // since sync packet is incorrect, don't decode it and continue with data break; default: // decode sync DecodeSyncPacket(read_frames); break; } int res2 = DataRecv(); UseRingBuffer(audio_input, audio_input_buffer, read_frames, frames); return res2; } catch (JackNetException& e) { jack_error(e.what()); return -1; } } int Write(int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames) { try { // frames = -1 means : entire buffer if (frames < 0) frames = fParams.fPeriodSize; assert(audio_output == fParams.fSendAudioChannels); for (int audio_port_index = 0; audio_port_index < audio_output; audio_port_index++) { assert(audio_output_buffer[audio_port_index]); fNetAudioCaptureBuffer->SetBuffer(audio_port_index, audio_output_buffer[audio_port_index]); } for (int midi_port_index = 0; midi_port_index < midi_output; midi_port_index++) { assert(((JackMidiBuffer**)midi_output_buffer)[midi_port_index]); fNetMidiCaptureBuffer->SetBuffer(midi_port_index, ((JackMidiBuffer**)midi_output_buffer)[midi_port_index]); } EncodeSyncPacket(frames); // send sync if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } // send data if (DataSend() == SOCKET_ERROR) { return SOCKET_ERROR; } return 0; } catch (JackNetException& e) { jack_error(e.what()); return -1; } } // Transport void EncodeTransportData() {} void DecodeTransportData() {} }; struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterface { // Data buffers float** fAudioCaptureBuffer; float** fAudioPlaybackBuffer; JackMidiBuffer** fMidiCaptureBuffer; JackMidiBuffer** fMidiPlaybackBuffer; JackThread fThread; JackNetSlaveProcessCallback fProcessCallback; void* fProcessArg; JackNetSlaveShutdownCallback fShutdownCallback; void* fShutdownArg; JackNetSlaveRestartCallback fRestartCallback; void* fRestartArg; JackNetSlaveErrorCallback fErrorCallback; void* fErrorArg; JackNetSlaveBufferSizeCallback fBufferSizeCallback; void* fBufferSizeArg; JackNetSlaveSampleRateCallback fSampleRateCallback; void* fSampleRateArg; int fConnectTimeOut; int fFrames; JackNetExtSlave(const char* ip, int port, const char* name, jack_slave_t* request) :fThread(this), fProcessCallback(NULL),fProcessArg(NULL), fShutdownCallback(NULL), fShutdownArg(NULL), fRestartCallback(NULL), fRestartArg(NULL), fErrorCallback(NULL), fErrorArg(NULL), fBufferSizeCallback(NULL), fBufferSizeArg(NULL), fSampleRateCallback(NULL), fSampleRateArg(NULL) { char host_name[JACK_CLIENT_NAME_SIZE + 1]; // Request parameters assert(strlen(ip) < 32); strcpy(fMulticastIP, ip); fParams.fMtu = request->mtu; fParams.fTransportSync = 0; fParams.fSendAudioChannels = request->audio_input; fParams.fReturnAudioChannels = request->audio_output; fParams.fSendMidiChannels = request->midi_input; fParams.fReturnMidiChannels = request->midi_output; fParams.fNetworkLatency = request->latency; fParams.fSampleEncoder = request->encoder; fParams.fKBps = request->kbps; fParams.fSlaveSyncMode = 1; fConnectTimeOut = request->time_out; // Create name with hostname and client name GetHostName(host_name, JACK_CLIENT_NAME_SIZE); snprintf(fParams.fName, JACK_CLIENT_NAME_SIZE, "%s_%s", host_name, name); fSocket.GetName(fParams.fSlaveNetName); // Set the socket parameters fSocket.SetPort(port); fSocket.SetAddress(fMulticastIP, port); fAudioCaptureBuffer = NULL; fAudioPlaybackBuffer = NULL; fMidiCaptureBuffer = NULL; fMidiPlaybackBuffer = NULL; } virtual ~JackNetExtSlave() {} void AllocPorts() { // Set buffers if (fParams.fSendAudioChannels > 0) { fAudioCaptureBuffer = new float*[fParams.fSendAudioChannels]; for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { fAudioCaptureBuffer[audio_port_index] = new float[fParams.fPeriodSize]; memset(fAudioCaptureBuffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetAudioCaptureBuffer->SetBuffer(audio_port_index, fAudioCaptureBuffer[audio_port_index]); } } if (fParams.fSendMidiChannels > 0) { fMidiCaptureBuffer = new JackMidiBuffer*[fParams.fSendMidiChannels]; for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { fMidiCaptureBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; memset(fMidiCaptureBuffer[midi_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetMidiCaptureBuffer->SetBuffer(midi_port_index, fMidiCaptureBuffer[midi_port_index]); } } if (fParams.fReturnAudioChannels > 0) { fAudioPlaybackBuffer = new float*[fParams.fReturnAudioChannels]; for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { fAudioPlaybackBuffer[audio_port_index] = new float[fParams.fPeriodSize]; memset(fAudioPlaybackBuffer[audio_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, fAudioPlaybackBuffer[audio_port_index]); } } if (fParams.fReturnMidiChannels > 0) { fMidiPlaybackBuffer = new JackMidiBuffer*[fParams.fReturnMidiChannels]; for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { fMidiPlaybackBuffer[midi_port_index] = (JackMidiBuffer*)new float[fParams.fPeriodSize]; memset(fMidiPlaybackBuffer[midi_port_index], 0, sizeof(float) * fParams.fPeriodSize); fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, fMidiPlaybackBuffer[midi_port_index]); } } } void FreePorts() { if (fAudioCaptureBuffer) { for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { delete[] fAudioCaptureBuffer[audio_port_index]; } delete[] fAudioCaptureBuffer; fAudioCaptureBuffer = NULL; } if (fMidiCaptureBuffer) { for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { delete[] fMidiCaptureBuffer[midi_port_index]; } delete[] fMidiCaptureBuffer; fMidiCaptureBuffer = NULL; } if (fAudioPlaybackBuffer) { for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { delete[] fAudioPlaybackBuffer[audio_port_index]; } delete[] fAudioPlaybackBuffer; fAudioPlaybackBuffer = NULL; } if (fMidiPlaybackBuffer) { for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { delete[] (fMidiPlaybackBuffer[midi_port_index]); } delete[] fMidiPlaybackBuffer; fMidiPlaybackBuffer = NULL; } } int Open(jack_master_t* result) { // Check audio/midi parameters if (fParams.fSendAudioChannels == 0 && fParams.fReturnAudioChannels == 0 && fParams.fSendMidiChannels == 0 && fParams.fReturnMidiChannels == 0) { jack_error("Incorrect audio/midi channels number..."); return -1; } // Check MTU parameters if ((fParams.fMtu < DEFAULT_MTU) && (fParams.fMtu > MAX_MTU)) { jack_error("MTU is not in the expected range [%d ... %d]", DEFAULT_MTU, MAX_MTU); return -1; } // Check CELT encoder parameters if ((fParams.fSampleEncoder == JackCeltEncoder) && (fParams.fKBps == 0)) { jack_error("CELT encoder with 0 for kps..."); return -1; } if ((fParams.fSampleEncoder == JackOpusEncoder) && (fParams.fKBps == 0)) { jack_error("Opus encoder with 0 for kps..."); return -1; } // Check latency if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) { jack_error("Network latency is limited to %d", NETWORK_MAX_LATENCY); return -1; } // Init network connection if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) { jack_error("Initing network fails..."); return -1; } // Finish connection... if (!JackNetSlaveInterface::InitRendering()) { jack_error("Starting network fails..."); return -1; } // Then set global parameters if (!SetParams()) { jack_error("SetParams error..."); return -1; } // Set result if (result != NULL) { result->buffer_size = fParams.fPeriodSize; result->sample_rate = fParams.fSampleRate; result->audio_input = fParams.fSendAudioChannels; result->audio_output = fParams.fReturnAudioChannels; result->midi_input = fParams.fSendMidiChannels; result->midi_output = fParams.fReturnMidiChannels; strcpy(result->master_name, fParams.fMasterNetName); } // By default fFrames is fPeriodSize fFrames = fParams.fPeriodSize; SessionParamsDisplay(&fParams); AllocPorts(); return 0; } int Restart() { // Do it until client possibly decides to stop trying to connect... while (true) { // If restart cb is set, then call it if (fRestartCallback) { if (fRestartCallback(fRestartArg) != 0) { return -1; } // Otherwise if shutdown cb is set, then call it } else if (fShutdownCallback) { fShutdownCallback(fShutdownArg); } // Init network connection if (!JackNetSlaveInterface::InitConnection(fConnectTimeOut)) { jack_error("Initing network fails after time_out, retry..."); } else { break; } } // Finish connection if (!JackNetSlaveInterface::InitRendering()) { jack_error("Starting network fails..."); return -1; } // Then set global parameters if (!SetParams()) { jack_error("SetParams error..."); return -1; } // We need to notify possibly new buffer size and sample rate (see Execute) if (fBufferSizeCallback) { if (fBufferSizeCallback(fParams.fPeriodSize, fBufferSizeArg) != 0) { jack_error("New buffer size = %d cannot be used...", fParams.fPeriodSize); return -1; } } if (fSampleRateCallback) { if (fSampleRateCallback(fParams.fSampleRate, fSampleRateArg) != 0) { jack_error("New sample rate = %d cannot be used...", fParams.fSampleRate); return -1; } } AllocPorts(); return 0; } int Close() { fSocket.Close(); FreePorts(); return 0; } // Transport void EncodeTransportData() {} void DecodeTransportData() {} bool Init() { // Will do "something" on OSX only... UInt64 period, constraint; period = constraint = UInt64(1000000000.f * (float(fParams.fPeriodSize) / float(fParams.fSampleRate))); UInt64 computation = JackTools::ComputationMicroSec(fParams.fPeriodSize) * 1000; fThread.SetParams(period, computation, constraint); return (fThread.AcquireSelfRealTime(80) == 0); // TODO: get a value from the server } bool IsRunning() { return (fThread.GetStatus() == JackThread::kRunning); } bool Execute() { try { /* Fist cycle use an INT_MAX time out, so that connection is considered established (with PACKET_TIMEOUT later on) when the first cycle has been done. */ DummyProcess(); // keep running even in case of error while (fThread.GetStatus() == JackThread::kRunning) { if (Process() == SOCKET_ERROR) { return false; } } return false; } catch (JackNetException& e) { // otherwise just restart... e.PrintMessage(); jack_info("NetSlave is restarted"); fThread.DropRealTime(); fThread.SetStatus(JackThread::kIniting); FreePorts(); if (Restart() == 0 && Init()) { fThread.SetStatus(JackThread::kRunning); return true; } else { return false; } } } int Read() { // receive sync (launch the cycle) switch (SyncRecv()) { case SOCKET_ERROR: return SOCKET_ERROR; case SYNC_PACKET_ERROR: // since sync packet is incorrect, don't decode it and continue with data if (fErrorCallback) { fErrorCallback(SYNC_PACKET_ERROR, fErrorArg); } break; default: // decode sync DecodeSyncPacket(fFrames); break; } int res = DataRecv(); if (res == DATA_PACKET_ERROR && fErrorCallback) { fErrorCallback(DATA_PACKET_ERROR, fErrorArg); } return res; } int Write() { EncodeSyncPacket(fFrames); if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } return DataSend(); } void DummyProcess() { // First cycle with INT_MAX time out SetPacketTimeOut(INT_MAX); // One cycle Process(); // Then use PACKET_TIMEOUT * fParams.fNetworkLatency for next cycles SetPacketTimeOut(std::max(int(PACKET_TIMEOUT), int(PACKET_TIMEOUT * fParams.fNetworkLatency))); } int Process() { // Read data from the network, throw JackNetException in case of network error... if (Read() == SOCKET_ERROR) { return SOCKET_ERROR; } if (fFrames < 0) fFrames = fParams.fPeriodSize; fProcessCallback(fFrames, fParams.fSendAudioChannels, fAudioCaptureBuffer, fParams.fSendMidiChannels, (void**)fMidiCaptureBuffer, fParams.fReturnAudioChannels, fAudioPlaybackBuffer, fParams.fReturnMidiChannels, (void**)fMidiPlaybackBuffer, fProcessArg); // Then write data to network, throw JackNetException in case of network error... if (Write() == SOCKET_ERROR) { return SOCKET_ERROR; } return 0; } int Start() { return (fProcessCallback == 0) ? -1 : fThread.StartSync(); } int Stop() { return (fProcessCallback == 0) ? -1 : fThread.Kill(); } // Callback int SetProcessCallback(JackNetSlaveProcessCallback net_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fProcessCallback = net_callback; fProcessArg = arg; return 0; } } int SetShutdownCallback(JackNetSlaveShutdownCallback shutdown_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fShutdownCallback = shutdown_callback; fShutdownArg = arg; return 0; } } int SetRestartCallback(JackNetSlaveRestartCallback restart_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fRestartCallback = restart_callback; fRestartArg = arg; return 0; } } int SetErrorCallback(JackNetSlaveErrorCallback error_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fErrorCallback = error_callback; fErrorArg = arg; return 0; } } int SetBufferSizeCallback(JackNetSlaveBufferSizeCallback bufsize_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fBufferSizeCallback = bufsize_callback; fBufferSizeArg = arg; return 0; } } int SetSampleRateCallback(JackNetSlaveSampleRateCallback samplerate_callback, void *arg) { if (fThread.GetStatus() == JackThread::kRunning) { return -1; } else { fSampleRateCallback = samplerate_callback; fSampleRateArg = arg; return 0; } } }; struct JackNetAdapter : public JackAudioAdapterInterface { JackNetAdapter(int input, int output, jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate) :JackAudioAdapterInterface(host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate) { fCaptureChannels = input; fPlaybackChannels = output; Create(); } void Create() { //ringbuffers if (fCaptureChannels > 0) { fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; } if (fPlaybackChannels > 0) { fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; } if (fAdaptative) { AdaptRingBufferSize(); jack_info("Ringbuffer automatic adaptative mode size = %d frames", fRingbufferCurSize); } else { if (fRingbufferCurSize > DEFAULT_RB_SIZE) { fRingbufferCurSize = DEFAULT_RB_SIZE; } jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize); } for (int i = 0; i < fCaptureChannels; i++ ) { fCaptureRingBuffer[i] = new JackResampler(); fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++ ) { fPlaybackRingBuffer[i] = new JackResampler(); fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } if (fCaptureChannels > 0) { jack_log("ReadSpace = %ld", fCaptureRingBuffer[0]->ReadSpace()); } if (fPlaybackChannels > 0) { jack_log("WriteSpace = %ld", fPlaybackRingBuffer[0]->WriteSpace()); } } virtual ~JackNetAdapter() { Destroy(); } void Flush() { for (int i = 0; i < fCaptureChannels; i++ ) { fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); } for (int i = 0; i < fPlaybackChannels; i++ ) { fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); } } }; } // end of namespace using namespace Jack; LIB_EXPORT jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result) { JackNetExtSlave* slave = new JackNetExtSlave(ip, port, name, request); if (slave->Open(result) == 0) { return (jack_net_slave_t*)slave; } else { delete slave; return NULL; } } LIB_EXPORT int jack_net_slave_close(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; slave->Close(); delete slave; return 0; } LIB_EXPORT int jack_set_net_slave_process_callback(jack_net_slave_t* net, JackNetSlaveProcessCallback net_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetProcessCallback(net_callback, arg); } LIB_EXPORT int jack_net_slave_activate(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->Start(); } LIB_EXPORT int jack_net_slave_deactivate(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->Stop(); } LIB_EXPORT int jack_net_slave_is_active(jack_net_slave_t* net) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->IsRunning(); } LIB_EXPORT int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetBufferSizeCallback(bufsize_callback, arg); } LIB_EXPORT int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetSampleRateCallback(samplerate_callback, arg); } LIB_EXPORT int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetShutdownCallback(shutdown_callback, arg); } LIB_EXPORT int jack_set_net_slave_restart_callback(jack_net_slave_t *net, JackNetSlaveRestartCallback restart_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetRestartCallback(restart_callback, arg); } LIB_EXPORT int jack_set_net_slave_error_callback(jack_net_slave_t *net, JackNetSlaveErrorCallback error_callback, void *arg) { JackNetExtSlave* slave = (JackNetExtSlave*)net; return slave->SetErrorCallback(error_callback, arg); } // Master API LIB_EXPORT jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result) { JackNetExtMaster* master = new JackNetExtMaster(ip, port, request); if (master->Open(result) == 0) { return (jack_net_master_t*)master; } else { delete master; return NULL; } } LIB_EXPORT int jack_net_master_close(jack_net_master_t* net) { JackNetExtMaster* master = (JackNetExtMaster*)net; master->Close(); delete master; return 0; } LIB_EXPORT int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer, -1); } LIB_EXPORT int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer, -1); } LIB_EXPORT int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Read(audio_input, audio_input_buffer, midi_input, midi_input_buffer, frames); } LIB_EXPORT int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames) { JackNetExtMaster* master = (JackNetExtMaster*)net; return master->Write(audio_output, audio_output_buffer, midi_output, midi_output_buffer, frames); } // Adapter API LIB_EXPORT jack_adapter_t* jack_create_adapter(int input, int output, jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate) { try { return (jack_adapter_t*)new JackNetAdapter(input, output, host_buffer_size, host_sample_rate, adapted_buffer_size, adapted_sample_rate); } catch (...) { return NULL; } } LIB_EXPORT int jack_destroy_adapter(jack_adapter_t* adapter) { delete((JackNetAdapter*)adapter); return 0; } LIB_EXPORT void jack_flush_adapter(jack_adapter_t* adapter) { JackNetAdapter* slave = (JackNetAdapter*)adapter; slave->Flush(); } LIB_EXPORT int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames) { JackNetAdapter* slave = (JackNetAdapter*)adapter; return slave->PushAndPull(input, output, frames); } LIB_EXPORT int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames) { JackNetAdapter* slave = (JackNetAdapter*)adapter; return slave->PullAndPush(input, output, frames); } static void jack_format_and_log(int level, const char *prefix, const char *fmt, va_list ap) { static const char* netjack_log = getenv("JACK_NETJACK_LOG"); static bool is_netjack_log = (netjack_log) ? atoi(netjack_log) : 0; if (is_netjack_log) { char buffer[300]; size_t len; if (prefix != NULL) { len = strlen(prefix); memcpy(buffer, prefix, len); } else { len = 0; } vsnprintf(buffer + len, sizeof(buffer) - len, fmt, ap); printf("%s", buffer); printf("\n"); } } LIB_EXPORT void jack_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); va_end(ap); } LIB_EXPORT void jack_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); va_end(ap); } LIB_EXPORT void jack_log(const char *fmt, ...) { va_list ap; va_start(ap, fmt); jack_format_and_log(LOG_LEVEL_INFO, "Jack: ", fmt, ap); va_end(ap); } jack2-1.9.22/common/JackNetAdapter.cpp000066400000000000000000000451111436671425200174270ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackNetAdapter.h" #include "JackException.h" #include "JackServerGlobals.h" #include "JackEngineControl.h" #include "JackArgParser.h" #include namespace Jack { JackNetAdapter::JackNetAdapter(jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params) : JackAudioAdapterInterface(buffer_size, sample_rate), JackNetSlaveInterface(), fThread(this) { jack_log("JackNetAdapter::JackNetAdapter"); /* Global parameter setting : we can't call JackNetSlaveInterface constructor with some parameters before, because we don't have full parametering right now, parameters will be parsed from the param list, and then JackNetSlaveInterface will be filled with proper values. */ char multicast_ip[32]; uint udp_port; GetHostName(fParams.fName, JACK_CLIENT_NAME_SIZE); fSocket.GetName(fParams.fSlaveNetName); fParams.fMtu = DEFAULT_MTU; // Deactivated for now... fParams.fTransportSync = 0; int send_audio = -1; int return_audio = -1; fParams.fSendMidiChannels = 0; fParams.fReturnMidiChannels = 0; fParams.fSampleRate = sample_rate; fParams.fPeriodSize = buffer_size; fParams.fSlaveSyncMode = 1; fParams.fNetworkLatency = NETWORK_DEFAULT_LATENCY; fParams.fSampleEncoder = JackFloatEncoder; fClient = jack_client; // Possibly use env variable const char* default_udp_port = getenv("JACK_NETJACK_PORT"); udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT; const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); if (default_multicast_ip) { strcpy(multicast_ip, default_multicast_ip); } else { strcpy(multicast_ip, DEFAULT_MULTICAST_IP); } //options parsing const JSList* node; const jack_driver_param_t* param; for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*) node->data; switch (param->character) { case 'a' : assert(strlen(param->value.str) < 32); strcpy(multicast_ip, param->value.str); break; case 'p' : udp_port = param->value.ui; break; case 'M' : fParams.fMtu = param->value.i; break; case 'C' : send_audio = param->value.i; break; case 'P' : return_audio = param->value.i; break; case 'n' : strncpy(fParams.fName, param->value.str, JACK_CLIENT_NAME_SIZE); break; case 't' : fParams.fTransportSync = param->value.ui; break; #if HAVE_CELT case 'c': if (param->value.i > 0) { fParams.fSampleEncoder = JackCeltEncoder; fParams.fKBps = param->value.i; } break; #endif #if HAVE_OPUS case 'O': if (param->value.i > 0) { fParams.fSampleEncoder = JackOpusEncoder; fParams.fKBps = param->value.i; } break; #endif case 'l' : fParams.fNetworkLatency = param->value.i; if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) { jack_error("Error : network latency is limited to %d\n", NETWORK_MAX_LATENCY); throw std::bad_alloc(); } break; case 'q': fQuality = param->value.ui; break; case 'g': fRingbufferCurSize = param->value.ui; fAdaptative = false; break; } } strcpy(fMulticastIP, multicast_ip); // Set the socket parameters fSocket.SetPort(udp_port); fSocket.SetAddress(fMulticastIP, udp_port); // If not set, takes default fParams.fSendAudioChannels = (send_audio == -1) ? 2 : send_audio; // If not set, takes default fParams.fReturnAudioChannels = (return_audio == -1) ? 2 : return_audio; // Set the audio adapter interface channel values SetInputs(fParams.fSendAudioChannels); SetOutputs(fParams.fReturnAudioChannels); // Soft buffers will be allocated later (once network initialization done) fSoftCaptureBuffer = NULL; fSoftPlaybackBuffer = NULL; } JackNetAdapter::~JackNetAdapter() { jack_log("JackNetAdapter::~JackNetAdapter"); if (fSoftCaptureBuffer) { for (int port_index = 0; port_index < fCaptureChannels; port_index++) { delete[] fSoftCaptureBuffer[port_index]; } delete[] fSoftCaptureBuffer; } if (fSoftPlaybackBuffer) { for (int port_index = 0; port_index < fPlaybackChannels; port_index++) { delete[] fSoftPlaybackBuffer[port_index]; } delete[] fSoftPlaybackBuffer; } } //open/close-------------------------------------------------------------------------- int JackNetAdapter::Open() { jack_info("NetAdapter started in %s mode %s Master's transport sync.", (fParams.fSlaveSyncMode) ? "sync" : "async", (fParams.fTransportSync) ? "with" : "without"); if (fThread.StartSync() < 0) { jack_error("Cannot start netadapter thread"); return -1; } return 0; } int JackNetAdapter::Close() { int res = 0; jack_log("JackNetAdapter::Close"); #ifdef JACK_MONITOR fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize); #endif if (fThread.Kill() < 0) { jack_error("Cannot kill thread"); res = -1; } fSocket.Close(); return res; } int JackNetAdapter::SetBufferSize(jack_nframes_t buffer_size) { JackAudioAdapterInterface::SetHostBufferSize(buffer_size); return 0; } //thread------------------------------------------------------------------------------ // TODO : if failure, thread exist... need to restart ? bool JackNetAdapter::Init() { jack_log("JackNetAdapter::Init"); //init network connection if (!JackNetSlaveInterface::Init()) { jack_error("JackNetSlaveInterface::Init() error..."); return false; } //then set global parameters if (!SetParams()) { jack_error("SetParams error..."); return false; } //set buffers if (fCaptureChannels > 0) { fSoftCaptureBuffer = new sample_t*[fCaptureChannels]; for (int port_index = 0; port_index < fCaptureChannels; port_index++) { fSoftCaptureBuffer[port_index] = new sample_t[fParams.fPeriodSize]; fNetAudioCaptureBuffer->SetBuffer(port_index, fSoftCaptureBuffer[port_index]); } } if (fPlaybackChannels > 0) { fSoftPlaybackBuffer = new sample_t*[fPlaybackChannels]; for (int port_index = 0; port_index < fPlaybackChannels; port_index++) { fSoftPlaybackBuffer[port_index] = new sample_t[fParams.fPeriodSize]; fNetAudioPlaybackBuffer->SetBuffer(port_index, fSoftPlaybackBuffer[port_index]); } } //set audio adapter parameters SetAdaptedBufferSize(fParams.fPeriodSize); SetAdaptedSampleRate(fParams.fSampleRate); // Will do "something" on OSX only... fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) { jack_error("AcquireSelfRealTime error"); } else { set_threaded_log_function(); } //init done, display parameters SessionParamsDisplay(&fParams); return true; } bool JackNetAdapter::Execute() { try { // Keep running even in case of error while (fThread.GetStatus() == JackThread::kRunning) { if (Process() == SOCKET_ERROR) { return false; } } return false; } catch (JackNetException& e) { // Otherwise just restart... e.PrintMessage(); jack_info("NetAdapter is restarted"); Reset(); fThread.DropSelfRealTime(); fThread.SetStatus(JackThread::kIniting); if (Init()) { fThread.SetStatus(JackThread::kRunning); return true; } else { return false; } } } //transport--------------------------------------------------------------------------- void JackNetAdapter::DecodeTransportData() { //TODO : we need here to get the actual timebase master to eventually release it from its duty (see JackNetDriver) //is there a new transport state ? if (fSendTransportData.fNewState &&(fSendTransportData.fState != jack_transport_query(fClient, NULL))) { switch (fSendTransportData.fState) { case JackTransportStopped : jack_transport_stop(fClient); jack_info("NetMaster : transport stops"); break; case JackTransportStarting : jack_transport_reposition(fClient, &fSendTransportData.fPosition); jack_transport_start(fClient); jack_info("NetMaster : transport starts"); break; case JackTransportRolling : // TODO, we need to : // - find a way to call TransportEngine->SetNetworkSync() // - turn the transport state to JackTransportRolling jack_info("NetMaster : transport rolls"); break; } } } void JackNetAdapter::EncodeTransportData() { //is there a timebase master change ? int refnum = -1; bool conditional = 0; //TODO : get the actual timebase master if (refnum != fLastTimebaseMaster) { //timebase master has released its function if (refnum == -1) { fReturnTransportData.fTimebaseMaster = RELEASE_TIMEBASEMASTER; jack_info("Sending a timebase master release request."); } else { //there is a new timebase master fReturnTransportData.fTimebaseMaster = (conditional) ? CONDITIONAL_TIMEBASEMASTER : TIMEBASEMASTER; jack_info("Sending a %s timebase master request.", (conditional) ? "conditional" : "non-conditional"); } fLastTimebaseMaster = refnum; } else { fReturnTransportData.fTimebaseMaster = NO_CHANGE; } //update transport state and position fReturnTransportData.fState = jack_transport_query(fClient, &fReturnTransportData.fPosition); //is it a new state (that the master need to know...) ? fReturnTransportData.fNewState = ((fReturnTransportData.fState != fLastTransportState) && (fReturnTransportData.fState != fSendTransportData.fState)); if (fReturnTransportData.fNewState) { jack_info("Sending transport state '%s'.", GetTransportState(fReturnTransportData.fState)); } fLastTransportState = fReturnTransportData.fState; } //read/write operations--------------------------------------------------------------- int JackNetAdapter::Read() { switch (SyncRecv()) { case SOCKET_ERROR: return SOCKET_ERROR; case SYNC_PACKET_ERROR: // Since sync packet is incorrect, don't decode it and continue with data break; default: //decode sync int unused_frames; DecodeSyncPacket(unused_frames); break; } return DataRecv(); } int JackNetAdapter::Write() { EncodeSyncPacket(); if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } return DataSend(); } //process----------------------------------------------------------------------------- int JackNetAdapter::Process() { //read data from the network //in case of fatal network error, stop the process if (Read() == SOCKET_ERROR) { return SOCKET_ERROR; } PushAndPull(fSoftCaptureBuffer, fSoftPlaybackBuffer, fAdaptedBufferSize); //then write data to network //in case of failure, stop process if (Write() == SOCKET_ERROR) { return SOCKET_ERROR; } return 0; } } // namespace Jack //loader------------------------------------------------------------------------------ #ifdef __cplusplus extern "C" { #endif #include "driver_interface.h" #include "JackAudioAdapter.h" using namespace Jack; SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("netadapter", JackDriverNone, "netjack net <==> audio backend adapter", &filler); strcpy(value.str, DEFAULT_MULTICAST_IP); jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address, or explicit IP of the master", NULL); value.i = DEFAULT_PORT; jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); value.i = DEFAULT_MTU; jack_driver_descriptor_add_parameter(desc, &filler, "mtu", 'M', JackDriverParamInt, &value, NULL, "MTU to the master", NULL); value.i = 2; jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", NULL); #if HAVE_CELT value.i = -1; jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamInt, &value, NULL, "Set CELT encoding and number of kBits per channel", NULL); #endif #if HAVE_OPUS value.i = -1; jack_driver_descriptor_add_parameter(desc, &filler, "opus", 'O', JackDriverParamInt, &value, NULL, "Set Opus encoding and number of kBits per channel", NULL); #endif strcpy(value.str, "'hostname'"); jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL); value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "transport-sync", 't', JackDriverParamUInt, &value, NULL, "Sync transport with master's", NULL); value.ui = 5U; jack_driver_descriptor_add_parameter(desc, &filler, "latency", 'l', JackDriverParamUInt, &value, NULL, "Network latency", NULL); value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "quality", 'q', JackDriverParamInt, &value, NULL, "Resample algorithm quality (0 - 4)", NULL); value.i = 32768; jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)"); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect netadapter to system ports", NULL); return desc; } SERVER_EXPORT int jack_internal_initialize(jack_client_t* client, const JSList* params) { jack_log("Loading netadapter"); Jack::JackAudioAdapter* adapter; jack_nframes_t buffer_size = jack_get_buffer_size(client); jack_nframes_t sample_rate = jack_get_sample_rate(client); try { adapter = new Jack::JackAudioAdapter(client, new Jack::JackNetAdapter(client, buffer_size, sample_rate, params), params); assert(adapter); if (adapter->Open() == 0) { return 0; } else { delete adapter; return 1; } } catch (...) { jack_info("netadapter allocation error"); return 1; } } SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); Jack::JackArgParser parser(load_init); if (parser.GetArgc() > 0) { parse_params = parser.ParseParams(desc, ¶ms); } if (parse_params) { res = jack_internal_initialize(jack_client, params); parser.FreeParams(params); } return res; } SERVER_EXPORT void jack_finish(void* arg) { Jack::JackAudioAdapter* adapter = static_cast(arg); if (adapter) { jack_log("Unloading netadapter"); adapter->Close(); delete adapter; } } #ifdef __cplusplus } #endif jack2-1.9.22/common/JackNetAdapter.h000066400000000000000000000036351436671425200171010ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackNetAdapter__ #define __JackNetAdapter__ #include "JackAudioAdapterInterface.h" #include "JackNetInterface.h" namespace Jack { /*! \brief Net adapter. */ class JackNetAdapter : public JackAudioAdapterInterface, public JackNetSlaveInterface, public JackRunnableInterface { private: //jack data jack_client_t* fClient; //transport data int fLastTransportState; int fLastTimebaseMaster; //sample buffers sample_t** fSoftCaptureBuffer; sample_t** fSoftPlaybackBuffer; //adapter thread JackThread fThread; //transport void EncodeTransportData(); void DecodeTransportData(); public: JackNetAdapter(jack_client_t* jack_client, jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params); ~JackNetAdapter(); int Open(); int Close(); int SetBufferSize(jack_nframes_t buffer_size); bool Init(); bool Execute(); int Read(); int Write(); int Process(); }; } #endif jack2-1.9.22/common/JackNetDriver.cpp000066400000000000000000001042571436671425200173110ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackCompilerDeps.h" #include "driver_interface.h" #include "JackNetDriver.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" #include "JackWaitThreadedDriver.h" using namespace std; namespace Jack { JackNetDriver::JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, const char* ip, int udp_port, int mtu, int midi_input_ports, int midi_output_ports, char* net_name, uint transport_sync, int network_latency, int celt_encoding, int opus_encoding, bool auto_save) : JackWaiterDriver(name, alias, engine, table), JackNetSlaveInterface(ip, udp_port) { jack_log("JackNetDriver::JackNetDriver ip %s, port %d", ip, udp_port); // Use the hostname if no name parameter was given if (strcmp(net_name, "") == 0) { GetHostName(net_name, JACK_CLIENT_NAME_SIZE); } fParams.fMtu = mtu; fWantedMIDICaptureChannels = midi_input_ports; fWantedMIDIPlaybackChannels = midi_output_ports; if (celt_encoding > 0) { fParams.fSampleEncoder = JackCeltEncoder; fParams.fKBps = celt_encoding; } else if (opus_encoding > 0) { fParams.fSampleEncoder = JackOpusEncoder; fParams.fKBps = opus_encoding; } else { fParams.fSampleEncoder = JackFloatEncoder; //fParams.fSampleEncoder = JackIntEncoder; } strcpy(fParams.fName, net_name); fSocket.GetName(fParams.fSlaveNetName); fParams.fTransportSync = transport_sync; fParams.fNetworkLatency = network_latency; fSendTransportData.fState = -1; fReturnTransportData.fState = -1; fLastTransportState = -1; fLastTimebaseMaster = -1; fMidiCapturePortList = NULL; fMidiPlaybackPortList = NULL; fWantedAudioCaptureChannels = -1; fWantedAudioPlaybackChannels = -1; fAutoSave = auto_save; #ifdef JACK_MONITOR fNetTimeMon = NULL; fRcvSyncUst = 0; #endif } JackNetDriver::~JackNetDriver() { delete[] fMidiCapturePortList; delete[] fMidiPlaybackPortList; #ifdef JACK_MONITOR delete fNetTimeMon; #endif } //open, close, attach and detach------------------------------------------------------ int JackNetDriver::Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { // Keep initial wanted values fWantedAudioCaptureChannels = inchannels; fWantedAudioPlaybackChannels = outchannels; return JackWaiterDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } int JackNetDriver::Close() { #ifdef JACK_MONITOR if (fNetTimeMon) { fNetTimeMon->Save(); } #endif FreeAll(); return JackWaiterDriver::Close(); } // Attach and Detach are defined as empty methods: port allocation is done when driver actually start (that is in Init) int JackNetDriver::Attach() { return 0; } int JackNetDriver::Detach() { return 0; } //init and restart-------------------------------------------------------------------- /* JackNetDriver is wrapped in a JackWaitThreadedDriver decorator that behaves as a "dummy driver, until Init method returns. */ bool JackNetDriver::Initialize() { jack_log("JackNetDriver::Initialize"); if (fAutoSave) { SaveConnections(0); } FreePorts(); // New loading, but existing socket, restart the driver if (fSocket.IsSocket()) { jack_info("Restarting driver..."); FreeAll(); } // Set the parameters to send fParams.fSendAudioChannels = fWantedAudioCaptureChannels; fParams.fReturnAudioChannels = fWantedAudioPlaybackChannels; fParams.fSendMidiChannels = fWantedMIDICaptureChannels; fParams.fReturnMidiChannels = fWantedMIDIPlaybackChannels; fParams.fSlaveSyncMode = fEngineControl->fSyncMode; // Display some additional infos jack_info("NetDriver started in %s mode %s Master's transport sync.", (fParams.fSlaveSyncMode) ? "sync" : "async", (fParams.fTransportSync) ? "with" : "without"); // Init network if (!JackNetSlaveInterface::Init()) { jack_error("Starting network fails..."); return false; } // Set global parameters if (!SetParams()) { jack_error("SetParams error..."); return false; } // If -1 at connection time for audio, in/out audio channels count is sent by the master fCaptureChannels = fParams.fSendAudioChannels; fPlaybackChannels = fParams.fReturnAudioChannels; // If -1 at connection time for MIDI, in/out MIDI channels count is sent by the master (in fParams struct) // Allocate midi ports lists delete[] fMidiCapturePortList; delete[] fMidiPlaybackPortList; if (fParams.fSendMidiChannels > 0) { fMidiCapturePortList = new jack_port_id_t [fParams.fSendMidiChannels]; assert(fMidiCapturePortList); for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { fMidiCapturePortList[midi_port_index] = 0; } } if (fParams.fReturnMidiChannels > 0) { fMidiPlaybackPortList = new jack_port_id_t [fParams.fReturnMidiChannels]; assert(fMidiPlaybackPortList); for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { fMidiPlaybackPortList[midi_port_index] = 0; } } // Register jack ports if (AllocPorts() != 0) { jack_error("Can't allocate ports."); return false; } // Init done, display parameters SessionParamsDisplay(&fParams); // Monitor #ifdef JACK_MONITOR string plot_name; // NetTimeMon plot_name = string(fParams.fName); plot_name += string("_slave"); plot_name += (fEngineControl->fSyncMode) ? string("_sync") : string("_async"); plot_name += string("_latency"); fNetTimeMon = new JackGnuPlotMonitor(128, 5, plot_name); string net_time_mon_fields[] = { string("sync decoded"), string("end of read"), string("start of write"), string("sync send"), string("end of write") }; string net_time_mon_options[] = { string("set xlabel \"audio cycles\""), string("set ylabel \"% of audio cycle\"") }; fNetTimeMon->SetPlotFile(net_time_mon_options, 2, net_time_mon_fields, 5); #endif // Driver parametering JackTimedDriver::SetBufferSize(fParams.fPeriodSize); JackTimedDriver::SetSampleRate(fParams.fSampleRate); JackDriver::NotifyBufferSize(fParams.fPeriodSize); JackDriver::NotifySampleRate(fParams.fSampleRate); // Transport engine parametering fEngineControl->fTransport.SetNetworkSync(fParams.fTransportSync); if (fAutoSave) { LoadConnections(0); } return true; } void JackNetDriver::FreeAll() { FreePorts(); delete[] fTxBuffer; delete[] fRxBuffer; delete fNetAudioCaptureBuffer; delete fNetAudioPlaybackBuffer; delete fNetMidiCaptureBuffer; delete fNetMidiPlaybackBuffer; delete[] fMidiCapturePortList; delete[] fMidiPlaybackPortList; fTxBuffer = NULL; fRxBuffer = NULL; fNetAudioCaptureBuffer = NULL; fNetAudioPlaybackBuffer = NULL; fNetMidiCaptureBuffer = NULL; fNetMidiPlaybackBuffer = NULL; fMidiCapturePortList = NULL; fMidiPlaybackPortList = NULL; #ifdef JACK_MONITOR delete fNetTimeMon; fNetTimeMon = NULL; #endif } void JackNetDriver::UpdateLatencies() { jack_latency_range_t input_range; jack_latency_range_t output_range; jack_latency_range_t monitor_range; for (int i = 0; i < fCaptureChannels; i++) { input_range.max = input_range.min = float(fParams.fNetworkLatency * fEngineControl->fBufferSize) / 2.f; fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &input_range); } for (int i = 0; i < fPlaybackChannels; i++) { output_range.max = output_range.min = float(fParams.fNetworkLatency * fEngineControl->fBufferSize) / 2.f; if (!fEngineControl->fSyncMode) { output_range.max = output_range.min += fEngineControl->fBufferSize; } fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &output_range); if (fWithMonitorPorts) { monitor_range.min = monitor_range.max = 0; fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &monitor_range); } } } //jack ports and buffers-------------------------------------------------------------- int JackNetDriver::AllocPorts() { jack_log("JackNetDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); /* fNetAudioCaptureBuffer fNetAudioPlaybackBuffer fSendAudioChannels fReturnAudioChannels fCapturePortList fPlaybackPortList fCaptureChannels ==> SLAVE ==> fPlaybackChannels "capture_" "playback_" */ JackPort* port; jack_port_id_t port_index; char name[REAL_JACK_PORT_NAME_SIZE+1]; char alias[REAL_JACK_PORT_NAME_SIZE+1]; int audio_port_index; int midi_port_index; //audio for (audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++) { snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, audio_port_index + 1); snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, audio_port_index + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); fCapturePortList[audio_port_index] = port_index; jack_log("JackNetDriver::AllocPorts() fCapturePortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_index, port->GetLatency()); } for (audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) { snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, audio_port_index + 1); snprintf(name, sizeof(name), "%s:playback_%d",fClientControl.fName, audio_port_index + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); port->SetAlias(alias); fPlaybackPortList[audio_port_index] = port_index; jack_log("JackNetDriver::AllocPorts() fPlaybackPortList[%d] audio_port_index = %ld fPortLatency = %ld", audio_port_index, port_index, port->GetLatency()); } //midi for (midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, midi_port_index + 1); snprintf(name, sizeof (name), "%s:midi_capture_%d", fClientControl.fName, midi_port_index + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); fMidiCapturePortList[midi_port_index] = port_index; jack_log("JackNetDriver::AllocPorts() fMidiCapturePortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_index, port->GetLatency()); } for (midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, midi_port_index + 1); snprintf(name, sizeof(name), "%s:midi_playback_%d", fClientControl.fName, midi_port_index + 1); if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", name); return -1; } port = fGraphManager->GetPort(port_index); fMidiPlaybackPortList[midi_port_index] = port_index; jack_log("JackNetDriver::AllocPorts() fMidiPlaybackPortList[%d] midi_port_index = %ld fPortLatency = %ld", midi_port_index, port_index, port->GetLatency()); } UpdateLatencies(); return 0; } int JackNetDriver::FreePorts() { jack_log("JackNetDriver::FreePorts"); for (int audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++) { if (fCapturePortList[audio_port_index] > 0) { fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[audio_port_index]); fCapturePortList[audio_port_index] = 0; } } for (int audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) { if (fPlaybackPortList[audio_port_index] > 0) { fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[audio_port_index]); fPlaybackPortList[audio_port_index] = 0; } } for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { if (fMidiCapturePortList && fMidiCapturePortList[midi_port_index] > 0) { fGraphManager->ReleasePort(fClientControl.fRefNum, fMidiCapturePortList[midi_port_index]); fMidiCapturePortList[midi_port_index] = 0; } } for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { if (fMidiPlaybackPortList && fMidiPlaybackPortList[midi_port_index] > 0) { fEngine->PortUnRegister(fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index]); fMidiPlaybackPortList[midi_port_index] = 0; } } return 0; } void JackNetDriver::SaveConnections(int alias) { JackDriver::SaveConnections(alias); const char** connections; if (fMidiCapturePortList) { for (int i = 0; i < fParams.fSendMidiChannels; ++i) { if (fMidiCapturePortList[i] && (connections = fGraphManager->GetConnections(fMidiCapturePortList[i])) != 0) { for (int j = 0; connections[j]; j++) { JackPort* port_id = fGraphManager->GetPort(fGraphManager->GetPort(connections[j])); fConnections.push_back(make_pair(port_id->GetType(), make_pair(fGraphManager->GetPort(fMidiCapturePortList[i])->GetName(), connections[j]))); jack_info("Save connection: %s %s", fGraphManager->GetPort(fMidiCapturePortList[i])->GetName(), connections[j]); } free(connections); } } } if (fMidiPlaybackPortList) { for (int i = 0; i < fParams.fReturnMidiChannels; ++i) { if (fMidiPlaybackPortList[i] && (connections = fGraphManager->GetConnections(fMidiPlaybackPortList[i])) != 0) { for (int j = 0; connections[j]; j++) { JackPort* port_id = fGraphManager->GetPort(fGraphManager->GetPort(connections[j])); fConnections.push_back(make_pair(port_id->GetType(), make_pair(connections[j], fGraphManager->GetPort(fMidiPlaybackPortList[i])->GetName()))); jack_info("Save connection: %s %s", connections[j], fGraphManager->GetPort(fMidiPlaybackPortList[i])->GetName()); } free(connections); } } } } JackMidiBuffer* JackNetDriver::GetMidiInputBuffer(int port_index) { return static_cast(fGraphManager->GetBuffer(fMidiCapturePortList[port_index], fEngineControl->fBufferSize)); } JackMidiBuffer* JackNetDriver::GetMidiOutputBuffer(int port_index) { return static_cast(fGraphManager->GetBuffer(fMidiPlaybackPortList[port_index], fEngineControl->fBufferSize)); } //transport--------------------------------------------------------------------------- void JackNetDriver::DecodeTransportData() { //is there a new timebase master on the net master ? // - release timebase master only if it's a non-conditional request // - no change or no request : don't do anything // - conditional request : don't change anything too, the master will know if this slave is actually the timebase master int refnum; bool conditional; if (fSendTransportData.fTimebaseMaster == TIMEBASEMASTER) { fEngineControl->fTransport.GetTimebaseMaster(refnum, conditional); if (refnum != -1) { fEngineControl->fTransport.ResetTimebase(refnum); } jack_info("The NetMaster is now the new timebase master."); } //is there a transport state change to handle ? if (fSendTransportData.fNewState &&(fSendTransportData.fState != fEngineControl->fTransport.GetState())) { switch (fSendTransportData.fState) { case JackTransportStopped : fEngineControl->fTransport.SetCommand(TransportCommandStop); jack_info("Master stops transport."); break; case JackTransportStarting : fEngineControl->fTransport.RequestNewPos(&fSendTransportData.fPosition); fEngineControl->fTransport.SetCommand(TransportCommandStart); jack_info("Master starts transport frame = %d", fSendTransportData.fPosition.frame); break; case JackTransportRolling : //fEngineControl->fTransport.SetCommand(TransportCommandStart); fEngineControl->fTransport.SetState(JackTransportRolling); jack_info("Master is rolling."); break; } } } void JackNetDriver::EncodeTransportData() { // is there a timebase master change ? int refnum; bool conditional; fEngineControl->fTransport.GetTimebaseMaster(refnum, conditional); if (refnum != fLastTimebaseMaster) { // timebase master has released its function if (refnum == -1) { fReturnTransportData.fTimebaseMaster = RELEASE_TIMEBASEMASTER; jack_info("Sending a timebase master release request."); } else { // there is a new timebase master fReturnTransportData.fTimebaseMaster = (conditional) ? CONDITIONAL_TIMEBASEMASTER : TIMEBASEMASTER; jack_info("Sending a %s timebase master request.", (conditional) ? "conditional" : "non-conditional"); } fLastTimebaseMaster = refnum; } else { fReturnTransportData.fTimebaseMaster = NO_CHANGE; } // update transport state and position fReturnTransportData.fState = fEngineControl->fTransport.Query(&fReturnTransportData.fPosition); // is it a new state (that the master need to know...) ? fReturnTransportData.fNewState = ((fReturnTransportData.fState == JackTransportNetStarting) && (fReturnTransportData.fState != fLastTransportState) && (fReturnTransportData.fState != fSendTransportData.fState)); if (fReturnTransportData.fNewState) { jack_info("Sending '%s'.", GetTransportState(fReturnTransportData.fState)); } fLastTransportState = fReturnTransportData.fState; } //driver processes-------------------------------------------------------------------- int JackNetDriver::Read() { // buffers for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { fNetMidiCaptureBuffer->SetBuffer(midi_port_index, GetMidiInputBuffer(midi_port_index)); } for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { #ifdef OPTIMIZED_PROTOCOL if (fGraphManager->GetConnectionsNum(fCapturePortList[audio_port_index]) > 0) { fNetAudioCaptureBuffer->SetBuffer(audio_port_index, GetInputBuffer(audio_port_index)); } else { fNetAudioCaptureBuffer->SetBuffer(audio_port_index, NULL); } #else fNetAudioCaptureBuffer->SetBuffer(audio_port_index, GetInputBuffer(audio_port_index)); #endif } #ifdef JACK_MONITOR fNetTimeMon->New(); #endif switch (SyncRecv()) { case SOCKET_ERROR: return SOCKET_ERROR; case SYNC_PACKET_ERROR: // since sync packet is incorrect, don't decode it and continue with data break; default: // decode sync int unused_frames; DecodeSyncPacket(unused_frames); break; } #ifdef JACK_MONITOR // For timing fRcvSyncUst = GetMicroSeconds(); #endif #ifdef JACK_MONITOR fNetTimeMon->Add(float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f); #endif // audio, midi or sync if driver is late switch (DataRecv()) { case SOCKET_ERROR: return SOCKET_ERROR; case DATA_PACKET_ERROR: jack_time_t cur_time = GetMicroSeconds(); NotifyXRun(cur_time, float(cur_time - fBeginDateUst)); // Better this value than nothing... break; } // take the time at the beginning of the cycle JackDriver::CycleTakeBeginTime(); #ifdef JACK_MONITOR fNetTimeMon->Add(float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f); #endif return 0; } int JackNetDriver::Write() { // buffers for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, GetMidiOutputBuffer(midi_port_index)); } for (int audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++) { #ifdef OPTIMIZED_PROTOCOL // Port is connected on other side... if (fNetAudioPlaybackBuffer->GetConnected(audio_port_index) && (fGraphManager->GetConnectionsNum(fPlaybackPortList[audio_port_index]) > 0)) { fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, GetOutputBuffer(audio_port_index)); } else { fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, NULL); } #else fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, GetOutputBuffer(audio_port_index)); #endif } #ifdef JACK_MONITOR fNetTimeMon->AddLast(float(GetMicroSeconds() - fRcvSyncUst) / float(fEngineControl->fPeriodUsecs) * 100.f); #endif EncodeSyncPacket(); // send sync if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } #ifdef JACK_MONITOR fNetTimeMon->Add(((float)(GetMicroSeconds() - fRcvSyncUst) / (float)fEngineControl->fPeriodUsecs) * 100.f); #endif // send data if (DataSend() == SOCKET_ERROR) { return SOCKET_ERROR; } #ifdef JACK_MONITOR fNetTimeMon->AddLast(((float)(GetMicroSeconds() - fRcvSyncUst) / (float)fEngineControl->fPeriodUsecs) * 100.f); #endif return 0; } //driver loader----------------------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("net", JackDriverMaster, "netjack slave backend component", &filler); strcpy(value.str, DEFAULT_MULTICAST_IP); jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address, or explicit IP of the master", NULL); value.i = DEFAULT_PORT; jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); value.i = DEFAULT_MTU; jack_driver_descriptor_add_parameter(desc, &filler, "mtu", 'M', JackDriverParamInt, &value, NULL, "MTU to the master", NULL); value.i = -1; jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", "Number of audio input ports. If -1, audio physical input from the master"); jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", "Number of audio output ports. If -1, audio physical output from the master"); value.i = -1; jack_driver_descriptor_add_parameter(desc, &filler, "midi-in-ports", 'i', JackDriverParamInt, &value, NULL, "Number of midi input ports", "Number of MIDI input ports. If -1, MIDI physical input from the master"); jack_driver_descriptor_add_parameter(desc, &filler, "midi-out-ports", 'o', JackDriverParamInt, &value, NULL, "Number of midi output ports", "Number of MIDI output ports. If -1, MIDI physical output from the master"); #if HAVE_CELT value.i = -1; jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamInt, &value, NULL, "Set CELT encoding and number of kBits per channel", NULL); #endif #if HAVE_OPUS value.i = -1; jack_driver_descriptor_add_parameter(desc, &filler, "opus", 'O', JackDriverParamInt, &value, NULL, "Set Opus encoding and number of kBits per channel", NULL); #endif strcpy(value.str, "'hostname'"); jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-save", 's', JackDriverParamBool, &value, NULL, "Save/restore connection state when restarting", NULL); /* Deactivated for now.. value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "transport-sync", 't', JackDriverParamUInt, &value, NULL, "Sync transport with master's", NULL); */ value.ui = 5U; jack_driver_descriptor_add_parameter(desc, &filler, "latency", 'l', JackDriverParamUInt, &value, NULL, "Network latency", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { char multicast_ip[32]; char net_name[JACK_CLIENT_NAME_SIZE+1] = {0}; int udp_port; int mtu = DEFAULT_MTU; // Deactivated for now... uint transport_sync = 0; jack_nframes_t period_size = 1024; // to be used while waiting for master period_size jack_nframes_t sample_rate = 48000; // to be used while waiting for master sample_rate int audio_capture_ports = -1; int audio_playback_ports = -1; int midi_input_ports = -1; int midi_output_ports = -1; int celt_encoding = -1; int opus_encoding = -1; bool monitor = false; int network_latency = 5; const JSList* node; const jack_driver_param_t* param; bool auto_save = false; // Possibly use env variable for UDP port const char* default_udp_port = getenv("JACK_NETJACK_PORT"); udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT; // Possibly use env variable for multicast IP const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); strcpy(multicast_ip, (default_multicast_ip) ? default_multicast_ip : DEFAULT_MULTICAST_IP); for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*) node->data; switch (param->character) { case 'a' : assert(strlen(param->value.str) < 32); strcpy(multicast_ip, param->value.str); break; case 'p': udp_port = param->value.ui; break; case 'M': mtu = param->value.i; break; case 'C': audio_capture_ports = param->value.i; break; case 'P': audio_playback_ports = param->value.i; break; case 'i': midi_input_ports = param->value.i; break; case 'o': midi_output_ports = param->value.i; break; #if HAVE_CELT case 'c': celt_encoding = param->value.i; break; #endif #if HAVE_OPUS case 'O': opus_encoding = param->value.i; break; #endif case 'n' : strncpy(net_name, param->value.str, JACK_CLIENT_NAME_SIZE); break; case 's': auto_save = true; break; /* Deactivated for now.. case 't' : transport_sync = param->value.ui; break; */ case 'l' : network_latency = param->value.ui; if (network_latency > NETWORK_MAX_LATENCY) { printf("Error : network latency is limited to %d\n", NETWORK_MAX_LATENCY); return NULL; } break; } } try { Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver( new Jack::JackNetDriver("system", "net_pcm", engine, table, multicast_ip, udp_port, mtu, midi_input_ports, midi_output_ports, net_name, transport_sync, network_latency, celt_encoding, opus_encoding, auto_save)); if (driver->Open(period_size, sample_rate, 1, 1, audio_capture_ports, audio_playback_ports, monitor, "from_master_", "to_master_", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } catch (...) { return NULL; } } #ifdef __cplusplus } #endif } jack2-1.9.22/common/JackNetDriver.h000066400000000000000000000071011436671425200167440ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackNetDriver__ #define __JackNetDriver__ #include "JackTimedDriver.h" #include "JackNetInterface.h" //#define JACK_MONITOR namespace Jack { /** \Brief This class describes the Net Backend */ class JackNetDriver : public JackWaiterDriver, public JackNetSlaveInterface { private: //jack data jack_port_id_t* fMidiCapturePortList; jack_port_id_t* fMidiPlaybackPortList; //transport int fLastTransportState; int fLastTimebaseMaster; // The wanted value at creation time (may be different than the value actually returned by the master) int fWantedAudioCaptureChannels; int fWantedAudioPlaybackChannels; int fWantedMIDICaptureChannels; int fWantedMIDIPlaybackChannels; bool fAutoSave; //monitoring #ifdef JACK_MONITOR JackGnuPlotMonitor* fNetTimeMon; jack_time_t fRcvSyncUst; #endif bool Initialize(); void FreeAll(); int AllocPorts(); int FreePorts(); //transport void EncodeTransportData(); void DecodeTransportData(); JackMidiBuffer* GetMidiInputBuffer(int port_index); JackMidiBuffer* GetMidiOutputBuffer(int port_index); void SaveConnections(int alias); void UpdateLatencies(); public: JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports, char* net_name, uint transport_sync, int network_latency, int celt_encoding, int opus_encoding, bool auto_save); virtual ~JackNetDriver(); int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); int Close(); int Attach(); int Detach(); int Read(); int Write(); // BufferSize can't be changed bool IsFixedBufferSize() { return true; } int SetBufferSize(jack_nframes_t buffer_size) { return -1; } int SetSampleRate(jack_nframes_t sample_rate) { return -1; } }; } #endif jack2-1.9.22/common/JackNetInterface.cpp000066400000000000000000001045331436671425200177530ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackNetInterface.h" #include "JackException.h" #include "JackError.h" #include using namespace std; /* TODO : since midi buffers now uses up to BUFFER_SIZE_MAX frames, probably also use BUFFER_SIZE_MAX in everything related to MIDI events handling (see MidiBufferInit in JackMidiPort.cpp) */ namespace Jack { // JackNetInterface******************************************* JackNetInterface::JackNetInterface() : fSocket() { Initialize(); } JackNetInterface::JackNetInterface(const char* multicast_ip, int port) : fSocket(multicast_ip, port) { strcpy(fMulticastIP, multicast_ip); Initialize(); } JackNetInterface::JackNetInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip) : fSocket(socket) { fParams = params; strcpy(fMulticastIP, multicast_ip); Initialize(); } void JackNetInterface::Initialize() { fSetTimeOut = false; fTxBuffer = NULL; fRxBuffer = NULL; fNetAudioCaptureBuffer = NULL; fNetAudioPlaybackBuffer = NULL; fNetMidiCaptureBuffer = NULL; fNetMidiPlaybackBuffer = NULL; memset(&fSendTransportData, 0, sizeof(net_transport_data_t)); memset(&fReturnTransportData, 0, sizeof(net_transport_data_t)); fPacketTimeOut = PACKET_TIMEOUT * NETWORK_DEFAULT_LATENCY; } void JackNetInterface::FreeNetworkBuffers() { delete fNetMidiCaptureBuffer; delete fNetMidiPlaybackBuffer; delete fNetAudioCaptureBuffer; delete fNetAudioPlaybackBuffer; fNetMidiCaptureBuffer = NULL; fNetMidiPlaybackBuffer = NULL; fNetAudioCaptureBuffer = NULL; fNetAudioPlaybackBuffer = NULL; } JackNetInterface::~JackNetInterface() { jack_log("JackNetInterface::~JackNetInterface"); fSocket.Close(); delete[] fTxBuffer; delete[] fRxBuffer; delete fNetAudioCaptureBuffer; delete fNetAudioPlaybackBuffer; delete fNetMidiCaptureBuffer; delete fNetMidiPlaybackBuffer; } int JackNetInterface::SetNetBufferSize() { // audio float audio_size = (fNetAudioCaptureBuffer) ? fNetAudioCaptureBuffer->GetCycleSize() : (fNetAudioPlaybackBuffer) ? fNetAudioPlaybackBuffer->GetCycleSize() : 0; jack_log("audio_size %f", audio_size); // midi float midi_size = (fNetMidiCaptureBuffer) ? fNetMidiCaptureBuffer->GetCycleSize() : (fNetMidiPlaybackBuffer) ? fNetMidiPlaybackBuffer->GetCycleSize() : 0; jack_log("midi_size %f", midi_size); // bufsize = sync + audio + midi int bufsize = NETWORK_MAX_LATENCY * (fParams.fMtu + (int)audio_size + (int)midi_size); jack_log("SetNetBufferSize bufsize = %d", bufsize); // tx buffer if (fSocket.SetOption(SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) == SOCKET_ERROR) { return SOCKET_ERROR; } // rx buffer if (fSocket.SetOption(SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) == SOCKET_ERROR) { return SOCKET_ERROR; } return 0; } bool JackNetInterface::SetParams() { // TX header init memset(&fTxHeader, 0, sizeof(fTxHeader)); strcpy(fTxHeader.fPacketType, "header"); fTxHeader.fID = fParams.fID; fTxHeader.fCycle = 0; fTxHeader.fSubCycle = 0; fTxHeader.fIsLastPckt = 0; // RX header init memset(&fRxHeader, 0, sizeof(fTxHeader)); strcpy(fRxHeader.fPacketType, "header"); fRxHeader.fID = fParams.fID; fRxHeader.fCycle = 0; fRxHeader.fSubCycle = 0; fRxHeader.fIsLastPckt = 0; // network buffers fTxBuffer = new char[fParams.fMtu]; fRxBuffer = new char[fParams.fMtu]; assert(fTxBuffer); assert(fRxBuffer); // net audio/midi buffers'addresses fTxData = fTxBuffer + HEADER_SIZE; fRxData = fRxBuffer + HEADER_SIZE; return true; } int JackNetInterface::MidiSend(NetMidiBuffer* buffer, int midi_channnels, int audio_channels) { if (midi_channnels > 0) { // set global header fields and get the number of midi packets fTxHeader.fDataType = 'm'; uint data_size = buffer->RenderFromJackPorts(); fTxHeader.fNumPacket = buffer->GetNumPackets(data_size, PACKET_AVAILABLE_SIZE(&fParams)); for (uint subproc = 0; subproc < fTxHeader.fNumPacket; subproc++) { fTxHeader.fSubCycle = subproc; fTxHeader.fIsLastPckt = ((subproc == (fTxHeader.fNumPacket - 1)) && audio_channels == 0) ? 1 : 0; fTxHeader.fPacketSize = HEADER_SIZE + buffer->RenderToNetwork(subproc, data_size); memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); //PacketHeaderDisplay(&fTxHeader); if (Send(fTxHeader.fPacketSize, 0) == SOCKET_ERROR) { return SOCKET_ERROR; } } } return 0; } int JackNetInterface::AudioSend(NetAudioBuffer* buffer, int audio_channels) { // audio if (audio_channels > 0) { fTxHeader.fDataType = 'a'; fTxHeader.fActivePorts = buffer->RenderFromJackPorts(fTxHeader.fFrames); fTxHeader.fNumPacket = buffer->GetNumPackets(fTxHeader.fActivePorts); for (uint subproc = 0; subproc < fTxHeader.fNumPacket; subproc++) { fTxHeader.fSubCycle = subproc; fTxHeader.fIsLastPckt = (subproc == (fTxHeader.fNumPacket - 1)) ? 1 : 0; fTxHeader.fPacketSize = HEADER_SIZE + buffer->RenderToNetwork(subproc, fTxHeader.fActivePorts); memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); //PacketHeaderDisplay(&fTxHeader); if (Send(fTxHeader.fPacketSize, 0) == SOCKET_ERROR) { return SOCKET_ERROR; } } } return 0; } int JackNetInterface::MidiRecv(packet_header_t* rx_head, NetMidiBuffer* buffer, uint& recvd_midi_pckt) { int rx_bytes = Recv(rx_head->fPacketSize, 0); fRxHeader.fCycle = rx_head->fCycle; fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; buffer->RenderFromNetwork(rx_head->fSubCycle, rx_bytes - HEADER_SIZE); // Last midi packet is received, so finish rendering... if (++recvd_midi_pckt == rx_head->fNumPacket) { buffer->RenderToJackPorts(); } //PacketHeaderDisplay(rx_head); return rx_bytes; } int JackNetInterface::AudioRecv(packet_header_t* rx_head, NetAudioBuffer* buffer) { int rx_bytes = Recv(rx_head->fPacketSize, 0); fRxHeader.fCycle = rx_head->fCycle; fRxHeader.fSubCycle = rx_head->fSubCycle; fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; fRxHeader.fActivePorts = rx_head->fActivePorts; fRxHeader.fFrames = rx_head->fFrames; rx_bytes = buffer->RenderFromNetwork(rx_head->fCycle, rx_head->fSubCycle, fRxHeader.fActivePorts); // Last audio packet is received, so finish rendering... if (fRxHeader.fIsLastPckt) { buffer->RenderToJackPorts(fRxHeader.fFrames); } //PacketHeaderDisplay(rx_head); return rx_bytes; } int JackNetInterface::FinishRecv(NetAudioBuffer* buffer) { if (buffer) { buffer->RenderToJackPorts(fRxHeader.fFrames); } else { jack_error("FinishRecv with null buffer..."); } return DATA_PACKET_ERROR; } NetAudioBuffer* JackNetInterface::AudioBufferFactory(int nports, char* buffer) { switch (fParams.fSampleEncoder) { case JackFloatEncoder: return new NetFloatAudioBuffer(&fParams, nports, buffer); case JackIntEncoder: return new NetIntAudioBuffer(&fParams, nports, buffer); #if HAVE_CELT case JackCeltEncoder: return new NetCeltAudioBuffer(&fParams, nports, buffer, fParams.fKBps); #endif #if HAVE_OPUS case JackOpusEncoder: return new NetOpusAudioBuffer(&fParams, nports, buffer, fParams.fKBps); #endif } throw std::bad_alloc(); } void JackNetInterface::SetRcvTimeOut() { if (!fSetTimeOut) { if (fSocket.SetTimeOut(fPacketTimeOut) == SOCKET_ERROR) { jack_error("Can't set rx timeout : %s", StrError(NET_ERROR_CODE)); return; } fSetTimeOut = true; } } // JackNetMasterInterface ************************************************************************************ bool JackNetMasterInterface::Init() { jack_log("JackNetMasterInterface::Init : ID %u", fParams.fID); session_params_t host_params; uint attempt = 0; int rx_bytes = 0; // socket if (fSocket.NewSocket() == SOCKET_ERROR) { jack_error("Can't create socket : %s", StrError(NET_ERROR_CODE)); return false; } // timeout on receive (for init) if (fSocket.SetTimeOut(MASTER_INIT_TIMEOUT) < 0) { jack_error("Can't set init timeout : %s", StrError(NET_ERROR_CODE)); } // connect if (fSocket.Connect() == SOCKET_ERROR) { jack_error("Can't connect : %s", StrError(NET_ERROR_CODE)); return false; } // send 'SLAVE_SETUP' until 'START_MASTER' received jack_info("Sending parameters to %s...", fParams.fSlaveNetName); do { session_params_t net_params; memset(&net_params, 0, sizeof(session_params_t)); SetPacketType(&fParams, SLAVE_SETUP); SessionParamsHToN(&fParams, &net_params); if (fSocket.Send(&net_params, sizeof(session_params_t), 0) == SOCKET_ERROR) { jack_error("Error in send : %s", StrError(NET_ERROR_CODE)); } memset(&net_params, 0, sizeof(session_params_t)); if (((rx_bytes = fSocket.Recv(&net_params, sizeof(session_params_t), 0)) == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { jack_error("Problem with network"); return false; } SessionParamsNToH(&net_params, &host_params); } while ((GetPacketType(&host_params) != START_MASTER) && (++attempt < SLAVE_SETUP_RETRY)); if (attempt == SLAVE_SETUP_RETRY) { jack_error("Slave doesn't respond, exiting"); return false; } return true; } bool JackNetMasterInterface::SetParams() { jack_log("JackNetMasterInterface::SetParams audio in = %d audio out = %d MIDI in = %d MIDI out = %d", fParams.fSendAudioChannels, fParams.fReturnAudioChannels, fParams.fSendMidiChannels, fParams.fReturnMidiChannels); JackNetInterface::SetParams(); fTxHeader.fDataStream = 's'; fRxHeader.fDataStream = 'r'; fMaxCycleOffset = fParams.fNetworkLatency; // midi net buffers if (fParams.fSendMidiChannels > 0) { fNetMidiCaptureBuffer = new NetMidiBuffer(&fParams, fParams.fSendMidiChannels, fTxData); } if (fParams.fReturnMidiChannels > 0) { fNetMidiPlaybackBuffer = new NetMidiBuffer(&fParams, fParams.fReturnMidiChannels, fRxData); } try { // audio net buffers if (fParams.fSendAudioChannels > 0) { fNetAudioCaptureBuffer = AudioBufferFactory(fParams.fSendAudioChannels, fTxData); assert(fNetAudioCaptureBuffer); } if (fParams.fReturnAudioChannels > 0) { fNetAudioPlaybackBuffer = AudioBufferFactory(fParams.fReturnAudioChannels, fRxData); assert(fNetAudioPlaybackBuffer); } } catch (exception&) { jack_error("NetAudioBuffer on master allocation error..."); return false; } // set the new buffer size if (SetNetBufferSize() == SOCKET_ERROR) { jack_error("Can't set net buffer sizes : %s", StrError(NET_ERROR_CODE)); goto error; } return true; error: FreeNetworkBuffers(); return false; } void JackNetMasterInterface::Exit() { jack_log("JackNetMasterInterface::Exit, ID %u", fParams.fID); // stop process fRunning = false; // send a 'multicast euthanasia request' - new socket is required on macosx jack_info("Exiting '%s' %s", fParams.fName, fMulticastIP); SetPacketType(&fParams, KILL_MASTER); JackNetSocket mcast_socket(fMulticastIP, fSocket.GetPort()); session_params_t net_params; memset(&net_params, 0, sizeof(session_params_t)); SessionParamsHToN(&fParams, &net_params); if (mcast_socket.NewSocket() == SOCKET_ERROR) { jack_error("Can't create socket : %s", StrError(NET_ERROR_CODE)); } if (mcast_socket.SendTo(&net_params, sizeof(session_params_t), 0, fMulticastIP) == SOCKET_ERROR) { jack_error("Can't send suicide request : %s", StrError(NET_ERROR_CODE)); } mcast_socket.Close(); } void JackNetMasterInterface::FatalRecvError() { // fatal connection issue, exit jack_error("Recv connection lost error = %s, '%s' exiting", StrError(NET_ERROR_CODE), fParams.fName); // ask to the manager to properly remove the master Exit(); // UGLY temporary way to be sure the thread does not call code possibly causing a deadlock in JackEngine. ThreadExit(); } void JackNetMasterInterface::FatalSendError() { // fatal connection issue, exit jack_error("Send connection lost error = %s, '%s' exiting", StrError(NET_ERROR_CODE), fParams.fName); // ask to the manager to properly remove the master Exit(); // UGLY temporary way to be sure the thread does not call code possibly causing a deadlock in JackEngine. ThreadExit(); } int JackNetMasterInterface::Recv(size_t size, int flags) { int rx_bytes; if (((rx_bytes = fSocket.Recv(fRxBuffer, size, flags)) == SOCKET_ERROR) && fRunning) { FatalRecvError(); } packet_header_t* header = reinterpret_cast(fRxBuffer); PacketHeaderNToH(header, header); return rx_bytes; } int JackNetMasterInterface::Send(size_t size, int flags) { int tx_bytes; packet_header_t* header = reinterpret_cast(fTxBuffer); PacketHeaderHToN(header, header); if (((tx_bytes = fSocket.Send(fTxBuffer, size, flags)) == SOCKET_ERROR) && fRunning) { FatalSendError(); } return tx_bytes; } int JackNetMasterInterface::SyncSend() { SetRcvTimeOut(); fTxHeader.fCycle++; fTxHeader.fSubCycle = 0; fTxHeader.fDataType = 's'; fTxHeader.fIsLastPckt = (fParams.fSendMidiChannels == 0 && fParams.fSendAudioChannels == 0) ? 1 : 0; fTxHeader.fPacketSize = HEADER_SIZE + fTxHeader.fActivePorts * sizeof(int); // Data part is used to encode active ports memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); //PacketHeaderDisplay(&fTxHeader); return Send(fTxHeader.fPacketSize, 0); } int JackNetMasterInterface::DataSend() { if (MidiSend(fNetMidiCaptureBuffer, fParams.fSendMidiChannels, fParams.fSendAudioChannels) == SOCKET_ERROR) { return SOCKET_ERROR; } return AudioSend(fNetAudioCaptureBuffer, fParams.fSendAudioChannels); } int JackNetMasterInterface::SyncRecv() { int rx_bytes = 0; packet_header_t* rx_head = reinterpret_cast(fRxBuffer); // receive sync (launch the cycle) do { rx_bytes = Recv(fParams.fMtu, MSG_PEEK); // connection issue (return -1) if (rx_bytes == SOCKET_ERROR) { return SOCKET_ERROR; } } while (strcmp(rx_head->fPacketType, "header") != 0); //PacketHeaderDisplay(rx_head); if (rx_head->fDataType != 's') { jack_error("Wrong packet type : %c", rx_head->fDataType); // not the last packet.. fRxHeader.fIsLastPckt = 0; return SYNC_PACKET_ERROR; } fCurrentCycleOffset = fTxHeader.fCycle - rx_head->fCycle; if (fCurrentCycleOffset < fMaxCycleOffset && !fSynched) { jack_info("Syncing with latency = %d", fCurrentCycleOffset); return NET_SYNCHING; } else { if (fCurrentCycleOffset == fMaxCycleOffset) { // when the sync offset is reached fSynched = true; } rx_bytes = Recv(rx_head->fPacketSize, 0); fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; return rx_bytes; } } int JackNetMasterInterface::DataRecv() { int rx_bytes = 0; uint recvd_midi_pckt = 0; packet_header_t* rx_head = reinterpret_cast(fRxBuffer); while (!fRxHeader.fIsLastPckt) { // how much data is queued on the rx buffer ? rx_bytes = Recv(fParams.fMtu, MSG_PEEK); // error here, problem with recv, just skip the cycle (return -1) if (rx_bytes == SOCKET_ERROR) { return rx_bytes; } if (rx_bytes && (rx_head->fDataStream == 'r') && (rx_head->fID == fParams.fID)) { // read data switch (rx_head->fDataType) { case 'm': // midi rx_bytes = MidiRecv(rx_head, fNetMidiPlaybackBuffer, recvd_midi_pckt); break; case 'a': // audio rx_bytes = AudioRecv(rx_head, fNetAudioPlaybackBuffer); break; case 's': // sync jack_info("NetMaster : missing last data packet from '%s'", fParams.fName); return FinishRecv(fNetAudioPlaybackBuffer); } } } return rx_bytes; } void JackNetMasterInterface::EncodeSyncPacket(int frames) { // This method contains every step of sync packet information coding // first of all, clear sync packet memset(fTxData, 0, PACKET_AVAILABLE_SIZE(&fParams)); // Transport not used for now... /* // then, first step : transport if (fParams.fTransportSync) { EncodeTransportData(); TransportDataHToN(&fSendTransportData, &fSendTransportData); // copy to TxBuffer memcpy(fTxData, &fSendTransportData, sizeof(net_transport_data_t)); } // then others (freewheel etc.) // ... */ // Write active ports list fTxHeader.fActivePorts = (fNetAudioPlaybackBuffer) ? fNetAudioPlaybackBuffer->ActivePortsToNetwork(fTxData) : 0; fTxHeader.fFrames = frames; } void JackNetMasterInterface::DecodeSyncPacket(int& frames) { // This method contains every step of sync packet information decoding process // Transport not used for now... /* // first : transport if (fParams.fTransportSync) { // copy received transport data to transport data structure memcpy(&fReturnTransportData, fRxData, sizeof(net_transport_data_t)); TransportDataNToH(&fReturnTransportData, &fReturnTransportData); DecodeTransportData(); } // then others // ... */ packet_header_t* rx_head = reinterpret_cast(fRxBuffer); // Read active ports list if (fNetAudioCaptureBuffer) { fNetAudioCaptureBuffer->ActivePortsFromNetwork(fRxData, rx_head->fActivePorts); } frames = rx_head->fFrames; } // JackNetSlaveInterface ************************************************************************************************ uint JackNetSlaveInterface::fSlaveCounter = 0; void JackNetSlaveInterface::InitAPI() { // open Socket API with the first slave if (fSlaveCounter++ == 0) { if (SocketAPIInit() < 0) { jack_error("Can't init Socket API, exiting..."); throw std::bad_alloc(); } } } bool JackNetSlaveInterface::Init() { jack_log("JackNetSlaveInterface::Init()"); // set the parameters to send strcpy(fParams.fPacketType, "params"); fParams.fProtocolVersion = NETWORK_PROTOCOL; SetPacketType(&fParams, SLAVE_AVAILABLE); // init loop : get a master and start, do it until connection is ok net_status_t status; do { // first, get a master, do it until a valid connection is running do { status = SendAvailableToMaster(); if (status == NET_SOCKET_ERROR) { return false; } } while (status != NET_CONNECTED); // then tell the master we are ready jack_info("Initializing connection with %s...", fParams.fMasterNetName); status = SendStartToMaster(); if (status == NET_ERROR) { return false; } } while (status != NET_ROLLING); return true; } // Separate the connection protocol into two separated step bool JackNetSlaveInterface::InitConnection(int time_out_sec) { jack_log("JackNetSlaveInterface::InitConnection time_out_sec = %d", time_out_sec); int try_count = (time_out_sec > 0) ? int((1000000.f * float(time_out_sec)) / float(SLAVE_INIT_TIMEOUT)) : INT_MAX; // set the parameters to send strcpy(fParams.fPacketType, "params"); fParams.fProtocolVersion = NETWORK_PROTOCOL; SetPacketType(&fParams, SLAVE_AVAILABLE); return (SendAvailableToMaster(try_count) == NET_CONNECTED); } bool JackNetSlaveInterface::InitRendering() { jack_log("JackNetSlaveInterface::InitRendering()"); net_status_t status; do { // then tell the master we are ready jack_info("Initializing connection with %s...", fParams.fMasterNetName); status = SendStartToMaster(); if (status == NET_ERROR) { return false; } } while (status != NET_ROLLING); return true; } net_status_t JackNetSlaveInterface::SendAvailableToMaster(int try_count) { jack_log("JackNetSlaveInterface::SendAvailableToMaster try_count = %d", try_count); // utility session_params_t host_params; int rx_bytes = 0; // socket if (fSocket.NewSocket() == SOCKET_ERROR) { jack_error("Fatal error : network unreachable - %s", StrError(NET_ERROR_CODE)); return NET_SOCKET_ERROR; } if (fSocket.IsLocal(fMulticastIP)) { jack_info("Local IP is used..."); } else { // bind the socket if (fSocket.Bind() == SOCKET_ERROR) { jack_error("Can't bind the socket : %s", StrError(NET_ERROR_CODE)); return NET_SOCKET_ERROR; } } // timeout on receive (for init) if (fSocket.SetTimeOut(SLAVE_INIT_TIMEOUT) == SOCKET_ERROR) { jack_error("Can't set init timeout : %s", StrError(NET_ERROR_CODE)); } // disable local loop if (fSocket.SetLocalLoop() == SOCKET_ERROR) { jack_error("Can't disable multicast loop : %s", StrError(NET_ERROR_CODE)); } // send 'AVAILABLE' until 'SLAVE_SETUP' received jack_info("Waiting for a master..."); do { // send 'available' session_params_t net_params; memset(&net_params, 0, sizeof(session_params_t)); SessionParamsHToN(&fParams, &net_params); if (fSocket.SendTo(&net_params, sizeof(session_params_t), 0, fMulticastIP) == SOCKET_ERROR) { jack_error("Error in data send : %s", StrError(NET_ERROR_CODE)); } // filter incoming packets : don't exit while no error is detected memset(&net_params, 0, sizeof(session_params_t)); rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0); SessionParamsNToH(&net_params, &host_params); if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { jack_error("Can't receive : %s", StrError(NET_ERROR_CODE)); return NET_RECV_ERROR; } } while (strcmp(host_params.fPacketType, fParams.fPacketType) && (GetPacketType(&host_params) != SLAVE_SETUP) && (--try_count > 0)); // time out failure.. if (try_count == 0) { jack_error("Time out error in connect"); return NET_CONNECT_ERROR; } // everything is OK, copy parameters fParams = host_params; // connect the socket if (fSocket.Connect() == SOCKET_ERROR) { jack_error("Error in connect : %s", StrError(NET_ERROR_CODE)); return NET_CONNECT_ERROR; } return NET_CONNECTED; } net_status_t JackNetSlaveInterface::SendStartToMaster() { jack_log("JackNetSlaveInterface::SendStartToMaster"); // tell the master to start session_params_t net_params; memset(&net_params, 0, sizeof(session_params_t)); SetPacketType(&fParams, START_MASTER); SessionParamsHToN(&fParams, &net_params); if (fSocket.Send(&net_params, sizeof(session_params_t), 0) == SOCKET_ERROR) { jack_error("Error in send : %s", StrError(NET_ERROR_CODE)); return (fSocket.GetError() == NET_CONN_ERROR) ? NET_ERROR : NET_SEND_ERROR; } return NET_ROLLING; } bool JackNetSlaveInterface::SetParams() { jack_log("JackNetSlaveInterface::SetParams audio in = %d audio out = %d MIDI in = %d MIDI out = %d", fParams.fSendAudioChannels, fParams.fReturnAudioChannels, fParams.fSendMidiChannels, fParams.fReturnMidiChannels); JackNetInterface::SetParams(); fTxHeader.fDataStream = 'r'; fRxHeader.fDataStream = 's'; // midi net buffers if (fParams.fSendMidiChannels > 0) { fNetMidiCaptureBuffer = new NetMidiBuffer(&fParams, fParams.fSendMidiChannels, fRxData); } if (fParams.fReturnMidiChannels > 0) { fNetMidiPlaybackBuffer = new NetMidiBuffer(&fParams, fParams.fReturnMidiChannels, fTxData); } try { // audio net buffers if (fParams.fSendAudioChannels > 0) { fNetAudioCaptureBuffer = AudioBufferFactory(fParams.fSendAudioChannels, fRxData); assert(fNetAudioCaptureBuffer); } if (fParams.fReturnAudioChannels > 0) { fNetAudioPlaybackBuffer = AudioBufferFactory(fParams.fReturnAudioChannels, fTxData); assert(fNetAudioPlaybackBuffer); } } catch (exception&) { jack_error("NetAudioBuffer on slave allocation error..."); return false; } // set the new buffer sizes if (SetNetBufferSize() == SOCKET_ERROR) { jack_error("Can't set net buffer sizes : %s", StrError(NET_ERROR_CODE)); goto error; } return true; error: FreeNetworkBuffers(); return false; } void JackNetSlaveInterface::FatalRecvError() { throw JackNetException("Recv connection lost error"); } void JackNetSlaveInterface::FatalSendError() { throw JackNetException("Send connection lost error"); } int JackNetSlaveInterface::Recv(size_t size, int flags) { int rx_bytes = fSocket.Recv(fRxBuffer, size, flags); // handle errors if (rx_bytes == SOCKET_ERROR) { FatalRecvError(); } packet_header_t* header = reinterpret_cast(fRxBuffer); PacketHeaderNToH(header, header); return rx_bytes; } int JackNetSlaveInterface::Send(size_t size, int flags) { packet_header_t* header = reinterpret_cast(fTxBuffer); PacketHeaderHToN(header, header); int tx_bytes = fSocket.Send(fTxBuffer, size, flags); // handle errors if (tx_bytes == SOCKET_ERROR) { FatalSendError(); } return tx_bytes; } int JackNetSlaveInterface::SyncRecv() { SetRcvTimeOut(); int rx_bytes = 0; packet_header_t* rx_head = reinterpret_cast(fRxBuffer); // receive sync (launch the cycle) do { rx_bytes = Recv(fParams.fMtu, 0); // connection issue (return -1) if (rx_bytes == SOCKET_ERROR) { return rx_bytes; } } while (strcmp(rx_head->fPacketType, "header") != 0); if (rx_head->fDataType != 's') { jack_error("Wrong packet type : %c", rx_head->fDataType); // not the last packet... fRxHeader.fIsLastPckt = 0; return SYNC_PACKET_ERROR; } //PacketHeaderDisplay(rx_head); fRxHeader.fIsLastPckt = rx_head->fIsLastPckt; return rx_bytes; } int JackNetSlaveInterface::DataRecv() { int rx_bytes = 0; uint recvd_midi_pckt = 0; packet_header_t* rx_head = reinterpret_cast(fRxBuffer); while (!fRxHeader.fIsLastPckt) { // how much data is queued on the rx buffer ? rx_bytes = Recv(fParams.fMtu, MSG_PEEK); // error here, just skip the cycle (return -1) if (rx_bytes == SOCKET_ERROR) { return rx_bytes; } if (rx_bytes && (rx_head->fDataStream == 's') && (rx_head->fID == fParams.fID)) { // read data switch (rx_head->fDataType) { case 'm': // midi rx_bytes = MidiRecv(rx_head, fNetMidiCaptureBuffer, recvd_midi_pckt); break; case 'a': // audio rx_bytes = AudioRecv(rx_head, fNetAudioCaptureBuffer); break; case 's': // sync jack_info("NetSlave : missing last data packet"); return FinishRecv(fNetAudioCaptureBuffer); } } } fRxHeader.fCycle = rx_head->fCycle; return rx_bytes; } int JackNetSlaveInterface::SyncSend() { // tx header if (fParams.fSlaveSyncMode) { fTxHeader.fCycle = fRxHeader.fCycle; } else { fTxHeader.fCycle++; } fTxHeader.fSubCycle = 0; fTxHeader.fDataType = 's'; fTxHeader.fIsLastPckt = (fParams.fReturnMidiChannels == 0 && fParams.fReturnAudioChannels == 0) ? 1 : 0; fTxHeader.fPacketSize = HEADER_SIZE + fTxHeader.fActivePorts * sizeof(int); // Data part is used to encode active ports memcpy(fTxBuffer, &fTxHeader, HEADER_SIZE); //PacketHeaderDisplay(&fTxHeader); return Send(fTxHeader.fPacketSize, 0); } int JackNetSlaveInterface::DataSend() { if (MidiSend(fNetMidiPlaybackBuffer, fParams.fReturnMidiChannels, fParams.fReturnAudioChannels) == SOCKET_ERROR) { return SOCKET_ERROR; } return AudioSend(fNetAudioPlaybackBuffer, fParams.fReturnAudioChannels); } // network sync------------------------------------------------------------------------ void JackNetSlaveInterface::EncodeSyncPacket(int frames) { // This method contains every step of sync packet information coding // first of all, clear sync packet memset(fTxData, 0, PACKET_AVAILABLE_SIZE(&fParams)); // then first step : transport // Transport is not used for now... /* if (fParams.fTransportSync) { EncodeTransportData(); TransportDataHToN(&fReturnTransportData, &fReturnTransportData); // copy to TxBuffer memcpy(fTxData, &fReturnTransportData, sizeof(net_transport_data_t)); } // then others // ... */ // Write active ports list fTxHeader.fActivePorts = (fNetAudioCaptureBuffer) ? fNetAudioCaptureBuffer->ActivePortsToNetwork(fTxData) : 0; fTxHeader.fFrames = frames; } void JackNetSlaveInterface::DecodeSyncPacket(int& frames) { // This method contains every step of sync packet information decoding process // Transport not used for now... /* // first : transport if (fParams.fTransportSync) { // copy received transport data to transport data structure memcpy(&fSendTransportData, fRxData, sizeof(net_transport_data_t)); TransportDataNToH(&fSendTransportData, &fSendTransportData); DecodeTransportData(); } // then others // ... */ packet_header_t* rx_head = reinterpret_cast(fRxBuffer); // Read active ports list if (fNetAudioPlaybackBuffer) { fNetAudioPlaybackBuffer->ActivePortsFromNetwork(fRxData, rx_head->fActivePorts); } frames = rx_head->fFrames; } } jack2-1.9.22/common/JackNetInterface.h000066400000000000000000000160341436671425200174160ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackNetInterface__ #define __JackNetInterface__ #include "JackNetTool.h" #include namespace Jack { #define DEFAULT_MULTICAST_IP "225.3.19.154" #define DEFAULT_PORT 19000 #define DEFAULT_MTU 1500 #define MAX_MTU 9000 #define SLAVE_SETUP_RETRY 5 #define MANAGER_INIT_TIMEOUT 1000000 * 2 // in usec #define MASTER_INIT_TIMEOUT 1000000 * 10 // in usec #define SLAVE_INIT_TIMEOUT 1000000 * 1 // in usec #define PACKET_TIMEOUT 1000000 // in usec #define NETWORK_DEFAULT_LATENCY 2 #define NETWORK_MAX_LATENCY 30 // maximum possible latency in network master/slave loop /** \Brief This class describes the basic Net Interface, used by both master and slave. */ class SERVER_EXPORT JackNetInterface { friend class JackNetExt; protected: bool fSetTimeOut; int fPacketTimeOut; void Initialize(); session_params_t fParams; JackNetSocket fSocket; char fMulticastIP[32]; // headers packet_header_t fTxHeader; packet_header_t fRxHeader; // transport net_transport_data_t fSendTransportData; net_transport_data_t fReturnTransportData; // network buffers char* fTxBuffer; char* fRxBuffer; char* fTxData; char* fRxData; // JACK buffers NetMidiBuffer* fNetMidiCaptureBuffer; NetMidiBuffer* fNetMidiPlaybackBuffer; NetAudioBuffer* fNetAudioCaptureBuffer; NetAudioBuffer* fNetAudioPlaybackBuffer; // utility methods int SetNetBufferSize(); void FreeNetworkBuffers(); // virtual methods : depends on the sub class master/slave virtual bool SetParams(); virtual bool Init() = 0; // transport virtual void EncodeTransportData() = 0; virtual void DecodeTransportData() = 0; // sync packet virtual void EncodeSyncPacket(int frames = -1) = 0; virtual void DecodeSyncPacket(int& frames) = 0; virtual int SyncRecv() = 0; virtual int SyncSend() = 0; virtual int DataRecv() = 0; virtual int DataSend() = 0; virtual int Send(size_t size, int flags) = 0; virtual int Recv(size_t size, int flags) = 0; virtual void FatalRecvError() = 0; virtual void FatalSendError() = 0; int MidiSend(NetMidiBuffer* buffer, int midi_channnels, int audio_channels); int AudioSend(NetAudioBuffer* buffer, int audio_channels); int MidiRecv(packet_header_t* rx_head, NetMidiBuffer* buffer, uint& recvd_midi_pckt); int AudioRecv(packet_header_t* rx_head, NetAudioBuffer* buffer); int FinishRecv(NetAudioBuffer* buffer); void SetRcvTimeOut(); void SetPacketTimeOut(int time_out) { // New time out fPacketTimeOut = time_out; fSetTimeOut = false; } NetAudioBuffer* AudioBufferFactory(int nports, char* buffer); public: JackNetInterface(); JackNetInterface(const char* multicast_ip, int port); JackNetInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip); virtual ~JackNetInterface(); }; /** \Brief This class describes the Net Interface for masters (NetMaster) */ class SERVER_EXPORT JackNetMasterInterface : public JackNetInterface { protected: bool fRunning; int fCurrentCycleOffset; int fMaxCycleOffset; bool fSynched; bool Init(); bool SetParams(); void Exit(); int SyncRecv(); int SyncSend(); int DataRecv(); int DataSend(); // sync packet void EncodeSyncPacket(int frames = -1); void DecodeSyncPacket(int& frames); int Send(size_t size, int flags); int Recv(size_t size, int flags); void FatalRecvError(); void FatalSendError(); public: JackNetMasterInterface() : JackNetInterface(), fRunning(false), fCurrentCycleOffset(0), fMaxCycleOffset(0), fSynched(false) {} JackNetMasterInterface(session_params_t& params, JackNetSocket& socket, const char* multicast_ip) : JackNetInterface(params, socket, multicast_ip), fRunning(false), fCurrentCycleOffset(0), fMaxCycleOffset(0), fSynched(false) {} virtual~JackNetMasterInterface() {} }; /** \Brief This class describes the Net Interface for slaves (NetDriver and NetAdapter) */ class SERVER_EXPORT JackNetSlaveInterface : public JackNetInterface { protected: static uint fSlaveCounter; bool Init(); bool InitConnection(int time_out_sec); bool InitRendering(); net_status_t SendAvailableToMaster(int count = INT_MAX); net_status_t SendStartToMaster(); bool SetParams(); int SyncRecv(); int SyncSend(); int DataRecv(); int DataSend(); // sync packet void EncodeSyncPacket(int frames = -1); void DecodeSyncPacket(int& frames); int Recv(size_t size, int flags); int Send(size_t size, int flags); void FatalRecvError(); void FatalSendError(); void InitAPI(); public: JackNetSlaveInterface() : JackNetInterface() { InitAPI(); } JackNetSlaveInterface(const char* ip, int port) : JackNetInterface(ip, port) { InitAPI(); } virtual ~JackNetSlaveInterface() { // close Socket API with the last slave if (--fSlaveCounter == 0) { SocketAPIEnd(); } } }; } #endif jack2-1.9.22/common/JackNetManager.cpp000066400000000000000000001121501436671425200174170ustar00rootroot00000000000000/* Copyright(C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackNetManager.h" #include "JackArgParser.h" #include "JackServerGlobals.h" #include "JackLockedEngine.h" #include "thread.h" using namespace std; namespace Jack { //JackNetMaster****************************************************************************************************** JackNetMaster::JackNetMaster(JackNetSocket& socket, session_params_t& params, const char* multicast_ip) : JackNetMasterInterface(params, socket, multicast_ip) { jack_log("JackNetMaster::JackNetMaster"); //settings fName = const_cast(fParams.fName); fClient = NULL; fSendTransportData.fState = -1; fReturnTransportData.fState = -1; fLastTransportState = -1; int port_index; //jack audio ports fAudioCapturePorts = new jack_port_t* [fParams.fSendAudioChannels]; for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { fAudioCapturePorts[port_index] = NULL; } fAudioPlaybackPorts = new jack_port_t* [fParams.fReturnAudioChannels]; for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { fAudioPlaybackPorts[port_index] = NULL; } //jack midi ports fMidiCapturePorts = new jack_port_t* [fParams.fSendMidiChannels]; for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { fMidiCapturePorts[port_index] = NULL; } fMidiPlaybackPorts = new jack_port_t* [fParams.fReturnMidiChannels]; for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { fMidiPlaybackPorts[port_index] = NULL; } //monitor #ifdef JACK_MONITOR fPeriodUsecs = (int)(1000000.f * ((float) fParams.fPeriodSize / (float) fParams.fSampleRate)); string plot_name; plot_name = string(fParams.fName); plot_name += string("_master"); plot_name += string((fParams.fSlaveSyncMode) ? "_sync" : "_async"); plot_name += string("_latency"); fNetTimeMon = new JackGnuPlotMonitor(128, 4, plot_name); string net_time_mon_fields[] = { string("sync send"), string("end of send"), string("sync recv"), string("end of cycle") }; string net_time_mon_options[] = { string("set xlabel \"audio cycles\""), string("set ylabel \"% of audio cycle\"") }; fNetTimeMon->SetPlotFile(net_time_mon_options, 2, net_time_mon_fields, 4); #endif } JackNetMaster::~JackNetMaster() { jack_log("JackNetMaster::~JackNetMaster ID = %u", fParams.fID); if (fClient) { jack_deactivate(fClient); FreePorts(); jack_client_close(fClient); } delete[] fAudioCapturePorts; delete[] fAudioPlaybackPorts; delete[] fMidiCapturePorts; delete[] fMidiPlaybackPorts; #ifdef JACK_MONITOR fNetTimeMon->Save(); delete fNetTimeMon; #endif } //init-------------------------------------------------------------------------------- bool JackNetMaster::Init(bool auto_connect) { //network init if (!JackNetMasterInterface::Init()) { jack_error("JackNetMasterInterface::Init() error..."); return false; } //set global parameters if (!SetParams()) { jack_error("SetParams error..."); return false; } //jack client and process jack_status_t status; if ((fClient = jack_client_open(fName, JackNullOption, &status, NULL)) == NULL) { jack_error("Can't open a new JACK client"); return false; } if (jack_set_process_callback(fClient, SetProcess, this) < 0) { goto fail; } if (jack_set_buffer_size_callback(fClient, SetBufferSize, this) < 0) { goto fail; } if (jack_set_sample_rate_callback(fClient, SetSampleRate, this) < 0) { goto fail; } if (jack_set_latency_callback(fClient, LatencyCallback, this) < 0) { goto fail; } /* if (jack_set_port_connect_callback(fClient, SetConnectCallback, this) < 0) { goto fail; } */ if (AllocPorts() != 0) { jack_error("Can't allocate JACK ports"); goto fail; } //process can now run fRunning = true; //finally activate jack client if (jack_activate(fClient) != 0) { jack_error("Can't activate JACK client"); goto fail; } if (auto_connect) { ConnectPorts(); } jack_info("New NetMaster started"); return true; fail: FreePorts(); jack_client_close(fClient); fClient = NULL; return false; } //jack ports-------------------------------------------------------------------------- int JackNetMaster::AllocPorts() { int i; char name[32]; jack_log("JackNetMaster::AllocPorts"); //audio for (i = 0; i < fParams.fSendAudioChannels; i++) { snprintf(name, sizeof(name), "to_slave_%d", i+1); if ((fAudioCapturePorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0)) == NULL) { return -1; } } for (i = 0; i < fParams.fReturnAudioChannels; i++) { snprintf(name, sizeof(name), "from_slave_%d", i+1); if ((fAudioPlaybackPorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0)) == NULL) { return -1; } } //midi for (i = 0; i < fParams.fSendMidiChannels; i++) { snprintf(name, sizeof(name), "midi_to_slave_%d", i+1); if ((fMidiCapturePorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 0)) == NULL) { return -1; } } for (i = 0; i < fParams.fReturnMidiChannels; i++) { snprintf(name, sizeof(name), "midi_from_slave_%d", i+1); if ((fMidiPlaybackPorts[i] = jack_port_register(fClient, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal, 0)) == NULL) { return -1; } } return 0; } void JackNetMaster::ConnectPorts() { const char** ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput); if (ports != NULL) { for (int i = 0; i < fParams.fSendAudioChannels && ports[i]; i++) { jack_connect(fClient, ports[i], jack_port_name(fAudioCapturePorts[i])); } jack_free(ports); } ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput); if (ports != NULL) { for (int i = 0; i < fParams.fReturnAudioChannels && ports[i]; i++) { jack_connect(fClient, jack_port_name(fAudioPlaybackPorts[i]), ports[i]); } jack_free(ports); } } void JackNetMaster::FreePorts() { jack_log("JackNetMaster::FreePorts ID = %u", fParams.fID); int port_index; for (port_index = 0; port_index < fParams.fSendAudioChannels; port_index++) { if (fAudioCapturePorts[port_index]) { jack_port_unregister(fClient, fAudioCapturePorts[port_index]); } } for (port_index = 0; port_index < fParams.fReturnAudioChannels; port_index++) { if (fAudioPlaybackPorts[port_index]) { jack_port_unregister(fClient, fAudioPlaybackPorts[port_index]); } } for (port_index = 0; port_index < fParams.fSendMidiChannels; port_index++) { if (fMidiCapturePorts[port_index]) { jack_port_unregister(fClient, fMidiCapturePorts[port_index]); } } for (port_index = 0; port_index < fParams.fReturnMidiChannels; port_index++) { if (fMidiPlaybackPorts[port_index]) { jack_port_unregister(fClient, fMidiPlaybackPorts[port_index]); } } } //transport--------------------------------------------------------------------------- void JackNetMaster::EncodeTransportData() { //is there a new timebase master ? //TODO : check if any timebase callback has been called (and if it's conditional or not) and set correct value... fSendTransportData.fTimebaseMaster = NO_CHANGE; //update state and position fSendTransportData.fState = static_cast(jack_transport_query(fClient, &fSendTransportData.fPosition)); //is it a new state ? fSendTransportData.fNewState = ((fSendTransportData.fState != fLastTransportState) && (fSendTransportData.fState != fReturnTransportData.fState)); if (fSendTransportData.fNewState) { jack_info("Sending '%s' to '%s' frame = %ld", GetTransportState(fSendTransportData.fState), fParams.fName, fSendTransportData.fPosition.frame); } fLastTransportState = fSendTransportData.fState; } void JackNetMaster::DecodeTransportData() { //is there timebase master change ? if (fReturnTransportData.fTimebaseMaster != NO_CHANGE) { int timebase = 0; switch (fReturnTransportData.fTimebaseMaster) { case RELEASE_TIMEBASEMASTER : timebase = jack_release_timebase(fClient); if (timebase < 0) { jack_error("Can't release timebase master"); } else { jack_info("'%s' isn't the timebase master anymore", fParams.fName); } break; case TIMEBASEMASTER : timebase = jack_set_timebase_callback(fClient, 0, SetTimebaseCallback, this); if (timebase < 0) { jack_error("Can't set a new timebase master"); } else { jack_info("'%s' is the new timebase master", fParams.fName); } break; case CONDITIONAL_TIMEBASEMASTER : timebase = jack_set_timebase_callback(fClient, 1, SetTimebaseCallback, this); if (timebase != EBUSY) { if (timebase < 0) jack_error("Can't set a new timebase master"); else jack_info("'%s' is the new timebase master", fParams.fName); } break; } } //is the slave in a new transport state and is this state different from master's ? if (fReturnTransportData.fNewState && (fReturnTransportData.fState != jack_transport_query(fClient, NULL))) { switch (fReturnTransportData.fState) { case JackTransportStopped : jack_transport_stop(fClient); jack_info("'%s' stops transport", fParams.fName); break; case JackTransportStarting : if (jack_transport_reposition(fClient, &fReturnTransportData.fPosition) == EINVAL) jack_error("Can't set new position"); jack_transport_start(fClient); jack_info("'%s' starts transport frame = %d", fParams.fName, fReturnTransportData.fPosition.frame); break; case JackTransportNetStarting : jack_info("'%s' is ready to roll...", fParams.fName); break; case JackTransportRolling : jack_info("'%s' is rolling", fParams.fName); break; } } } void JackNetMaster::SetTimebaseCallback(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void* arg) { static_cast(arg)->TimebaseCallback(pos); } void JackNetMaster::TimebaseCallback(jack_position_t* pos) { pos->bar = fReturnTransportData.fPosition.bar; pos->beat = fReturnTransportData.fPosition.beat; pos->tick = fReturnTransportData.fPosition.tick; pos->bar_start_tick = fReturnTransportData.fPosition.bar_start_tick; pos->beats_per_bar = fReturnTransportData.fPosition.beats_per_bar; pos->beat_type = fReturnTransportData.fPosition.beat_type; pos->ticks_per_beat = fReturnTransportData.fPosition.ticks_per_beat; pos->beats_per_minute = fReturnTransportData.fPosition.beats_per_minute; } //sync-------------------------------------------------------------------------------- bool JackNetMaster::IsSlaveReadyToRoll() { return (fReturnTransportData.fState == JackTransportNetStarting); } int JackNetMaster::SetBufferSize(jack_nframes_t nframes, void* arg) { JackNetMaster* obj = static_cast(arg); if (nframes != obj->fParams.fPeriodSize) { jack_error("Cannot currently handle buffer size change, so JackNetMaster proxy will be removed..."); obj->Exit(); } return 0; } int JackNetMaster::SetSampleRate(jack_nframes_t nframes, void* arg) { JackNetMaster* obj = static_cast(arg); if (nframes != obj->fParams.fSampleRate) { jack_error("Cannot currently handle sample rate change, so JackNetMaster proxy will be removed..."); obj->Exit(); } return 0; } void JackNetMaster::LatencyCallback(jack_latency_callback_mode_t mode, void* arg) { JackNetMaster* obj = static_cast(arg); jack_nframes_t port_latency = jack_get_buffer_size(obj->fClient); jack_latency_range_t range; //audio for (int i = 0; i < obj->fParams.fSendAudioChannels; i++) { //port latency range.min = range.max = float(obj->fParams.fNetworkLatency * port_latency) / 2.f; jack_port_set_latency_range(obj->fAudioCapturePorts[i], JackPlaybackLatency, &range); } //audio for (int i = 0; i < obj->fParams.fReturnAudioChannels; i++) { //port latency range.min = range.max = float(obj->fParams.fNetworkLatency * port_latency) / 2.f + ((obj->fParams.fSlaveSyncMode) ? 0 : port_latency); jack_port_set_latency_range(obj->fAudioPlaybackPorts[i], JackCaptureLatency, &range); } //midi for (int i = 0; i < obj->fParams.fSendMidiChannels; i++) { //port latency range.min = range.max = float(obj->fParams.fNetworkLatency * port_latency) / 2.f; jack_port_set_latency_range(obj->fMidiCapturePorts[i], JackPlaybackLatency, &range); } //midi for (int i = 0; i < obj->fParams.fReturnMidiChannels; i++) { //port latency range.min = range.max = obj->fParams.fNetworkLatency * port_latency + ((obj->fParams.fSlaveSyncMode) ? 0 : port_latency); jack_port_set_latency_range(obj->fMidiPlaybackPorts[i], JackCaptureLatency, &range); } } //process----------------------------------------------------------------------------- int JackNetMaster::SetProcess(jack_nframes_t nframes, void* arg) { try { return static_cast(arg)->Process(); } catch (JackNetException& e) { return 0; } } void JackNetMaster::SetConnectCallback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg) { static_cast(arg)->ConnectCallback(a, b, connect); } void JackNetMaster::ConnectCallback(jack_port_id_t a, jack_port_id_t b, int connect) { jack_info("JackNetMaster::ConnectCallback a = %d b = %d connect = %d", a, b, connect); if (connect) { jack_connect(fClient, jack_port_name(jack_port_by_id(fClient, a)), "system:playback_1"); } } int JackNetMaster::Process() { if (!fRunning) { return 0; } #ifdef JACK_MONITOR jack_time_t begin_time = GetMicroSeconds(); fNetTimeMon->New(); #endif //buffers for (int midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++) { fNetMidiCaptureBuffer->SetBuffer(midi_port_index, static_cast(jack_port_get_buffer(fMidiCapturePorts[midi_port_index], fParams.fPeriodSize))); } for (int audio_port_index = 0; audio_port_index < fParams.fSendAudioChannels; audio_port_index++) { #ifdef OPTIMIZED_PROTOCOL if (fNetAudioCaptureBuffer->GetConnected(audio_port_index)) { // Port is connected on other side... fNetAudioCaptureBuffer->SetBuffer(audio_port_index, ((jack_port_connected(fAudioCapturePorts[audio_port_index]) > 0) ? static_cast(jack_port_get_buffer(fAudioCapturePorts[audio_port_index], fParams.fPeriodSize)) : NULL)); } else { fNetAudioCaptureBuffer->SetBuffer(audio_port_index, NULL); } #else fNetAudioCaptureBuffer->SetBuffer(audio_port_index, static_cast(jack_port_get_buffer(fAudioCapturePorts[audio_port_index], fParams.fPeriodSize))); #endif // TODO } for (int midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++) { fNetMidiPlaybackBuffer->SetBuffer(midi_port_index, static_cast(jack_port_get_buffer(fMidiPlaybackPorts[midi_port_index], fParams.fPeriodSize))); } for (int audio_port_index = 0; audio_port_index < fParams.fReturnAudioChannels; audio_port_index++) { #ifdef OPTIMIZED_PROTOCOL sample_t* out = (jack_port_connected(fAudioPlaybackPorts[audio_port_index]) > 0) ? static_cast(jack_port_get_buffer(fAudioPlaybackPorts[audio_port_index], fParams.fPeriodSize)) : NULL; if (out) { memset(out, 0, sizeof(float) * fParams.fPeriodSize); } fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out); #else sample_t* out = static_cast(jack_port_get_buffer(fAudioPlaybackPorts[audio_port_index], fParams.fPeriodSize)); if (out) { memset(out, 0, sizeof(float) * fParams.fPeriodSize); } fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out))); #endif } // encode the first packet EncodeSyncPacket(); if (SyncSend() == SOCKET_ERROR) { return SOCKET_ERROR; } #ifdef JACK_MONITOR fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif // send data if (DataSend() == SOCKET_ERROR) { return SOCKET_ERROR; } #ifdef JACK_MONITOR fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif // receive sync int res = SyncRecv(); switch (res) { case NET_SYNCHING: case SOCKET_ERROR: return res; case SYNC_PACKET_ERROR: // Since sync packet is incorrect, don't decode it and continue with data break; default: // Decode sync int unused_frames; DecodeSyncPacket(unused_frames); break; } #ifdef JACK_MONITOR fNetTimeMon->Add((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif // receive data res = DataRecv(); switch (res) { case 0: case SOCKET_ERROR: return res; case DATA_PACKET_ERROR: // Well not a real XRun... JackServerGlobals::fInstance->GetEngine()->NotifyClientXRun(ALL_CLIENTS); break; } #ifdef JACK_MONITOR fNetTimeMon->AddLast((((float)(GetMicroSeconds() - begin_time)) / (float) fPeriodUsecs) * 100.f); #endif return 0; } void JackNetMaster::SaveConnections(connections_list_t& connections) { // Audio for (int i = 0; i < fParams.fSendAudioChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fAudioCapturePorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(connected_port[port], jack_port_name(fAudioCapturePorts[i]))); jack_log("INPUT %s ==> %s", connected_port[port], jack_port_name(fAudioCapturePorts[i])); } jack_free(connected_port); } } for (int i = 0; i < fParams.fReturnAudioChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fAudioPlaybackPorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(jack_port_name(fAudioPlaybackPorts[i]), connected_port[port])); jack_log("OUTPUT %s ==> %s", jack_port_name(fAudioPlaybackPorts[i]), connected_port[port]); } jack_free(connected_port); } } // MIDI for (int i = 0; i < fParams.fSendMidiChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fMidiCapturePorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(connected_port[port], jack_port_name(fMidiCapturePorts[i]))); jack_log("INPUT %s ==> %s", connected_port[port], jack_port_name(fMidiCapturePorts[i])); } jack_free(connected_port); } } for (int i = 0; i < fParams.fReturnMidiChannels; i++) { const char** connected_port = jack_port_get_all_connections(fClient, fMidiPlaybackPorts[i]); if (connected_port != NULL) { for (int port = 0; connected_port[port]; port++) { connections.push_back(make_pair(jack_port_name(fMidiPlaybackPorts[i]), connected_port[port])); jack_log("OUTPUT %s ==> %s", jack_port_name(fMidiPlaybackPorts[i]), connected_port[port]); } jack_free(connected_port); } } } void JackNetMaster::LoadConnections(const connections_list_t& connections) { list >::const_iterator it; for (it = connections.begin(); it != connections.end(); it++) { pair connection = *it; jack_connect(fClient, connection.first.c_str(), connection.second.c_str()); } } //JackNetMasterManager*********************************************************************************************** JackNetMasterManager::JackNetMasterManager(jack_client_t* client, const JSList* params) : fSocket() { jack_log("JackNetMasterManager::JackNetMasterManager"); fClient = client; fName = jack_get_client_name(fClient); fGlobalID = 0; fRunning = true; fAutoConnect = false; fAutoSave = false; const JSList* node; const jack_driver_param_t* param; jack_on_shutdown(fClient, SetShutDown, this); // Possibly use env variable const char* default_udp_port = getenv("JACK_NETJACK_PORT"); fSocket.SetPort((default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT); const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); if (default_multicast_ip) { strcpy(fMulticastIP, default_multicast_ip); } else { strcpy(fMulticastIP, DEFAULT_MULTICAST_IP); } for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*) node->data; switch (param->character) { case 'a' : if (strlen(param->value.str) < 32) { strcpy(fMulticastIP, param->value.str); } else { jack_error("Can't use multicast address %s, using default %s", param->value.ui, DEFAULT_MULTICAST_IP); } break; case 'p': fSocket.SetPort(param->value.ui); break; case 'c': fAutoConnect = true; break; case 's': fAutoSave = true; break; } } //set sync callback jack_set_sync_callback(fClient, SetSyncCallback, this); //activate the client (for sync callback) if (jack_activate(fClient) != 0) { jack_error("Can't activate the NetManager client, transport disabled"); } //launch the manager thread if (jack_client_create_thread(fClient, &fThread, 0, 0, NetManagerThread, this)) { jack_error("Can't create the NetManager control thread"); } } JackNetMasterManager::~JackNetMasterManager() { jack_log("JackNetMasterManager::~JackNetMasterManager"); ShutDown(); } int JackNetMasterManager::CountIO(const char* type, int flags) { int count = 0; const char** ports = jack_get_ports(fClient, NULL, type, flags); if (ports != NULL) { while (ports[count]) { count++; } jack_free(ports); } return count; } void JackNetMasterManager::SetShutDown(void* arg) { static_cast(arg)->ShutDown(); } void JackNetMasterManager::ShutDown() { jack_log("JackNetMasterManager::ShutDown"); if (fRunning) { jack_client_kill_thread(fClient, fThread); fRunning = false; } master_list_t::iterator it; for (it = fMasterList.begin(); it != fMasterList.end(); it++) { delete (*it); } fMasterList.clear(); fSocket.Close(); SocketAPIEnd(); } int JackNetMasterManager::SetSyncCallback(jack_transport_state_t state, jack_position_t* pos, void* arg) { return static_cast(arg)->SyncCallback(state, pos); } int JackNetMasterManager::SyncCallback(jack_transport_state_t state, jack_position_t* pos) { //check if each slave is ready to roll int res = 1; master_list_it_t it; for (it = fMasterList.begin(); it != fMasterList.end(); it++) { if (!(*it)->IsSlaveReadyToRoll()) { res = 0; } } jack_log("JackNetMasterManager::SyncCallback returns '%s'", (res) ? "true" : "false"); return res; } void* JackNetMasterManager::NetManagerThread(void* arg) { JackNetMasterManager* master_manager = static_cast(arg); jack_info("Starting Jack NetManager"); jack_info("Listening on '%s:%d'", master_manager->fMulticastIP, master_manager->fSocket.GetPort()); master_manager->Run(); return NULL; } void JackNetMasterManager::Run() { jack_log("JackNetMasterManager::Run"); //utility variables int attempt = 0; //data session_params_t host_params; int rx_bytes = 0; JackNetMaster* net_master; //init socket API (win32) if (SocketAPIInit() < 0) { jack_error("Can't init Socket API, exiting..."); return; } //socket if (fSocket.NewSocket() == SOCKET_ERROR) { jack_error("Can't create NetManager input socket : %s", StrError(NET_ERROR_CODE)); return; } //bind the socket to the local port if (fSocket.Bind() == SOCKET_ERROR) { jack_error("Can't bind NetManager socket : %s", StrError(NET_ERROR_CODE)); fSocket.Close(); return; } //join multicast group if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) { jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE)); } //local loop if (fSocket.SetLocalLoop() == SOCKET_ERROR) { jack_error("Can't set local loop : %s", StrError(NET_ERROR_CODE)); } //set a timeout on the multicast receive (the thread can now be cancelled) if (fSocket.SetTimeOut(MANAGER_INIT_TIMEOUT) == SOCKET_ERROR) { jack_error("Can't set timeout : %s", StrError(NET_ERROR_CODE)); } //main loop, wait for data, deal with it and wait again do { session_params_t net_params; rx_bytes = fSocket.CatchHost(&net_params, sizeof(session_params_t), 0); SessionParamsNToH(&net_params, &host_params); if ((rx_bytes == SOCKET_ERROR) && (fSocket.GetError() != NET_NO_DATA)) { jack_error("Error in receive : %s", StrError(NET_ERROR_CODE)); if (++attempt == 10) { jack_error("Can't receive on the socket, exiting net manager"); return; } } if (rx_bytes == sizeof(session_params_t)) { switch (GetPacketType(&host_params)) { case SLAVE_AVAILABLE: if ((net_master = InitMaster(host_params))) { SessionParamsDisplay(&net_master->fParams); } else { jack_error("Can't init new NetMaster..."); } jack_info("Waiting for a slave..."); break; case KILL_MASTER: if (KillMaster(&host_params)) { jack_info("Waiting for a slave..."); } break; default: break; } } } while (fRunning); } JackNetMaster* JackNetMasterManager::InitMaster(session_params_t& params) { jack_log("JackNetMasterManager::InitMaster slave : %s", params.fName); //check MASTER <<==> SLAVE network protocol coherency if (params.fProtocolVersion != NETWORK_PROTOCOL) { jack_error("Error : slave '%s' is running with a different protocol %d != %d", params.fName, params.fProtocolVersion, NETWORK_PROTOCOL); return NULL; } //settings fSocket.GetName(params.fMasterNetName); params.fID = ++fGlobalID; params.fSampleRate = jack_get_sample_rate(fClient); params.fPeriodSize = jack_get_buffer_size(fClient); if (params.fSendAudioChannels == -1) { params.fSendAudioChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput); jack_info("Takes physical %d audio input(s) for slave", params.fSendAudioChannels); } if (params.fReturnAudioChannels == -1) { params.fReturnAudioChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput); jack_info("Takes physical %d audio output(s) for slave", params.fReturnAudioChannels); } if (params.fSendMidiChannels == -1) { params.fSendMidiChannels = CountIO(JACK_DEFAULT_MIDI_TYPE, JackPortIsPhysical | JackPortIsOutput); jack_info("Takes physical %d MIDI input(s) for slave", params.fSendMidiChannels); } if (params.fReturnMidiChannels == -1) { params.fReturnMidiChannels = CountIO(JACK_DEFAULT_MIDI_TYPE, JackPortIsPhysical | JackPortIsInput); jack_info("Takes physical %d MIDI output(s) for slave", params.fReturnMidiChannels); } //create a new master and add it to the list JackNetMaster* master = new JackNetMaster(fSocket, params, fMulticastIP); if (master->Init(fAutoConnect)) { fMasterList.push_back(master); if (fAutoSave && fMasterConnectionList.find(params.fName) != fMasterConnectionList.end()) { master->LoadConnections(fMasterConnectionList[params.fName]); } return master; } else { delete master; return NULL; } } master_list_it_t JackNetMasterManager::FindMaster(uint32_t id) { jack_log("JackNetMasterManager::FindMaster ID = %u", id); master_list_it_t it; for (it = fMasterList.begin(); it != fMasterList.end(); it++) { if ((*it)->fParams.fID == id) { return it; } } return it; } int JackNetMasterManager::KillMaster(session_params_t* params) { jack_log("JackNetMasterManager::KillMaster ID = %u", params->fID); master_list_it_t master_it = FindMaster(params->fID); if (master_it != fMasterList.end()) { if (fAutoSave) { fMasterConnectionList[params->fName].clear(); (*master_it)->SaveConnections(fMasterConnectionList[params->fName]); } fMasterList.erase(master_it); delete (*master_it); return 1; } return 0; } }//namespace static Jack::JackNetMasterManager* master_manager = NULL; #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("netmanager", JackDriverNone, "netjack multi-cast master component", &filler); strcpy(value.str, DEFAULT_MULTICAST_IP); jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address", NULL); value.i = DEFAULT_PORT; jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect netmaster to system ports", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-save", 's', JackDriverParamBool, &value, NULL, "Save/restore netmaster connection state when restarted", NULL); return desc; } SERVER_EXPORT int jack_internal_initialize(jack_client_t* jack_client, const JSList* params) { if (master_manager) { jack_error("Master Manager already loaded"); return 1; } else { jack_log("Loading Master Manager"); master_manager = new Jack::JackNetMasterManager(jack_client, params); return (master_manager) ? 0 : 1; } } SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); Jack::JackArgParser parser(load_init); if (parser.GetArgc() > 0) { parse_params = parser.ParseParams(desc, ¶ms); } if (parse_params) { res = jack_internal_initialize(jack_client, params); parser.FreeParams(params); } return res; } SERVER_EXPORT void jack_finish(void* arg) { if (master_manager) { jack_log("Unloading Master Manager"); delete master_manager; master_manager = NULL; } } #ifdef __cplusplus } #endif jack2-1.9.22/common/JackNetManager.h000066400000000000000000000105731436671425200170720ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JACKNETMANAGER_H__ #define __JACKNETMANAGER_H__ #include "JackNetInterface.h" #include "jack.h" #include #include namespace Jack { class JackNetMasterManager; /** \Brief This class describes a Net Master */ typedef std::list > connections_list_t; class JackNetMaster : public JackNetMasterInterface { friend class JackNetMasterManager; private: static int SetProcess(jack_nframes_t nframes, void* arg); static int SetBufferSize(jack_nframes_t nframes, void* arg); static int SetSampleRate(jack_nframes_t nframes, void* arg); static void SetTimebaseCallback(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void* arg); static void SetConnectCallback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg); static void LatencyCallback(jack_latency_callback_mode_t mode, void* arg); //jack client jack_client_t* fClient; const char* fName; //jack ports jack_port_t** fAudioCapturePorts; jack_port_t** fAudioPlaybackPorts; jack_port_t** fMidiCapturePorts; jack_port_t** fMidiPlaybackPorts; //sync and transport int fLastTransportState; //monitoring #ifdef JACK_MONITOR jack_time_t fPeriodUsecs; JackGnuPlotMonitor* fNetTimeMon; #endif bool Init(bool auto_connect); int AllocPorts(); void FreePorts(); //transport void EncodeTransportData(); void DecodeTransportData(); int Process(); void TimebaseCallback(jack_position_t* pos); void ConnectPorts(); void ConnectCallback(jack_port_id_t a, jack_port_id_t b, int connect); void SaveConnections(connections_list_t& connections); void LoadConnections(const connections_list_t& connections); public: JackNetMaster(JackNetSocket& socket, session_params_t& params, const char* multicast_ip); ~JackNetMaster(); bool IsSlaveReadyToRoll(); }; typedef std::list master_list_t; typedef master_list_t::iterator master_list_it_t; typedef std::map master_connections_list_t; /** \Brief This class describer the Network Manager */ class JackNetMasterManager { friend class JackNetMaster; private: static void SetShutDown(void* arg); static int SetSyncCallback(jack_transport_state_t state, jack_position_t* pos, void* arg); static void* NetManagerThread(void* arg); jack_client_t* fClient; const char* fName; char fMulticastIP[32]; JackNetSocket fSocket; jack_native_thread_t fThread; master_list_t fMasterList; master_connections_list_t fMasterConnectionList; uint32_t fGlobalID; bool fRunning; bool fAutoConnect; bool fAutoSave; void Run(); JackNetMaster* InitMaster(session_params_t& params); master_list_it_t FindMaster(uint32_t client_id); int KillMaster(session_params_t* params); int SyncCallback(jack_transport_state_t state, jack_position_t* pos); int CountIO(const char* type, int flags); void ShutDown(); public: JackNetMasterManager(jack_client_t* jack_client, const JSList* params); ~JackNetMasterManager(); }; } #endif jack2-1.9.22/common/JackNetOneDriver.cpp000066400000000000000000001257451436671425200177600ustar00rootroot00000000000000/* Copyright (C) 2018 Karl Linden Copyright (C) 2008-2011 Torben Horn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include "JackNetOneDriver.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" #include "JackGraphManager.h" #include "JackWaitThreadedDriver.h" #include "JackTools.h" #include "driver_interface.h" #include "netjack.h" #include "netjack_packet.h" #if HAVE_SAMPLERATE #include #endif #if HAVE_CELT #include #endif #if HAVE_OPUS #include #include #endif #define MIN(x,y) ((x)<(y) ? (x) : (y)) using namespace std; namespace Jack { JackNetOneDriver::JackNetOneDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports, int sample_rate, int period_size, int resample_factor, const char* net_name, uint transport_sync, int bitdepth, int use_autoconfig, int latency, int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val) : JackWaiterDriver(name, alias, engine, table) { jack_log("JackNetOneDriver::JackNetOneDriver port %d", port); #ifdef WIN32 WSADATA wsa; WSAStartup(MAKEWORD(2, 0), &wsa); #endif netjack_init(& (this->netj), NULL, // client name, capture_ports, playback_ports, midi_input_ports, midi_output_ports, sample_rate, period_size, port, transport_sync, resample_factor, 0, bitdepth, use_autoconfig, latency, redundancy, dont_htonl_floats, always_deadline, jitter_val); } JackNetOneDriver::~JackNetOneDriver() { // No destructor yet. } //open, close, attach and detach------------------------------------------------------ int JackNetOneDriver::Close() { // Generic audio driver close int res = JackWaiterDriver::Close(); FreePorts(); netjack_release(&netj); return res; } int JackNetOneDriver::Attach() { return 0; } int JackNetOneDriver::Detach() { return 0; } int JackNetOneDriver::AllocPorts() { jack_port_id_t port_index; char buf[64]; unsigned int chn; //if (netj.handle_transport_sync) // jack_set_sync_callback(netj.client, (JackSyncCallback) net_driver_sync_cb, NULL); for (chn = 0; chn < netj.capture_channels_audio; chn++) { snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", buf); return -1; } //port = fGraphManager->GetPort(port_index); netj.capture_ports = jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_index); if (netj.bitdepth == CELT_MODE) { #if HAVE_CELT #if HAVE_CELT_API_0_11 celt_int32 lookahead; CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create_custom(celt_mode, 1, NULL)); #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 celt_int32 lookahead; CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create(celt_mode, 1, NULL)); #else celt_int32_t lookahead; CELTMode *celt_mode = celt_mode_create(netj.sample_rate, 1, netj.period_size, NULL); netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create(celt_mode)); #endif celt_mode_info(celt_mode, CELT_GET_LOOKAHEAD, &lookahead); netj.codec_latency = 2 * lookahead; #endif } else if (netj.bitdepth == OPUS_MODE) { #if HAVE_OPUS OpusCustomMode *opus_mode = opus_custom_mode_create(netj.sample_rate, netj.period_size, NULL); // XXX free me in the end OpusCustomDecoder *decoder = opus_custom_decoder_create( opus_mode, 1, NULL ); netj.capture_srcs = jack_slist_append(netj.capture_srcs, decoder); #endif } else { #if HAVE_SAMPLERATE netj.capture_srcs = jack_slist_append(netj.capture_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); #endif } } for (chn = netj.capture_channels_audio; chn < netj.capture_channels; chn++) { snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", buf); return -1; } //port = fGraphManager->GetPort(port_index); netj.capture_ports = jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_index); } for (chn = 0; chn < netj.playback_channels_audio; chn++) { snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", buf); return -1; } //port = fGraphManager->GetPort(port_index); netj.playback_ports = jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_index); if (netj.bitdepth == CELT_MODE) { #if HAVE_CELT #if HAVE_CELT_API_0_11 CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create_custom(celt_mode, 1, NULL)); #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 CELTMode *celt_mode = celt_mode_create(netj.sample_rate, netj.period_size, NULL); netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create(celt_mode, 1, NULL)); #else CELTMode *celt_mode = celt_mode_create(netj.sample_rate, 1, netj.period_size, NULL); netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create(celt_mode)); #endif #endif } else if (netj.bitdepth == OPUS_MODE) { #if HAVE_OPUS const int kbps = netj.resample_factor; jack_error("NEW ONE OPUS ENCODER 128 <> %d!!", kbps); int err; OpusCustomMode *opus_mode = opus_custom_mode_create( netj.sample_rate, netj.period_size, &err ); // XXX free me in the end if (err != OPUS_OK) { jack_error("opus mode failed"); } OpusCustomEncoder *oe = opus_custom_encoder_create( opus_mode, 1, &err ); if (err != OPUS_OK) { jack_error("opus mode failed"); } opus_custom_encoder_ctl(oe, OPUS_SET_BITRATE(kbps*1024)); // bits per second opus_custom_encoder_ctl(oe, OPUS_SET_COMPLEXITY(10)); opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY)); netj.playback_srcs = jack_slist_append(netj.playback_srcs, oe); #endif } else { #if HAVE_SAMPLERATE netj.playback_srcs = jack_slist_append(netj.playback_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); #endif } } for (chn = netj.playback_channels_audio; chn < netj.playback_channels; chn++) { snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); if (fEngine->PortRegister(fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { jack_error("driver: cannot register port for %s", buf); return -1; } //port = fGraphManager->GetPort(port_index); netj.playback_ports = jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_index); } return 0; } //init and restart-------------------------------------------------------------------- bool JackNetOneDriver::Initialize() { jack_log("JackNetOneDriver::Init"); FreePorts(); netjack_release(&netj); //display some additional infos jack_info("NetOne driver started"); if (netjack_startup(&netj)) { return false; } //register jack ports if (AllocPorts() != 0) { jack_error("Can't allocate ports."); return false; } //monitor //driver parametering JackTimedDriver::SetBufferSize(netj.period_size); JackTimedDriver::SetSampleRate(netj.sample_rate); JackDriver::NotifyBufferSize(netj.period_size); JackDriver::NotifySampleRate(netj.sample_rate); //transport engine parametering fEngineControl->fTransport.SetNetworkSync(true); return true; } //jack ports and buffers-------------------------------------------------------------- //driver processes-------------------------------------------------------------------- int JackNetOneDriver::Read() { int delay; delay = netjack_wait(&netj); if (delay) { NotifyXRun(fBeginDateUst, (float) delay); jack_error("netxruns... duration: %dms", delay / 1000); } if ((netj.num_lost_packets * netj.period_size / netj.sample_rate) > 2) throw JackNetException(); //netjack_read(&netj, netj.period_size); JackDriver::CycleTakeBeginTime(); jack_position_t local_trans_pos; jack_transport_state_t local_trans_state; unsigned int *packet_buf, *packet_bufX; if (! netj.packet_data_valid) { jack_log("data not valid"); render_payload_to_jack_ports (netj.bitdepth, NULL, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats); return 0; } packet_buf = netj.rx_buf; jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); netj.reply_port = pkthdr->reply_port; netj.latency = pkthdr->latency; // Special handling for latency=0 if (netj.latency == 0) netj.resync_threshold = 0; else netj.resync_threshold = MIN(15, pkthdr->latency - 1); // check whether, we should handle the transport sync stuff, or leave trnasports untouched. if (netj.handle_transport_sync) { #if 1 unsigned int compensated_tranport_pos = (pkthdr->transport_frame + (pkthdr->latency * netj.period_size) + netj.codec_latency); // read local transport info.... //local_trans_state = jack_transport_query(netj.client, &local_trans_pos); local_trans_state = fEngineControl->fTransport.Query(&local_trans_pos); // Now check if we have to start or stop local transport to sync to remote... switch (pkthdr->transport_state) { case JackTransportStarting: // the master transport is starting... so we set our reply to the sync_callback; if (local_trans_state == JackTransportStopped) { fEngineControl->fTransport.SetCommand(TransportCommandStart); //jack_transport_start(netj.client); //last_transport_state = JackTransportStopped; netj.sync_state = 0; jack_info("locally stopped... starting..."); } if (local_trans_pos.frame != compensated_tranport_pos) { jack_position_t new_pos = local_trans_pos; new_pos.frame = compensated_tranport_pos + 2 * netj.period_size; new_pos.valid = (jack_position_bits_t) 0; fEngineControl->fTransport.RequestNewPos(&new_pos); //jack_transport_locate(netj.client, compensated_tranport_pos); //last_transport_state = JackTransportRolling; netj.sync_state = 0; jack_info("starting locate to %d", compensated_tranport_pos); } break; case JackTransportStopped: netj.sync_state = 1; if (local_trans_pos.frame != (pkthdr->transport_frame)) { jack_position_t new_pos = local_trans_pos; new_pos.frame = pkthdr->transport_frame; new_pos.valid = (jack_position_bits_t)0; fEngineControl->fTransport.RequestNewPos(&new_pos); //jack_transport_locate(netj.client, (pkthdr->transport_frame)); jack_info("transport is stopped locate to %d", pkthdr->transport_frame); } if (local_trans_state != JackTransportStopped) //jack_transport_stop(netj.client); fEngineControl->fTransport.SetCommand(TransportCommandStop); break; case JackTransportRolling: netj.sync_state = 1; // if(local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * netj.period_size)) { // jack_transport_locate(netj.client, (pkthdr->transport_frame + (pkthdr->latency + 2) * netj.period_size)); // jack_info("running locate to %d", pkthdr->transport_frame + (pkthdr->latency)*netj.period_size); // } if (local_trans_state != JackTransportRolling) fEngineControl->fTransport.SetState(JackTransportRolling); break; case JackTransportLooping: break; } #endif } render_payload_to_jack_ports (netj.bitdepth, packet_bufX, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats); packet_cache_release_packet(netj.packcache, netj.expected_framecnt); return 0; } int JackNetOneDriver::Write() { int syncstate = netj.sync_state | ((fEngineControl->fTransport.GetState() == JackTransportNetStarting) ? 1 : 0); uint32_t *packet_buf, *packet_bufX; int packet_size = get_sample_size(netj.bitdepth) * netj.playback_channels * netj.net_period_up + sizeof(jacknet_packet_header); jacknet_packet_header *pkthdr; packet_buf = (uint32_t *) alloca(packet_size); pkthdr = (jacknet_packet_header *)packet_buf; if (netj.running_free) { return 0; } // offset packet_bufX by the packetheader. packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); pkthdr->sync_state = syncstate;; pkthdr->latency = netj.time_to_deadline; //printf("time to deadline = %d goodness=%d\n", (int)netj.time_to_deadline, netj.deadline_goodness); pkthdr->framecnt = netj.expected_framecnt; render_jack_ports_to_payload(netj.bitdepth, netj.playback_ports, netj.playback_srcs, netj.period_size, packet_bufX, netj.net_period_up, netj.dont_htonl_floats); packet_header_hton(pkthdr); if (netj.srcaddress_valid) { unsigned int r; static const int flag = 0; if (netj.reply_port) netj.syncsource_address.sin_port = htons(netj.reply_port); for (r = 0; r < netj.redundancy; r++) netjack_sendto(netj.sockfd, (char *)packet_buf, packet_size, flag, (struct sockaddr*) & (netj.syncsource_address), sizeof(struct sockaddr_in), netj.mtu); } return 0; } void JackNetOneDriver::FreePorts () { JSList *node = netj.capture_ports; while (node != NULL) { JSList *this_node = node; jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); fEngine->PortUnRegister(fClientControl.fRefNum, port_index); } netj.capture_ports = NULL; node = netj.playback_ports; while (node != NULL) { JSList *this_node = node; jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); fEngine->PortUnRegister(fClientControl.fRefNum, port_index); } netj.playback_ports = NULL; if (netj.bitdepth == CELT_MODE) { #if HAVE_CELT node = netj.playback_srcs; while (node != NULL) { JSList *this_node = node; CELTEncoder *enc = (CELTEncoder *) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); celt_encoder_destroy(enc); } netj.playback_srcs = NULL; node = netj.capture_srcs; while (node != NULL) { JSList *this_node = node; CELTDecoder *dec = (CELTDecoder *) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); celt_decoder_destroy(dec); } netj.capture_srcs = NULL; #endif } else if (netj.bitdepth == OPUS_MODE) { #if HAVE_OPUS node = netj.playback_srcs; while (node != NULL) { JSList *this_node = node; OpusCustomEncoder *enc = (OpusCustomEncoder *) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); opus_custom_encoder_destroy(enc); } netj.playback_srcs = NULL; node = netj.capture_srcs; while (node != NULL) { JSList *this_node = node; OpusCustomDecoder *dec = (OpusCustomDecoder *) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); opus_custom_decoder_destroy(dec); } netj.capture_srcs = NULL; #endif } else { #if HAVE_SAMPLERATE node = netj.playback_srcs; while (node != NULL) { JSList *this_node = node; SRC_STATE *state = (SRC_STATE *) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); src_delete(state); } netj.playback_srcs = NULL; node = netj.capture_srcs; while (node != NULL) { JSList *this_node = node; SRC_STATE *state = (SRC_STATE *) node->data; node = jack_slist_remove_link(node, this_node); jack_slist_free_1(this_node); src_delete(state); } netj.capture_srcs = NULL; #endif } } //Render functions-------------------------------------------------------------------- // render functions for float void JackNetOneDriver::render_payload_to_jack_ports_float(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) { uint32_t chn = 0; JSList *node = capture_ports; #if HAVE_SAMPLERATE JSList *src_node = capture_srcs; #endif uint32_t *packet_bufX = (uint32_t *)packet_payload; if (!packet_payload) return; while (node != NULL) { unsigned int i; int_float_t val; #if HAVE_SAMPLERATE SRC_DATA src; #endif jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; JackPort *port = fGraphManager->GetPort(port_index); jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); const char *porttype = port->GetType(); if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { #if HAVE_SAMPLERATE // audio port, resample if necessary if (net_period_down != nframes) { SRC_STATE *src_state = (SRC_STATE *)src_node->data; for (i = 0; i < net_period_down; i++) { packet_bufX[i] = ntohl (packet_bufX[i]); } src.data_in = (float *) packet_bufX; src.input_frames = net_period_down; src.data_out = buf; src.output_frames = nframes; src.src_ratio = (float) nframes / (float) net_period_down; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); src_node = jack_slist_next (src_node); } else #endif { if (dont_htonl_floats) { memcpy(buf, packet_bufX, net_period_down * sizeof(jack_default_audio_sample_t)); } else { for (i = 0; i < net_period_down; i++) { val.i = packet_bufX[i]; val.i = ntohl (val.i); buf[i] = val.f; } } } } else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down; uint32_t * buffer_uint32 = (uint32_t*)packet_bufX; decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void JackNetOneDriver::render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats) { uint32_t chn = 0; JSList *node = playback_ports; #if HAVE_SAMPLERATE JSList *src_node = playback_srcs; #endif uint32_t *packet_bufX = (uint32_t *) packet_payload; while (node != NULL) { #if HAVE_SAMPLERATE SRC_DATA src; #endif unsigned int i; int_float_t val; jack_port_id_t port_index = (jack_port_id_t)(intptr_t) node->data; JackPort *port = fGraphManager->GetPort(port_index); jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); const char *porttype = port->GetType(); if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { // audio port, resample if necessary #if HAVE_SAMPLERATE if (net_period_up != nframes) { SRC_STATE *src_state = (SRC_STATE *) src_node->data; src.data_in = buf; src.input_frames = nframes; src.data_out = (float *) packet_bufX; src.output_frames = net_period_up; src.src_ratio = (float) net_period_up / (float) nframes; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); for (i = 0; i < net_period_up; i++) { packet_bufX[i] = htonl (packet_bufX[i]); } src_node = jack_slist_next (src_node); } else #endif { if (dont_htonl_floats) { memcpy(packet_bufX, buf, net_period_up * sizeof(jack_default_audio_sample_t)); } else { for (i = 0; i < net_period_up; i++) { val.f = buf[i]; val.i = htonl (val.i); packet_bufX[i] = val.i; } } } } else if (strncmp(porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } #if HAVE_CELT // render functions for celt. void JackNetOneDriver::render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) { uint32_t chn = 0; JSList *node = capture_ports; JSList *src_node = capture_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_id_t port_index = (jack_port_id_t) (intptr_t)node->data; JackPort *port = fGraphManager->GetPort(port_index); jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); const char *portname = port->GetType(); if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { // audio port, decode celt data. CELTDecoder *decoder = (CELTDecoder *)src_node->data; #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 if (!packet_payload) celt_decode_float(decoder, NULL, net_period_down, buf, nframes); else celt_decode_float(decoder, packet_bufX, net_period_down, buf, nframes); #else if (!packet_payload) celt_decode_float(decoder, NULL, net_period_down, buf); else celt_decode_float(decoder, packet_bufX, net_period_down, buf); #endif src_node = jack_slist_next (src_node); } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; if (packet_payload) decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void JackNetOneDriver::render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) { uint32_t chn = 0; JSList *node = playback_ports; JSList *src_node = playback_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_id_t port_index = (jack_port_id_t) (intptr_t) node->data; JackPort *port = fGraphManager->GetPort(port_index); jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); const char *portname = port->GetType(); if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { // audio port, encode celt data. int encoded_bytes; jack_default_audio_sample_t *floatbuf = (jack_default_audio_sample_t *)alloca (sizeof(jack_default_audio_sample_t) * nframes); memcpy(floatbuf, buf, nframes * sizeof(jack_default_audio_sample_t)); CELTEncoder *encoder = (CELTEncoder *)src_node->data; #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 encoded_bytes = celt_encode_float(encoder, floatbuf, nframes, packet_bufX, net_period_up); #else encoded_bytes = celt_encode_float(encoder, floatbuf, NULL, packet_bufX, net_period_up); #endif if (encoded_bytes != (int)net_period_up) jack_error("something in celt changed. netjack needs to be changed to handle this."); src_node = jack_slist_next(src_node); } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } #endif #if HAVE_OPUS #define CDO (sizeof(short)) ///< compressed data offset (first 2 bytes are length) // render functions for Opus. void JackNetOneDriver::render_payload_to_jack_ports_opus (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) { int chn = 0; JSList *node = capture_ports; JSList *src_node = capture_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_id_t port_index = (jack_port_id_t) (intptr_t)node->data; JackPort *port = fGraphManager->GetPort(port_index); jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); const char *portname = port->GetType(); if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { // audio port, decode opus data. OpusCustomDecoder *decoder = (OpusCustomDecoder*) src_node->data; if( !packet_payload ) memset(buf, 0, nframes * sizeof(float)); else { unsigned short len; memcpy(&len, packet_bufX, CDO); len = ntohs(len); opus_custom_decode_float( decoder, packet_bufX + CDO, len, buf, nframes ); } src_node = jack_slist_next (src_node); } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; if( packet_payload ) decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void JackNetOneDriver::render_jack_ports_to_payload_opus (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) { int chn = 0; JSList *node = playback_ports; JSList *src_node = playback_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_id_t port_index = (jack_port_id_t) (intptr_t) node->data; JackPort *port = fGraphManager->GetPort(port_index); jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); const char *portname = port->GetType(); if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { // audio port, encode opus data. int encoded_bytes; jack_default_audio_sample_t *floatbuf = (jack_default_audio_sample_t *)alloca (sizeof(jack_default_audio_sample_t) * nframes); memcpy(floatbuf, buf, nframes * sizeof(jack_default_audio_sample_t)); OpusCustomEncoder *encoder = (OpusCustomEncoder*) src_node->data; encoded_bytes = opus_custom_encode_float( encoder, floatbuf, nframes, packet_bufX + CDO, net_period_up - CDO ); unsigned short len = htons(encoded_bytes); memcpy(packet_bufX, &len, CDO); src_node = jack_slist_next( src_node ); } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } #endif /* Wrapper functions with bitdepth argument... */ void JackNetOneDriver::render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) { #if HAVE_CELT if (bitdepth == CELT_MODE) render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); else #endif #if HAVE_OPUS if (bitdepth == OPUS_MODE) render_payload_to_jack_ports_opus (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); else #endif render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); } void JackNetOneDriver::render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats) { #if HAVE_CELT if (bitdepth == CELT_MODE) render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); else #endif #if HAVE_OPUS if (bitdepth == OPUS_MODE) render_jack_ports_to_payload_opus (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); else #endif render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); } //driver loader----------------------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor () { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("netone", JackDriverMaster, "netjack one slave backend component", &filler); value.ui = 2U; jack_driver_descriptor_add_parameter(desc, &filler, "audio-ins", 'i', JackDriverParamUInt, &value, NULL, "Number of capture channels (defaults to 2)", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "audio-outs", 'o', JackDriverParamUInt, &value, NULL, "Number of playback channels (defaults to 2)", NULL); value.ui = 1U; jack_driver_descriptor_add_parameter(desc, &filler, "midi-ins", 'I', JackDriverParamUInt, &value, NULL, "Number of midi capture channels (defaults to 1)", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "midi-outs", 'O', JackDriverParamUInt, &value, NULL, "Number of midi playback channels (defaults to 1)", NULL); value.ui = 48000U; jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); value.ui = 1024U; jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); value.ui = 5U; jack_driver_descriptor_add_parameter(desc, &filler, "num-periods", 'n', JackDriverParamUInt, &value, NULL, "Network latency setting in no. of periods", NULL); value.ui = 3000U; jack_driver_descriptor_add_parameter(desc, &filler, "listen-port", 'l', JackDriverParamUInt, &value, NULL, "The socket port we are listening on for sync packets", NULL); value.ui = 1U; jack_driver_descriptor_add_parameter(desc, &filler, "factor", 'f', JackDriverParamUInt, &value, NULL, "Factor for sample rate reduction", NULL); value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "upstream-factor", 'u', JackDriverParamUInt, &value, NULL, "Factor for sample rate reduction on the upstream", NULL); #if HAVE_CELT value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamUInt, &value, NULL, "Set CELT encoding and number of kbits per channel", NULL); #endif #if HAVE_OPUS value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "opus", 'P', JackDriverParamUInt, &value, NULL, "Set Opus encoding and number of kbits per channel", NULL); #endif value.ui = 0U; jack_driver_descriptor_add_parameter(desc, &filler, "bit-depth", 'b', JackDriverParamUInt, &value, NULL, "Sample bit-depth (0 for float, 8 for 8bit and 16 for 16bit)", NULL); value.i = true; jack_driver_descriptor_add_parameter(desc, &filler, "transport-sync", 't', JackDriverParamBool, &value, NULL, "Whether to slave the transport to the master transport", NULL); value.ui = true; jack_driver_descriptor_add_parameter(desc, &filler, "autoconf", 'a', JackDriverParamBool, &value, NULL, "Whether to use Autoconfig, or just start", NULL); value.ui = 1U; jack_driver_descriptor_add_parameter(desc, &filler, "redundancy", 'R', JackDriverParamUInt, &value, NULL, "Send packets N times", NULL); value.ui = false; jack_driver_descriptor_add_parameter(desc, &filler, "native-endian", 'e', JackDriverParamBool, &value, NULL, "Don't convert samples to network byte order", NULL); value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "jitterval", 'J', JackDriverParamInt, &value, NULL, "Attempted jitterbuffer microseconds on master", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "always-deadline", 'D', JackDriverParamBool, &value, NULL, "Always use deadline", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { jack_nframes_t sample_rate = 48000; jack_nframes_t resample_factor = 1; jack_nframes_t period_size = 1024; unsigned int capture_ports = 2; unsigned int playback_ports = 2; unsigned int capture_ports_midi = 1; unsigned int playback_ports_midi = 1; unsigned int listen_port = 3000; unsigned int bitdepth = 0; unsigned int handle_transport_sync = 1; unsigned int use_autoconfig = 1; unsigned int latency = 5; unsigned int redundancy = 1; unsigned int mtu = 1400; #if HAVE_SAMPLERATE unsigned int resample_factor_up = 1; #endif int dont_htonl_floats = 0; int always_deadline = 0; int jitter_val = 0; const JSList * node; const jack_driver_param_t * param; for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*) node->data; switch (param->character) { case 'i': capture_ports = param->value.ui; break; case 'o': playback_ports = param->value.ui; break; case 'I': capture_ports_midi = param->value.ui; break; case 'O': playback_ports_midi = param->value.ui; break; case 'r': sample_rate = param->value.ui; break; case 'p': period_size = param->value.ui; break; case 'l': listen_port = param->value.ui; break; case 'f': #if HAVE_SAMPLERATE resample_factor = param->value.ui; #else jack_error("not built with libsamplerate support"); return NULL; #endif break; case 'u': #if HAVE_SAMPLERATE resample_factor_up = param->value.ui; #else jack_error("not built with libsamplerate support"); return NULL; #endif break; case 'b': bitdepth = param->value.ui; break; case 'c': #if HAVE_CELT bitdepth = CELT_MODE; resample_factor = param->value.ui; #else jack_error("not built with celt support"); return NULL; #endif break; case 'P': #if HAVE_OPUS bitdepth = OPUS_MODE; resample_factor = param->value.ui; jack_error("OPUS: %d\n", resample_factor); #else jack_error("not built with Opus support"); return NULL; #endif break; case 't': handle_transport_sync = param->value.ui; break; case 'a': use_autoconfig = param->value.ui; break; case 'n': latency = param->value.ui; break; case 'R': redundancy = param->value.ui; break; case 'H': dont_htonl_floats = param->value.ui; break; case 'J': jitter_val = param->value.i; break; case 'D': always_deadline = param->value.ui; break; } } try { Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver ( new Jack::JackNetOneDriver("system", "net_pcm", engine, table, listen_port, mtu, capture_ports_midi, playback_ports_midi, capture_ports, playback_ports, sample_rate, period_size, resample_factor, "net_pcm", handle_transport_sync, bitdepth, use_autoconfig, latency, redundancy, dont_htonl_floats, always_deadline, jitter_val)); if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, 0, "from_master", "to_master", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } catch (...) { return NULL; } #if HAVE_SAMPLERATE // unused (void)resample_factor_up; #endif } #ifdef __cplusplus } #endif } jack2-1.9.22/common/JackNetOneDriver.h000066400000000000000000000071711436671425200174150ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Torben Horn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackNetDriver__ #define __JackNetDriver__ #include "JackTimedDriver.h" #include "netjack.h" #include "netjack_packet.h" namespace Jack { /** \Brief This class describes the Net Backend */ class JackNetOneDriver : public JackWaiterDriver { private: netjack_driver_state_t netj; void render_payload_to_jack_ports_float(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); void render_jack_ports_to_payload_float(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats ); #if HAVE_CELT void render_payload_to_jack_ports_celt(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); void render_jack_ports_to_payload_celt(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); #endif #if HAVE_OPUS void render_payload_to_jack_ports_opus(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); void render_jack_ports_to_payload_opus(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); #endif void render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); void render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats); public: JackNetOneDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports, int sample_rate, int period_size, int resample_factor, const char* net_name, uint transport_sync, int bitdepth, int use_autoconfig, int latency, int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val); virtual ~JackNetOneDriver(); int Close(); int Attach(); int Detach(); int Read(); int Write(); bool Initialize(); int AllocPorts(); void FreePorts(); // BufferSize can't be changed bool IsFixedBufferSize() { return true; } int SetBufferSize(jack_nframes_t buffer_size) { return -1; } int SetSampleRate(jack_nframes_t sample_rate) { return -1; } }; } #endif jack2-1.9.22/common/JackNetSocket.h000066400000000000000000000024451436671425200167470ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackNetSocket__ #define __JackNetSocket__ #include "JackCompilerDeps.h" #include #include #include #include namespace Jack { //get host name********************************* SERVER_EXPORT int GetHostName(char * name, int size); //net errors *********************************** enum _net_error { NET_CONN_ERROR = 10000, NET_OP_ERROR, NET_NO_DATA, NET_NO_NETWORK, NET_NO_ERROR }; typedef enum _net_error net_error_t; } #endif jack2-1.9.22/common/JackNetTool.cpp000066400000000000000000001500151436671425200167640ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackNetTool.h" #include "JackError.h" #ifdef __APPLE__ #include class HardwareClock { public: HardwareClock(); void Reset(); void Update(); float GetDeltaTime() const; double GetTime() const; private: double m_clockToSeconds; uint64_t m_startAbsTime; uint64_t m_lastAbsTime; double m_time; float m_deltaTime; }; HardwareClock::HardwareClock() { mach_timebase_info_data_t info; mach_timebase_info(&info); m_clockToSeconds = (double)info.numer/info.denom/1000000000.0; Reset(); } void HardwareClock::Reset() { m_startAbsTime = mach_absolute_time(); m_lastAbsTime = m_startAbsTime; m_time = m_startAbsTime*m_clockToSeconds; m_deltaTime = 1.0f/60.0f; } void HardwareClock::Update() { const uint64_t currentTime = mach_absolute_time(); const uint64_t dt = currentTime - m_lastAbsTime; m_time = currentTime*m_clockToSeconds; m_deltaTime = (double)dt*m_clockToSeconds; m_lastAbsTime = currentTime; } float HardwareClock::GetDeltaTime() const { return m_deltaTime; } double HardwareClock::GetTime() const { return m_time; } #endif using namespace std; namespace Jack { // NetMidiBuffer********************************************************************************** NetMidiBuffer::NetMidiBuffer(session_params_t* params, uint32_t nports, char* net_buffer) { fNPorts = nports; fMaxBufsize = fNPorts * sizeof(sample_t) * params->fPeriodSize; fMaxPcktSize = params->fMtu - sizeof(packet_header_t); fBuffer = new char[fMaxBufsize]; fPortBuffer = new JackMidiBuffer* [fNPorts]; for (int port_index = 0; port_index < fNPorts; port_index++) { fPortBuffer[port_index] = NULL; } fNetBuffer = net_buffer; fCycleBytesSize = params->fMtu * (max(params->fSendMidiChannels, params->fReturnMidiChannels) * params->fPeriodSize * sizeof(sample_t) / (params->fMtu - sizeof(packet_header_t))); } NetMidiBuffer::~NetMidiBuffer() { delete[] fBuffer; delete[] fPortBuffer; } size_t NetMidiBuffer::GetCycleSize() { return fCycleBytesSize; } int NetMidiBuffer::GetNumPackets(int data_size, int max_size) { int res1 = data_size % max_size; int res2 = data_size / max_size; return (res1) ? res2 + 1 : res2; } void NetMidiBuffer::SetBuffer(int index, JackMidiBuffer* buffer) { fPortBuffer[index] = buffer; } JackMidiBuffer* NetMidiBuffer::GetBuffer(int index) { return fPortBuffer[index]; } void NetMidiBuffer::DisplayEvents() { for (int port_index = 0; port_index < fNPorts; port_index++) { for (uint event = 0; event < fPortBuffer[port_index]->event_count; event++) { if (fPortBuffer[port_index]->IsValid()) { jack_info("port %d : midi event %u/%u -> time : %u, size : %u", port_index + 1, event + 1, fPortBuffer[port_index]->event_count, fPortBuffer[port_index]->events[event].time, fPortBuffer[port_index]->events[event].size); } } } } int NetMidiBuffer::RenderFromJackPorts() { int pos = 0; size_t copy_size; for (int port_index = 0; port_index < fNPorts; port_index++) { char* write_pos = fBuffer + pos; copy_size = sizeof(JackMidiBuffer) + fPortBuffer[port_index]->event_count * sizeof(JackMidiEvent); memcpy(fBuffer + pos, fPortBuffer[port_index], copy_size); pos += copy_size; memcpy(fBuffer + pos, fPortBuffer[port_index] + (fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos), fPortBuffer[port_index]->write_pos); pos += fPortBuffer[port_index]->write_pos; JackMidiBuffer* midi_buffer = reinterpret_cast(write_pos); MidiBufferHToN(midi_buffer, midi_buffer); } return pos; } void NetMidiBuffer::RenderToJackPorts() { int pos = 0; size_t copy_size; for (int port_index = 0; port_index < fNPorts; port_index++) { JackMidiBuffer* midi_buffer = reinterpret_cast(fBuffer + pos); MidiBufferNToH(midi_buffer, midi_buffer); copy_size = sizeof(JackMidiBuffer) + reinterpret_cast(fBuffer + pos)->event_count * sizeof(JackMidiEvent); memcpy(fPortBuffer[port_index], fBuffer + pos, copy_size); pos += copy_size; memcpy(fPortBuffer[port_index] + (fPortBuffer[port_index]->buffer_size - fPortBuffer[port_index]->write_pos), fBuffer + pos, fPortBuffer[port_index]->write_pos); pos += fPortBuffer[port_index]->write_pos; } } void NetMidiBuffer::RenderFromNetwork(int sub_cycle, size_t copy_size) { memcpy(fBuffer + sub_cycle * fMaxPcktSize, fNetBuffer, copy_size); } int NetMidiBuffer::RenderToNetwork(int sub_cycle, size_t total_size) { int size = total_size - sub_cycle * fMaxPcktSize; int copy_size = (size <= fMaxPcktSize) ? size : fMaxPcktSize; memcpy(fNetBuffer, fBuffer + sub_cycle * fMaxPcktSize, copy_size); return copy_size; } // net audio buffer ********************************************************************************* NetAudioBuffer::NetAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer) { fNPorts = nports; fNetBuffer = net_buffer; fNumPackets = 0; fPortBuffer = new sample_t*[fNPorts]; fConnectedPorts = new bool[fNPorts]; for (int port_index = 0; port_index < fNPorts; port_index++) { fPortBuffer[port_index] = NULL; fConnectedPorts[port_index] = true; } fLastSubCycle = 0; fPeriodSize = 0; fSubPeriodSize = 0; fSubPeriodBytesSize = 0; fCycleDuration = 0.f; fCycleBytesSize = 0; } NetAudioBuffer::~NetAudioBuffer() { delete [] fConnectedPorts; delete [] fPortBuffer; } void NetAudioBuffer::SetBuffer(int index, sample_t* buffer) { fPortBuffer[index] = buffer; } sample_t* NetAudioBuffer::GetBuffer(int index) { return fPortBuffer[index]; } int NetAudioBuffer::CheckPacket(int cycle, int sub_cycle) { int res; if (sub_cycle != fLastSubCycle + 1) { jack_error("Packet(s) missing from... %d %d", fLastSubCycle, sub_cycle); res = DATA_PACKET_ERROR; } else { res = 0; } fLastSubCycle = sub_cycle; return res; } void NetAudioBuffer::NextCycle() { // reset for next cycle fLastSubCycle = -1; } void NetAudioBuffer::Cleanup() { for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { memset(fPortBuffer[port_index], 0, fPeriodSize * sizeof(sample_t)); } } } //network<->buffer int NetAudioBuffer::ActivePortsToNetwork(char* net_buffer) { int active_ports = 0; int* active_port_address = (int*)net_buffer; for (int port_index = 0; port_index < fNPorts; port_index++) { // Write the active port number if (fPortBuffer[port_index]) { *active_port_address = htonl(port_index); active_port_address++; active_ports++; assert(active_ports < 256); } } return active_ports; } void NetAudioBuffer::ActivePortsFromNetwork(char* net_buffer, uint32_t port_num) { int* active_port_address = (int*)net_buffer; for (int port_index = 0; port_index < fNPorts; port_index++) { fConnectedPorts[port_index] = false; } for (uint port_index = 0; port_index < port_num; port_index++) { int active_port = ntohl(*active_port_address); assert(active_port < fNPorts); fConnectedPorts[active_port] = true; active_port_address++; } } int NetAudioBuffer::RenderFromJackPorts(int unused_frames) { // Count active ports int active_ports = 0; for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { active_ports++; } } return active_ports; } void NetAudioBuffer::RenderToJackPorts(int unused_frames) { // Nothing to do NextCycle(); } // Float converter NetFloatAudioBuffer::NetFloatAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer) : NetAudioBuffer(params, nports, net_buffer) { fPeriodSize = params->fPeriodSize; fPacketSize = PACKET_AVAILABLE_SIZE(params); UpdateParams(max(params->fReturnAudioChannels, params->fSendAudioChannels)); fCycleDuration = float(fSubPeriodSize) / float(params->fSampleRate); fCycleBytesSize = params->fMtu * (fPeriodSize / fSubPeriodSize); fLastSubCycle = -1; } NetFloatAudioBuffer::~NetFloatAudioBuffer() {} // needed size in bytes for an entire cycle size_t NetFloatAudioBuffer::GetCycleSize() { return fCycleBytesSize; } // cycle duration in sec float NetFloatAudioBuffer::GetCycleDuration() { return fCycleDuration; } void NetFloatAudioBuffer::UpdateParams(int active_ports) { if (active_ports == 0) { fSubPeriodSize = fPeriodSize; } else { jack_nframes_t period = int(powf(2.f, int(log(float(fPacketSize) / (active_ports * sizeof(sample_t))) / log(2.)))); fSubPeriodSize = (period > fPeriodSize) ? fPeriodSize : period; } fSubPeriodBytesSize = fSubPeriodSize * sizeof(sample_t) + sizeof(int); // The port number in coded on 4 bytes fNumPackets = fPeriodSize / fSubPeriodSize; // At least one packet } int NetFloatAudioBuffer::GetNumPackets(int active_ports) { UpdateParams(active_ports); /* jack_log("GetNumPackets packet = %d fPeriodSize = %d fSubPeriodSize = %d fSubPeriodBytesSize = %d", fPeriodSize / fSubPeriodSize, fPeriodSize, fSubPeriodSize, fSubPeriodBytesSize); */ return fNumPackets; } //jack<->buffer int NetFloatAudioBuffer::RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) { // Cleanup all JACK ports at the beginning of the cycle if (sub_cycle == 0) { Cleanup(); } if (port_num > 0) { UpdateParams(port_num); for (uint32_t port_index = 0; port_index < port_num; port_index++) { // Only copy to active ports : read the active port number then audio data int* active_port_address = (int*)(fNetBuffer + port_index * fSubPeriodBytesSize); int active_port = ntohl(*active_port_address); RenderFromNetwork((char*)(active_port_address + 1), active_port, sub_cycle); } } return CheckPacket(cycle, sub_cycle); } int NetFloatAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) { int active_ports = 0; for (int port_index = 0; port_index < fNPorts; port_index++) { // Only copy from active ports : write the active port number then audio data if (fPortBuffer[port_index]) { int* active_port_address = (int*)(fNetBuffer + active_ports * fSubPeriodBytesSize); *active_port_address = htonl(port_index); RenderToNetwork((char*)(active_port_address + 1), port_index, sub_cycle); active_ports++; } } return port_num * fSubPeriodBytesSize; } #ifdef __BIG_ENDIAN__ static inline jack_default_audio_sample_t SwapFloat(jack_default_audio_sample_t f) { union { jack_default_audio_sample_t f; unsigned char b[4]; } dat1, dat2; dat1.f = f; dat2.b[0] = dat1.b[3]; dat2.b[1] = dat1.b[2]; dat2.b[2] = dat1.b[1]; dat2.b[3] = dat1.b[0]; return dat2.f; } void NetFloatAudioBuffer::RenderFromNetwork(char* net_buffer, int active_port, int sub_cycle) { if (fPortBuffer[active_port]) { jack_default_audio_sample_t* src = (jack_default_audio_sample_t*)(net_buffer); jack_default_audio_sample_t* dst = (jack_default_audio_sample_t*)(fPortBuffer[active_port] + sub_cycle * fSubPeriodSize); for (unsigned int sample = 0; sample < (fSubPeriodBytesSize - sizeof(int)) / sizeof(jack_default_audio_sample_t); sample++) { dst[sample] = SwapFloat(src[sample]); } } } void NetFloatAudioBuffer::RenderToNetwork(char* net_buffer, int active_port, int sub_cycle) { for (int port_index = 0; port_index < fNPorts; port_index++ ) { jack_default_audio_sample_t* src = (jack_default_audio_sample_t*)(fPortBuffer[active_port] + sub_cycle * fSubPeriodSize); jack_default_audio_sample_t* dst = (jack_default_audio_sample_t*)(net_buffer); for (unsigned int sample = 0; sample < (fSubPeriodBytesSize - sizeof(int)) / sizeof(jack_default_audio_sample_t); sample++) { dst[sample] = SwapFloat(src[sample]); } } } #else void NetFloatAudioBuffer::RenderFromNetwork(char* net_buffer, int active_port, int sub_cycle) { if (fPortBuffer[active_port]) { memcpy(fPortBuffer[active_port] + sub_cycle * fSubPeriodSize, net_buffer, fSubPeriodBytesSize - sizeof(int)); } } void NetFloatAudioBuffer::RenderToNetwork(char* net_buffer, int active_port, int sub_cycle) { memcpy(net_buffer, fPortBuffer[active_port] + sub_cycle * fSubPeriodSize, fSubPeriodBytesSize - sizeof(int)); } #endif // Celt audio buffer ********************************************************************************* #if HAVE_CELT #define KPS 32 #define KPS_DIV 8 NetCeltAudioBuffer::NetCeltAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps) :NetAudioBuffer(params, nports, net_buffer) { fCeltMode = new CELTMode*[fNPorts]; fCeltEncoder = new CELTEncoder*[fNPorts]; fCeltDecoder = new CELTDecoder*[fNPorts]; memset(fCeltMode, 0, fNPorts * sizeof(CELTMode*)); memset(fCeltEncoder, 0, fNPorts * sizeof(CELTEncoder*)); memset(fCeltDecoder, 0, fNPorts * sizeof(CELTDecoder*)); int error = CELT_OK; for (int i = 0; i < fNPorts; i++) { fCeltMode[i] = celt_mode_create(params->fSampleRate, params->fPeriodSize, &error); if (error != CELT_OK) { jack_log("NetCeltAudioBuffer celt_mode_create err = %d", error); goto error; } #if HAVE_CELT_API_0_11 fCeltEncoder[i] = celt_encoder_create_custom(fCeltMode[i], 1, &error); if (error != CELT_OK) { jack_log("NetCeltAudioBuffer celt_encoder_create_custom err = %d", error); goto error; } celt_encoder_ctl(fCeltEncoder[i], CELT_SET_COMPLEXITY(1)); fCeltDecoder[i] = celt_decoder_create_custom(fCeltMode[i], 1, &error); if (error != CELT_OK) { jack_log("NetCeltAudioBuffer celt_decoder_create_custom err = %d", error); goto error; } celt_decoder_ctl(fCeltDecoder[i], CELT_SET_COMPLEXITY(1)); #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 fCeltEncoder[i] = celt_encoder_create(fCeltMode[i], 1, &error); if (error != CELT_OK) { jack_log("NetCeltAudioBuffer celt_mode_create err = %d", error); goto error; } celt_encoder_ctl(fCeltEncoder[i], CELT_SET_COMPLEXITY(1)); fCeltDecoder[i] = celt_decoder_create(fCeltMode[i], 1, &error); if (error != CELT_OK) { jack_log("NetCeltAudioBuffer celt_decoder_create err = %d", error); goto error; } celt_decoder_ctl(fCeltDecoder[i], CELT_SET_COMPLEXITY(1)); #else fCeltEncoder[i] = celt_encoder_create(fCeltMode[i]); if (error != CELT_OK) { jack_log("NetCeltAudioBuffer celt_encoder_create err = %d", error); goto error; } celt_encoder_ctl(fCeltEncoder[i], CELT_SET_COMPLEXITY(1)); fCeltDecoder[i] = celt_decoder_create(fCeltMode[i]); if (error != CELT_OK) { jack_log("NetCeltAudioBuffer celt_decoder_create err = %d", error); goto error; } celt_decoder_ctl(fCeltDecoder[i], CELT_SET_COMPLEXITY(1)); #endif } { fPeriodSize = params->fPeriodSize; fCompressedSizeByte = (kbps * params->fPeriodSize * 1024) / (params->fSampleRate * 8); jack_log("NetCeltAudioBuffer fCompressedSizeByte %d", fCompressedSizeByte); fCompressedBuffer = new unsigned char* [fNPorts]; for (int port_index = 0; port_index < fNPorts; port_index++) { fCompressedBuffer[port_index] = new unsigned char[fCompressedSizeByte]; memset(fCompressedBuffer[port_index], 0, fCompressedSizeByte * sizeof(char)); } int res1 = (fNPorts * fCompressedSizeByte) % PACKET_AVAILABLE_SIZE(params); int res2 = (fNPorts * fCompressedSizeByte) / PACKET_AVAILABLE_SIZE(params); fNumPackets = (res1) ? (res2 + 1) : res2; jack_log("NetCeltAudioBuffer res1 = %d res2 = %d", res1, res2); fSubPeriodBytesSize = fCompressedSizeByte / fNumPackets; fLastSubPeriodBytesSize = fSubPeriodBytesSize + fCompressedSizeByte % fNumPackets; jack_log("NetCeltAudioBuffer fNumPackets = %d fSubPeriodBytesSize = %d, fLastSubPeriodBytesSize = %d", fNumPackets, fSubPeriodBytesSize, fLastSubPeriodBytesSize); fCycleDuration = float(fSubPeriodBytesSize / sizeof(sample_t)) / float(params->fSampleRate); fCycleBytesSize = params->fMtu * fNumPackets; fLastSubCycle = -1; return; } error: FreeCelt(); throw std::bad_alloc(); } NetCeltAudioBuffer::~NetCeltAudioBuffer() { FreeCelt(); for (int port_index = 0; port_index < fNPorts; port_index++) { delete [] fCompressedBuffer[port_index]; } delete [] fCompressedBuffer; } void NetCeltAudioBuffer::FreeCelt() { for (int i = 0; i < fNPorts; i++) { if (fCeltEncoder[i]) { celt_encoder_destroy(fCeltEncoder[i]); } if (fCeltDecoder[i]) { celt_decoder_destroy(fCeltDecoder[i]); } if (fCeltMode[i]) { celt_mode_destroy(fCeltMode[i]); } } delete [] fCeltMode; delete [] fCeltEncoder; delete [] fCeltDecoder; } size_t NetCeltAudioBuffer::GetCycleSize() { return fCycleBytesSize; } float NetCeltAudioBuffer::GetCycleDuration() { return fCycleDuration; } int NetCeltAudioBuffer::GetNumPackets(int active_ports) { return fNumPackets; } int NetCeltAudioBuffer::RenderFromJackPorts(int nframes) { float buffer[BUFFER_SIZE_MAX]; for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { memcpy(buffer, fPortBuffer[port_index], fPeriodSize * sizeof(sample_t)); } else { memset(buffer, 0, fPeriodSize * sizeof(sample_t)); } #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 //int res = celt_encode_float(fCeltEncoder[port_index], buffer, fPeriodSize, fCompressedBuffer[port_index], fCompressedSizeByte); int res = celt_encode_float(fCeltEncoder[port_index], buffer, nframes, fCompressedBuffer[port_index], fCompressedSizeByte); #else int res = celt_encode_float(fCeltEncoder[port_index], buffer, NULL, fCompressedBuffer[port_index], fCompressedSizeByte); #endif if (res != fCompressedSizeByte) { jack_error("celt_encode_float error fCompressedSizeByte = %d res = %d", fCompressedSizeByte, res); } } // All ports active return fNPorts; } void NetCeltAudioBuffer::RenderToJackPorts(int nframes) { for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 //int res = celt_decode_float(fCeltDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizeByte, fPortBuffer[port_index], fPeriodSize); int res = celt_decode_float(fCeltDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizeByte, fPortBuffer[port_index], nframes); #else int res = celt_decode_float(fCeltDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizeByte, fPortBuffer[port_index]); #endif if (res != CELT_OK) { jack_error("celt_decode_float error fCompressedSizeByte = %d res = %d", fCompressedSizeByte, res); } } } NextCycle(); } //network<->buffer int NetCeltAudioBuffer::RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) { // Cleanup all JACK ports at the beginning of the cycle if (sub_cycle == 0) { Cleanup(); } if (port_num > 0) { int sub_period_bytes_size; // Last packet of the cycle if (sub_cycle == fNumPackets - 1) { sub_period_bytes_size = fLastSubPeriodBytesSize; } else { sub_period_bytes_size = fSubPeriodBytesSize; } for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fNetBuffer + port_index * sub_period_bytes_size, sub_period_bytes_size); } } return CheckPacket(cycle, sub_cycle); } int NetCeltAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) { int sub_period_bytes_size; // Last packet of the cycle if (sub_cycle == fNumPackets - 1) { sub_period_bytes_size = fLastSubPeriodBytesSize; } else { sub_period_bytes_size = fSubPeriodBytesSize; } for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fNetBuffer + port_index * sub_period_bytes_size, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, sub_period_bytes_size); } return fNPorts * sub_period_bytes_size; } #endif #if HAVE_OPUS #define CDO (sizeof(short)) ///< compressed data offset (first 2 bytes are length) NetOpusAudioBuffer::NetOpusAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps) :NetAudioBuffer(params, nports, net_buffer) { fOpusMode = new OpusCustomMode*[fNPorts]; fOpusEncoder = new OpusCustomEncoder*[fNPorts]; fOpusDecoder = new OpusCustomDecoder*[fNPorts]; fCompressedSizesByte = new unsigned short[fNPorts]; memset(fOpusMode, 0, fNPorts * sizeof(OpusCustomMode*)); memset(fOpusEncoder, 0, fNPorts * sizeof(OpusCustomEncoder*)); memset(fOpusDecoder, 0, fNPorts * sizeof(OpusCustomDecoder*)); memset(fCompressedSizesByte, 0, fNPorts * sizeof(short)); int error = OPUS_OK; for (int i = 0; i < fNPorts; i++) { /* Allocate en/decoders */ fOpusMode[i] = opus_custom_mode_create(params->fSampleRate, params->fPeriodSize, &error); if (error != OPUS_OK) { jack_log("NetOpusAudioBuffer opus_custom_mode_create err = %d", error); goto error; } fOpusEncoder[i] = opus_custom_encoder_create(fOpusMode[i], 1, &error); if (error != OPUS_OK) { jack_log("NetOpusAudioBuffer opus_custom_encoder_create err = %d", error); goto error; } fOpusDecoder[i] = opus_custom_decoder_create(fOpusMode[i], 1, &error); if (error != OPUS_OK) { jack_log("NetOpusAudioBuffer opus_custom_decoder_create err = %d", error); goto error; } opus_custom_encoder_ctl(fOpusEncoder[i], OPUS_SET_BITRATE(kbps*1024)); // bits per second opus_custom_encoder_ctl(fOpusEncoder[i], OPUS_SET_COMPLEXITY(10)); opus_custom_encoder_ctl(fOpusEncoder[i], OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); opus_custom_encoder_ctl(fOpusEncoder[i], OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY)); } { fCompressedMaxSizeByte = (kbps * params->fPeriodSize * 1024) / (params->fSampleRate * 8); fPeriodSize = params->fPeriodSize; jack_log("NetOpusAudioBuffer fCompressedMaxSizeByte %d", fCompressedMaxSizeByte); fCompressedBuffer = new unsigned char* [fNPorts]; for (int port_index = 0; port_index < fNPorts; port_index++) { fCompressedBuffer[port_index] = new unsigned char[fCompressedMaxSizeByte]; memset(fCompressedBuffer[port_index], 0, fCompressedMaxSizeByte * sizeof(char)); } int res1 = (fNPorts * (fCompressedMaxSizeByte + CDO)) % PACKET_AVAILABLE_SIZE(params); int res2 = (fNPorts * (fCompressedMaxSizeByte + CDO)) / PACKET_AVAILABLE_SIZE(params); fNumPackets = (res1) ? (res2 + 1) : res2; jack_log("NetOpusAudioBuffer res1 = %d res2 = %d", res1, res2); fSubPeriodBytesSize = (fCompressedMaxSizeByte + CDO) / fNumPackets; fLastSubPeriodBytesSize = fSubPeriodBytesSize + (fCompressedMaxSizeByte + CDO) % fNumPackets; if (fNumPackets == 1) { fSubPeriodBytesSize = fLastSubPeriodBytesSize; } jack_log("NetOpusAudioBuffer fNumPackets = %d fSubPeriodBytesSize = %d, fLastSubPeriodBytesSize = %d", fNumPackets, fSubPeriodBytesSize, fLastSubPeriodBytesSize); fCycleDuration = float(fSubPeriodBytesSize / sizeof(sample_t)) / float(params->fSampleRate); fCycleBytesSize = params->fMtu * fNumPackets; fLastSubCycle = -1; return; } error: FreeOpus(); throw std::bad_alloc(); } NetOpusAudioBuffer::~NetOpusAudioBuffer() { FreeOpus(); for (int port_index = 0; port_index < fNPorts; port_index++) { delete [] fCompressedBuffer[port_index]; } delete [] fCompressedBuffer; delete [] fCompressedSizesByte; } void NetOpusAudioBuffer::FreeOpus() { for (int i = 0; i < fNPorts; i++) { if (fOpusEncoder[i]) { opus_custom_encoder_destroy(fOpusEncoder[i]); fOpusEncoder[i] = 0; } if (fOpusDecoder[i]) { opus_custom_decoder_destroy(fOpusDecoder[i]); fOpusDecoder[i] = 0; } if (fOpusMode[i]) { opus_custom_mode_destroy(fOpusMode[i]); fOpusMode[i] = 0; } } delete [] fOpusEncoder; delete [] fOpusDecoder; delete [] fOpusMode; } size_t NetOpusAudioBuffer::GetCycleSize() { return fCycleBytesSize; } float NetOpusAudioBuffer::GetCycleDuration() { return fCycleDuration; } int NetOpusAudioBuffer::GetNumPackets(int active_ports) { return fNumPackets; } int NetOpusAudioBuffer::RenderFromJackPorts(int nframes) { float buffer[BUFFER_SIZE_MAX]; for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { memcpy(buffer, fPortBuffer[port_index], fPeriodSize * sizeof(sample_t)); } else { memset(buffer, 0, fPeriodSize * sizeof(sample_t)); } int res = opus_custom_encode_float(fOpusEncoder[port_index], buffer, ((nframes == -1) ? fPeriodSize : nframes), fCompressedBuffer[port_index], fCompressedMaxSizeByte); if (res < 0 || res >= 65535) { jack_error("opus_custom_encode_float error res = %d", res); fCompressedSizesByte[port_index] = 0; } else { fCompressedSizesByte[port_index] = res; } } // All ports active return fNPorts; } void NetOpusAudioBuffer::RenderToJackPorts(int nframes) { for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { int res = opus_custom_decode_float(fOpusDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizesByte[port_index], fPortBuffer[port_index], ((nframes == -1) ? fPeriodSize : nframes)); if (res < 0 || res != ((nframes == -1) ? (int)fPeriodSize : nframes)) { jack_error("opus_custom_decode_float error fCompressedSizeByte = %d res = %d", fCompressedSizesByte[port_index], res); } } } NextCycle(); } //network<->buffer int NetOpusAudioBuffer::RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) { // Cleanup all JACK ports at the beginning of the cycle if (sub_cycle == 0) { Cleanup(); } if (port_num > 0) { if (sub_cycle == 0) { for (int port_index = 0; port_index < fNPorts; port_index++) { size_t len = *((size_t*)(fNetBuffer + port_index * fSubPeriodBytesSize)); fCompressedSizesByte[port_index] = ntohs(len); memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fNetBuffer + CDO + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize - CDO); } } else if (sub_cycle == fNumPackets - 1) { for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fNetBuffer + port_index * fLastSubPeriodBytesSize, fLastSubPeriodBytesSize); } } else { for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fNetBuffer + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize); } } } return CheckPacket(cycle, sub_cycle); } int NetOpusAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) { if (sub_cycle == 0) { for (int port_index = 0; port_index < fNPorts; port_index++) { unsigned short len = htons(fCompressedSizesByte[port_index]); memcpy(fNetBuffer + port_index * fSubPeriodBytesSize, &len, CDO); memcpy(fNetBuffer + port_index * fSubPeriodBytesSize + CDO, fCompressedBuffer[port_index], fSubPeriodBytesSize - CDO); } return fNPorts * fSubPeriodBytesSize; } else if (sub_cycle == fNumPackets - 1) { for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fNetBuffer + port_index * fLastSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fLastSubPeriodBytesSize); } return fNPorts * fLastSubPeriodBytesSize; } else { for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fNetBuffer + port_index * fSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fSubPeriodBytesSize); } return fNPorts * fSubPeriodBytesSize; } } #endif NetIntAudioBuffer::NetIntAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer) : NetAudioBuffer(params, nports, net_buffer) { fPeriodSize = params->fPeriodSize; fCompressedSizeByte = (params->fPeriodSize * sizeof(short)); jack_log("NetIntAudioBuffer fCompressedSizeByte %d", fCompressedSizeByte); fIntBuffer = new short* [fNPorts]; for (int port_index = 0; port_index < fNPorts; port_index++) { fIntBuffer[port_index] = new short[fPeriodSize]; memset(fIntBuffer[port_index], 0, fPeriodSize * sizeof(short)); } int res1 = (fNPorts * fCompressedSizeByte) % PACKET_AVAILABLE_SIZE(params); int res2 = (fNPorts * fCompressedSizeByte) / PACKET_AVAILABLE_SIZE(params); jack_log("NetIntAudioBuffer res1 = %d res2 = %d", res1, res2); fNumPackets = (res1) ? (res2 + 1) : res2; fSubPeriodBytesSize = fCompressedSizeByte / fNumPackets; fLastSubPeriodBytesSize = fSubPeriodBytesSize + fCompressedSizeByte % fNumPackets; fSubPeriodSize = fSubPeriodBytesSize / sizeof(short); jack_log("NetIntAudioBuffer fNumPackets = %d fSubPeriodBytesSize = %d, fLastSubPeriodBytesSize = %d", fNumPackets, fSubPeriodBytesSize, fLastSubPeriodBytesSize); fCycleDuration = float(fSubPeriodBytesSize / sizeof(sample_t)) / float(params->fSampleRate); fCycleBytesSize = params->fMtu * fNumPackets; fLastSubCycle = -1; } NetIntAudioBuffer::~NetIntAudioBuffer() { for (int port_index = 0; port_index < fNPorts; port_index++) { delete [] fIntBuffer[port_index]; } delete [] fIntBuffer; } size_t NetIntAudioBuffer::GetCycleSize() { return fCycleBytesSize; } float NetIntAudioBuffer::GetCycleDuration() { return fCycleDuration; } int NetIntAudioBuffer::GetNumPackets(int active_ports) { return fNumPackets; } int NetIntAudioBuffer::RenderFromJackPorts(int nframes) { for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { for (int frame = 0; frame < nframes; frame++) { fIntBuffer[port_index][frame] = short(fPortBuffer[port_index][frame] * 32767.f); } } else { memset(fIntBuffer[port_index], 0, fPeriodSize * sizeof(short)); } } // All ports active return fNPorts; } void NetIntAudioBuffer::RenderToJackPorts(int nframes) { float coef = 1.f / 32767.f; for (int port_index = 0; port_index < fNPorts; port_index++) { if (fPortBuffer[port_index]) { for (int frame = 0; frame < nframes; frame++) { fPortBuffer[port_index][frame] = float(fIntBuffer[port_index][frame] * coef); } } } NextCycle(); } //network<->buffer int NetIntAudioBuffer::RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) { // Cleanup all JACK ports at the beginning of the cycle if (sub_cycle == 0) { Cleanup(); } if (port_num > 0) { int sub_period_bytes_size; // Last packet if (sub_cycle == fNumPackets - 1) { sub_period_bytes_size = fLastSubPeriodBytesSize; } else { sub_period_bytes_size = fSubPeriodBytesSize; } for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fIntBuffer[port_index] + sub_cycle * fSubPeriodSize, fNetBuffer + port_index * sub_period_bytes_size, sub_period_bytes_size); } } return CheckPacket(cycle, sub_cycle); } int NetIntAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) { int sub_period_bytes_size; // Last packet if (sub_cycle == fNumPackets - 1) { sub_period_bytes_size = fLastSubPeriodBytesSize; } else { sub_period_bytes_size = fSubPeriodBytesSize; } for (int port_index = 0; port_index < fNPorts; port_index++) { memcpy(fNetBuffer + port_index * sub_period_bytes_size, fIntBuffer[port_index] + sub_cycle * fSubPeriodSize, sub_period_bytes_size); } return fNPorts * sub_period_bytes_size; } // SessionParams ************************************************************************************ SERVER_EXPORT void SessionParamsHToN(session_params_t* src_params, session_params_t* dst_params) { memcpy(dst_params, src_params, sizeof(session_params_t)); dst_params->fProtocolVersion = htonl(src_params->fProtocolVersion); dst_params->fPacketID = htonl(src_params->fPacketID); dst_params->fMtu = htonl(src_params->fMtu); dst_params->fID = htonl(src_params->fID); dst_params->fTransportSync = htonl(src_params->fTransportSync); dst_params->fSendAudioChannels = htonl(src_params->fSendAudioChannels); dst_params->fReturnAudioChannels = htonl(src_params->fReturnAudioChannels); dst_params->fSendMidiChannels = htonl(src_params->fSendMidiChannels); dst_params->fReturnMidiChannels = htonl(src_params->fReturnMidiChannels); dst_params->fSampleRate = htonl(src_params->fSampleRate); dst_params->fPeriodSize = htonl(src_params->fPeriodSize); dst_params->fSampleEncoder = htonl(src_params->fSampleEncoder); dst_params->fKBps = htonl(src_params->fKBps); dst_params->fSlaveSyncMode = htonl(src_params->fSlaveSyncMode); dst_params->fNetworkLatency = htonl(src_params->fNetworkLatency); } SERVER_EXPORT void SessionParamsNToH(session_params_t* src_params, session_params_t* dst_params) { memcpy(dst_params, src_params, sizeof(session_params_t)); dst_params->fProtocolVersion = ntohl(src_params->fProtocolVersion); dst_params->fPacketID = ntohl(src_params->fPacketID); dst_params->fMtu = ntohl(src_params->fMtu); dst_params->fID = ntohl(src_params->fID); dst_params->fTransportSync = ntohl(src_params->fTransportSync); dst_params->fSendAudioChannels = ntohl(src_params->fSendAudioChannels); dst_params->fReturnAudioChannels = ntohl(src_params->fReturnAudioChannels); dst_params->fSendMidiChannels = ntohl(src_params->fSendMidiChannels); dst_params->fReturnMidiChannels = ntohl(src_params->fReturnMidiChannels); dst_params->fSampleRate = ntohl(src_params->fSampleRate); dst_params->fPeriodSize = ntohl(src_params->fPeriodSize); dst_params->fSampleEncoder = ntohl(src_params->fSampleEncoder); dst_params->fKBps = ntohl(src_params->fKBps); dst_params->fSlaveSyncMode = ntohl(src_params->fSlaveSyncMode); dst_params->fNetworkLatency = ntohl(src_params->fNetworkLatency); } SERVER_EXPORT void SessionParamsDisplay(session_params_t* params) { char encoder[16]; switch (params->fSampleEncoder) { case JackFloatEncoder: strcpy(encoder, "float"); break; case JackIntEncoder: strcpy(encoder, "integer"); break; case JackCeltEncoder: strcpy(encoder, "CELT"); break; case JackOpusEncoder: strcpy(encoder, "OPUS"); break; } jack_info("**************** Network parameters ****************"); jack_info("Name : %s", params->fName); jack_info("Protocol revision : %d", params->fProtocolVersion); jack_info("MTU : %u", params->fMtu); jack_info("Master name : %s", params->fMasterNetName); jack_info("Slave name : %s", params->fSlaveNetName); jack_info("ID : %u", params->fID); jack_info("Transport Sync : %s", (params->fTransportSync) ? "yes" : "no"); jack_info("Send channels (audio - midi) : %d - %d", params->fSendAudioChannels, params->fSendMidiChannels); jack_info("Return channels (audio - midi) : %d - %d", params->fReturnAudioChannels, params->fReturnMidiChannels); jack_info("Sample rate : %u frames per second", params->fSampleRate); jack_info("Period size : %u frames per period", params->fPeriodSize); jack_info("Network latency : %u cycles", params->fNetworkLatency); switch (params->fSampleEncoder) { case (JackFloatEncoder): jack_info("SampleEncoder : %s", "Float"); break; case (JackIntEncoder): jack_info("SampleEncoder : %s", "16 bits integer"); break; case (JackCeltEncoder): jack_info("SampleEncoder : %s", "CELT"); jack_info("kBits : %d", params->fKBps); break; case (JackOpusEncoder): jack_info("SampleEncoder : %s", "OPUS"); jack_info("kBits : %d", params->fKBps); break; }; jack_info("Slave mode : %s", (params->fSlaveSyncMode) ? "sync" : "async"); jack_info("****************************************************"); } SERVER_EXPORT sync_packet_type_t GetPacketType(session_params_t* params) { switch (params->fPacketID) { case 0: return SLAVE_AVAILABLE; case 1: return SLAVE_SETUP; case 2: return START_MASTER; case 3: return START_SLAVE; case 4: return KILL_MASTER; } return INVALID; } SERVER_EXPORT int SetPacketType(session_params_t* params, sync_packet_type_t packet_type) { switch (packet_type) { case INVALID: return -1; case SLAVE_AVAILABLE: params->fPacketID = 0; break; case SLAVE_SETUP: params->fPacketID = 1; break; case START_MASTER: params->fPacketID = 2; break; case START_SLAVE: params->fPacketID = 3; break; case KILL_MASTER: params->fPacketID = 4; } return 0; } // Packet header ********************************************************************************** SERVER_EXPORT void PacketHeaderHToN(packet_header_t* src_header, packet_header_t* dst_header) { memcpy(dst_header, src_header, sizeof(packet_header_t)); dst_header->fDataType = htonl(src_header->fDataType); dst_header->fDataStream = htonl(src_header->fDataStream); dst_header->fID = htonl(src_header->fID); dst_header->fNumPacket = htonl(src_header->fNumPacket); dst_header->fPacketSize = htonl(src_header->fPacketSize); dst_header->fActivePorts = htonl(src_header->fActivePorts); dst_header->fCycle = htonl(src_header->fCycle); dst_header->fSubCycle = htonl(src_header->fSubCycle); dst_header->fFrames = htonl(src_header->fFrames); dst_header->fIsLastPckt = htonl(src_header->fIsLastPckt); } SERVER_EXPORT void PacketHeaderNToH(packet_header_t* src_header, packet_header_t* dst_header) { memcpy(dst_header, src_header, sizeof(packet_header_t)); dst_header->fDataType = ntohl(src_header->fDataType); dst_header->fDataStream = ntohl(src_header->fDataStream); dst_header->fID = ntohl(src_header->fID); dst_header->fNumPacket = ntohl(src_header->fNumPacket); dst_header->fPacketSize = ntohl(src_header->fPacketSize); dst_header->fActivePorts = ntohl(src_header->fActivePorts); dst_header->fCycle = ntohl(src_header->fCycle); dst_header->fSubCycle = ntohl(src_header->fSubCycle); dst_header->fFrames = ntohl(src_header->fFrames); dst_header->fIsLastPckt = ntohl(src_header->fIsLastPckt); } SERVER_EXPORT void PacketHeaderDisplay(packet_header_t* header) { jack_info("********************Header********************"); jack_info("Data type : %c", header->fDataType); jack_info("Data stream : %c", header->fDataStream); jack_info("ID : %u", header->fID); jack_info("Cycle : %u", header->fCycle); jack_info("SubCycle : %u", header->fSubCycle); jack_info("Active ports : %u", header->fActivePorts); jack_info("DATA packets : %u", header->fNumPacket); jack_info("DATA size : %u", header->fPacketSize); jack_info("DATA frames : %d", header->fFrames); jack_info("Last packet : '%s'", (header->fIsLastPckt) ? "yes" : "no"); jack_info("**********************************************"); } SERVER_EXPORT void NetTransportDataDisplay(net_transport_data_t* data) { jack_info("********************Network Transport********************"); jack_info("Transport new state : %u", data->fNewState); jack_info("Transport timebase master : %u", data->fTimebaseMaster); jack_info("Transport cycle state : %u", data->fState); jack_info("**********************************************"); } SERVER_EXPORT void MidiBufferHToN(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer) { dst_buffer->magic = htonl(src_buffer->magic); dst_buffer->buffer_size = htonl(src_buffer->buffer_size); dst_buffer->nframes = htonl(src_buffer->nframes); dst_buffer->write_pos = htonl(src_buffer->write_pos); dst_buffer->event_count = htonl(src_buffer->event_count); dst_buffer->lost_events = htonl(src_buffer->lost_events); } SERVER_EXPORT void MidiBufferNToH(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer) { dst_buffer->magic = ntohl(src_buffer->magic); dst_buffer->buffer_size = ntohl(src_buffer->buffer_size); dst_buffer->nframes = ntohl(src_buffer->nframes); dst_buffer->write_pos = ntohl(src_buffer->write_pos); dst_buffer->event_count = ntohl(src_buffer->event_count); dst_buffer->lost_events = ntohl(src_buffer->lost_events); } SERVER_EXPORT void TransportDataHToN(net_transport_data_t* src_params, net_transport_data_t* dst_params) { dst_params->fNewState = htonl(src_params->fNewState); dst_params->fTimebaseMaster = htonl(src_params->fTimebaseMaster); dst_params->fState = htonl(src_params->fState); dst_params->fPosition.unique_1 = htonll(src_params->fPosition.unique_1); dst_params->fPosition.usecs = htonl(src_params->fPosition.usecs); dst_params->fPosition.frame_rate = htonl(src_params->fPosition.frame_rate); dst_params->fPosition.frame = htonl(src_params->fPosition.frame); dst_params->fPosition.valid = (jack_position_bits_t)htonl((uint32_t)src_params->fPosition.valid); dst_params->fPosition.bar = htonl(src_params->fPosition.bar); dst_params->fPosition.beat = htonl(src_params->fPosition.beat); dst_params->fPosition.tick = htonl(src_params->fPosition.tick); dst_params->fPosition.bar_start_tick = htonll((uint64_t)src_params->fPosition.bar_start_tick); dst_params->fPosition.beats_per_bar = htonl((uint32_t)src_params->fPosition.beats_per_bar); dst_params->fPosition.beat_type = htonl((uint32_t)src_params->fPosition.beat_type); dst_params->fPosition.ticks_per_beat = htonll((uint64_t)src_params->fPosition.ticks_per_beat); dst_params->fPosition.beats_per_minute = htonll((uint64_t)src_params->fPosition.beats_per_minute); dst_params->fPosition.frame_time = htonll((uint64_t)src_params->fPosition.frame_time); dst_params->fPosition.next_time = htonll((uint64_t)src_params->fPosition.next_time); dst_params->fPosition.bbt_offset = htonl(src_params->fPosition.bbt_offset); dst_params->fPosition.audio_frames_per_video_frame = htonl((uint32_t)src_params->fPosition.audio_frames_per_video_frame); dst_params->fPosition.video_offset = htonl(src_params->fPosition.video_offset); dst_params->fPosition.unique_2 = htonll(src_params->fPosition.unique_2); } SERVER_EXPORT void TransportDataNToH(net_transport_data_t* src_params, net_transport_data_t* dst_params) { dst_params->fNewState = ntohl(src_params->fNewState); dst_params->fTimebaseMaster = ntohl(src_params->fTimebaseMaster); dst_params->fState = ntohl(src_params->fState); dst_params->fPosition.unique_1 = ntohll(src_params->fPosition.unique_1); dst_params->fPosition.usecs = ntohl(src_params->fPosition.usecs); dst_params->fPosition.frame_rate = ntohl(src_params->fPosition.frame_rate); dst_params->fPosition.frame = ntohl(src_params->fPosition.frame); dst_params->fPosition.valid = (jack_position_bits_t)ntohl((uint32_t)src_params->fPosition.valid); dst_params->fPosition.bar = ntohl(src_params->fPosition.bar); dst_params->fPosition.beat = ntohl(src_params->fPosition.beat); dst_params->fPosition.tick = ntohl(src_params->fPosition.tick); dst_params->fPosition.bar_start_tick = ntohll((uint64_t)src_params->fPosition.bar_start_tick); dst_params->fPosition.beats_per_bar = ntohl((uint32_t)src_params->fPosition.beats_per_bar); dst_params->fPosition.beat_type = ntohl((uint32_t)src_params->fPosition.beat_type); dst_params->fPosition.ticks_per_beat = ntohll((uint64_t)src_params->fPosition.ticks_per_beat); dst_params->fPosition.beats_per_minute = ntohll((uint64_t)src_params->fPosition.beats_per_minute); dst_params->fPosition.frame_time = ntohll((uint64_t)src_params->fPosition.frame_time); dst_params->fPosition.next_time = ntohll((uint64_t)src_params->fPosition.next_time); dst_params->fPosition.bbt_offset = ntohl(src_params->fPosition.bbt_offset); dst_params->fPosition.audio_frames_per_video_frame = ntohl((uint32_t)src_params->fPosition.audio_frames_per_video_frame); dst_params->fPosition.video_offset = ntohl(src_params->fPosition.video_offset); dst_params->fPosition.unique_2 = ntohll(src_params->fPosition.unique_2); } // Utility ******************************************************************************************************* SERVER_EXPORT int SocketAPIInit() { #ifdef WIN32 WORD wVersionRequested = MAKEWORD(2, 2); WSADATA wsaData; if (WSAStartup(wVersionRequested, &wsaData) != 0) { jack_error("WSAStartup error : %s", strerror(NET_ERROR_CODE)); return -1; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { jack_error("Could not find a usable version of Winsock.dll\n"); WSACleanup(); return -1; } #endif return 0; } SERVER_EXPORT int SocketAPIEnd() { #ifdef WIN32 return WSACleanup(); #endif return 0; } SERVER_EXPORT const char* GetTransportState(int transport_state) { switch (transport_state) { case JackTransportRolling: return "rolling"; case JackTransportStarting: return "starting"; case JackTransportStopped: return "stopped"; case JackTransportNetStarting: return "netstarting"; } return NULL; } } jack2-1.9.22/common/JackNetTool.h000066400000000000000000000427461436671425200164440ustar00rootroot00000000000000/* Copyright (C) 2008-2011 Romain Moret at Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackMidiPort.h" #include "JackTools.h" #include "types.h" #include "transport.h" #ifndef WIN32 #include #endif #include using namespace std; #ifndef htonll #ifdef __BIG_ENDIAN__ #define htonll(x) (x) #define ntohll(x) (x) #else #define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl(x >> 32)) #define ntohll(x) ((((uint64_t)ntohl(x)) << 32) + ntohl(x >> 32)) #endif #endif #define NETWORK_PROTOCOL 8 #define NET_SYNCHING 0 #define SYNC_PACKET_ERROR -2 #define DATA_PACKET_ERROR -3 #define OPTIMIZED_PROTOCOL 1 #define UDP_HEADER_SIZE 64 // 40 bytes for IP header in IPV6, 20 in IPV4, 8 for UDP, so take 64 #define HEADER_SIZE (sizeof(packet_header_t)) #define PACKET_AVAILABLE_SIZE(params) ((params)->fMtu - UDP_HEADER_SIZE - HEADER_SIZE) namespace Jack { typedef struct _session_params session_params_t; typedef struct _packet_header packet_header_t; typedef struct _net_transport_data net_transport_data_t; typedef struct sockaddr socket_address_t; typedef struct in_addr address_t; typedef jack_default_audio_sample_t sample_t; enum JackNetEncoder { JackFloatEncoder = 0, JackIntEncoder = 1, JackCeltEncoder = 2, JackOpusEncoder = 3, }; //session params ****************************************************************************** /** \brief This structure contains master/slave connection parameters, it's used to setup the whole system We have : - some info like version, type and packet id - names - network parameters (hostnames and mtu) - number of audio and midi channels - sample rate and buffersize - number of audio frames in one network packet (depends on the channel number) - is the NetDriver in Sync or ASync mode ? - is the NetDriver linked with the master's transport Data encoding : headers (session_params and packet_header) are encoded using HTN kind of functions but float data are kept in LITTLE_ENDIAN format (to avoid 2 conversions in the more common LITTLE_ENDIAN <==> LITTLE_ENDIAN connection case). */ PRE_PACKED_STRUCTURE struct _session_params { char fPacketType[8]; //packet type ('param') uint32_t fProtocolVersion; //version int32_t fPacketID; //indicates the packet type char fName[JACK_CLIENT_NAME_SIZE]; //slave's name char fMasterNetName[JACK_SERVER_NAME_SIZE]; //master hostname (network) char fSlaveNetName[JACK_SERVER_NAME_SIZE]; //slave hostname (network) uint32_t fMtu; //connection mtu uint32_t fID; //slave's ID uint32_t fTransportSync; //is the transport synced ? int32_t fSendAudioChannels; //number of master->slave channels int32_t fReturnAudioChannels; //number of slave->master channels int32_t fSendMidiChannels; //number of master->slave midi channels int32_t fReturnMidiChannels; //number of slave->master midi channels uint32_t fSampleRate; //session sample rate uint32_t fPeriodSize; //period size uint32_t fSampleEncoder; //samples encoder uint32_t fKBps; //KB per second for CELT encoder uint32_t fSlaveSyncMode; //is the slave in sync mode ? uint32_t fNetworkLatency; //network latency } POST_PACKED_STRUCTURE; //net status ********************************************************************************** /** \Brief This enum groups network error by type */ enum _net_status { NET_SOCKET_ERROR = 0, NET_CONNECT_ERROR, NET_ERROR, NET_SEND_ERROR, NET_RECV_ERROR, NET_CONNECTED, NET_ROLLING }; typedef enum _net_status net_status_t; //sync packet type **************************************************************************** /** \Brief This enum indicates the type of a sync packet (used in the initialization phase) */ enum _sync_packet_type { INVALID = 0, //... SLAVE_AVAILABLE, //a slave is available SLAVE_SETUP, //slave configuration START_MASTER, //slave is ready, start master START_SLAVE, //master is ready, activate slave KILL_MASTER //master must stop }; typedef enum _sync_packet_type sync_packet_type_t; //packet header ******************************************************************************* /** \Brief This structure is a complete header A header indicates : - it is a header - the type of data the packet contains (sync, midi or audio) - the path of the packet (send -master->slave- or return -slave->master-) - the unique ID of the slave - the sample's bitdepth (unused for now) - the size of the midi data contains in the packet (indicates how much midi data will be sent) - the number of midi packet(s) : more than one is very unusual, it depends on the midi load - the ID of the current cycle (used to check missing packets) - the ID of the packet subcycle (for audio data) - a flag indicating this packet is the last of the cycle (for sync robustness, it's better to process this way) - a flag indicating if, in async mode, the previous graph was not finished or not - padding to fill 64 bytes */ PRE_PACKED_STRUCTURE struct _packet_header { char fPacketType[8]; //packet type ('headr') uint32_t fDataType; //'a' for audio, 'm' for midi and 's' for sync uint32_t fDataStream; //'s' for send, 'r' for return uint32_t fID; //unique ID of the slave uint32_t fNumPacket; //number of data packets of the cycle uint32_t fPacketSize; //packet size in bytes uint32_t fActivePorts; //number of active ports uint32_t fCycle; //process cycle counter uint32_t fSubCycle; //midi/audio subcycle counter int32_t fFrames; //process cycle size in frames (can be -1 to indicate entire buffer) uint32_t fIsLastPckt; //is it the last packet of a given cycle ('y' or 'n') } POST_PACKED_STRUCTURE; //net timebase master /** \Brief This enum describes timebase master's type */ enum _net_timebase_master { NO_CHANGE = 0, RELEASE_TIMEBASEMASTER = 1, TIMEBASEMASTER = 2, CONDITIONAL_TIMEBASEMASTER = 3 }; typedef enum _net_timebase_master net_timebase_master_t; //transport data ****************************************************************************** /** \Brief This structure contains transport data to be sent over the network */ PRE_PACKED_STRUCTURE struct _net_transport_data { uint32_t fNewState; //is it a state change uint32_t fTimebaseMaster; //is there a new timebase master int32_t fState; //current cycle state jack_position_t fPosition; //current cycle position } POST_PACKED_STRUCTURE; //midi data *********************************************************************************** /** \Brief Midi buffer and operations class This class is a toolset to manipulate Midi buffers. A JackMidiBuffer has a fixed size, which is the same than an audio buffer size. An intermediate fixed size buffer allows to uninterleave midi data (from jack ports). But for a big majority of the process cycles, this buffer is filled less than 1%, Sending over a network 99% of useless data seems completely unappropriate. The idea is to count effective midi data, and then send the smallest packet we can. To do it, we use an intermediate buffer. We have two methods to convert data from jack ports to intermediate buffer, And two others to convert this intermediate buffer to a network buffer (header + payload data) */ class SERVER_EXPORT NetMidiBuffer { private: int fNPorts; size_t fMaxBufsize; int fMaxPcktSize; char* fBuffer; char* fNetBuffer; JackMidiBuffer** fPortBuffer; size_t fCycleBytesSize; // needed size in bytes ofr an entire cycle public: NetMidiBuffer(session_params_t* params, uint32_t nports, char* net_buffer); ~NetMidiBuffer(); void Reset(); // needed size in bytes for an entire cycle size_t GetCycleSize(); int GetNumPackets(int data_sizen, int max_size); void SetBuffer(int index, JackMidiBuffer* buffer); JackMidiBuffer* GetBuffer(int index); //utility void DisplayEvents(); //jack<->buffer int RenderFromJackPorts(); void RenderToJackPorts(); //network<->buffer void RenderFromNetwork(int sub_cycle, size_t copy_size); int RenderToNetwork(int sub_cycle, size_t total_size); }; // audio data ********************************************************************************* class SERVER_EXPORT NetAudioBuffer { protected: int fNPorts; int fLastSubCycle; int fNumPackets; char* fNetBuffer; sample_t** fPortBuffer; bool* fConnectedPorts; jack_nframes_t fPeriodSize; jack_nframes_t fSubPeriodSize; size_t fSubPeriodBytesSize; float fCycleDuration; // in sec size_t fCycleBytesSize; // needed size in bytes for an entire cycle int CheckPacket(int cycle, int sub_cycle); void NextCycle(); void Cleanup(); public: NetAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer); virtual ~NetAudioBuffer(); bool GetConnected(int port_index) { return fConnectedPorts[port_index]; } void SetConnected(int port_index, bool state) { fConnectedPorts[port_index] = state; } // needed syze in bytes ofr an entire cycle virtual size_t GetCycleSize() = 0; // cycle duration in sec virtual float GetCycleDuration() = 0; virtual int GetNumPackets(int active_ports) = 0; virtual void SetBuffer(int index, sample_t* buffer); virtual sample_t* GetBuffer(int index); //jack<->buffer virtual int RenderFromJackPorts(int nframes); virtual void RenderToJackPorts(int nframes); //network<->buffer virtual int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num) = 0; virtual int RenderToNetwork(int sub_cycle, uint32_t port_num) = 0; virtual int ActivePortsToNetwork(char* net_buffer); virtual void ActivePortsFromNetwork(char* net_buffer, uint32_t port_num); }; class SERVER_EXPORT NetFloatAudioBuffer : public NetAudioBuffer { private: int fPacketSize; void UpdateParams(int active_ports); void RenderFromNetwork(char* net_buffer, int active_port, int sub_cycle); void RenderToNetwork(char* net_buffer, int active_port, int sub_cycle); public: NetFloatAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer); virtual ~NetFloatAudioBuffer(); // needed size in bytes for an entire cycle size_t GetCycleSize(); // cycle duration in sec float GetCycleDuration(); int GetNumPackets(int active_ports); //jack<->buffer int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num); int RenderToNetwork(int sub_cycle, uint32_t port_num); }; #if HAVE_CELT #include class SERVER_EXPORT NetCeltAudioBuffer : public NetAudioBuffer { private: CELTMode** fCeltMode; CELTEncoder** fCeltEncoder; CELTDecoder** fCeltDecoder; int fCompressedSizeByte; unsigned char** fCompressedBuffer; size_t fLastSubPeriodBytesSize; void FreeCelt(); public: NetCeltAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps); virtual ~NetCeltAudioBuffer(); // needed size in bytes for an entire cycle size_t GetCycleSize(); // cycle duration in sec float GetCycleDuration(); int GetNumPackets(int active_ports); //jack<->buffer int RenderFromJackPorts(int nframes); void RenderToJackPorts(int nframes); //network<->buffer int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num); int RenderToNetwork(int sub_cycle, uint32_t port_num); }; #endif #if HAVE_OPUS #include #include class SERVER_EXPORT NetOpusAudioBuffer : public NetAudioBuffer { private: OpusCustomMode** fOpusMode; OpusCustomEncoder** fOpusEncoder; OpusCustomDecoder** fOpusDecoder; int fCompressedMaxSizeByte; unsigned short* fCompressedSizesByte; size_t fLastSubPeriodBytesSize; unsigned char** fCompressedBuffer; void FreeOpus(); public: NetOpusAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps); virtual ~NetOpusAudioBuffer(); // needed size in bytes for an entire cycle size_t GetCycleSize(); // cycle duration in sec float GetCycleDuration(); int GetNumPackets(int active_ports); //jack<->buffer int RenderFromJackPorts(int nframes); void RenderToJackPorts(int nframes); //network<->buffer int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num); int RenderToNetwork(int sub_cycle, uint32_t port_num); }; #endif class SERVER_EXPORT NetIntAudioBuffer : public NetAudioBuffer { private: int fCompressedSizeByte; size_t fLastSubPeriodBytesSize; short** fIntBuffer; public: NetIntAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer); virtual ~NetIntAudioBuffer(); // needed size in bytes for an entire cycle size_t GetCycleSize(); // cycle duration in sec float GetCycleDuration(); int GetNumPackets(int active_ports); //jack<->buffer int RenderFromJackPorts(int nframes); void RenderToJackPorts(int nframes); //network<->buffer int RenderFromNetwork(int cycle, int sub_cycle, uint32_t port_num); int RenderToNetwork(int sub_cycle, uint32_t port_num); }; //utility ************************************************************************************* //socket API management SERVER_EXPORT int SocketAPIInit(); SERVER_EXPORT int SocketAPIEnd(); //n<-->h functions SERVER_EXPORT void SessionParamsHToN(session_params_t* src_params, session_params_t* dst_params); SERVER_EXPORT void SessionParamsNToH(session_params_t* src_params, session_params_t* dst_params); SERVER_EXPORT void PacketHeaderHToN(packet_header_t* src_header, packet_header_t* dst_header); SERVER_EXPORT void PacketHeaderNToH(packet_header_t* src_header, packet_header_t* dst_header); SERVER_EXPORT void MidiBufferHToN(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer); SERVER_EXPORT void MidiBufferNToH(JackMidiBuffer* src_buffer, JackMidiBuffer* dst_buffer); SERVER_EXPORT void TransportDataHToN(net_transport_data_t* src_params, net_transport_data_t* dst_params); SERVER_EXPORT void TransportDataNToH(net_transport_data_t* src_params, net_transport_data_t* dst_params); //display session parameters SERVER_EXPORT void SessionParamsDisplay(session_params_t* params); //display packet header SERVER_EXPORT void PacketHeaderDisplay(packet_header_t* header); //get the packet type from a session parameters SERVER_EXPORT sync_packet_type_t GetPacketType(session_params_t* params); //set the packet type in a session parameters SERVER_EXPORT int SetPacketType(session_params_t* params, sync_packet_type_t packet_type); //transport utility SERVER_EXPORT const char* GetTransportState(int transport_state); SERVER_EXPORT void NetTransportDataDisplay(net_transport_data_t* data); } jack2-1.9.22/common/JackNotification.h000066400000000000000000000030651436671425200174750ustar00rootroot00000000000000/* Copyright (C) 2007 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackNotification__ #define __JackNotification__ namespace Jack { /*! \brief Notifications sent by the server for clients. */ enum NotificationType { kAddClient = 0, kRemoveClient = 1, kActivateClient = 2, kXRunCallback = 3, kGraphOrderCallback = 4, kBufferSizeCallback = 5, kSampleRateCallback = 6, kStartFreewheelCallback = 7, kStopFreewheelCallback = 8, kPortRegistrationOnCallback = 9, kPortRegistrationOffCallback = 10, kPortConnectCallback = 11, kPortDisconnectCallback = 12, kPortRenameCallback = 13, kRealTimeCallback = 14, kShutDownCallback = 15, kQUIT = 16, kSessionCallback = 17, kLatencyCallback = 18, kPropertyChangeCallback = 19, kMaxNotification = 64 // To keep some room in JackClientControl fCallback table }; } // end of namespace #endif jack2-1.9.22/common/JackPlatformPlug.h000066400000000000000000000015101436671425200174540ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackPlatformPlug__ #define __JackPlatformPlug__ #include "JackPlatformPlug_os.h" #endif jack2-1.9.22/common/JackPort.cpp000066400000000000000000000160161436671425200163260ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackPort.h" #include "JackError.h" #include "JackPortType.h" #include #include namespace Jack { JackPort::JackPort() { Release(); } bool JackPort::Allocate(int refnum, const char* port_name, const char* port_type, JackPortFlags flags) { jack_port_type_id_t id = GetPortTypeId(port_type); assert(id >= 0 && id <= PORT_TYPES_MAX); if (id == PORT_TYPES_MAX) { return false; } fTypeId = id; fFlags = flags; fRefNum = refnum; strcpy(fName, port_name); fInUse = true; fLatency = 0; fTotalLatency = 0; fMonitorRequests = 0; fPlaybackLatency.min = fPlaybackLatency.max = 0; fCaptureLatency.min = fCaptureLatency.max = 0; fTied = NO_PORT; fAlias1[0] = '\0'; fAlias2[0] = '\0'; // DB: At this point we do not know current buffer size in frames, // but every time buffer will be returned to any user, // it will be called with either ClearBuffer or MixBuffers // with correct current buffer size. // So it is safe to init with 0 here. ClearBuffer(0); return true; } void JackPort::Release() { fTypeId = 0; fFlags = JackPortIsInput; fRefNum = -1; fInUse = false; fLatency = 0; fTotalLatency = 0; fMonitorRequests = 0; fPlaybackLatency.min = fPlaybackLatency.max = 0; fCaptureLatency.min = fCaptureLatency.max = 0; fTied = NO_PORT; fAlias1[0] = '\0'; fAlias2[0] = '\0'; } int JackPort::GetRefNum() const { return fRefNum; } jack_nframes_t JackPort::GetLatency() const { return fLatency; } jack_nframes_t JackPort::GetTotalLatency() const { return fTotalLatency; } void JackPort::SetLatency(jack_nframes_t nframes) { fLatency = nframes; /* setup the new latency values here, * so we don't need to change the backend codes. */ if (fFlags & JackPortIsOutput) { fCaptureLatency.min = nframes; fCaptureLatency.max = nframes; } if (fFlags & JackPortIsInput) { fPlaybackLatency.min = nframes; fPlaybackLatency.max = nframes; } } void JackPort::SetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range) { if (mode == JackCaptureLatency) { fCaptureLatency = *range; /* hack to set latency up for * backend ports */ if ((fFlags & JackPortIsOutput) && (fFlags & JackPortIsPhysical)) { fLatency = (range->min + range->max) / 2; } } else { fPlaybackLatency = *range; /* hack to set latency up for * backend ports */ if ((fFlags & JackPortIsInput) && (fFlags & JackPortIsPhysical)) { fLatency = (range->min + range->max) / 2; } } } void JackPort::GetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range) const { if (mode == JackCaptureLatency) { *range = fCaptureLatency; } else { *range = fPlaybackLatency; } } int JackPort::Tie(jack_port_id_t port_index) { fTied = port_index; return 0; } int JackPort::UnTie() { fTied = NO_PORT; return 0; } int JackPort::RequestMonitor(bool onoff) { /** jackd.h * If @ref JackPortCanMonitor is set for this @a port, turn input * monitoring on or off. Otherwise, do nothing. if (!(fFlags & JackPortCanMonitor)) return -1; */ if (onoff) { fMonitorRequests++; } else if (fMonitorRequests) { fMonitorRequests--; } return 0; } int JackPort::EnsureMonitor(bool onoff) { /** jackd.h * If @ref JackPortCanMonitor is set for this @a port, turn input * monitoring on or off. Otherwise, do nothing. if (!(fFlags & JackPortCanMonitor)) return -1; */ if (onoff) { if (fMonitorRequests == 0) { fMonitorRequests++; } } else { if (fMonitorRequests > 0) { fMonitorRequests = 0; } } return 0; } const char* JackPort::GetName() const { return fName; } const char* JackPort::GetShortName() const { /* we know there is always a colon, because we put it there ... */ return strchr(fName, ':') + 1; } int JackPort::GetFlags() const { return fFlags; } const char* JackPort::GetType() const { const JackPortType* type = GetPortType(fTypeId); return type->fName; } void JackPort::SetName(const char* new_name) { char* colon = strchr(fName, ':'); int len = sizeof(fName) - ((int) (colon - fName)) - 2; snprintf(colon + 1, len, "%s", new_name); } bool JackPort::NameEquals(const char* target) { char buf[REAL_JACK_PORT_NAME_SIZE+1]; /* this nasty, nasty kludge is here because between 0.109.0 and 0.109.1, the ALSA audio backend had the name "ALSA", whereas as before and after it, it was called "alsa_pcm". this stops breakage for any setups that have saved "alsa_pcm" or "ALSA" in their connection state. */ if (strncmp(target, "ALSA:capture", 12) == 0 || strncmp(target, "ALSA:playback", 13) == 0) { snprintf(buf, sizeof(buf), "alsa_pcm%s", target + 4); target = buf; } return (strcmp(fName, target) == 0 || strcmp(fAlias1, target) == 0 || strcmp(fAlias2, target) == 0); } int JackPort::GetAliases(char* const aliases[2]) { int cnt = 0; if (fAlias1[0] != '\0') { strncpy(aliases[0], fAlias1, REAL_JACK_PORT_NAME_SIZE); cnt++; } if (fAlias2[0] != '\0') { strncpy(aliases[1], fAlias2, REAL_JACK_PORT_NAME_SIZE); cnt++; } return cnt; } int JackPort::SetAlias(const char* alias) { if (fAlias1[0] == '\0') { strncpy(fAlias1, alias, sizeof(fAlias1)); } else if (fAlias2[0] == '\0') { strncpy(fAlias2, alias, sizeof(fAlias2)); } else { return -1; } return 0; } int JackPort::UnsetAlias(const char* alias) { if (strcmp(fAlias1, alias) == 0) { fAlias1[0] = '\0'; } else if (strcmp(fAlias2, alias) == 0) { fAlias2[0] = '\0'; } else { return -1; } return 0; } void JackPort::ClearBuffer(jack_nframes_t frames) { const JackPortType* type = GetPortType(fTypeId); (type->init)(GetBuffer(), frames * sizeof(jack_default_audio_sample_t), frames); } void JackPort::MixBuffers(void** src_buffers, int src_count, jack_nframes_t buffer_size) { const JackPortType* type = GetPortType(fTypeId); (type->mixdown)(GetBuffer(), src_buffers, src_count, buffer_size); } } // end of namespace jack2-1.9.22/common/JackPort.h000066400000000000000000000064211436671425200157720ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackPort__ #define __JackPort__ #include "types.h" #include "JackConstants.h" #include "JackCompilerDeps.h" namespace Jack { #define ALL_PORTS 0xFFFF #define NO_PORT 0xFFFE /*! \brief Base class for port. */ PRE_PACKED_STRUCTURE class SERVER_EXPORT JackPort { friend class JackGraphManager; private: int fTypeId; enum JackPortFlags fFlags; char fName[REAL_JACK_PORT_NAME_SIZE+1]; char fAlias1[REAL_JACK_PORT_NAME_SIZE+1]; char fAlias2[REAL_JACK_PORT_NAME_SIZE+1]; int fRefNum; jack_nframes_t fLatency; jack_nframes_t fTotalLatency; jack_latency_range_t fPlaybackLatency; jack_latency_range_t fCaptureLatency; uint8_t fMonitorRequests; bool fInUse; jack_port_id_t fTied; // Locally tied source port jack_default_audio_sample_t fBuffer[BUFFER_SIZE_MAX + 8]; bool IsUsed() const { return fInUse; } // RT void ClearBuffer(jack_nframes_t frames); void MixBuffers(void** src_buffers, int src_count, jack_nframes_t frames); public: JackPort(); bool Allocate(int refnum, const char* port_name, const char* port_type, JackPortFlags flags); void Release(); const char* GetName() const; const char* GetShortName() const; void SetName(const char* name); int GetAliases(char* const aliases[2]); int SetAlias(const char* alias); int UnsetAlias(const char* alias); bool NameEquals(const char* target); int GetFlags() const; const char* GetType() const; int Tie(jack_port_id_t port_index); int UnTie(); jack_nframes_t GetLatency() const; void SetLatency(jack_nframes_t latency); void SetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range); void GetLatencyRange(jack_latency_callback_mode_t mode, jack_latency_range_t* range) const; jack_nframes_t GetTotalLatency() const; int RequestMonitor(bool onoff); int EnsureMonitor(bool onoff); bool MonitoringInput() { return (fMonitorRequests > 0); } // Since we are in shared memory, the resulting pointer cannot be cached, so align it here... jack_default_audio_sample_t* GetBuffer() { return (jack_default_audio_sample_t*)((uintptr_t)fBuffer & ~31L) + 8; } int GetRefNum() const; } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackPortType.cpp000066400000000000000000000030321436671425200171620ustar00rootroot00000000000000/* Copyright (C) 2007 Dmitry Baikov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackPortType.h" #include #include namespace Jack { static const JackPortType* gPortTypes[] = { &gAudioPortType, &gMidiPortType, }; const jack_port_type_id_t PORT_TYPES_MAX = sizeof(gPortTypes) / sizeof(gPortTypes[0]); jack_port_type_id_t GetPortTypeId(const char* port_type) { for (jack_port_type_id_t i = 0; i < PORT_TYPES_MAX; ++i) { const JackPortType* type = gPortTypes[i]; assert(type != NULL); if (strcmp(port_type, type->fName) == 0) { return i; } } return PORT_TYPES_MAX; } const JackPortType* GetPortType(jack_port_type_id_t type_id) { if (type_id >= PORT_TYPES_MAX) return NULL; const JackPortType* type = gPortTypes[type_id]; assert(type != NULL); return type; } } // namespace Jack jack2-1.9.22/common/JackPortType.h000066400000000000000000000026451436671425200166400ustar00rootroot00000000000000/* Copyright (C) 2007 Dmitry Baikov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackPortType__ #define __JackPortType__ #include "types.h" #include "JackConstants.h" #include namespace Jack { extern const jack_port_type_id_t PORT_TYPES_MAX; struct JackPortType { const char* fName; size_t (*size)(); void (*init)(void* buffer, size_t buffer_size, jack_nframes_t nframes); void (*mixdown)(void *mixbuffer, void** src_buffers, int src_count, jack_nframes_t nframes); }; extern jack_port_type_id_t GetPortTypeId(const char* port_type); extern const struct JackPortType* GetPortType(jack_port_type_id_t port_type_id); extern const struct JackPortType gAudioPortType; extern const struct JackPortType gMidiPortType; } // namespace Jack #endif jack2-1.9.22/common/JackProfiler.cpp000066400000000000000000000240621436671425200171640ustar00rootroot00000000000000/* Copyright (C) 2009 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackProfiler.h" #include "JackServerGlobals.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" #include "JackArgParser.h" #include #include namespace Jack { JackProfilerClient::JackProfilerClient(jack_client_t* client, const char* name) :fClient(client) { char port_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; fRefNum = JackServerGlobals::fInstance->GetEngine()->GetClientRefNum(name); snprintf(port_name, sizeof(port_name) - 1, "%s:scheduling", name); fSchedulingPort = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); snprintf(port_name, sizeof(port_name) - 1, "%s:duration", name); fDurationPort = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } JackProfilerClient::~JackProfilerClient() { jack_port_unregister(fClient, fSchedulingPort); jack_port_unregister(fClient, fDurationPort); } #ifdef JACK_MONITOR JackProfiler::JackProfiler(jack_client_t* client, const JSList* params) :fClient(client), fLastMeasure(NULL) #else JackProfiler::JackProfiler(jack_client_t* client, const JSList* params) :fClient(client) #endif { jack_log("JackProfiler::JackProfiler"); fCPULoadPort = fDriverPeriodPort = fDriverEndPort = NULL; const JSList* node; const jack_driver_param_t* param; for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*)node->data; switch (param->character) { case 'c': fCPULoadPort = jack_port_register(client, "cpu_load", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); break; case 'p': fDriverPeriodPort = jack_port_register(client, "driver_period", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); break; case 'e': fDriverEndPort = jack_port_register(client, "driver_end_time", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); break; } } // Resigster all running clients const char **ports = jack_get_ports(client, NULL, NULL, 0); if (ports) { for (int i = 0; ports[i]; ++i) { std::string str = std::string(ports[i]); ClientRegistration(str.substr(0, str.find_first_of(':')).c_str(), 1, this); } free(ports); } jack_set_process_callback(client, Process, this); jack_set_client_registration_callback(client, ClientRegistration, this); jack_activate(client); } JackProfiler::~JackProfiler() { jack_log("JackProfiler::~JackProfiler"); } void JackProfiler::ClientRegistration(const char* name, int val, void *arg) { #ifdef JACK_MONITOR JackProfiler* profiler = static_cast(arg); // Filter client or "system" name if (strcmp(name, jack_get_client_name(profiler->fClient)) == 0 || strcmp(name, "system") == 0) return; profiler->fMutex.Lock(); if (val) { std::map::iterator it = profiler->fClientTable.find(name); if (it == profiler->fClientTable.end()) { jack_log("Client %s added", name); profiler->fClientTable[name] = new JackProfilerClient(profiler->fClient, name); } } else { std::map::iterator it = profiler->fClientTable.find(name); if (it != profiler->fClientTable.end()) { jack_log("Client %s removed", name); profiler->fClientTable.erase(it); delete((*it).second); } } profiler->fMutex.Unlock(); #endif } int JackProfiler::Process(jack_nframes_t nframes, void* arg) { JackProfiler* profiler = static_cast(arg); if (profiler->fCPULoadPort) { float* buffer_cpu_load = (float*)jack_port_get_buffer(profiler->fCPULoadPort, nframes); float cpu_load = jack_cpu_load(profiler->fClient); for (unsigned int i = 0; i < nframes; i++) { buffer_cpu_load[i] = cpu_load / 100.f; } } #ifdef JACK_MONITOR JackEngineControl* control = JackServerGlobals::fInstance->GetEngineControl(); JackEngineProfiling* engine_profiler = &control->fProfiler; JackTimingMeasure* measure = engine_profiler->GetCurMeasure(); if (profiler->fLastMeasure && profiler->fMutex.Trylock()) { if (profiler->fDriverPeriodPort) { float* buffer_driver_period = (float*)jack_port_get_buffer(profiler->fDriverPeriodPort, nframes); float value1 = (float(measure->fPeriodUsecs) - float(measure->fCurCycleBegin - profiler->fLastMeasure->fCurCycleBegin)) / float(measure->fPeriodUsecs); for (unsigned int i = 0; i < nframes; i++) { buffer_driver_period[i] = value1; } } if (profiler->fDriverEndPort) { float* buffer_driver_end_time = (float*)jack_port_get_buffer(profiler->fDriverEndPort, nframes); float value2 = (float(measure->fPrevCycleEnd - profiler->fLastMeasure->fCurCycleBegin)) / float(measure->fPeriodUsecs); for (unsigned int i = 0; i < nframes; i++) { buffer_driver_end_time[i] = value2; } } std::map::iterator it; for (it = profiler->fClientTable.begin(); it != profiler->fClientTable.end(); it++) { int ref = (*it).second->fRefNum; long d5 = long(measure->fClientTable[ref].fSignaledAt - profiler->fLastMeasure->fCurCycleBegin); long d6 = long(measure->fClientTable[ref].fAwakeAt - profiler->fLastMeasure->fCurCycleBegin); long d7 = long(measure->fClientTable[ref].fFinishedAt - profiler->fLastMeasure->fCurCycleBegin); float* buffer_scheduling = (float*)jack_port_get_buffer((*it).second->fSchedulingPort, nframes); float value3 = float(d6 - d5) / float(measure->fPeriodUsecs); jack_log("Scheduling %f", value3); for (unsigned int i = 0; i < nframes; i++) { buffer_scheduling[i] = value3; } float* buffer_duration = (float*)jack_port_get_buffer((*it).second->fDurationPort, nframes); float value4 = float(d7 - d6) / float(measure->fPeriodUsecs); jack_log("Duration %f", value4); for (unsigned int i = 0; i < nframes; i++) { buffer_duration[i] = value4; } } profiler->fMutex.Unlock(); } profiler->fLastMeasure = measure; #endif return 0; } } // namespace Jack #ifdef __cplusplus extern "C" { #endif #include "driver_interface.h" using namespace Jack; static Jack::JackProfiler* profiler = NULL; SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("profiler", JackDriverNone, "real-time server profiling", &filler); value.i = FALSE; jack_driver_descriptor_add_parameter(desc, &filler, "cpu-load", 'c', JackDriverParamBool, &value, NULL, "Show DSP CPU load", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "driver-period", 'p', JackDriverParamBool, &value, NULL, "Show driver period", NULL); jack_driver_descriptor_add_parameter(desc, &filler, "driver-end-time", 'e', JackDriverParamBool, &value, NULL, "Show driver end time", NULL); return desc; } SERVER_EXPORT int jack_internal_initialize(jack_client_t* jack_client, const JSList* params) { if (profiler) { jack_info("profiler already loaded"); return 1; } jack_log("Loading profiler"); try { profiler = new Jack::JackProfiler(jack_client, params); assert(profiler); return 0; } catch (...) { return 1; } } SERVER_EXPORT int jack_initialize(jack_client_t* jack_client, const char* load_init) { JSList* params = NULL; bool parse_params = true; int res = 1; jack_driver_desc_t* desc = jack_get_descriptor(); Jack::JackArgParser parser ( load_init ); if ( parser.GetArgc() > 0 ) parse_params = parser.ParseParams ( desc, ¶ms ); if (parse_params) { res = jack_internal_initialize ( jack_client, params ); parser.FreeParams ( params ); } return res; } SERVER_EXPORT void jack_finish(void* arg) { Jack::JackProfiler* profiler = static_cast(arg); if (profiler) { jack_log("Unloading profiler"); delete profiler; } } #ifdef __cplusplus } #endif jack2-1.9.22/common/JackProfiler.h000066400000000000000000000034711436671425200166320ustar00rootroot00000000000000/* Copyright (C) 2009 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackProfiler__ #define __JackProfiler__ #include "JackConstants.h" #include "JackPlatformPlug.h" #include "jack.h" #include "jslist.h" #include #include #ifdef JACK_MONITOR #include "JackEngineProfiling.h" #endif namespace Jack { struct JackProfilerClient { int fRefNum; jack_client_t* fClient; jack_port_t* fSchedulingPort; jack_port_t* fDurationPort; JackProfilerClient(jack_client_t* client, const char* name); ~JackProfilerClient(); }; /*! \brief Server real-time monitoring */ class JackProfiler { private: jack_client_t* fClient; jack_port_t* fCPULoadPort; jack_port_t* fDriverPeriodPort; jack_port_t* fDriverEndPort; #ifdef JACK_MONITOR JackTimingMeasure* fLastMeasure; std::map fClientTable; JackMutex fMutex; #endif public: JackProfiler(jack_client_t* jack_client, const JSList* params); ~JackProfiler(); static int Process(jack_nframes_t nframes, void* arg); static void ClientRegistration(const char* name, int val, void *arg); }; } #endif jack2-1.9.22/common/JackProxyDriver.cpp000066400000000000000000000533201436671425200176760ustar00rootroot00000000000000/* Copyright (C) 2014 Cédric Schieli This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "JackCompilerDeps.h" #include "driver_interface.h" #include "JackEngineControl.h" #include "JackLockedEngine.h" #include "JackWaitCallbackDriver.h" #include "JackProxyDriver.h" using namespace std; namespace Jack { JackProxyDriver::JackProxyDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, const char* upstream, const char* promiscuous, char* client_name, bool auto_connect, bool auto_save) : JackRestarterDriver(name, alias, engine, table) { jack_log("JackProxyDriver::JackProxyDriver upstream %s", upstream); assert(strlen(upstream) < JACK_CLIENT_NAME_SIZE); strcpy(fUpstream, upstream); assert(strlen(client_name) < JACK_CLIENT_NAME_SIZE); strcpy(fClientName, client_name); if (promiscuous) { fPromiscuous = strdup(promiscuous); } fAutoConnect = auto_connect; fAutoSave = auto_save; } JackProxyDriver::~JackProxyDriver() { if (fHandle) { UnloadJackModule(fHandle); } } int JackProxyDriver::LoadClientLib() { // Already loaded if (fHandle) { return 0; } fHandle = LoadJackModule(JACK_PROXY_CLIENT_LIB); if (!fHandle) { return -1; } LoadSymbols(); return 0; } //open, close, attach and detach------------------------------------------------------ int JackProxyDriver::Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { fDetectPlaybackChannels = (outchannels == -1); fDetectCaptureChannels = (inchannels == -1); if (LoadClientLib() != 0) { jack_error("Cannot dynamically load client library !"); return -1; } return JackWaiterDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } int JackProxyDriver::Close() { FreePorts(); return JackWaiterDriver::Close(); } // Attach and Detach are defined as empty methods: port allocation is done when driver actually start (that is in Init) int JackProxyDriver::Attach() { return 0; } int JackProxyDriver::Detach() { return 0; } //init and restart-------------------------------------------------------------------- /* JackProxyDriver is wrapped in a JackWaitCallbackDriver decorator that behaves as a "dummy driver, until Initialize method returns. */ bool JackProxyDriver::Initialize() { jack_log("JackProxyDriver::Initialize"); // save existing local connections if needed if (fAutoSave) { SaveConnections(0); } // new loading, but existing client, restart the driver if (fClient) { jack_info("JackProxyDriver restarting..."); jack_client_close(fClient); } FreePorts(); // display some additional infos jack_info("JackProxyDriver started in %s mode.", (fEngineControl->fSyncMode) ? "sync" : "async"); do { jack_status_t status; char *old = NULL; if (fPromiscuous) { // as we are fiddling with the environment variable content, save it const char* tmp = getenv("JACK_PROMISCUOUS_SERVER"); if (tmp) { old = strdup(tmp); } // temporary enable promiscuous mode if (setenv("JACK_PROMISCUOUS_SERVER", fPromiscuous, 1) < 0) { free(old); jack_error("Error allocating memory."); return false; } } jack_info("JackProxyDriver connecting to %s", fUpstream); fClient = jack_client_open(fClientName, static_cast(JackNoStartServer|JackServerName), &status, fUpstream); if (fPromiscuous) { // restore previous environment variable content if (old) { if (setenv("JACK_PROMISCUOUS_SERVER", old, 1) < 0) { free(old); jack_error("Error allocating memory."); return false; } free(old); } else { unsetenv("JACK_PROMISCUOUS_SERVER"); } } // the connection failed, try again later if (!fClient) { JackSleep(1000000); } } while (!fClient); jack_info("JackProxyDriver connected to %s", fUpstream); // we are connected, let's register some callbacks jack_on_shutdown(fClient, shutdown_callback, this); if (jack_set_process_callback(fClient, process_callback, this) != 0) { jack_error("Cannot set process callback."); return false; } if (jack_set_buffer_size_callback(fClient, bufsize_callback, this) != 0) { jack_error("Cannot set buffer size callback."); return false; } if (jack_set_sample_rate_callback(fClient, srate_callback, this) != 0) { jack_error("Cannot set sample rate callback."); return false; } if (jack_set_port_connect_callback(fClient, connect_callback, this) != 0) { jack_error("Cannot set port connect callback."); return false; } // detect upstream physical playback ports if needed if (fDetectPlaybackChannels) { fPlaybackChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput); } // detect upstream physical capture ports if needed if (fDetectCaptureChannels) { fCaptureChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput); } if (AllocPorts() != 0) { jack_error("Can't allocate ports."); return false; } bufsize_callback(jack_get_buffer_size(fClient)); srate_callback(jack_get_sample_rate(fClient)); // restore local connections if needed if (fAutoSave) { LoadConnections(0); } // everything is ready, start upstream processing if (jack_activate(fClient) != 0) { jack_error("Cannot activate jack client."); return false; } // connect upstream ports if needed if (fAutoConnect) { ConnectPorts(); } return true; } int JackProxyDriver::Stop() { if (fClient && (jack_deactivate(fClient) != 0)) { jack_error("Cannot deactivate jack client."); return -1; } return 0; } //client callbacks--------------------------------------------------------------------------- int JackProxyDriver::process_callback(jack_nframes_t nframes, void* arg) { assert(static_cast(arg)); return static_cast(arg)->Process(); } int JackProxyDriver::bufsize_callback(jack_nframes_t nframes, void* arg) { assert(static_cast(arg)); return static_cast(arg)->bufsize_callback(nframes); } int JackProxyDriver::bufsize_callback(jack_nframes_t nframes) { if (JackTimedDriver::SetBufferSize(nframes) == 0) { return -1; } JackDriver::NotifyBufferSize(nframes); return 0; } int JackProxyDriver::srate_callback(jack_nframes_t nframes, void* arg) { assert(static_cast(arg)); return static_cast(arg)->srate_callback(nframes); } int JackProxyDriver::srate_callback(jack_nframes_t nframes) { if (JackTimedDriver::SetSampleRate(nframes) == 0) { return -1; } JackDriver::NotifySampleRate(nframes); return 0; } void JackProxyDriver::connect_callback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg) { assert(static_cast(arg)); static_cast(arg)->connect_callback(a, b, connect); } void JackProxyDriver::connect_callback(jack_port_id_t a, jack_port_id_t b, int connect) { jack_port_t* port; int i; // skip port if not our own port = jack_port_by_id(fClient, a); if (!jack_port_is_mine(fClient, port)) { port = jack_port_by_id(fClient, b); if (!jack_port_is_mine(fClient, port)) { return; } } for (i = 0; i < fCaptureChannels; i++) { if (fUpstreamPlaybackPorts[i] == port) { fUpstreamPlaybackPortConnected[i] = connect; } } for (i = 0; i < fPlaybackChannels; i++) { if (fUpstreamCapturePorts[i] == port) { fUpstreamCapturePortConnected[i] = connect; } } } void JackProxyDriver::shutdown_callback(void* arg) { assert(static_cast(arg)); static_cast(arg)->RestartWait(); } //jack ports and buffers-------------------------------------------------------------- int JackProxyDriver::CountIO(const char* type, int flags) { int count = 0; const char** ports = jack_get_ports(fClient, NULL, type, flags); if (ports != NULL) { while (ports[count]) { count++; } jack_free(ports); } return count; } int JackProxyDriver::AllocPorts() { jack_log("JackProxyDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); char proxy[REAL_JACK_PORT_NAME_SIZE]; int i; fUpstreamPlaybackPorts = new jack_port_t* [fCaptureChannels]; fUpstreamPlaybackPortConnected = new int [fCaptureChannels]; for (i = 0; i < fCaptureChannels; i++) { snprintf(proxy, sizeof(proxy), "%s:to_client_%d", fClientName, i + 1); fUpstreamPlaybackPorts[i] = jack_port_register(fClient, proxy, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0); if (fUpstreamPlaybackPorts[i] == NULL) { jack_error("driver: cannot register upstream port %s", proxy); return -1; } fUpstreamPlaybackPortConnected[i] = 0; } fUpstreamCapturePorts = new jack_port_t* [fPlaybackChannels]; fUpstreamCapturePortConnected = new int [fPlaybackChannels]; for (i = 0; i < fPlaybackChannels; i++) { snprintf(proxy, sizeof(proxy), "%s:from_client_%d", fClientName, i + 1); fUpstreamCapturePorts[i] = jack_port_register(fClient, proxy, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0); if (fUpstreamCapturePorts[i] == NULL) { jack_error("driver: cannot register upstream port %s", proxy); return -1; } fUpstreamCapturePortConnected[i] = 0; } // local ports are registered here return JackAudioDriver::Attach(); } int JackProxyDriver::FreePorts() { jack_log("JackProxyDriver::FreePorts"); int i; for (i = 0; i < fCaptureChannels; i++) { if (fCapturePortList[i] > 0) { fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[i]); fCapturePortList[i] = 0; } if (fUpstreamPlaybackPorts && fUpstreamPlaybackPorts[i]) { fUpstreamPlaybackPorts[i] = NULL; } } for (i = 0; i < fPlaybackChannels; i++) { if (fPlaybackPortList[i] > 0) { fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[i]); fPlaybackPortList[i] = 0; } if (fUpstreamCapturePorts && fUpstreamCapturePorts[i]) { fUpstreamCapturePorts[i] = NULL; } } delete[] fUpstreamPlaybackPorts; delete[] fUpstreamPlaybackPortConnected; delete[] fUpstreamCapturePorts; delete[] fUpstreamCapturePortConnected; fUpstreamPlaybackPorts = NULL; fUpstreamPlaybackPortConnected = NULL; fUpstreamCapturePorts = NULL; fUpstreamCapturePortConnected = NULL; return 0; } void JackProxyDriver::ConnectPorts() { jack_log("JackProxyDriver::ConnectPorts"); const char** ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput); if (ports != NULL) { for (int i = 0; i < fCaptureChannels && ports[i]; i++) { jack_connect(fClient, ports[i], jack_port_name(fUpstreamPlaybackPorts[i])); } jack_free(ports); } ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput); if (ports != NULL) { for (int i = 0; i < fPlaybackChannels && ports[i]; i++) { jack_connect(fClient, jack_port_name(fUpstreamCapturePorts[i]), ports[i]); } jack_free(ports); } } //driver processes-------------------------------------------------------------------- int JackProxyDriver::Read() { // take the time at the beginning of the cycle JackDriver::CycleTakeBeginTime(); int i; void *from, *to; size_t buflen = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; for (i = 0; i < fCaptureChannels; i++) { if (fUpstreamPlaybackPortConnected[i]) { from = jack_port_get_buffer(fUpstreamPlaybackPorts[i], fEngineControl->fBufferSize); to = GetInputBuffer(i); memcpy(to, from, buflen); } } return 0; } int JackProxyDriver::Write() { int i; void *from, *to; size_t buflen = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; for (i = 0; i < fPlaybackChannels; i++) { if (fUpstreamCapturePortConnected[i]) { to = jack_port_get_buffer(fUpstreamCapturePorts[i], fEngineControl->fBufferSize); from = GetOutputBuffer(i); memcpy(to, from, buflen); } } return 0; } //driver loader----------------------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor() { jack_driver_desc_t * desc; jack_driver_desc_filler_t filler; jack_driver_param_value_t value; desc = jack_driver_descriptor_construct("proxy", JackDriverMaster, "proxy backend", &filler); strcpy(value.str, DEFAULT_UPSTREAM); jack_driver_descriptor_add_parameter(desc, &filler, "upstream", 'u', JackDriverParamString, &value, NULL, "Name of the upstream jack server", NULL); strcpy(value.str, ""); jack_driver_descriptor_add_parameter(desc, &filler, "promiscuous", 'p', JackDriverParamString, &value, NULL, "Promiscuous group", NULL); value.i = -1; jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", "Number of audio input ports. If -1, audio physical input from the master"); jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", "Number of audio output ports. If -1, audio physical output from the master"); strcpy(value.str, "proxy"); jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "use-username", 'U', JackDriverParamBool, &value, NULL, "Use current username as client name", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect proxy to upstream system ports", NULL); value.i = false; jack_driver_descriptor_add_parameter(desc, &filler, "auto-save", 's', JackDriverParamBool, &value, NULL, "Save/restore connection state when restarting", NULL); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { char upstream[JACK_CLIENT_NAME_SIZE + 1]; char promiscuous[JACK_CLIENT_NAME_SIZE + 1] = {0}; char client_name[JACK_CLIENT_NAME_SIZE + 1]; jack_nframes_t period_size = 1024; // to be used while waiting for master period_size jack_nframes_t sample_rate = 48000; // to be used while waiting for master sample_rate int capture_ports = -1; int playback_ports = -1; const JSList* node; const jack_driver_param_t* param; bool auto_connect = false; bool auto_save = false; bool use_promiscuous = false; // Possibly use env variable for upstream name const char* default_upstream = getenv("JACK_PROXY_UPSTREAM"); strcpy(upstream, (default_upstream) ? default_upstream : DEFAULT_UPSTREAM); // Possibly use env variable for upstream promiscuous const char* default_promiscuous = getenv("JACK_PROXY_PROMISCUOUS"); strcpy(promiscuous, (default_promiscuous) ? default_promiscuous : ""); // Possibly use env variable for client name const char* default_client_name = getenv("JACK_PROXY_CLIENT_NAME"); strcpy(client_name, (default_client_name) ? default_client_name : DEFAULT_CLIENT_NAME); #ifdef WIN32 const char* username = getenv("USERNAME"); #else const char* username = getenv("LOGNAME"); #endif for (node = params; node; node = jack_slist_next(node)) { param = (const jack_driver_param_t*) node->data; switch (param->character) { case 'u' : assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE); strcpy(upstream, param->value.str); break; case 'p': assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE); use_promiscuous = true; strcpy(promiscuous, param->value.str); break; case 'C': capture_ports = param->value.i; break; case 'P': playback_ports = param->value.i; break; case 'n' : assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE); strncpy(client_name, param->value.str, JACK_CLIENT_NAME_SIZE); break; case 'U' : if (username && *username) { assert(strlen(username) < JACK_CLIENT_NAME_SIZE); strncpy(client_name, username, JACK_CLIENT_NAME_SIZE); } case 'c': auto_connect = true; break; case 's': auto_save = true; break; } } try { Jack::JackDriverClientInterface* driver = new Jack::JackWaitCallbackDriver( new Jack::JackProxyDriver("system", "proxy_pcm", engine, table, upstream, use_promiscuous ? promiscuous : NULL, client_name, auto_connect, auto_save)); if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, false, "capture_", "playback_", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } catch (...) { return NULL; } } #ifdef __cplusplus } #endif } jack2-1.9.22/common/JackProxyDriver.h000066400000000000000000000236051436671425200173460ustar00rootroot00000000000000/* Copyright (C) 2014 Cédric Schieli This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __JackProxyDriver__ #define __JackProxyDriver__ #include "JackTimedDriver.h" #define DEFAULT_UPSTREAM "default" /*!< Default upstream Jack server to connect to */ #define DEFAULT_CLIENT_NAME "proxy" /*!< Default client name to use when connecting to upstream Jack server */ #ifdef __APPLE__ #define JACK_PROXY_CLIENT_LIB "libjack.0.dylib" #elif defined(WIN32) #ifdef _WIN64 #define JACK_PROXY_CLIENT_LIB "libjack64.dll" #else #define JACK_PROXY_CLIENT_LIB "libjack.dll" #endif #else #define JACK_PROXY_CLIENT_LIB "libjack.so.0" #endif #define PROXY_DEF_SYMBOL(ret,name,...) ret (*name) (__VA_ARGS__) #define PROXY_LOAD_SYMBOL(ret,name,...) name = (ret (*) (__VA_ARGS__)) GetJackProc(fHandle, #name); assert(name) namespace Jack { /*! \Brief This class describes the Proxy Backend It uses plain Jack API to connect to an upstream server. The latter is either running as the same user, or is running in promiscuous mode. The main use case is the multi-user, multi-session, shared workstation: - a classic server with hw driver is launched system-wide at boot time, in promiscuous mode, optionally restricted to the audio group - in each user session, a jackdbus server is automatically started with JackProxyDriver as master driver, automatically connected to the system-wide one - optionally, each user run PulseAudio with a pulse-jack bridge */ class JackProxyDriver : public JackRestarterDriver { private: char fUpstream[JACK_CLIENT_NAME_SIZE+1]; /* #include #include #include namespace Jack { #define CheckRes(exp) { if ((exp) < 0) { jack_error("CheckRes error"); return -1; } } #define CheckSize() { CheckRes(trans->Read(&fSize, sizeof(int))); if (fSize != Size()) { jack_error("CheckSize error size = %d Size() = %d", fSize, Size()); return -1; } } /*! \brief Session API constants. */ enum JackSessionReply { kImmediateSessionReply = 1, kPendingSessionReply = 2 }; /*! \brief Request from client to server. */ struct JackRequest { enum RequestType { kRegisterPort = 1, kUnRegisterPort = 2, kConnectPorts = 3, kDisconnectPorts = 4, kSetTimeBaseClient = 5, kActivateClient = 6, kDeactivateClient = 7, kDisconnectPort = 8, kSetClientCapabilities = 9, kGetPortConnections = 10, kGetPortNConnections = 11, kReleaseTimebase = 12, kSetTimebaseCallback = 13, kSetBufferSize = 20, kSetFreeWheel = 21, kClientCheck = 22, kClientOpen = 23, kClientClose = 24, kConnectNamePorts = 25, kDisconnectNamePorts = 26, kGetInternalClientName = 27, kInternalClientHandle = 28, kInternalClientLoad = 29, kInternalClientUnload = 30, kPortRename = 31, kNotification = 32, kSessionNotify = 33, kSessionReply = 34, kGetClientByUUID = 35, kReserveClientName = 36, kGetUUIDByClient = 37, kClientHasSessionCallback = 38, kComputeTotalLatencies = 39, kPropertyChangeNotify = 40 }; RequestType fType; int fSize; JackRequest(): fType((RequestType)0), fSize(0) {} JackRequest(RequestType type): fType(type), fSize(0) {} virtual ~JackRequest() {} virtual int Read(detail::JackChannelTransactionInterface* trans) { return trans->Read(&fType, sizeof(RequestType)); } virtual int Write(detail::JackChannelTransactionInterface* trans) { return -1; } virtual int Write(detail::JackChannelTransactionInterface* trans, int size) { fSize = size; CheckRes(trans->Write(&fType, sizeof(RequestType))); return trans->Write(&fSize, sizeof(int)); } virtual int Size() { return 0; } }; /*! \brief Result from the server. */ struct JackResult { int fResult; JackResult(): fResult( -1) {} JackResult(int result): fResult(result) {} virtual ~JackResult() {} virtual int Read(detail::JackChannelTransactionInterface* trans) { return trans->Read(&fResult, sizeof(int)); } virtual int Write(detail::JackChannelTransactionInterface* trans) { return trans->Write(&fResult, sizeof(int)); } }; /*! \brief CheckClient request. */ struct JackClientCheckRequest : public JackRequest { char fName[JACK_CLIENT_NAME_SIZE+1]; int fProtocol; int fOptions; int fOpen; jack_uuid_t fUUID; JackClientCheckRequest() : fProtocol(0), fOptions(0), fOpen(0), fUUID(JACK_UUID_EMPTY_INITIALIZER) { memset(fName, 0, sizeof(fName)); } JackClientCheckRequest(const char* name, int protocol, int options, jack_uuid_t uuid, int open = false) : JackRequest(JackRequest::kClientCheck), fProtocol(protocol), fOptions(options), fOpen(open), fUUID(uuid) { memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fProtocol, sizeof(int))); CheckRes(trans->Read(&fOptions, sizeof(int))); CheckRes(trans->Read(&fUUID, sizeof(jack_uuid_t))); return trans->Read(&fOpen, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fProtocol, sizeof(int))); CheckRes(trans->Write(&fOptions, sizeof(int))); CheckRes(trans->Write(&fUUID, sizeof(jack_uuid_t))); return trans->Write(&fOpen, sizeof(int)); } int Size() { return sizeof(fName) + 3 * sizeof(int) + sizeof(jack_uuid_t); } }; /*! \brief CheckClient result. */ struct JackClientCheckResult : public JackResult { char fName[JACK_CLIENT_NAME_SIZE+1]; int fStatus; JackClientCheckResult(): JackResult(), fStatus(0) { memset(fName, 0, sizeof(fName)); } JackClientCheckResult(int32_t result, const char* name, int status) : JackResult(result), fStatus(status) { memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); } int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fStatus, sizeof(int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fStatus, sizeof(int))); return 0; } }; /*! \brief NewClient request. */ struct JackClientOpenRequest : public JackRequest { int fPID; jack_uuid_t fUUID; char fName[JACK_CLIENT_NAME_SIZE+1]; JackClientOpenRequest() : fPID(0), fUUID(JACK_UUID_EMPTY_INITIALIZER) { memset(fName, 0, sizeof(fName)); } JackClientOpenRequest(const char* name, int pid, jack_uuid_t uuid): JackRequest(JackRequest::kClientOpen) { memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); fPID = pid; fUUID = uuid; } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fPID, sizeof(int))); CheckRes(trans->Read(&fUUID, sizeof(jack_uuid_t))); return trans->Read(&fName, sizeof(fName)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fPID, sizeof(int))); CheckRes(trans->Write(&fUUID, sizeof(jack_uuid_t))); return trans->Write(&fName, sizeof(fName)); } int Size() { return sizeof(int) + sizeof(jack_uuid_t) + sizeof(fName); } }; /*! \brief NewClient result. */ struct JackClientOpenResult : public JackResult { int fSharedEngine; int fSharedClient; int fSharedGraph; JackClientOpenResult() : JackResult(), fSharedEngine(-1), fSharedClient(-1), fSharedGraph(-1) {} JackClientOpenResult(int32_t result, int index1, int index2, int index3) : JackResult(result), fSharedEngine(index1), fSharedClient(index2), fSharedGraph(index3) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fSharedEngine, sizeof(int))); CheckRes(trans->Read(&fSharedClient, sizeof(int))); CheckRes(trans->Read(&fSharedGraph, sizeof(int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fSharedEngine, sizeof(int))); CheckRes(trans->Write(&fSharedClient, sizeof(int))); CheckRes(trans->Write(&fSharedGraph, sizeof(int))); return 0; } }; /*! \brief CloseClient request. */ struct JackClientCloseRequest : public JackRequest { int fRefNum; JackClientCloseRequest() : fRefNum(0) {} JackClientCloseRequest(int refnum): JackRequest(JackRequest::kClientClose), fRefNum(refnum) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); return trans->Read(&fRefNum, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); return trans->Write(&fRefNum, sizeof(int)); } int Size() { return sizeof(int); } }; /*! \brief Activate request. */ struct JackActivateRequest : public JackRequest { int fRefNum; int fIsRealTime; JackActivateRequest() : fRefNum(0), fIsRealTime(0) {} JackActivateRequest(int refnum, int is_real_time) : JackRequest(JackRequest::kActivateClient), fRefNum(refnum), fIsRealTime(is_real_time) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); return trans->Read(&fIsRealTime, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); return trans->Write(&fIsRealTime, sizeof(int)); } int Size() { return 2 * sizeof(int); } }; /*! \brief Deactivate request. */ struct JackDeactivateRequest : public JackRequest { int fRefNum; JackDeactivateRequest() : fRefNum(0) {} JackDeactivateRequest(int refnum): JackRequest(JackRequest::kDeactivateClient), fRefNum(refnum) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); return trans->Read(&fRefNum, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); return trans->Write(&fRefNum, sizeof(int)); } int Size() { return sizeof(int); } }; /*! \brief PortRegister request. */ struct JackPortRegisterRequest : public JackRequest { int fRefNum; char fName[JACK_PORT_NAME_SIZE + 1]; // port short name char fPortType[JACK_PORT_TYPE_SIZE + 1]; unsigned int fFlags; unsigned int fBufferSize; JackPortRegisterRequest() : fRefNum(0), fFlags(0), fBufferSize(0) { memset(fName, 0, sizeof(fName)); memset(fPortType, 0, sizeof(fPortType)); } JackPortRegisterRequest(int refnum, const char* name, const char* port_type, unsigned int flags, unsigned int buffer_size) : JackRequest(JackRequest::kRegisterPort), fRefNum(refnum), fFlags(flags), fBufferSize(buffer_size) { memset(fName, 0, sizeof(fName)); memset(fPortType, 0, sizeof(fPortType)); strncpy(fName, name, sizeof(fName)-1); strncpy(fPortType, port_type, sizeof(fPortType)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fPortType, sizeof(fPortType))); CheckRes(trans->Read(&fFlags, sizeof(unsigned int))); CheckRes(trans->Read(&fBufferSize, sizeof(unsigned int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fPortType, sizeof(fPortType))); CheckRes(trans->Write(&fFlags, sizeof(unsigned int))); CheckRes(trans->Write(&fBufferSize, sizeof(unsigned int))); return 0; } int Size() { return sizeof(int) + sizeof(fName) + sizeof(fPortType) + 2 * sizeof(unsigned int); } }; /*! \brief PortRegister result. */ struct JackPortRegisterResult : public JackResult { jack_port_id_t fPortIndex; JackPortRegisterResult(): JackResult(), fPortIndex(NO_PORT) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); return trans->Read(&fPortIndex, sizeof(jack_port_id_t)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); return trans->Write(&fPortIndex, sizeof(jack_port_id_t)); } }; /*! \brief PortUnregister request. */ struct JackPortUnRegisterRequest : public JackRequest { int fRefNum; jack_port_id_t fPortIndex; JackPortUnRegisterRequest() : fRefNum(0), fPortIndex(0) {} JackPortUnRegisterRequest(int refnum, jack_port_id_t index) : JackRequest(JackRequest::kUnRegisterPort), fRefNum(refnum), fPortIndex(index) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fPortIndex, sizeof(jack_port_id_t))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fPortIndex, sizeof(jack_port_id_t))); return 0; } int Size() { return sizeof(int) + sizeof(jack_port_id_t); } }; /*! \brief PortConnectName request. */ struct JackPortConnectNameRequest : public JackRequest { int fRefNum; char fSrc[REAL_JACK_PORT_NAME_SIZE+1]; // port full name char fDst[REAL_JACK_PORT_NAME_SIZE+1]; // port full name JackPortConnectNameRequest() : fRefNum(0) { memset(fSrc, 0, sizeof(fSrc)); memset(fDst, 0, sizeof(fDst)); } JackPortConnectNameRequest(int refnum, const char* src_name, const char* dst_name) : JackRequest(JackRequest::kConnectNamePorts), fRefNum(refnum) { memset(fSrc, 0, sizeof(fSrc)); memset(fDst, 0, sizeof(fDst)); strncpy(fSrc, src_name, sizeof(fSrc)-1); strncpy(fDst, dst_name, sizeof(fDst)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fSrc, sizeof(fSrc))); CheckRes(trans->Read(&fDst, sizeof(fDst))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fSrc, sizeof(fSrc))); CheckRes(trans->Write(&fDst, sizeof(fDst))); return 0; } int Size() { return sizeof(int) + sizeof(fSrc) + sizeof(fDst); } }; /*! \brief PortDisconnectName request. */ struct JackPortDisconnectNameRequest : public JackRequest { int fRefNum; char fSrc[REAL_JACK_PORT_NAME_SIZE+1]; // port full name char fDst[REAL_JACK_PORT_NAME_SIZE+1]; // port full name JackPortDisconnectNameRequest() : fRefNum(0) { memset(fSrc, 0, sizeof(fSrc)); memset(fDst, 0, sizeof(fDst)); } JackPortDisconnectNameRequest(int refnum, const char* src_name, const char* dst_name) : JackRequest(JackRequest::kDisconnectNamePorts), fRefNum(refnum) { memset(fSrc, 0, sizeof(fSrc)); memset(fDst, 0, sizeof(fDst)); strncpy(fSrc, src_name, sizeof(fSrc)-1); strncpy(fDst, dst_name, sizeof(fDst)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fSrc, sizeof(fSrc))); CheckRes(trans->Read(&fDst, sizeof(fDst))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fSrc, sizeof(fSrc))); CheckRes(trans->Write(&fDst, sizeof(fDst))); return 0; } int Size() { return sizeof(int) + sizeof(fSrc) + sizeof(fDst); } }; /*! \brief PortConnect request. */ struct JackPortConnectRequest : public JackRequest { int fRefNum; jack_port_id_t fSrc; jack_port_id_t fDst; JackPortConnectRequest() : fRefNum(0), fSrc(0), fDst(0) {} JackPortConnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst) : JackRequest(JackRequest::kConnectPorts), fRefNum(refnum), fSrc(src), fDst(dst) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fSrc, sizeof(jack_port_id_t))); CheckRes(trans->Read(&fDst, sizeof(jack_port_id_t))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fSrc, sizeof(jack_port_id_t))); CheckRes(trans->Write(&fDst, sizeof(jack_port_id_t))); return 0; } int Size() { return sizeof(int) + sizeof(jack_port_id_t) + sizeof(jack_port_id_t); } }; /*! \brief PortDisconnect request. */ struct JackPortDisconnectRequest : public JackRequest { int fRefNum; jack_port_id_t fSrc; jack_port_id_t fDst; JackPortDisconnectRequest() : fRefNum(0), fSrc(0), fDst(0) {} JackPortDisconnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst) : JackRequest(JackRequest::kDisconnectPorts), fRefNum(refnum), fSrc(src), fDst(dst) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fSrc, sizeof(jack_port_id_t))); CheckRes(trans->Read(&fDst, sizeof(jack_port_id_t))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fSrc, sizeof(jack_port_id_t))); CheckRes(trans->Write(&fDst, sizeof(jack_port_id_t))); return 0; } int Size() { return sizeof(int) + sizeof(jack_port_id_t) + sizeof(jack_port_id_t); } }; /*! \brief PortRename request. */ struct JackPortRenameRequest : public JackRequest { int fRefNum; jack_port_id_t fPort; char fName[JACK_PORT_NAME_SIZE + 1]; // port short name JackPortRenameRequest() : fRefNum(0), fPort(0) { memset(fName, 0, sizeof(fName)); } JackPortRenameRequest(int refnum, jack_port_id_t port, const char* name) : JackRequest(JackRequest::kPortRename), fRefNum(refnum), fPort(port) { memset(fName, 0, sizeof(fName)); strncpy(fName, name, sizeof(fName)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fPort, sizeof(jack_port_id_t))); CheckRes(trans->Read(&fName, sizeof(fName))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fPort, sizeof(jack_port_id_t))); CheckRes(trans->Write(&fName, sizeof(fName))); return 0; } int Size() { return sizeof(int) + sizeof(jack_port_id_t) + sizeof(fName); } }; /*! \brief SetBufferSize request. */ struct JackSetBufferSizeRequest : public JackRequest { jack_nframes_t fBufferSize; JackSetBufferSizeRequest() : fBufferSize(0) {} JackSetBufferSizeRequest(jack_nframes_t buffer_size) : JackRequest(JackRequest::kSetBufferSize), fBufferSize(buffer_size) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); return trans->Read(&fBufferSize, sizeof(jack_nframes_t)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); return trans->Write(&fBufferSize, sizeof(jack_nframes_t)); } int Size() { return sizeof(jack_nframes_t); } }; /*! \brief SetFreeWheel request. */ struct JackSetFreeWheelRequest : public JackRequest { int fOnOff; JackSetFreeWheelRequest() : fOnOff(0) {} JackSetFreeWheelRequest(int onoff) : JackRequest(JackRequest::kSetFreeWheel), fOnOff(onoff) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); return trans->Read(&fOnOff, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); return trans->Write(&fOnOff, sizeof(int)); } int Size() { return sizeof(int); } }; /*! \brief ComputeTotalLatencies request. */ struct JackComputeTotalLatenciesRequest : public JackRequest { JackComputeTotalLatenciesRequest() : JackRequest(JackRequest::kComputeTotalLatencies) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); return 0; } int Size() { return 0; } }; /*! \brief ReleaseTimebase request. */ struct JackReleaseTimebaseRequest : public JackRequest { int fRefNum; JackReleaseTimebaseRequest() : fRefNum(0) {} JackReleaseTimebaseRequest(int refnum) : JackRequest(JackRequest::kReleaseTimebase), fRefNum(refnum) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); return trans->Read(&fRefNum, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); return trans->Write(&fRefNum, sizeof(int)); } int Size() { return sizeof(int); } }; /*! \brief SetTimebaseCallback request. */ struct JackSetTimebaseCallbackRequest : public JackRequest { int fRefNum; int fConditionnal; JackSetTimebaseCallbackRequest() : fRefNum(0), fConditionnal(0) {} JackSetTimebaseCallbackRequest(int refnum, int conditional) : JackRequest(JackRequest::kSetTimebaseCallback), fRefNum(refnum), fConditionnal(conditional) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); return trans->Read(&fConditionnal, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); return trans->Write(&fConditionnal, sizeof(int)); } int Size() { return sizeof(int) + sizeof(int); } }; /*! \brief GetInternalClientName request. */ struct JackGetInternalClientNameRequest : public JackRequest { int fRefNum; int fIntRefNum; JackGetInternalClientNameRequest() : fRefNum(0), fIntRefNum(0) {} JackGetInternalClientNameRequest(int refnum, int int_ref) : JackRequest(JackRequest::kGetInternalClientName), fRefNum(refnum), fIntRefNum(int_ref) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); return trans->Read(&fIntRefNum, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); return trans->Write(&fIntRefNum, sizeof(int)); } int Size() { return sizeof(int) + sizeof(int); } }; /*! \brief GetInternalClient result. */ struct JackGetInternalClientNameResult : public JackResult { char fName[JACK_CLIENT_NAME_SIZE+1]; JackGetInternalClientNameResult(): JackResult() { memset(fName, 0, sizeof(fName)); } JackGetInternalClientNameResult(int32_t result, const char* name) : JackResult(result) { memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); } int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fName, sizeof(fName))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fName, sizeof(fName))); return 0; } int Size() { return sizeof(fName); } }; /*! \brief InternalClientHandle request. */ struct JackInternalClientHandleRequest : public JackRequest { int fRefNum; char fName[JACK_CLIENT_NAME_SIZE+1]; JackInternalClientHandleRequest() : fRefNum(0) { memset(fName, 0, sizeof(fName)); } JackInternalClientHandleRequest(int refnum, const char* client_name) : JackRequest(JackRequest::kInternalClientHandle), fRefNum(refnum) { memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", client_name); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); return trans->Read(&fName, sizeof(fName)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); return trans->Write(&fName, sizeof(fName)); } int Size() { return sizeof(int) + sizeof(fName); } }; /*! \brief InternalClientHandle result. */ struct JackInternalClientHandleResult : public JackResult { int fStatus; int fIntRefNum; JackInternalClientHandleResult(): JackResult(), fStatus(0), fIntRefNum(0) {} JackInternalClientHandleResult(int32_t result, int status, int int_ref) : JackResult(result), fStatus(status), fIntRefNum(int_ref) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fStatus, sizeof(int))); CheckRes(trans->Read(&fIntRefNum, sizeof(int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fStatus, sizeof(int))); CheckRes(trans->Write(&fIntRefNum, sizeof(int))); return 0; } int Size() { return sizeof(int) + sizeof(int); } }; /*! \brief InternalClientLoad request. */ struct JackInternalClientLoadRequest : public JackRequest { #ifndef MAX_PATH #define MAX_PATH 256 #endif int fRefNum; char fName[JACK_CLIENT_NAME_SIZE+1]; char fDllName[MAX_PATH+1]; char fLoadInitName[JACK_LOAD_INIT_LIMIT+1]; int fOptions; jack_uuid_t fUUID; JackInternalClientLoadRequest() : fRefNum(0), fOptions(0), fUUID(JACK_UUID_EMPTY_INITIALIZER) { memset(fName, 0, sizeof(fName)); memset(fDllName, 0, sizeof(fDllName)); memset(fLoadInitName, 0, sizeof(fLoadInitName)); } JackInternalClientLoadRequest(int refnum, const char* client_name, const char* so_name, const char* objet_data, int options, jack_uuid_t uuid ) : JackRequest(JackRequest::kInternalClientLoad), fRefNum(refnum), fOptions(options), fUUID(uuid) { memset(fName, 0, sizeof(fName)); memset(fDllName, 0, sizeof(fDllName)); memset(fLoadInitName, 0, sizeof(fLoadInitName)); strncpy(fName, client_name, sizeof(fName)-1); if (so_name) { strncpy(fDllName, so_name, sizeof(fDllName)-1); } if (objet_data) { strncpy(fLoadInitName, objet_data, sizeof(fLoadInitName)-1); } } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fDllName, sizeof(fDllName))); CheckRes(trans->Read(&fLoadInitName, sizeof(fLoadInitName))); CheckRes(trans->Read(&fUUID, sizeof(jack_uuid_t))); return trans->Read(&fOptions, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fDllName, sizeof(fDllName))); CheckRes(trans->Write(&fLoadInitName, sizeof(fLoadInitName))); CheckRes(trans->Write(&fUUID, sizeof(jack_uuid_t))); return trans->Write(&fOptions, sizeof(int)); } int Size() { return sizeof(int) + sizeof(fName) + sizeof(fDllName) + sizeof(fLoadInitName) + sizeof(int) + sizeof(jack_uuid_t); } }; /*! \brief InternalClientLoad result. */ struct JackInternalClientLoadResult : public JackResult { int fStatus; int fIntRefNum; JackInternalClientLoadResult(): JackResult(), fStatus(0), fIntRefNum(0) {} JackInternalClientLoadResult(int32_t result, int status, int int_ref) : JackResult(result), fStatus(status), fIntRefNum(int_ref) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fStatus, sizeof(int))); CheckRes(trans->Read(&fIntRefNum, sizeof(int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fStatus, sizeof(int))); CheckRes(trans->Write(&fIntRefNum, sizeof(int))); return 0; } int Size() { return sizeof(int) + sizeof(int); } }; /*! \brief InternalClientUnload request. */ struct JackInternalClientUnloadRequest : public JackRequest { int fRefNum; int fIntRefNum; JackInternalClientUnloadRequest() : fRefNum(0), fIntRefNum(0) {} JackInternalClientUnloadRequest(int refnum, int int_ref) : JackRequest(JackRequest::kInternalClientUnload), fRefNum(refnum), fIntRefNum(int_ref) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); return trans->Read(&fIntRefNum, sizeof(int)); } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); return trans->Write(&fIntRefNum, sizeof(int)); } int Size() { return sizeof(int) + sizeof(int); } }; /*! \brief InternalClientLoad result. */ struct JackInternalClientUnloadResult : public JackResult { int fStatus; JackInternalClientUnloadResult(): JackResult(), fStatus(0) {} JackInternalClientUnloadResult(int32_t result, int status) : JackResult(result), fStatus(status) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fStatus, sizeof(int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fStatus, sizeof(int))); return 0; } int Size() { return sizeof(int); } }; /*! \brief ClientNotification request. */ struct JackClientNotificationRequest : public JackRequest { int fRefNum; int fNotify; int fValue; JackClientNotificationRequest() : fRefNum(0), fNotify(0), fValue(0) {} JackClientNotificationRequest(int refnum, int notify, int value) : JackRequest(JackRequest::kNotification), fRefNum(refnum), fNotify(notify), fValue(value) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fNotify, sizeof(int))); CheckRes(trans->Read(&fValue, sizeof(int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fNotify, sizeof(int))); CheckRes(trans->Write(&fValue, sizeof(int))); return 0; } int Size() { return 3 * sizeof(int); } }; struct JackSessionCommand { char fUUID[JACK_UUID_STRING_SIZE]; char fClientName[JACK_CLIENT_NAME_SIZE+1]; char fCommand[JACK_SESSION_COMMAND_SIZE+1]; jack_session_flags_t fFlags; JackSessionCommand() : fFlags(JackSessionSaveError) { memset(fUUID, 0, sizeof(fUUID)); memset(fClientName, 0, sizeof(fClientName)); memset(fCommand, 0, sizeof(fCommand)); } JackSessionCommand(const char *uuid, const char *clientname, const char *command, jack_session_flags_t flags) { memset(fUUID, 0, sizeof(fUUID)); memset(fClientName, 0, sizeof(fClientName)); memset(fCommand, 0, sizeof(fCommand)); strncpy(fUUID, uuid, sizeof(fUUID)-1); strncpy(fClientName, clientname, sizeof(fClientName)-1); strncpy(fCommand, command, sizeof(fCommand)-1); fFlags = flags; } }; struct JackSessionNotifyResult : public JackResult { std::list fCommandList; bool fDone; JackSessionNotifyResult(): JackResult(), fDone(false) {} JackSessionNotifyResult(int32_t result) : JackResult(result), fDone(false) {} int Read(detail::JackChannelTransactionInterface* trans) { if (trans == NULL) { return 0; } CheckRes(JackResult::Read(trans)); while (true) { JackSessionCommand buffer; CheckRes(trans->Read(buffer.fUUID, sizeof(buffer.fUUID))); if (buffer.fUUID[0] == '\0') break; CheckRes(trans->Read(buffer.fClientName, sizeof(buffer.fClientName))); CheckRes(trans->Read(buffer.fCommand, sizeof(buffer.fCommand))); CheckRes(trans->Read(&(buffer.fFlags), sizeof(buffer.fFlags))); fCommandList.push_back(buffer); } fDone = true; return 0; } int Write(detail::JackChannelTransactionInterface* trans) { if (trans == NULL) { fDone = true; return 0; } char terminator[JACK_UUID_STRING_SIZE]; memset(terminator, 0, sizeof(terminator)); CheckRes(JackResult::Write(trans)); for (std::list::iterator i = fCommandList.begin(); i != fCommandList.end(); i++) { CheckRes(trans->Write(i->fUUID, sizeof(i->fUUID))); CheckRes(trans->Write(i->fClientName, sizeof(i->fClientName))); CheckRes(trans->Write(i->fCommand, sizeof(i->fCommand))); CheckRes(trans->Write(&(i->fFlags), sizeof(i->fFlags))); } CheckRes(trans->Write(terminator, sizeof(terminator))); return 0; } jack_session_command_t* GetCommands() { /* TODO: some kind of signal should be used instead */ while (!fDone) { JackSleep(50000); /* 50 ms */ } jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (fCommandList.size() + 1)); int i = 0; for (std::list::iterator ci = fCommandList.begin(); ci != fCommandList.end(); ci++) { session_command[i].uuid = strdup(ci->fUUID); session_command[i].client_name = strdup(ci->fClientName); session_command[i].command = strdup(ci->fCommand); session_command[i].flags = ci->fFlags; i += 1; } session_command[i].uuid = NULL; session_command[i].client_name = NULL; session_command[i].command = NULL; session_command[i].flags = (jack_session_flags_t)0; return session_command; } }; /*! \brief SessionNotify request. */ struct JackSessionNotifyRequest : public JackRequest { char fPath[JACK_MESSAGE_SIZE+1]; char fDst[JACK_CLIENT_NAME_SIZE+1]; jack_session_event_type_t fEventType; int fRefNum; JackSessionNotifyRequest() : fEventType(JackSessionSave), fRefNum(0) {} JackSessionNotifyRequest(int refnum, const char* path, jack_session_event_type_t type, const char* dst) : JackRequest(JackRequest::kSessionNotify), fEventType(type), fRefNum(refnum) { memset(fPath, 0, sizeof(fPath)); memset(fDst, 0, sizeof(fDst)); strncpy(fPath, path, sizeof(fPath)-1); if (dst) { strncpy(fDst, dst, sizeof(fDst)-1); } } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(fRefNum))); CheckRes(trans->Read(&fPath, sizeof(fPath))); CheckRes(trans->Read(&fDst, sizeof(fDst))); CheckRes(trans->Read(&fEventType, sizeof(fEventType))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(fRefNum))); CheckRes(trans->Write(&fPath, sizeof(fPath))); CheckRes(trans->Write(&fDst, sizeof(fDst))); CheckRes(trans->Write(&fEventType, sizeof(fEventType))); return 0; } int Size() { return sizeof(fRefNum) + sizeof(fPath) + sizeof(fDst) + sizeof(fEventType); } }; struct JackSessionReplyRequest : public JackRequest { int fRefNum; JackSessionReplyRequest() : fRefNum(0) {} JackSessionReplyRequest(int refnum) : JackRequest(JackRequest::kSessionReply), fRefNum(refnum) {} int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fRefNum, sizeof(int))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fRefNum, sizeof(int))); return 0; } int Size() { return sizeof(int); } }; struct JackClientNameResult : public JackResult { char fName[JACK_CLIENT_NAME_SIZE+1]; JackClientNameResult(): JackResult() { memset(fName, 0, sizeof(fName)); } JackClientNameResult(int32_t result, const char* name) : JackResult(result) { memset(fName, 0, sizeof(fName)); strncpy(fName, name, sizeof(fName)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fName, sizeof(fName))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fName, sizeof(fName))); return 0; } }; struct JackUUIDResult : public JackResult { char fUUID[JACK_UUID_STRING_SIZE]; JackUUIDResult(): JackResult() { memset(fUUID, 0, sizeof(fUUID)); } JackUUIDResult(int32_t result, const char* uuid) : JackResult(result) { memset(fUUID, 0, sizeof(fUUID)); strncpy(fUUID, uuid, sizeof(fUUID)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Read(trans)); CheckRes(trans->Read(&fUUID, sizeof(fUUID))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackResult::Write(trans)); CheckRes(trans->Write(&fUUID, sizeof(fUUID))); return 0; } }; struct JackGetUUIDRequest : public JackRequest { char fName[JACK_CLIENT_NAME_SIZE+1]; JackGetUUIDRequest() { memset(fName, 0, sizeof(fName)); } JackGetUUIDRequest(const char* client_name) : JackRequest(JackRequest::kGetUUIDByClient) { memset(fName, 0, sizeof(fName)); strncpy(fName, client_name, sizeof(fName)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fName, sizeof(fName))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fName, sizeof(fName))); return 0; } int Size() { return sizeof(fName); } }; struct JackGetClientNameRequest : public JackRequest { char fUUID[JACK_UUID_STRING_SIZE]; JackGetClientNameRequest() { memset(fUUID, 0, sizeof(fUUID)); } JackGetClientNameRequest(const char* uuid) : JackRequest(JackRequest::kGetClientByUUID) { memset(fUUID, 0, sizeof(fUUID)); strncpy(fUUID, uuid, sizeof(fUUID)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fUUID, sizeof(fUUID))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fUUID, sizeof(fUUID))); return 0; } int Size() { return sizeof(fUUID); } }; struct JackReserveNameRequest : public JackRequest { int fRefNum; char fName[JACK_CLIENT_NAME_SIZE+1]; char fUUID[JACK_UUID_STRING_SIZE]; JackReserveNameRequest() : fRefNum(0) { memset(fName, 0, sizeof(fName)); memset(fUUID, 0, sizeof(fUUID)); } JackReserveNameRequest(int refnum, const char *name, const char* uuid) : JackRequest(JackRequest::kReserveClientName), fRefNum(refnum) { memset(fName, 0, sizeof(fName)); memset(fUUID, 0, sizeof(fUUID)); strncpy(fName, name, sizeof(fName)-1); strncpy(fUUID, uuid, sizeof(fUUID)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fUUID, sizeof(fUUID))); CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fRefNum, sizeof(fRefNum))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fUUID, sizeof(fUUID))); CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fRefNum, sizeof(fRefNum))); return 0; } int Size() { return sizeof(fUUID) + sizeof(fName) + sizeof(fRefNum); } }; struct JackClientHasSessionCallbackRequest : public JackRequest { char fName[JACK_CLIENT_NAME_SIZE+1]; JackClientHasSessionCallbackRequest() { memset(fName, 0, sizeof(fName)); } JackClientHasSessionCallbackRequest(const char *name) : JackRequest(JackRequest::kClientHasSessionCallback) { memset(fName, 0, sizeof(fName)); strncpy(fName, name, sizeof(fName)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fName, sizeof(fName))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fName, sizeof(fName))); return 0; } int Size() { return sizeof(fName); } }; struct JackPropertyChangeNotifyRequest : public JackRequest { jack_uuid_t fSubject; char fKey[MAX_PATH+1]; jack_property_change_t fChange; JackPropertyChangeNotifyRequest() : fChange((jack_property_change_t)0) { jack_uuid_clear(&fSubject); memset(fKey, 0, sizeof(fKey)); } JackPropertyChangeNotifyRequest(jack_uuid_t subject, const char* key, jack_property_change_t change) : JackRequest(JackRequest::kPropertyChangeNotify), fChange(change) { jack_uuid_copy(&fSubject, subject); memset(fKey, 0, sizeof(fKey)); if (key) strncpy(fKey, key, sizeof(fKey)-1); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fSubject, sizeof(fSubject))); CheckRes(trans->Read(&fKey, sizeof(fKey))); CheckRes(trans->Read(&fChange, sizeof(fChange))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(JackRequest::Write(trans, Size())); CheckRes(trans->Write(&fSubject, sizeof(fSubject))); CheckRes(trans->Write(&fKey, sizeof(fKey))); CheckRes(trans->Write(&fChange, sizeof(fChange))); return 0; } int Size() { return sizeof(fSubject) + sizeof(fKey) + sizeof(fChange); } }; /*! \brief ClientNotification. */ struct JackClientNotification { int fSize; char fName[JACK_CLIENT_NAME_SIZE+1]; int fRefNum; int fNotify; int fValue1; int fValue2; int fSync; char fMessage[JACK_MESSAGE_SIZE+1]; JackClientNotification(): fSize(0), fRefNum(0), fNotify(-1), fValue1(-1), fValue2(-1), fSync(0) { memset(fName, 0, sizeof(fName)); memset(fMessage, 0, sizeof(fMessage)); } JackClientNotification(const char* name, int refnum, int notify, int sync, const char* message, int value1, int value2) : fRefNum(refnum), fNotify(notify), fValue1(value1), fValue2(value2), fSync(sync) { memset(fName, 0, sizeof(fName)); memset(fMessage, 0, sizeof(fMessage)); strncpy(fName, name, sizeof(fName)-1); if (message) { strncpy(fMessage, message, sizeof(fMessage)-1); } fSize = Size(); } int Read(detail::JackChannelTransactionInterface* trans) { CheckSize(); CheckRes(trans->Read(&fName, sizeof(fName))); CheckRes(trans->Read(&fRefNum, sizeof(int))); CheckRes(trans->Read(&fNotify, sizeof(int))); CheckRes(trans->Read(&fValue1, sizeof(int))); CheckRes(trans->Read(&fValue2, sizeof(int))); CheckRes(trans->Read(&fSync, sizeof(int))); CheckRes(trans->Read(&fMessage, sizeof(fMessage))); return 0; } int Write(detail::JackChannelTransactionInterface* trans) { CheckRes(trans->Write(&fSize, sizeof(int))); CheckRes(trans->Write(&fName, sizeof(fName))); CheckRes(trans->Write(&fRefNum, sizeof(int))); CheckRes(trans->Write(&fNotify, sizeof(int))); CheckRes(trans->Write(&fValue1, sizeof(int))); CheckRes(trans->Write(&fValue2, sizeof(int))); CheckRes(trans->Write(&fSync, sizeof(int))); CheckRes(trans->Write(&fMessage, sizeof(fMessage))); return 0; } int Size() { return sizeof(int) + sizeof(fName) + 5 * sizeof(int) + sizeof(fMessage); } }; } // end of namespace #endif jack2-1.9.22/common/JackRequestDecoder.cpp000066400000000000000000000331541436671425200203220ustar00rootroot00000000000000/* Copyright (C) 2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackRequestDecoder.h" #include "JackServer.h" #include "JackLockedEngine.h" #include "JackChannel.h" #include #include using namespace std; namespace Jack { #define CheckRead(req, socket) { if (req.Read(socket) < 0) { jack_error("CheckRead error"); return -1; } } #define CheckWriteName(error, socket) { if (res.Write(socket) < 0) { jack_error("%s write error name = %s", error, req.fName); } } #define CheckWriteRefNum(error, socket) { if (res.Write(socket) < 0) { jack_error("%s write error ref = %d", error, req.fRefNum); } } #define CheckWrite(error, socket) { if (res.Write(socket) < 0) { jack_error("%s write error", error); } } JackRequestDecoder::JackRequestDecoder(JackServer* server, JackClientHandlerInterface* handler) :fServer(server), fHandler(handler) {} JackRequestDecoder::~JackRequestDecoder() {} int JackRequestDecoder::HandleRequest(detail::JackChannelTransactionInterface* socket, int type_aux) { JackRequest::RequestType type = (JackRequest::RequestType)type_aux; // Read data switch (type) { case JackRequest::kClientCheck: { jack_log("JackRequest::ClientCheck"); JackClientCheckRequest req; JackClientCheckResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->ClientCheck(req.fName, req.fUUID, res.fName, req.fProtocol, req.fOptions, &res.fStatus); CheckWriteName("JackRequest::ClientCheck", socket); // Atomic ClientCheck followed by ClientOpen on same socket if (req.fOpen) { JackRequest header; header.Read(socket); return HandleRequest(socket, header.fType); } break; } case JackRequest::kClientOpen: { jack_log("JackRequest::ClientOpen"); JackClientOpenRequest req; JackClientOpenResult res; CheckRead(req, socket); fHandler->ClientAdd(socket, &req, &res); CheckWriteName("JackRequest::ClientOpen", socket); break; } case JackRequest::kClientClose: { jack_log("JackRequest::ClientClose"); JackClientCloseRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->ClientExternalClose(req.fRefNum); CheckWriteRefNum("JackRequest::ClientClose", socket); fHandler->ClientRemove(socket, req.fRefNum); // Will cause the wrapping thread to stop return -1; } case JackRequest::kActivateClient: { JackActivateRequest req; JackResult res; jack_log("JackRequest::ActivateClient"); CheckRead(req, socket); res.fResult = fServer->GetEngine()->ClientActivate(req.fRefNum, req.fIsRealTime); CheckWriteRefNum("JackRequest::ActivateClient", socket); break; } case JackRequest::kDeactivateClient: { jack_log("JackRequest::DeactivateClient"); JackDeactivateRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->ClientDeactivate(req.fRefNum); CheckWriteRefNum("JackRequest::DeactivateClient", socket); break; } case JackRequest::kRegisterPort: { jack_log("JackRequest::RegisterPort"); JackPortRegisterRequest req; JackPortRegisterResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->PortRegister(req.fRefNum, req.fName, req.fPortType, req.fFlags, req.fBufferSize, &res.fPortIndex); CheckWriteRefNum("JackRequest::RegisterPort", socket); break; } case JackRequest::kUnRegisterPort: { jack_log("JackRequest::UnRegisterPort"); JackPortUnRegisterRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->PortUnRegister(req.fRefNum, req.fPortIndex); CheckWriteRefNum("JackRequest::UnRegisterPort", socket); break; } case JackRequest::kConnectNamePorts: { jack_log("JackRequest::ConnectNamePorts"); JackPortConnectNameRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->PortConnect(req.fRefNum, req.fSrc, req.fDst); CheckWriteRefNum("JackRequest::ConnectNamePorts", socket); break; } case JackRequest::kDisconnectNamePorts: { jack_log("JackRequest::DisconnectNamePorts"); JackPortDisconnectNameRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->PortDisconnect(req.fRefNum, req.fSrc, req.fDst); CheckWriteRefNum("JackRequest::DisconnectNamePorts", socket); break; } case JackRequest::kConnectPorts: { jack_log("JackRequest::ConnectPorts"); JackPortConnectRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->PortConnect(req.fRefNum, req.fSrc, req.fDst); CheckWriteRefNum("JackRequest::ConnectPorts", socket); break; } case JackRequest::kDisconnectPorts: { jack_log("JackRequest::DisconnectPorts"); JackPortDisconnectRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->PortDisconnect(req.fRefNum, req.fSrc, req.fDst); CheckWriteRefNum("JackRequest::DisconnectPorts", socket); break; } case JackRequest::kPortRename: { jack_log("JackRequest::PortRename"); JackPortRenameRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->PortRename(req.fRefNum, req.fPort, req.fName); CheckWriteRefNum("JackRequest::PortRename", socket); break; } case JackRequest::kSetBufferSize: { jack_log("JackRequest::SetBufferSize"); JackSetBufferSizeRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->SetBufferSize(req.fBufferSize); CheckWrite("JackRequest::SetBufferSize", socket); break; } case JackRequest::kSetFreeWheel: { jack_log("JackRequest::SetFreeWheel"); JackSetFreeWheelRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->SetFreewheel(req.fOnOff); CheckWrite("JackRequest::SetFreeWheel", socket); break; } case JackRequest::kComputeTotalLatencies: { jack_log("JackRequest::ComputeTotalLatencies"); JackComputeTotalLatenciesRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->ComputeTotalLatencies(); CheckWrite("JackRequest::ComputeTotalLatencies", socket); break; } case JackRequest::kReleaseTimebase: { jack_log("JackRequest::ReleaseTimebase"); JackReleaseTimebaseRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->ReleaseTimebase(req.fRefNum); CheckWriteRefNum("JackRequest::ReleaseTimebase", socket); break; } case JackRequest::kSetTimebaseCallback: { jack_log("JackRequest::SetTimebaseCallback"); JackSetTimebaseCallbackRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->SetTimebaseCallback(req.fRefNum, req.fConditionnal); CheckWriteRefNum("JackRequest::SetTimebaseCallback", socket); break; } case JackRequest::kGetInternalClientName: { jack_log("JackRequest::GetInternalClientName"); JackGetInternalClientNameRequest req; JackGetInternalClientNameResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->GetInternalClientName(req.fIntRefNum, res.fName); CheckWriteRefNum("JackRequest::GetInternalClientName", socket); break; } case JackRequest::kInternalClientHandle: { jack_log("JackRequest::InternalClientHandle"); JackInternalClientHandleRequest req; JackInternalClientHandleResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->InternalClientHandle(req.fName, &res.fStatus, &res.fIntRefNum); CheckWriteRefNum("JackRequest::InternalClientHandle", socket); break; } case JackRequest::kInternalClientLoad: { jack_log("JackRequest::InternalClientLoad"); JackInternalClientLoadRequest req; JackInternalClientLoadResult res; CheckRead(req, socket); res.fResult = fServer->InternalClientLoad1(req.fName, req.fDllName, req.fLoadInitName, req.fOptions, &res.fIntRefNum, req.fUUID, &res.fStatus); CheckWriteName("JackRequest::InternalClientLoad", socket); break; } case JackRequest::kInternalClientUnload: { jack_log("JackRequest::InternalClientUnload"); JackInternalClientUnloadRequest req; JackInternalClientUnloadResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->InternalClientUnload(req.fIntRefNum, &res.fStatus); CheckWriteRefNum("JackRequest::InternalClientUnload", socket); break; } case JackRequest::kNotification: { jack_log("JackRequest::Notification"); JackClientNotificationRequest req; CheckRead(req, socket); if (req.fNotify == kQUIT) { jack_log("JackRequest::Notification kQUIT"); throw JackQuitException(); } else { fServer->Notify(req.fRefNum, req.fNotify, req.fValue); } break; } case JackRequest::kSessionNotify: { jack_log("JackRequest::SessionNotify"); JackSessionNotifyRequest req; CheckRead(req, socket); fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, socket, NULL); break; } case JackRequest::kSessionReply: { jack_log("JackRequest::SessionReply"); JackSessionReplyRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->SessionReply(req.fRefNum); CheckWrite("JackRequest::SessionReply", socket); break; } case JackRequest::kGetClientByUUID: { jack_log("JackRequest::GetClientByUUID"); JackGetClientNameRequest req; JackClientNameResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->GetClientNameForUUID(req.fUUID, res.fName); CheckWrite("JackRequest::GetClientByUUID", socket); break; } case JackRequest::kGetUUIDByClient: { jack_log("JackRequest::GetUUIDByClient"); JackGetUUIDRequest req; JackUUIDResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->GetUUIDForClientName(req.fName, res.fUUID); CheckWrite("JackRequest::GetUUIDByClient", socket); break; } case JackRequest::kReserveClientName: { jack_log("JackRequest::ReserveClientName"); JackReserveNameRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->ReserveClientName(req.fName, req.fUUID); CheckWrite("JackRequest::ReserveClientName", socket); break; } case JackRequest::kClientHasSessionCallback: { jack_log("JackRequest::ClientHasSessionCallback"); JackClientHasSessionCallbackRequest req; JackResult res; CheckRead(req, socket); res.fResult = fServer->GetEngine()->ClientHasSessionCallback(req.fName); CheckWrite("JackRequest::ClientHasSessionCallback", socket); break; } case JackRequest::kPropertyChangeNotify: { jack_log("JackRequest::PropertyChangeNotify"); JackPropertyChangeNotifyRequest req; CheckRead(req, socket); fServer->GetEngine()->PropertyChangeNotify(req.fSubject, req.fKey, req.fChange); break; } default: jack_error("Unknown request %ld", type); return -1; } return 0; } } // end of namespace jack2-1.9.22/common/JackRequestDecoder.h000066400000000000000000000031651436671425200177660ustar00rootroot00000000000000/* Copyright (C) 2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackRequestDecoder__ #define __JackRequestDecoder__ #include "JackChannel.h" namespace Jack { class JackServer; struct JackClientOpenRequest; struct JackClientOpenResult; struct JackClientHandlerInterface { virtual void ClientAdd(detail::JackChannelTransactionInterface* socket, JackClientOpenRequest* req, JackClientOpenResult* res) = 0; virtual void ClientRemove(detail::JackChannelTransactionInterface* socket, int refnum) = 0; virtual ~JackClientHandlerInterface() {} }; /*! \brief Request decoder */ class JackRequestDecoder { private: JackServer* fServer; JackClientHandlerInterface* fHandler; public: JackRequestDecoder(JackServer* server, JackClientHandlerInterface* handler); virtual ~JackRequestDecoder(); int HandleRequest(detail::JackChannelTransactionInterface* socket, int type); }; } // end of namespace #endif jack2-1.9.22/common/JackResampler.cpp000066400000000000000000000076401436671425200173370ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackResampler.h" #include "JackError.h" #include namespace Jack { JackRingBuffer::JackRingBuffer(int size):fRingBufferSize(size) { fRingBuffer = jack_ringbuffer_create(sizeof(jack_default_audio_sample_t) * fRingBufferSize); Reset(fRingBufferSize); } JackRingBuffer::~JackRingBuffer() { if (fRingBuffer) { jack_ringbuffer_free(fRingBuffer); } } void JackRingBuffer::Reset(unsigned int new_size) { fRingBufferSize = new_size; jack_ringbuffer_reset(fRingBuffer); jack_ringbuffer_reset_size(fRingBuffer, sizeof(jack_default_audio_sample_t) * fRingBufferSize); jack_ringbuffer_read_advance(fRingBuffer, (sizeof(jack_default_audio_sample_t) * new_size/2)); } unsigned int JackRingBuffer::ReadSpace() { return (jack_ringbuffer_read_space(fRingBuffer) / sizeof(jack_default_audio_sample_t)); } unsigned int JackRingBuffer::WriteSpace() { return (jack_ringbuffer_write_space(fRingBuffer) / sizeof(jack_default_audio_sample_t)); } unsigned int JackRingBuffer::Read(jack_default_audio_sample_t* buffer, unsigned int frames) { size_t len = jack_ringbuffer_read_space(fRingBuffer); jack_log("JackRingBuffer::Read input available = %ld", len / sizeof(jack_default_audio_sample_t)); if (len < frames * sizeof(jack_default_audio_sample_t)) { jack_error("JackRingBuffer::Read : producer too slow, missing frames = %d", frames); return 0; } else { jack_ringbuffer_read(fRingBuffer, (char*)buffer, frames * sizeof(jack_default_audio_sample_t)); return frames; } } unsigned int JackRingBuffer::Write(jack_default_audio_sample_t* buffer, unsigned int frames) { size_t len = jack_ringbuffer_write_space(fRingBuffer); jack_log("JackRingBuffer::Write output available = %ld", len / sizeof(jack_default_audio_sample_t)); if (len < frames * sizeof(jack_default_audio_sample_t)) { jack_error("JackRingBuffer::Write : consumer too slow, skip frames = %d", frames); return 0; } else { jack_ringbuffer_write(fRingBuffer, (char*)buffer, frames * sizeof(jack_default_audio_sample_t)); return frames; } } unsigned int JackRingBuffer::Read(void* buffer, unsigned int bytes) { size_t len = jack_ringbuffer_read_space(fRingBuffer); jack_log("JackRingBuffer::Read input available = %ld", len); if (len < bytes) { jack_error("JackRingBuffer::Read : producer too slow, missing bytes = %d", bytes); return 0; } else { jack_ringbuffer_read(fRingBuffer, (char*)buffer, bytes); return bytes; } } unsigned int JackRingBuffer::Write(void* buffer, unsigned int bytes) { size_t len = jack_ringbuffer_write_space(fRingBuffer); jack_log("JackRingBuffer::Write output available = %ld", len); if (len < bytes) { jack_error("JackRingBuffer::Write : consumer too slow, skip bytes = %d", bytes); return 0; } else { jack_ringbuffer_write(fRingBuffer, (char*)buffer, bytes); return bytes; } } unsigned int JackResampler::ReadResample(jack_default_audio_sample_t* buffer, unsigned int frames) { return Read(buffer, frames); } unsigned int JackResampler::WriteResample(jack_default_audio_sample_t* buffer, unsigned int frames) { return Write(buffer, frames); } } jack2-1.9.22/common/JackResampler.h000066400000000000000000000050451436671425200170010ustar00rootroot00000000000000/* Copyright (C) 2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackResampler__ #define __JackResampler__ #include "ringbuffer.h" #include "types.h" namespace Jack { #define DEFAULT_RB_SIZE 32768 #define DEFAULT_ADAPTATIVE_SIZE 2048 inline float Range(float min, float max, float val) { return (val < min) ? min : ((val > max) ? max : val); } /*! \brief Base class for RingBuffer in frames. */ class JackRingBuffer { protected: jack_ringbuffer_t* fRingBuffer; unsigned int fRingBufferSize; public: JackRingBuffer(int size = DEFAULT_RB_SIZE); virtual ~JackRingBuffer(); virtual void Reset(unsigned int new_size); // in frames virtual unsigned int Read(jack_default_audio_sample_t* buffer, unsigned int frames); virtual unsigned int Write(jack_default_audio_sample_t* buffer, unsigned int frames); // in bytes virtual unsigned int Read(void* buffer, unsigned int bytes); virtual unsigned int Write(void* buffer, unsigned int bytes); // in frames virtual unsigned int ReadSpace(); virtual unsigned int WriteSpace(); unsigned int GetError() { return (jack_ringbuffer_read_space(fRingBuffer) / sizeof(float)) - (fRingBufferSize / 2); } }; /*! \brief Base class for Resampler. */ class JackResampler : public JackRingBuffer { protected: double fRatio; public: JackResampler():JackRingBuffer(),fRatio(1) {} virtual ~JackResampler() {} virtual unsigned int ReadResample(jack_default_audio_sample_t* buffer, unsigned int frames); virtual unsigned int WriteResample(jack_default_audio_sample_t* buffer, unsigned int frames); void SetRatio(double ratio) { fRatio = Range(0.25, 4.0, ratio); } double GetRatio() { return fRatio; } }; } #endif jack2-1.9.22/common/JackRestartThreadedDriver.cpp000066400000000000000000000030051436671425200216350ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackRestartThreadedDriver.h" #include "JackException.h" namespace Jack { bool JackRestartThreadedDriver::Execute() { try { // Keep running even in case of error while (fThread.GetStatus() == JackThread::kRunning) { Process(); } return false; } catch (JackNetException& e) { e.PrintMessage(); jack_info("Driver is restarted"); fThread.DropSelfRealTime(); // Thread in kIniting status again... fThread.SetStatus(JackThread::kIniting); if (Init()) { // Thread in kRunning status again... fThread.SetStatus(JackThread::kRunning); return true; } else { return false; } } } } // end of namespace jack2-1.9.22/common/JackRestartThreadedDriver.h000066400000000000000000000024161436671425200213070ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackRestartThreadedDriver__ #define __JackRestartThreadedDriver__ #include "JackThreadedDriver.h" namespace Jack { /*! \brief Restart a driver after an exception is thrown. */ class SERVER_EXPORT JackRestartThreadedDriver : public JackThreadedDriver { public: JackRestartThreadedDriver(JackDriver* driver) :JackThreadedDriver(driver) {} virtual ~JackRestartThreadedDriver() {} // JackRunnableInterface interface virtual bool Execute(); }; } // end of namespace #endif jack2-1.9.22/common/JackServer.cpp000066400000000000000000000327431436671425200166550ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackServerGlobals.h" #include "JackTime.h" #include "JackFreewheelDriver.h" #include "JackThreadedDriver.h" #include "JackGlobals.h" #include "JackLockedEngine.h" #include "JackAudioDriver.h" #include "JackChannel.h" #include "JackClientControl.h" #include "JackEngineControl.h" #include "JackGraphManager.h" #include "JackInternalClient.h" #include "JackError.h" #include "JackMessageBuffer.h" #include "JackInternalSessionLoader.h" const char * jack_get_self_connect_mode_description(char mode); namespace Jack { //---------------- // Server control //---------------- JackServer::JackServer(bool sync, bool temporary, int timeout, bool rt, int priority, int port_max, bool verbose, jack_timer_type_t clock, char self_connect_mode, const char* server_name) { if (rt) { jack_info("JACK server starting in realtime mode with priority %ld", priority); } else { jack_info("JACK server starting in non-realtime mode"); } jack_info("self-connect-mode is \"%s\"", jack_get_self_connect_mode_description(self_connect_mode)); fGraphManager = JackGraphManager::Allocate(port_max); fEngineControl = new JackEngineControl(sync, temporary, timeout, rt, priority, verbose, clock, server_name); fEngine = new JackLockedEngine(fGraphManager, GetSynchroTable(), fEngineControl, self_connect_mode); // A distinction is made between the threaded freewheel driver and the // regular freewheel driver because the freewheel driver needs to run in // threaded mode when freewheel mode is active and needs to run as a slave // when freewheel mode isn't active. JackFreewheelDriver* freewheelDriver = new JackFreewheelDriver(fEngine, GetSynchroTable()); fThreadedFreewheelDriver = new JackThreadedDriver(freewheelDriver); fFreewheelDriver = freewheelDriver; fDriverInfo = new JackDriverInfo(); fAudioDriver = NULL; fFreewheel = false; JackServerGlobals::fInstance = this; // Unique instance JackServerGlobals::fUserCount = 1; // One user JackGlobals::fVerbose = verbose; } JackServer::~JackServer() { JackGraphManager::Destroy(fGraphManager); delete fDriverInfo; delete fThreadedFreewheelDriver; delete fEngine; delete fEngineControl; } int JackServer::Open(jack_driver_desc_t* driver_desc, JSList* driver_params) { // TODO: move that in reworked JackServerGlobals::Init() if (!JackMessageBuffer::Create()) { jack_error("Cannot create message buffer"); } if ((fAudioDriver = fDriverInfo->Open(driver_desc, fEngine, GetSynchroTable(), driver_params)) == NULL) { jack_error("Cannot initialize driver"); goto fail_close1; } if (fRequestChannel.Open(fEngineControl->fServerName, this) < 0) { jack_error("Server channel open error"); goto fail_close2; } if (fEngine->Open() < 0) { jack_error("Cannot open engine"); goto fail_close3; } if (fFreewheelDriver->Open() < 0) { jack_error("Cannot open freewheel driver"); goto fail_close4; } if (fAudioDriver->Attach() < 0) { jack_error("Cannot attach audio driver"); goto fail_close5; } fFreewheelDriver->SetMaster(false); fAudioDriver->SetMaster(true); fAudioDriver->AddSlave(fFreewheelDriver); InitTime(); SetClockSource(fEngineControl->fClockSource); return 0; fail_close5: fFreewheelDriver->Close(); fail_close4: fEngine->Close(); fail_close3: fRequestChannel.Close(); fail_close2: fAudioDriver->Close(); fail_close1: JackMessageBuffer::Destroy(); return -1; } int JackServer::Close() { jack_log("JackServer::Close"); fRequestChannel.Close(); fAudioDriver->Detach(); fAudioDriver->Close(); fFreewheelDriver->Close(); fEngine->Close(); // TODO: move that in reworked JackServerGlobals::Destroy() JackMessageBuffer::Destroy(); EndTime(); return 0; } int JackServer::Start() { jack_log("JackServer::Start"); if (fAudioDriver->Start() < 0) { return -1; } return fRequestChannel.Start(); } int JackServer::Stop() { jack_log("JackServer::Stop"); int res = -1; if (fFreewheel) { if (fThreadedFreewheelDriver) { res = fThreadedFreewheelDriver->Stop(); } } else { if (fAudioDriver) { res = fAudioDriver->Stop(); } } fEngine->NotifyQuit(); fRequestChannel.Stop(); fEngine->NotifyFailure(JackFailure | JackServerError, JACK_SERVER_FAILURE); return res; } bool JackServer::IsRunning() { jack_log("JackServer::IsRunning"); assert(fAudioDriver); return fAudioDriver->IsRunning(); } //------------------ // Internal clients //------------------ int JackServer::InternalClientLoad1(const char* client_name, const char* so_name, const char* objet_data, int options, int* int_ref, jack_uuid_t uuid, int* status) { JackLoadableInternalClient* client = new JackLoadableInternalClient1(JackServerGlobals::fInstance, GetSynchroTable(), objet_data); assert(client); return InternalClientLoadAux(client, so_name, client_name, options, int_ref, uuid, status); } int JackServer::InternalClientLoad2(const char* client_name, const char* so_name, const JSList * parameters, int options, int* int_ref, jack_uuid_t uuid, int* status) { JackLoadableInternalClient* client = new JackLoadableInternalClient2(JackServerGlobals::fInstance, GetSynchroTable(), parameters); assert(client); return InternalClientLoadAux(client, so_name, client_name, options, int_ref, uuid, status); } int JackServer::InternalClientLoadAux(JackLoadableInternalClient* client, const char* so_name, const char* client_name, int options, int* int_ref, jack_uuid_t uuid, int* status) { // Clear status *status = 0; // Client object is internally kept in JackEngine if ((client->Init(so_name) < 0) || (client->Open(JackTools::DefaultServerName(), client_name, uuid, (jack_options_t)options, (jack_status_t*)status) < 0)) { delete client; int my_status1 = *status | JackFailure; *status = (jack_status_t)my_status1; *int_ref = 0; return -1; } else { *int_ref = client->GetClientControl()->fRefNum; return 0; } } //----------------------- // Internal session file //----------------------- int JackServer::LoadInternalSessionFile(const char* file) { JackInternalSessionLoader loader(this); return loader.Load(file); } //--------------------------- // From request thread : API //--------------------------- int JackServer::SetBufferSize(jack_nframes_t buffer_size) { jack_log("JackServer::SetBufferSize nframes = %ld", buffer_size); jack_nframes_t current_buffer_size = fEngineControl->fBufferSize; if (current_buffer_size == buffer_size) { jack_log("SetBufferSize: requirement for new buffer size equals current value"); return 0; } if (fAudioDriver->IsFixedBufferSize()) { jack_log("SetBufferSize: driver only supports a fixed buffer size"); return -1; } if (fAudioDriver->Stop() != 0) { jack_error("Cannot stop audio driver"); return -1; } if (fAudioDriver->SetBufferSize(buffer_size) == 0) { fEngine->NotifyBufferSize(buffer_size); return fAudioDriver->Start(); } else { // Failure: try to restore current value jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size); fAudioDriver->SetBufferSize(current_buffer_size); fAudioDriver->Start(); // SetBufferSize actually failed, so return an error... return -1; } } /* Freewheel mode is implemented by switching from the (audio [slaves] + freewheel) driver to the freewheel driver only: - "global" connection state is saved - all audio driver and slaves ports are deconnected, thus there is no more dependencies with the audio driver and slaves - the freewheel driver will be synchronized with the end of graph execution : all clients are connected to the freewheel driver - the freewheel driver becomes the "master" Normal mode is restored with the connections state valid before freewheel mode was done. Thus one consider that no graph state change can be done during freewheel mode. */ int JackServer::SetFreewheel(bool onoff) { jack_log("JackServer::SetFreewheel is = %ld want = %ld", fFreewheel, onoff); if (fFreewheel) { if (onoff) { return -1; } else { fFreewheel = false; fThreadedFreewheelDriver->Stop(); fGraphManager->Restore(&fConnectionState); // Restore connection state fEngine->NotifyFreewheel(onoff); fFreewheelDriver->SetMaster(false); fAudioDriver->SetMaster(true); return fAudioDriver->Start(); } } else { if (onoff) { fFreewheel = true; fAudioDriver->Stop(); fGraphManager->Save(&fConnectionState); // Save connection state // Disconnect all slaves std::list slave_list = fAudioDriver->GetSlaves(); std::list::const_iterator it; for (it = slave_list.begin(); it != slave_list.end(); it++) { JackDriver* slave = dynamic_cast(*it); assert(slave); fGraphManager->DisconnectAllPorts(slave->GetClientControl()->fRefNum); } // Disconnect master fGraphManager->DisconnectAllPorts(fAudioDriver->GetClientControl()->fRefNum); fEngine->NotifyFreewheel(onoff); fAudioDriver->SetMaster(false); fFreewheelDriver->SetMaster(true); return fThreadedFreewheelDriver->Start(); } else { return -1; } } } //--------------------------- // Coming from the RT thread //--------------------------- void JackServer::Notify(int refnum, int notify, int value) { switch (notify) { case kGraphOrderCallback: fEngine->NotifyGraphReorder(); break; case kXRunCallback: fEngine->NotifyClientXRun(refnum); break; } } //-------------------- // Backend management //-------------------- JackDriverInfo* JackServer::AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params) { JackDriverInfo* info = new JackDriverInfo(); JackDriverClientInterface* slave = info->Open(driver_desc, fEngine, GetSynchroTable(), driver_params); if (!slave) { goto error1; } if (slave->Attach() < 0) { goto error2; } slave->SetMaster(false); fAudioDriver->AddSlave(slave); return info; error2: slave->Close(); error1: delete info; return NULL; } void JackServer::RemoveSlave(JackDriverInfo* info) { JackDriverClientInterface* slave = info->GetBackend(); fAudioDriver->RemoveSlave(slave); slave->Detach(); slave->Close(); } int JackServer::SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_params) { std::list slave_list; std::list::const_iterator it; // Remove current master fAudioDriver->Stop(); fAudioDriver->Detach(); fAudioDriver->Close(); // Open new master JackDriverInfo* info = new JackDriverInfo(); JackDriverClientInterface* master = info->Open(driver_desc, fEngine, GetSynchroTable(), driver_params); if (!master) { goto error; } // Get slaves list slave_list = fAudioDriver->GetSlaves(); // Move slaves in new master for (it = slave_list.begin(); it != slave_list.end(); it++) { JackDriverInterface* slave = *it; master->AddSlave(slave); } // Delete old master delete fDriverInfo; // Activate master fAudioDriver = master; fDriverInfo = info; if (fAudioDriver->Attach() < 0) { goto error; } // Notify clients of new values fEngine->NotifyBufferSize(fEngineControl->fBufferSize); fEngine->NotifySampleRate(fEngineControl->fSampleRate); // And finally start fAudioDriver->SetMaster(true); return fAudioDriver->Start(); error: delete info; return -1; } //---------------------- // Transport management //---------------------- int JackServer::ReleaseTimebase(int refnum) { return fEngineControl->fTransport.ResetTimebase(refnum); } int JackServer::SetTimebaseCallback(int refnum, int conditional) { return fEngineControl->fTransport.SetTimebaseMaster(refnum, conditional); } JackLockedEngine* JackServer::GetEngine() { return fEngine; } JackSynchro* JackServer::GetSynchroTable() { return fSynchroTable; } JackEngineControl* JackServer::GetEngineControl() { return fEngineControl; } JackGraphManager* JackServer::GetGraphManager() { return fGraphManager; } } // end of namespace jack2-1.9.22/common/JackServer.h000066400000000000000000000070051436671425200163130ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackServer__ #define __JackServer__ #include "JackCompilerDeps.h" #include "driver_interface.h" #include "JackDriverLoader.h" #include "JackDriverInfo.h" #include "JackConnectionManager.h" #include "JackGlobals.h" #include "JackPlatformPlug.h" #include "jslist.h" namespace Jack { class JackGraphManager; class JackDriverClientInterface; struct JackEngineControl; class JackLockedEngine; class JackLoadableInternalClient; /*! \brief The Jack server. */ class SERVER_EXPORT JackServer { private: JackDriverInfo* fDriverInfo; JackDriverClientInterface* fAudioDriver; JackDriverClientInterface* fFreewheelDriver; JackDriverClientInterface* fThreadedFreewheelDriver; JackLockedEngine* fEngine; JackEngineControl* fEngineControl; JackGraphManager* fGraphManager; JackServerChannel fRequestChannel; JackConnectionManager fConnectionState; JackSynchro fSynchroTable[CLIENT_NUM]; bool fFreewheel; int InternalClientLoadAux(JackLoadableInternalClient* client, const char* so_name, const char* client_name, int options, int* int_ref, jack_uuid_t uuid, int* status); public: JackServer(bool sync, bool temporary, int timeout, bool rt, int priority, int port_max, bool verbose, jack_timer_type_t clock, char self_connect_mode, const char* server_name); ~JackServer(); // Server control int Open(jack_driver_desc_t* driver_desc, JSList* driver_params); int Close(); int Start(); int Stop(); bool IsRunning(); // RT thread void Notify(int refnum, int notify, int value); // From request thread : API int SetBufferSize(jack_nframes_t buffer_size); int SetFreewheel(bool onoff); // Internals clients int InternalClientLoad1(const char* client_name, const char* so_name, const char* objet_data, int options, int* int_ref, jack_uuid_t uuid, int* status); int InternalClientLoad2(const char* client_name, const char* so_name, const JSList * parameters, int options, int* int_ref, jack_uuid_t uuid, int* status); // Internal session file int LoadInternalSessionFile(const char* file); // Transport management int ReleaseTimebase(int refnum); int SetTimebaseCallback(int refnum, int conditional); // Backend management JackDriverInfo* AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params); void RemoveSlave(JackDriverInfo* info); int SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_params); // Object access JackLockedEngine* GetEngine(); JackEngineControl* GetEngineControl(); JackSynchro* GetSynchroTable(); JackGraphManager* GetGraphManager(); }; } // end of namespace #endif jack2-1.9.22/common/JackServerAPI.cpp000066400000000000000000000143421436671425200172020ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackGraphManager.h" #include "JackInternalClient.h" #include "JackServer.h" #include "JackDebugClient.h" #include "JackServerGlobals.h" #include "JackTools.h" #include "JackCompilerDeps.h" #include "JackLockedEngine.h" #ifdef __cplusplus extern "C" { #endif jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t options, jack_status_t* status); SERVER_EXPORT jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...); SERVER_EXPORT int jack_client_close (jack_client_t *client); SERVER_EXPORT int jack_get_client_pid (const char *name); #ifdef __cplusplus } #endif using namespace Jack; jack_client_t* jack_client_new_aux(const char* client_name, jack_options_t options, jack_status_t* status) { jack_varargs_t va; /* variable arguments */ jack_status_t my_status; JackClient* client; if (client_name == NULL) { jack_error("jack_client_new called with a NULL client_name"); return NULL; } jack_log("jack_client_new %s", client_name); if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; /* validate parameters */ if ((options & ~JackOpenOptions)) { int my_status1 = *status | (JackFailure | JackInvalidOption); *status = (jack_status_t)my_status1; return NULL; } /* parse variable arguments */ jack_varargs_init(&va); if (!JackServerGlobals::Init()) { // jack server initialisation int my_status1 = (JackFailure | JackServerError); *status = (jack_status_t)my_status1; return NULL; } if (JACK_DEBUG) { client = new JackDebugClient(new JackInternalClient(JackServerGlobals::fInstance, GetSynchroTable())); // Debug mode } else { client = new JackInternalClient(JackServerGlobals::fInstance, GetSynchroTable()); } int res = client->Open(va.server_name, client_name, va.session_id, options, status); if (res < 0) { delete client; JackServerGlobals::Destroy(); // jack server destruction int my_status1 = (JackFailure | JackServerError); *status = (jack_status_t)my_status1; return NULL; } else { return (jack_client_t*)client; } } jack_client_t* jack_client_open_aux(const char* client_name, jack_options_t options, jack_status_t* status, va_list ap) { jack_varargs_t va; /* variable arguments */ jack_status_t my_status; JackClient* client; if (client_name == NULL) { jack_error("jack_client_open called with a NULL client_name"); return NULL; } jack_log("jack_client_open %s", client_name); if (status == NULL) /* no status from caller? */ status = &my_status; /* use local status word */ *status = (jack_status_t)0; /* validate parameters */ if ((options & ~JackOpenOptions)) { int my_status1 = *status | (JackFailure | JackInvalidOption); *status = (jack_status_t)my_status1; return NULL; } /* parse variable arguments */ jack_varargs_parse(options, ap, &va); if (!JackServerGlobals::Init()) { // jack server initialisation int my_status1 = (JackFailure | JackServerError); *status = (jack_status_t)my_status1; return NULL; } if (JACK_DEBUG) { client = new JackDebugClient(new JackInternalClient(JackServerGlobals::fInstance, GetSynchroTable())); // Debug mode } else { client = new JackInternalClient(JackServerGlobals::fInstance, GetSynchroTable()); } int res = client->Open(va.server_name, client_name, va.session_id, options, status); if (res < 0) { delete client; JackServerGlobals::Destroy(); // jack server destruction int my_status1 = (JackFailure | JackServerError); *status = (jack_status_t)my_status1; return NULL; } else { return (jack_client_t*)client; } } SERVER_EXPORT jack_client_t* jack_client_open(const char* ext_client_name, jack_options_t options, jack_status_t* status, ...) { JackGlobals::CheckContext("jack_client_open"); try { assert(JackGlobals::fOpenMutex); JackGlobals::fOpenMutex->Lock(); va_list ap; va_start(ap, status); jack_client_t* res = jack_client_open_aux(ext_client_name, options, status, ap); va_end(ap); JackGlobals::fOpenMutex->Unlock(); return res; } catch (std::bad_alloc& e) { jack_error("Memory allocation error..."); return NULL; } catch (...) { jack_error("Unknown error..."); return NULL; } } SERVER_EXPORT int jack_client_close(jack_client_t* ext_client) { JackGlobals::CheckContext("jack_client_close"); assert(JackGlobals::fOpenMutex); JackGlobals::fOpenMutex->Lock(); int res = -1; jack_log("jack_client_close"); JackClient* client = (JackClient*)ext_client; if (client == NULL) { jack_error("jack_client_close called with a NULL client"); } else { res = client->Close(); delete client; JackServerGlobals::Destroy(); // jack server destruction jack_log("jack_client_close res = %d", res); } JackGlobals::fOpenMutex->Unlock(); return res; } SERVER_EXPORT int jack_get_client_pid(const char *name) { return (JackServerGlobals::fInstance != NULL) ? JackServerGlobals::fInstance->GetEngine()->GetClientPID(name) : 0; } jack2-1.9.22/common/JackServerGlobals.cpp000066400000000000000000000330041436671425200201500ustar00rootroot00000000000000/* Copyright (C) 2005 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackServerGlobals.h" #include "JackLockedEngine.h" #include "JackTools.h" #include "shm.h" #include #include static char* server_name = NULL; namespace Jack { JackServer* JackServerGlobals::fInstance; unsigned int JackServerGlobals::fUserCount; std::map JackServerGlobals::fSlavesList; std::map JackServerGlobals::fInternalsList; bool (* JackServerGlobals::on_device_acquire)(const char * device_name) = NULL; void (* JackServerGlobals::on_device_release)(const char * device_name) = NULL; void (* JackServerGlobals::on_device_reservation_loop)(void) = NULL; int JackServerGlobals::Start(const char* server_name, jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int temporary, int time_out_ms, int rt, int priority, int port_max, int verbose, jack_timer_type_t clock, char self_connect_mode) { jack_log("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld ", sync, time_out_ms, rt, priority, verbose); new JackServer(sync, temporary, time_out_ms, rt, priority, port_max, verbose, clock, self_connect_mode, server_name); // Will setup fInstance and fUserCount globals int res = fInstance->Open(driver_desc, driver_params); return (res < 0) ? res : fInstance->Start(); } void JackServerGlobals::Stop() { jack_log("Jackdmp: server close"); fInstance->Stop(); fInstance->Close(); } void JackServerGlobals::Delete() { jack_log("Jackdmp: delete server"); // Slave drivers std::map::iterator it1; for (it1 = fSlavesList.begin(); it1 != fSlavesList.end(); it1++) { JackDriverInfo* info = (*it1).second; if (info) { fInstance->RemoveSlave((info)); delete (info); } } fSlavesList.clear(); // Internal clients std::map ::iterator it2; for (it2 = fInternalsList.begin(); it2 != fInternalsList.end(); it2++) { int status; int refnum = (*it2).second; if (refnum > 0) { // Client object is internally kept in JackEngine, and will be deallocated in InternalClientUnload fInstance->GetEngine()->InternalClientUnload(refnum, &status); } } fInternalsList.clear(); delete fInstance; fInstance = NULL; } bool JackServerGlobals::Init() { int realtime = 0; int client_timeout = 0; /* msecs; if zero, use period size. */ int realtime_priority = 10; int verbose_aux = 0; unsigned int port_max = 128; int temporary = 0; int opt = 0; int option_index = 0; char *master_driver_name = NULL; char **master_driver_args = NULL; JSList* master_driver_params = NULL; jack_driver_desc_t* driver_desc; jack_timer_type_t clock_source = JACK_TIMER_SYSTEM_CLOCK; int driver_nargs = 1; JSList* drivers = NULL; int loopback = 0; int sync = 0; int rc, i; int res; int replace_registry = 0; FILE* fp = 0; char filename[255]; char buffer[255]; int argc = 0; char* argv[32]; // First user starts the server if (fUserCount++ == 0) { jack_log("JackServerGlobals Init"); const char *options = "-d:X:I:P:uvshVrRL:STFl:t:mn:p:" #ifdef __linux__ "c:" #endif ; struct option long_options[] = { #ifdef __linux__ { "clock-source", 1, 0, 'c' }, #endif { "loopback-driver", 1, 0, 'L' }, { "audio-driver", 1, 0, 'd' }, { "midi-driver", 1, 0, 'X' }, { "internal-client", 1, 0, 'I' }, { "verbose", 0, 0, 'v' }, { "help", 0, 0, 'h' }, { "port-max", 1, 0, 'p' }, { "no-mlock", 0, 0, 'm' }, { "name", 1, 0, 'n' }, { "unlock", 0, 0, 'u' }, { "realtime", 0, 0, 'R' }, { "no-realtime", 0, 0, 'r' }, { "replace-registry", 0, &replace_registry, 0 }, { "loopback", 0, 0, 'L' }, { "realtime-priority", 1, 0, 'P' }, { "timeout", 1, 0, 't' }, { "temporary", 0, 0, 'T' }, { "version", 0, 0, 'V' }, { "silent", 0, 0, 's' }, { "sync", 0, 0, 'S' }, { 0, 0, 0, 0 } }; snprintf(filename, 255, "%s/.jackdrc", getenv("HOME")); fp = fopen(filename, "r"); if (!fp) { fp = fopen("/etc/jackdrc", "r"); } // if still not found, check old config name for backwards compatibility if (!fp) { fp = fopen("/etc/jackd.conf", "r"); } argc = 0; if (fp) { res = fscanf(fp, "%s", buffer); while (res != 0 && res != EOF) { argv[argc] = (char*)malloc(64); strcpy(argv[argc], buffer); res = fscanf(fp, "%s", buffer); argc++; } fclose(fp); } /* For testing int argc = 15; char* argv[] = {"jackdmp", "-R", "-v", "-d", "coreaudio", "-p", "512", "-d", "~:Aggregate:0", "-r", "48000", "-i", "2", "-o", "2" }; */ opterr = 0; optind = 1; // Important : to reset argv parsing while (!master_driver_name && (opt = getopt_long(argc, argv, options, long_options, &option_index)) != EOF) { switch (opt) { case 'c': if (tolower (optarg[0]) == 'h') { clock_source = JACK_TIMER_HPET; } else if (tolower (optarg[0]) == 'c') { /* For backwards compatibility with scripts, allow * the user to request the cycle clock on the * command line, but use the system clock instead */ clock_source = JACK_TIMER_SYSTEM_CLOCK; } else if (tolower (optarg[0]) == 's') { clock_source = JACK_TIMER_SYSTEM_CLOCK; } else { jack_error("unknown option character %c", optopt); } break; case 'd': master_driver_name = optarg; break; case 'L': loopback = atoi(optarg); break; case 'X': fSlavesList[optarg] = NULL; break; case 'I': fInternalsList[optarg] = -1; break; case 'p': port_max = (unsigned int)atol(optarg); break; case 'm': break; case 'u': break; case 'v': verbose_aux = 1; break; case 'S': sync = 1; break; case 'n': server_name = optarg; break; case 'P': realtime_priority = atoi(optarg); break; case 'r': realtime = 0; break; case 'R': realtime = 1; break; case 'T': temporary = 1; break; case 't': client_timeout = atoi(optarg); break; default: jack_error("unknown option character %c", optopt); break; } } drivers = jack_drivers_load(drivers); if (!drivers) { jack_error("jackdmp: no drivers found; exiting"); goto error; } driver_desc = jack_find_driver_descriptor(drivers, master_driver_name); if (!driver_desc) { jack_error("jackdmp: unknown master driver '%s'", master_driver_name); goto error; } if (optind < argc) { driver_nargs = 1 + argc - optind; } else { driver_nargs = 1; } if (driver_nargs == 0) { jack_error("No driver specified ... hmm. JACK won't do" " anything when run like this."); goto error; } master_driver_args = (char**)malloc(sizeof(char*) * driver_nargs); master_driver_args[0] = master_driver_name; for (i = 1; i < driver_nargs; i++) { master_driver_args[i] = argv[optind++]; } if (jack_parse_driver_params(driver_desc, driver_nargs, master_driver_args, &master_driver_params)) { goto error; } #ifndef WIN32 if (server_name == NULL) { server_name = (char*)JackTools::DefaultServerName(); } #endif rc = jack_register_server(server_name, false); switch (rc) { case EEXIST: jack_error("`%s' server already active", server_name); goto error; case ENOSPC: jack_error("too many servers already active"); goto error; case ENOMEM: jack_error("no access to shm registry"); goto error; default: jack_info("server `%s' registered", server_name); } /* clean up shared memory and files from any previous instance of this server name */ jack_cleanup_shm(); JackTools::CleanupFiles(server_name); if (!realtime && client_timeout == 0) { client_timeout = 500; /* 0.5 sec; usable when non realtime. */ } for (i = 0; i < argc; i++) { free(argv[i]); } int res = Start(server_name, driver_desc, master_driver_params, sync, temporary, client_timeout, realtime, realtime_priority, port_max, verbose_aux, clock_source, JACK_DEFAULT_SELF_CONNECT_MODE); if (res < 0) { jack_error("Cannot start server... exit"); Delete(); jack_cleanup_shm(); JackTools::CleanupFiles(server_name); jack_unregister_server(server_name); goto error; } // Slave drivers std::map::iterator it1; for (it1 = fSlavesList.begin(); it1 != fSlavesList.end(); it1++) { const char* name = ((*it1).first).c_str(); driver_desc = jack_find_driver_descriptor(drivers, name); if (!driver_desc) { jack_error("jackdmp: unknown slave driver '%s'", name); } else { (*it1).second = fInstance->AddSlave(driver_desc, NULL); } } // Loopback driver if (loopback > 0) { driver_desc = jack_find_driver_descriptor(drivers, "loopback"); if (!driver_desc) { jack_error("jackdmp: unknown driver '%s'", "loopback"); } else { fSlavesList["loopback"] = fInstance->AddSlave(driver_desc, NULL); } } // Internal clients std::map::iterator it2; for (it2 = fInternalsList.begin(); it2 != fInternalsList.end(); it2++) { int status, refnum; const char* name = ((*it2).first).c_str(); fInstance->InternalClientLoad2(name, name, NULL, JackNullOption, &refnum, -1, &status); (*it2).second = refnum; } } if (master_driver_params) { jack_free_driver_params(master_driver_params); } return true; error: jack_log("JackServerGlobals Init error"); if (master_driver_params) { jack_free_driver_params(master_driver_params); } Destroy(); return false; } void JackServerGlobals::Destroy() { if (--fUserCount == 0) { jack_log("JackServerGlobals Destroy"); Stop(); Delete(); jack_cleanup_shm(); JackTools::CleanupFiles(server_name); jack_unregister_server(server_name); } } } // end of namespace jack2-1.9.22/common/JackServerGlobals.h000066400000000000000000000040261436671425200176170ustar00rootroot00000000000000/* Copyright (C) 2005 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackServerGlobals__ #define __JackServerGlobals__ #include "driver_interface.h" #include "JackDriverLoader.h" #include "JackCompilerDeps.h" #include "JackServer.h" #include namespace Jack { class JackClient; /*! \brief Global server static structure: singleton kind of pattern. */ struct SERVER_EXPORT JackServerGlobals { static JackServer* fInstance; static unsigned int fUserCount; static std::map fSlavesList; static std::map fInternalsList; static bool (* on_device_acquire)(const char* device_name); static void (* on_device_release)(const char* device_name); static void (* on_device_reservation_loop)(void); JackServerGlobals(); ~JackServerGlobals(); static bool Init(); static void Destroy(); static int Start(const char* server_name, jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int temporary, int time_out_ms, int rt, int priority, int port_max, int verbose, jack_timer_type_t clock, char self_connect_mode); static void Stop(); static void Delete(); }; } // end of namespace #endif jack2-1.9.22/common/JackServerLaunch.h000066400000000000000000000017131436671425200174460ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2006 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackServerLaunch__ #define __JackServerLaunch__ #include "varargs.h" #include "types.h" int try_start_server(jack_varargs_t* va, jack_options_t options, jack_status_t* status); #endif jack2-1.9.22/common/JackSession.h000066400000000000000000000035671436671425200165010ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004 Jack O'Quin Copyright (C) 2010 Torben Hohn This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_session_int_h__ #define __jack_session_int_h__ #include #ifdef __cplusplus extern "C" { #endif enum JackSessionEventType { JackSessionSave = 1, JackSessionSaveAndQuit = 2, JackSessionSaveTemplate = 3 }; typedef enum JackSessionEventType jack_session_event_type_t; enum JackSessionFlags { JackSessionSaveError = 0x01, JackSessionNeedTerminal = 0x02 }; typedef enum JackSessionFlags jack_session_flags_t; struct _jack_session_event { jack_session_event_type_t type; const char *session_dir; const char *client_uuid; char *command_line; jack_session_flags_t flags; uint32_t future; }; typedef struct _jack_session_event jack_session_event_t; typedef void (*JackSessionCallback)(jack_session_event_t *event, void *arg); typedef struct { const char *uuid; const char *client_name; const char *command; jack_session_flags_t flags; } jack_session_command_t; #ifdef __cplusplus } #endif #endif jack2-1.9.22/common/JackShmMem.cpp000066400000000000000000000077711436671425200166000ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackError.h" #include "JackShmMem.h" #include namespace Jack { static unsigned int fSegmentNum = 0; static jack_shm_info_t gInfo; JackShmMem::JackShmMem() { JackShmMemAble::Init(); LockMemory(); } JackShmMem::~JackShmMem() { UnlockMemory(); } void JackShmMemAble::Init() { fInfo.index = gInfo.index; fInfo.ptr.attached_at = gInfo.ptr.attached_at; fInfo.size = gInfo.size; } void* JackShmMem::operator new(size_t size, void* memory) { jack_log("JackShmMem::new placement size = %ld", size); return memory; } void* JackShmMem::operator new(size_t size) { jack_shm_info_t info; JackShmMem* obj; char name[64]; snprintf(name, sizeof(name), "/jack_shared%d", fSegmentNum++); if (jack_shmalloc(name, size, &info)) { jack_error("Cannot create shared memory segment of size = %d", size, strerror(errno)); goto error; } if (jack_attach_shm(&info)) { jack_error("Cannot attach shared memory segment name = %s err = %s", name, strerror(errno)); jack_destroy_shm(&info); goto error; } obj = (JackShmMem*)jack_shm_addr(&info); // It is unsafe to set object fields directly (may be overwritten during object initialization), // so use an intermediate global data gInfo.index = info.index; gInfo.size = size; gInfo.ptr.attached_at = info.ptr.attached_at; jack_log("JackShmMem::new index = %ld attached = %x size = %ld ", info.index, info.ptr.attached_at, size); return obj; error: jack_error("JackShmMem::new bad alloc", size); throw std::bad_alloc(); } void JackShmMem::operator delete(void* p, size_t size) { jack_shm_info_t info; JackShmMem* obj = (JackShmMem*)p; info.index = obj->fInfo.index; info.ptr.attached_at = obj->fInfo.ptr.attached_at; jack_log("JackShmMem::delete size = %ld index = %ld", size, info.index); jack_release_shm(&info); jack_destroy_shm(&info); } void JackShmMem::operator delete(void* obj) { if (obj) { JackShmMem::operator delete(obj, 0); } } void LockMemoryImp(void* ptr, size_t size) { if (CHECK_MLOCK((char*)ptr, size)) { jack_log("Succeeded in locking %u byte memory area", size); } else { jack_error("Cannot lock down %u byte memory area (%s)", size, strerror(errno)); } } void InitLockMemoryImp(void* ptr, size_t size) { if (CHECK_MLOCK((char*)ptr, size)) { memset(ptr, 0, size); jack_log("Succeeded in locking %u byte memory area", size); } else { jack_error("Cannot lock down %u byte memory area (%s)", size, strerror(errno)); } } void UnlockMemoryImp(void* ptr, size_t size) { if (CHECK_MUNLOCK((char*)ptr, size)) { jack_log("Succeeded in unlocking %u byte memory area", size); } else { jack_error("Cannot unlock down %u byte memory area (%s)", size, strerror(errno)); } } void LockAllMemory() { if (CHECK_MLOCKALL()) { jack_log("Succeeded in locking all memory"); } else { jack_error("Cannot lock all memory (%s)", strerror(errno)); } } void UnlockAllMemory() { if (CHECK_MUNLOCKALL()) { jack_log("Succeeded in unlocking all memory"); } else { jack_error("Cannot unlock all memory (%s)", strerror(errno)); } } } // end of namespace jack2-1.9.22/common/JackShmMem.h000066400000000000000000000216731436671425200162420ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackShmMem__ #define __JackShmMem__ #include "shm.h" #include "JackError.h" #include "JackCompilerDeps.h" #include // GCC 4.0 #include #include #include "JackShmMem_os.h" namespace Jack { void LockMemoryImp(void* ptr, size_t size); void InitLockMemoryImp(void* ptr, size_t size); void UnlockMemoryImp(void* ptr, size_t size); void LockAllMemory(); void UnlockAllMemory(); /*! \brief A class which objects possibly want to be allocated in shared memory derives from this class. */ class JackShmMemAble { protected: jack_shm_info_t fInfo; public: void Init(); int GetShmIndex() { return fInfo.index; } char* GetShmAddress() { return (char*)fInfo.ptr.attached_at; } void LockMemory() { LockMemoryImp(this, fInfo.size); } void UnlockMemory() { UnlockMemoryImp(this, fInfo.size); } }; /*! \brief The base class for shared memory management. A class which objects need to be allocated in shared memory derives from this class. */ class SERVER_EXPORT JackShmMem : public JackShmMemAble { protected: JackShmMem(); ~JackShmMem(); public: void* operator new(size_t size); void* operator new(size_t size, void* memory); void operator delete(void* p, size_t size); void operator delete(void* p); }; /*! \brief Pointer on shared memory segment in the client side. */ template class JackShmReadWritePtr { private: jack_shm_info_t fInfo; bool fInitDone; void Init(int index, const char* server_name = JACK_DEFAULT_SERVER_NAME) { if (fInfo.index < 0 && index >= 0) { jack_log("JackShmReadWritePtr::Init %ld %d", index, fInfo.index); if (jack_initialize_shm(server_name) < 0) { throw std::bad_alloc(); } fInfo.index = index; if (jack_attach_lib_shm(&fInfo)) { throw std::bad_alloc(); } GetShmAddress()->LockMemory(); fInitDone = true; } } public: JackShmReadWritePtr() { fInfo.index = -1; fInitDone = false; fInfo.ptr.attached_at = (char*)NULL; } JackShmReadWritePtr(int index, const char* server_name) { Init(index, server_name); } ~JackShmReadWritePtr() { if (!fInitDone) { jack_error("JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for %d, skipping unlock", fInfo.index); return; } if (fInfo.index >= 0) { jack_log("JackShmReadWritePtr::~JackShmReadWritePtr %d", fInfo.index); GetShmAddress()->UnlockMemory(); jack_release_lib_shm(&fInfo); fInfo.index = -1; } } T* operator->() const { return (T*)fInfo.ptr.attached_at; } operator T*() const { return (T*)fInfo.ptr.attached_at; } JackShmReadWritePtr& operator=(int index) { Init(index); return *this; } void SetShmIndex(int index, const char* server_name) { Init(index, server_name); } int GetShmIndex() { return fInfo.index; } T* GetShmAddress() { return (T*)fInfo.ptr.attached_at; } }; /*! \brief Pointer on shared memory segment in the client side: destroy the segment (used client control) */ template class JackShmReadWritePtr1 { private: jack_shm_info_t fInfo; bool fInitDone; void Init(int index, const char* server_name = JACK_DEFAULT_SERVER_NAME) { if (fInfo.index < 0 && index >= 0) { jack_log("JackShmReadWritePtr1::Init %ld %d", index, fInfo.index); if (jack_initialize_shm(server_name) < 0) { throw std::bad_alloc(); } fInfo.index = index; if (jack_attach_lib_shm(&fInfo)) { throw std::bad_alloc(); } GetShmAddress()->LockMemory(); fInitDone = true; /* nobody else needs to access this shared memory any more, so destroy it. because we have our own attachment to it, it won't vanish till we exit (and release it). */ jack_destroy_shm(&fInfo); } } public: JackShmReadWritePtr1() { fInfo.index = -1; fInitDone = false; fInfo.ptr.attached_at = NULL; } JackShmReadWritePtr1(int index, const char* server_name) { Init(index, server_name); } ~JackShmReadWritePtr1() { if (!fInitDone) { jack_error("JackShmReadWritePtr1::~JackShmReadWritePtr1 - Init not done for %d, skipping unlock", fInfo.index); return; } if (fInfo.index >= 0) { jack_log("JackShmReadWritePtr1::~JackShmReadWritePtr1 %d", fInfo.index); GetShmAddress()->UnlockMemory(); jack_release_lib_shm(&fInfo); fInfo.index = -1; } } T* operator->() const { return (T*)fInfo.ptr.attached_at; } operator T*() const { return (T*)fInfo.ptr.attached_at; } JackShmReadWritePtr1& operator=(int index) { Init(index); return *this; } void SetShmIndex(int index, const char* server_name) { Init(index, server_name); } int GetShmIndex() { return fInfo.index; } T* GetShmAddress() { return (T*)fInfo.ptr.attached_at; } }; /*! \brief Pointer on shared memory segment in the client side. */ template class JackShmReadPtr { private: jack_shm_info_t fInfo; bool fInitDone; void Init(int index, const char* server_name = JACK_DEFAULT_SERVER_NAME) { if (fInfo.index < 0 && index >= 0) { jack_log("JackShmPtrRead::Init %ld %d", index, fInfo.index); if (jack_initialize_shm(server_name) < 0) { throw std::bad_alloc(); } fInfo.index = index; if (jack_attach_lib_shm_read(&fInfo)) { throw std::bad_alloc(); } GetShmAddress()->LockMemory(); fInitDone = true; } } public: JackShmReadPtr() { fInfo.index = -1; fInitDone = false; fInfo.ptr.attached_at = NULL; } JackShmReadPtr(int index, const char* server_name) { Init(index, server_name); } ~JackShmReadPtr() { if (!fInitDone) { jack_error("JackShmReadPtr::~JackShmReadPtr - Init not done for %ld, skipping unlock", fInfo.index); return; } if (fInfo.index >= 0) { jack_log("JackShmPtrRead::~JackShmPtrRead %ld", fInfo.index); GetShmAddress()->UnlockMemory(); jack_release_lib_shm(&fInfo); fInfo.index = -1; } } T* operator->() const { return (T*)fInfo.ptr.attached_at; } operator T*() const { return (T*)fInfo.ptr.attached_at; } JackShmReadPtr& operator=(int index) { Init(index); return *this; } void SetShmIndex(int index, const char* server_name) { Init(index, server_name); } int GetShmIndex() { return fInfo.index; } T* GetShmAddress() { return (T*)fInfo.ptr.attached_at; } }; } // end of namespace #endif jack2-1.9.22/common/JackSynchro.h000066400000000000000000000041541436671425200164740ustar00rootroot00000000000000/* Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackSynchro__ #define __JackSynchro__ #include "JackConstants.h" namespace Jack { namespace detail { /*! \brief An inter process synchronization primitive. */ class JackSynchro { protected: char fName[SYNC_MAX_NAME_SIZE]; bool fFlush; // If true, signal are "flushed" : used for drivers that do no consume the signal public: JackSynchro(): fFlush(false) {} ~JackSynchro() {} bool Signal() { return true; } bool SignalAll() { return true; } bool Wait() { return true; } bool TimedWait(long usec) { return true; } bool Allocate(const char* name, const char* server_name, int value) { return true; } bool Connect(const char* name, const char* server_name) { return true; } bool ConnectInput(const char* name, const char* server_name) { return true; } bool ConnectOutput(const char* name, const char* server_name) { return true; } bool Disconnect() { return true; } void Destroy() {} void SetFlush(bool mode) { fFlush = mode; } }; } } // end of namespace #endif jack2-1.9.22/common/JackSystemDeps.h000066400000000000000000000015501436671425200171440ustar00rootroot00000000000000/* Copyright (C) 2004-2006 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackSystemDeps__ #define __JackSystemDeps__ #include "systemdeps.h" #include "JackSystemDeps_os.h" #endif jack2-1.9.22/common/JackThread.h000066400000000000000000000074321436671425200162600ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackThread__ #define __JackThread__ #include "JackCompilerDeps.h" #include "JackTypes.h" namespace Jack { /*! \brief The base class for runnable objects, that have an Init and Execute method to be called in a thread. */ class JackRunnableInterface { protected: JackRunnableInterface() {} virtual ~JackRunnableInterface() {} public: virtual bool Init() /*! Called once when the thread is started */ { return true; } virtual bool Execute() = 0; /*! Must be implemented by subclasses */ }; namespace detail { /*! \brief The thread base class. */ class SERVER_EXPORT JackThreadInterface { public: enum kThreadState {kIdle, kStarting, kIniting, kRunning}; protected: JackRunnableInterface* fRunnable; int fPriority; bool fRealTime; volatile kThreadState fStatus; int fCancellation; public: JackThreadInterface(JackRunnableInterface* runnable, int priority, bool real_time, int cancellation): fRunnable(runnable), fPriority(priority), fRealTime(real_time), fStatus(kIdle), fCancellation(cancellation) {} kThreadState GetStatus() { return fStatus; } void SetStatus(kThreadState status) { fStatus = status; } void SetParams(UInt64 period, UInt64 computation, UInt64 constraint) // Empty implementation, will only make sense on OSX... {} int Start(); int StartSync(); int Kill(); int Stop(); void Terminate(); int AcquireRealTime(); // Used when called from another thread int AcquireSelfRealTime(); // Used when called from thread itself int AcquireRealTime(int priority); // Used when called from another thread int AcquireSelfRealTime(int priority); // Used when called from thread itself int DropRealTime(); // Used when called from another thread int DropSelfRealTime(); // Used when called from thread itself jack_native_thread_t GetThreadID(); bool IsThread(); static int AcquireRealTimeImp(jack_native_thread_t thread, int priority); static int AcquireRealTimeImp(jack_native_thread_t thread, int priority, UInt64 period, UInt64 computation, UInt64 constraint); static int DropRealTimeImp(jack_native_thread_t thread); static int StartImp(jack_native_thread_t* thread, int priority, int realtime, void*(*start_routine)(void*), void* arg); static int StopImp(jack_native_thread_t thread); static int KillImp(jack_native_thread_t thread); }; } } // end of namespace bool jack_get_thread_realtime_priority_range(int * min_ptr, int * max_ptr); bool jack_tls_allocate_key(jack_tls_key *key_ptr); bool jack_tls_free_key(jack_tls_key key); bool jack_tls_set(jack_tls_key key, void *data_ptr); void *jack_tls_get(jack_tls_key key); #endif jack2-1.9.22/common/JackThreadedDriver.cpp000066400000000000000000000145331436671425200203000ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackThreadedDriver.h" #include "JackError.h" #include "JackTools.h" #include "JackGlobals.h" #include "JackEngineControl.h" namespace Jack { JackThreadedDriver::JackThreadedDriver(JackDriver* driver):fThread(this),fDriver(driver) {} JackThreadedDriver::~JackThreadedDriver() { delete fDriver; } int JackThreadedDriver::Open() { return fDriver->Open(); } int JackThreadedDriver::Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency) { return fDriver->Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } int JackThreadedDriver::Close() { return fDriver->Close(); } int JackThreadedDriver::Process() { return fDriver->Process(); } int JackThreadedDriver::Attach() { return fDriver->Attach(); } int JackThreadedDriver::Detach() { return fDriver->Detach(); } int JackThreadedDriver::Read() { return fDriver->Read(); } int JackThreadedDriver::Write() { return fDriver->Write(); } bool JackThreadedDriver::IsFixedBufferSize() { return fDriver->IsFixedBufferSize(); } int JackThreadedDriver::SetBufferSize(jack_nframes_t buffer_size) { return fDriver->SetBufferSize(buffer_size); } int JackThreadedDriver::SetSampleRate(jack_nframes_t sample_rate) { return fDriver->SetSampleRate(sample_rate); } void JackThreadedDriver::SetMaster(bool onoff) { fDriver->SetMaster(onoff); } bool JackThreadedDriver::GetMaster() { return fDriver->GetMaster(); } void JackThreadedDriver::AddSlave(JackDriverInterface* slave) { fDriver->AddSlave(slave); } void JackThreadedDriver::RemoveSlave(JackDriverInterface* slave) { fDriver->RemoveSlave(slave); } int JackThreadedDriver::ProcessReadSlaves() { return fDriver->ProcessReadSlaves(); } int JackThreadedDriver::ProcessWriteSlaves() { return fDriver->ProcessWriteSlaves(); } int JackThreadedDriver::ProcessRead() { return fDriver->ProcessRead(); } int JackThreadedDriver::ProcessWrite() { return fDriver->ProcessWrite(); } int JackThreadedDriver::ProcessReadSync() { return fDriver->ProcessReadSync(); } int JackThreadedDriver::ProcessWriteSync() { return fDriver->ProcessWriteSync(); } int JackThreadedDriver::ProcessReadAsync() { return fDriver->ProcessReadAsync(); } int JackThreadedDriver::ProcessWriteAsync() { return fDriver->ProcessWriteAsync(); } std::list JackThreadedDriver::GetSlaves() { return fDriver->GetSlaves(); } int JackThreadedDriver::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2) { return fDriver->ClientNotify(refnum, name, notify, sync, message, value1, value2); } JackClientControl* JackThreadedDriver::GetClientControl() const { return fDriver->GetClientControl(); } bool JackThreadedDriver::IsRealTime() const { return fDriver->IsRealTime(); } bool JackThreadedDriver::IsRunning() const { return fDriver->IsRunning(); } int JackThreadedDriver::Start() { jack_log("JackThreadedDriver::Start"); if (fDriver->Start() < 0) { jack_error("Cannot start driver"); return -1; } if (fThread.StartSync() < 0) { jack_error("Cannot start thread"); return -1; } return 0; } int JackThreadedDriver::Stop() { jack_log("JackThreadedDriver::Stop"); switch (fThread.GetStatus()) { // Kill the thread in Init phase case JackThread::kStarting: case JackThread::kIniting: if (fThread.Kill() < 0) { jack_error("Cannot kill thread"); } break; // Stop when the thread cycle is finished case JackThread::kRunning: if (fThread.Stop() < 0) { jack_error("Cannot stop thread"); } break; default: break; } if (fDriver->Stop() < 0) { jack_error("Cannot stop driver"); return -1; } return 0; } bool JackThreadedDriver::Execute() { return (Process() == 0); } bool JackThreadedDriver::Init() { if (fDriver->Initialize()) { SetRealTime(); return true; } else { return false; } } void JackThreadedDriver::SetRealTime() { if (fDriver->IsRealTime()) { jack_log("JackThreadedDriver::Init real-time"); // Will do "something" on OSX only... GetEngineControl()->fPeriod = GetEngineControl()->fConstraint = GetEngineControl()->fPeriodUsecs * 1000; GetEngineControl()->fComputation = JackTools::ComputationMicroSec(GetEngineControl()->fBufferSize) * 1000; fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); if (fThread.AcquireSelfRealTime(GetEngineControl()->fServerPriority) < 0) { jack_error("AcquireSelfRealTime error"); } else { set_threaded_log_function(); } } else { jack_log("JackThreadedDriver::Init non-realtime"); } } } // end of namespace jack2-1.9.22/common/JackThreadedDriver.h000066400000000000000000000063311436671425200177420ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackThreadedDriver__ #define __JackThreadedDriver__ #include "JackDriver.h" #include "JackPlatformPlug.h" namespace Jack { /*! \brief The base class for threaded drivers using a "decorator" pattern. Threaded drivers are used with blocking devices. */ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, public JackRunnableInterface { protected: JackThread fThread; JackDriver* fDriver; void SetRealTime(); public: JackThreadedDriver(JackDriver* driver); virtual ~JackThreadedDriver(); virtual int Open(); virtual int Open(jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency); virtual int Close(); virtual int Process(); virtual int Attach(); virtual int Detach(); virtual int Read(); virtual int Write(); virtual int Start(); virtual int Stop(); virtual bool IsFixedBufferSize(); virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetSampleRate(jack_nframes_t sample_rate); virtual void SetMaster(bool onoff); virtual bool GetMaster(); virtual void AddSlave(JackDriverInterface* slave); virtual void RemoveSlave(JackDriverInterface* slave); virtual std::list GetSlaves(); virtual int ProcessReadSlaves(); virtual int ProcessWriteSlaves(); virtual int ProcessRead(); virtual int ProcessWrite(); virtual int ProcessReadSync(); virtual int ProcessWriteSync(); virtual int ProcessReadAsync(); virtual int ProcessWriteAsync(); virtual int ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2); virtual JackClientControl* GetClientControl() const; virtual bool IsRealTime() const; virtual bool IsRunning() const; // JackRunnableInterface interface virtual bool Execute(); virtual bool Init(); }; } // end of namespace #endif jack2-1.9.22/common/JackTime.h000066400000000000000000000023121436671425200157370ustar00rootroot00000000000000/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackTime__ #define __JackTime__ #include "JackCompilerDeps.h" #include "JackTypes.h" #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT void InitTime(); SERVER_EXPORT void EndTime(); SERVER_EXPORT jack_time_t GetMicroSeconds(void); SERVER_EXPORT void JackSleep(long usec); void SetClockSource(jack_timer_type_t source); const char* ClockSourceName(jack_timer_type_t source); #ifdef __cplusplus } #endif #endif jack2-1.9.22/common/JackTimedDriver.cpp000066400000000000000000000052631436671425200176220ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackTimedDriver.h" #include "JackEngineControl.h" #include "JackTime.h" #include "JackCompilerDeps.h" #include #include #include namespace Jack { int JackTimedDriver::FirstCycle(jack_time_t cur_time_usec) { fAnchorTimeUsec = cur_time_usec; return int((double(fEngineControl->fBufferSize) * 1000000) / double(fEngineControl->fSampleRate)); } int JackTimedDriver::CurrentCycle(jack_time_t cur_time_usec) { return int(((double(fCycleCount) * double(fEngineControl->fBufferSize) * 1000000.) / double(fEngineControl->fSampleRate)) - (cur_time_usec - fAnchorTimeUsec)); } int JackTimedDriver::Start() { fCycleCount = 0; return JackAudioDriver::Start(); } void JackTimedDriver::ProcessWait() { jack_time_t cur_time_usec = GetMicroSeconds(); int wait_time_usec; if (fCycleCount++ == 0) { wait_time_usec = FirstCycle(cur_time_usec); } else { wait_time_usec = CurrentCycle(cur_time_usec); } if (wait_time_usec < 0) { NotifyXRun(cur_time_usec, float(cur_time_usec - fBeginDateUst)); fCycleCount = 0; wait_time_usec = 0; jack_error("JackTimedDriver::Process XRun = %ld usec", (cur_time_usec - fBeginDateUst)); } //jack_log("JackTimedDriver::Process wait_time = %d", wait_time_usec); JackSleep(wait_time_usec); } int JackWaiterDriver::ProcessNull() { JackDriver::CycleTakeBeginTime(); // Graph processing without Read/Write if (fEngineControl->fSyncMode) { ProcessGraphSync(); } else { ProcessGraphAsync(); } // Keep end cycle time JackDriver::CycleTakeEndTime(); ProcessWait(); return 0; } void JackRestarterDriver::SetRestartDriver(JackDriver* driver) { fRestartDriver = driver; } int JackRestarterDriver::RestartWait() { if (!fRestartDriver) { jack_error("JackRestartedDriver::RestartWait driver not set"); return -1; } return fRestartDriver->Start(); } } // end of namespace jack2-1.9.22/common/JackTimedDriver.h000066400000000000000000000051701436671425200172640ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackTimedDriver__ #define __JackTimedDriver__ #include "JackAudioDriver.h" namespace Jack { /*! \brief The timed driver. */ class SERVER_EXPORT JackTimedDriver : public JackAudioDriver { protected: int fCycleCount; jack_time_t fAnchorTimeUsec; int FirstCycle(jack_time_t cur_time); int CurrentCycle(jack_time_t cur_time); void ProcessWait(); public: JackTimedDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackAudioDriver(name, alias, engine, table), fCycleCount(0), fAnchorTimeUsec(0) {} virtual ~JackTimedDriver() {} // BufferSize can be changed bool IsFixedBufferSize() { return false; } int Start(); }; class SERVER_EXPORT JackWaiterDriver : public JackTimedDriver { public: JackWaiterDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackTimedDriver(name, alias, engine, table) {} virtual ~JackWaiterDriver() {} virtual int ProcessNull(); }; /*! \brief A restartable driver. When wrapped into a JackWaitCallbackDriver, this driver can restart the wrapper thread which will wait again for the Initialize method to return. */ class SERVER_EXPORT JackRestarterDriver : public JackWaiterDriver { private: JackDriver* fRestartDriver; /*!< The wrapper driver */ public: JackRestarterDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) : JackWaiterDriver(name, alias, engine, table), fRestartDriver(NULL) {} virtual ~JackRestarterDriver() {} void SetRestartDriver(JackDriver* driver); /*!< Let the wrapper register itself */ int RestartWait(); /*!< Restart the wrapper thread */ }; } // end of namespace #endif jack2-1.9.22/common/JackTools.cpp000066400000000000000000000177151436671425200165110ustar00rootroot00000000000000/* Copyright (C) 2006-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackConstants.h" #include "driver_interface.h" #include "JackTools.h" #include "JackError.h" #include #include #include #include #ifdef WIN32 #include #include "JackPlatformPlug_os.h" #endif using namespace std; namespace Jack { void JackTools::KillServer() { #ifdef WIN32 raise(SIGINT); #else kill(GetPID(), SIGINT); #endif } int JackTools::MkDir(const char* path) { #ifdef WIN32 return CreateDirectory(path, NULL) == 0; #else return mkdir(path, 0777) != 0; #endif } #define DEFAULT_TMP_DIR "/tmp" char* jack_tmpdir = (char*)DEFAULT_TMP_DIR; int JackTools::GetPID() { #ifdef WIN32 return _getpid(); #else return getpid(); #endif } int JackTools::GetUID() { #ifdef WIN32 return _getpid(); //#error "No getuid function available" #else return geteuid(); #endif } const char* JackTools::DefaultServerName() { const char* server_name; if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) { server_name = JACK_DEFAULT_SERVER_NAME; } return server_name; } /* returns the name of the per-user subdirectory of jack_tmpdir */ #ifdef WIN32 const char* JackTools::UserDir() { return ""; } const char* JackTools::ServerDir(const char* server_name, char* server_dir) { return ""; } void JackTools::CleanupFiles(const char* server_name) {} int JackTools::GetTmpdir() { return 0; } #else const char* JackTools::UserDir() { static char user_dir[JACK_PATH_MAX + 1] = ""; /* format the path name on the first call */ if (user_dir[0] == '\0') { if (getenv ("JACK_PROMISCUOUS_SERVER")) { snprintf(user_dir, sizeof(user_dir), "%s/jack", jack_tmpdir); } else { snprintf(user_dir, sizeof(user_dir), "%s/jack-%d", jack_tmpdir, GetUID()); } } return user_dir; } /* returns the name of the per-server subdirectory of jack_user_dir() */ const char* JackTools::ServerDir(const char* server_name, char* server_dir) { /* format the path name into the suppled server_dir char array, * assuming that server_dir is at least as large as JACK_PATH_MAX + 1 */ snprintf(server_dir, JACK_PATH_MAX + 1, "%s/%s", UserDir(), server_name); return server_dir; } void JackTools::CleanupFiles(const char* server_name) { DIR* dir; struct dirent *dirent; char dir_name[JACK_PATH_MAX + 1] = ""; ServerDir(server_name, dir_name); /* On termination, we remove all files that jackd creates so * subsequent attempts to start jackd will not believe that an * instance is already running. If the server crashes or is * terminated with SIGKILL, this is not possible. So, cleanup * is also attempted when jackd starts. * * There are several tricky issues. First, the previous JACK * server may have run for a different user ID, so its files * may be inaccessible. This is handled by using a separate * JACK_TMP_DIR subdirectory for each user. Second, there may * be other servers running with different names. Each gets * its own subdirectory within the per-user directory. The * current process has already registered as `server_name', so * we know there is no other server actively using that name. */ /* nothing to do if the server directory does not exist */ if ((dir = opendir(dir_name)) == NULL) { return; } /* unlink all the files in this directory, they are mine */ while ((dirent = readdir(dir)) != NULL) { char fullpath[JACK_PATH_MAX + 1]; if ((strcmp(dirent->d_name, ".") == 0) || (strcmp (dirent->d_name, "..") == 0)) { continue; } snprintf(fullpath, sizeof(fullpath), "%s/%s", dir_name, dirent->d_name); if (unlink(fullpath)) { jack_error("cannot unlink `%s' (%s)", fullpath, strerror(errno)); } } closedir(dir); /* now, delete the per-server subdirectory, itself */ if (rmdir(dir_name)) { jack_error("cannot remove `%s' (%s)", dir_name, strerror(errno)); } /* finally, delete the per-user subdirectory, if empty */ if (rmdir(UserDir())) { if (errno != ENOTEMPTY) { jack_error("cannot remove `%s' (%s)", UserDir(), strerror(errno)); } } } int JackTools::GetTmpdir() { FILE* in; size_t len; char buf[JACK_PATH_MAX + 2]; /* allow tmpdir to live anywhere, plus newline, plus null */ if ((in = popen("jackd -l", "r")) == NULL) { return -1; } if (fgets(buf, sizeof(buf), in) == NULL) { pclose(in); return -1; } len = strlen(buf); if (buf[len - 1] != '\n') { /* didn't get a whole line */ pclose(in); return -1; } jack_tmpdir = (char *)malloc(len); memcpy(jack_tmpdir, buf, len - 1); jack_tmpdir[len - 1] = '\0'; pclose(in); return 0; } #endif void JackTools::RewriteName(const char* name, char* new_name) { size_t i; for (i = 0; i < strlen(name); i++) { if ((name[i] == '/') || (name[i] == '\\')) { new_name[i] = '_'; } else { new_name[i] = name[i]; } } new_name[i] = '\0'; } #ifdef WIN32 void BuildClientPath(char* path_to_so, int path_len, const char* so_name) { snprintf(path_to_so, path_len, ADDON_DIR "/%s.dll", so_name); } void PrintLoadError(const char* so_name) { // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); // Display the error message and exit the process lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)so_name) + 40) * sizeof(TCHAR)); _snprintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("error loading %s err = %s"), so_name, (LPCTSTR)lpMsgBuf); jack_error((LPCTSTR)lpDisplayBuf); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); } #else void PrintLoadError(const char* so_name) { jack_log("error loading %s err = %s", so_name, dlerror()); } void BuildClientPath(char* path_to_so, int path_len, const char* so_name) { const char* internal_dir; if ((internal_dir = getenv("JACK_INTERNAL_DIR")) == 0) { if ((internal_dir = getenv("JACK_DRIVER_DIR")) == 0) { internal_dir = ADDON_DIR; } } snprintf(path_to_so, path_len, "%s/%s.so", internal_dir, so_name); } #endif } // end of namespace jack2-1.9.22/common/JackTools.h000066400000000000000000000146611436671425200161530ustar00rootroot00000000000000/* Copyright (C) 2006-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackTools__ #define __JackTools__ #ifdef WIN32 #include #define DIR_SEPARATOR '\\' #else #define DIR_SEPARATOR '/' #include #include #include #include #endif #ifdef __APPLE__ #include #endif #include "jslist.h" #include "JackCompilerDeps.h" #include "JackError.h" #include #include #include #include #include namespace Jack { /*! \brief Utility functions. */ struct SERVER_EXPORT JackTools { static int GetPID(); static int GetUID(); static void KillServer(); static int MkDir(const char* path); static const char* UserDir(); static const char* ServerDir(const char* server_name, char* server_dir); static const char* DefaultServerName(); static void CleanupFiles(const char* server_name); static int GetTmpdir(); static void RewriteName(const char* name, char* new_name); static void ThrowJackNetException(); // For OSX only static int ComputationMicroSec(int buffer_size) { if (buffer_size < 128) { return 500; } else if (buffer_size < 256) { return 300; } else { return 100; } } }; /*! \brief Generic monitoring class. Saves data to GnuPlot files ('.plt' and '.log' datafile) This template class allows to manipulate monitoring records, and automatically generate the GnuPlot config and data files. Operations are RT safe because it uses fixed size data buffers. You can set the number of measure points, and the number of records. To use it : - create a JackGnuPlotMonitor, you can use the data type you want. - create a temporary array for your measure - once you have filled this array with 'measure points' value, call write() to add it to the record - once you've done with your measurement, just call save() to save your data file You can also call SetPlotFile() to automatically generate '.plt' file from an options list. */ template class JackGnuPlotMonitor { private: uint32_t fMeasureCnt; uint32_t fMeasurePoints; uint32_t fMeasureId; T* fCurrentMeasure; T** fMeasureTable; uint32_t fTablePos; std::string fName; public: JackGnuPlotMonitor(uint32_t measure_cnt, uint32_t measure_points, std::string name) { jack_log ( "JackGnuPlotMonitor::JackGnuPlotMonitor %u measure points - %u measures", measure_points, measure_cnt ); fMeasureCnt = measure_cnt; fMeasurePoints = measure_points; fTablePos = 0; fName = name; fCurrentMeasure = new T[fMeasurePoints]; fMeasureTable = new T*[fMeasureCnt]; for ( uint32_t cnt = 0; cnt < fMeasureCnt; cnt++ ) { fMeasureTable[cnt] = new T[fMeasurePoints]; std::fill_n ( fMeasureTable[cnt], fMeasurePoints, 0 ); } } ~JackGnuPlotMonitor() { jack_log ( "JackGnuPlotMonitor::~JackGnuPlotMonitor" ); for ( uint32_t cnt = 0; cnt < fMeasureCnt; cnt++ ) delete[] fMeasureTable[cnt]; delete[] fMeasureTable; delete[] fCurrentMeasure; } T AddNew(T measure_point) { fMeasureId = 0; return fCurrentMeasure[fMeasureId++] = measure_point; } uint32_t New() { return fMeasureId = 0; } T Add(T measure_point) { return fCurrentMeasure[fMeasureId++] = measure_point; } uint32_t AddLast(T measure_point) { fCurrentMeasure[fMeasureId] = measure_point; fMeasureId = 0; return Write(); } uint32_t Write() { for ( uint32_t point = 0; point < fMeasurePoints; point++ ) fMeasureTable[fTablePos][point] = fCurrentMeasure[point]; if ( ++fTablePos == fMeasureCnt ) fTablePos = 0; return fTablePos; } int Save(std::string name = std::string ( "" )) { std::string filename = ( name.empty() ) ? fName : name; filename += ".log"; jack_log ( "JackGnuPlotMonitor::Save filename %s", filename.c_str() ); std::ofstream file ( filename.c_str() ); for ( uint32_t cnt = 0; cnt < fMeasureCnt; cnt++ ) { for ( uint32_t point = 0; point < fMeasurePoints; point++ ) file << fMeasureTable[cnt][point] << " \t"; file << std::endl; } file.close(); return 0; } int SetPlotFile(std::string* options_list, uint32_t options_number, std::string* field_names, uint32_t field_number, std::string name = std::string ( "" )) { std::string title = ( name.empty() ) ? fName : name; std::string plot_filename = title + ".plt"; std::string data_filename = title + ".log"; std::ofstream file ( plot_filename.c_str() ); file << "set multiplot" << std::endl; file << "set grid" << std::endl; file << "set title \"" << title << "\"" << std::endl; for ( uint32_t i = 0; i < options_number; i++ ) file << options_list[i] << std::endl; file << "plot "; for ( uint32_t row = 1; row <= field_number; row++ ) { file << "\"" << data_filename << "\" using " << row << " title \"" << field_names[row-1] << "\" with lines"; file << ( ( row < field_number ) ? ", " : "\n" ); } jack_log ( "JackGnuPlotMonitor::SetPlotFile - Save GnuPlot file to '%s'", plot_filename.c_str() ); file.close(); return 0; } }; void BuildClientPath(char* path_to_so, int path_len, const char* so_name); void PrintLoadError(const char* so_name); } #endif jack2-1.9.22/common/JackTransportEngine.cpp000066400000000000000000000256631436671425200205340ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackTransportEngine.h" #include "JackClientInterface.h" #include "JackClientControl.h" #include "JackEngineControl.h" #include "JackGlobals.h" #include "JackError.h" #include "JackTime.h" #include #include #include using namespace std; namespace Jack { JackTransportEngine::JackTransportEngine(): JackAtomicArrayState() { static_assert(offsetof(JackTransportEngine, fWriteCounter) % sizeof(fWriteCounter) == 0, "fWriteCounter must be first member of JackTransportEngine to ensure its alignment"); fTransportState = JackTransportStopped; fTransportCmd = fPreviousCmd = TransportCommandStop; fSyncTimeout = 10000000; /* 10 seconds default... in case of big netjack1 roundtrip */ fSyncTimeLeft = 0; fTimeBaseMaster = -1; fWriteCounter = 0; fConditionnal = false; fPendingPos = false; fNetworkSync = false; } // compute the number of cycle for timeout void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size) { long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate); fSyncTimeLeft = fSyncTimeout / buf_usecs; jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft); } // Server int JackTransportEngine::ResetTimebase(int refnum) { if (fTimeBaseMaster == refnum) { jack_position_t* request = WriteNextStateStart(2); // To check request->valid = (jack_position_bits_t)0; WriteNextStateStop(2); fTimeBaseMaster = -1; return 0; } else { return EINVAL; } } // Server int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal) { if (conditionnal && fTimeBaseMaster > 0) { if (refnum != fTimeBaseMaster) { jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster); return EBUSY; } else { jack_log("ref = %ld was already timebase master", refnum); return 0; } } else { fTimeBaseMaster = refnum; fConditionnal = conditionnal; jack_log("new timebase master: ref = %ld", refnum); return 0; } } // RT bool JackTransportEngine::CheckAllRolling(JackClientInterface** table) { for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; if (client && client->GetClientControl()->fTransportState != JackTransportRolling) { jack_log("CheckAllRolling ref = %ld is not rolling", i); return false; } } jack_log("CheckAllRolling"); return true; } // RT void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table) { for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; if (client) { JackClientControl* control = client->GetClientControl(); // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport.... control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling; control->fTransportSync = true; control->fTransportTimebase = true; jack_log("MakeAllStartingLocating ref = %ld", i); } } } // RT void JackTransportEngine::MakeAllStopping(JackClientInterface** table) { for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; if (client) { JackClientControl* control = client->GetClientControl(); control->fTransportState = JackTransportStopped; control->fTransportSync = false; control->fTransportTimebase = false; jack_log("MakeAllStopping ref = %ld", i); } } } // RT void JackTransportEngine::MakeAllLocating(JackClientInterface** table) { for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; if (client) { JackClientControl* control = client->GetClientControl(); control->fTransportState = JackTransportStopped; control->fTransportSync = true; control->fTransportTimebase = true; jack_log("MakeAllLocating ref = %ld", i); } } } // RT void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time) { jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state pending->usecs = time; pending->frame_rate = frame_rate; WriteNextStateStop(1); } // RT void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size) { TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state /* Handle any new transport command from the last cycle. */ transport_command_t cmd = fTransportCmd; if (cmd != fPreviousCmd) { fPreviousCmd = cmd; jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop")); } else { cmd = TransportCommandNone; } /* state transition switch */ switch (fTransportState) { case JackTransportStopped: // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state if (cmd == TransportCommandStart) { jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame); fTransportState = JackTransportStarting; MakeAllStartingLocating(table); SyncTimeout(frame_rate, buffer_size); } else if (fPendingPos) { jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame); MakeAllLocating(table); } break; case JackTransportStarting: if (cmd == TransportCommandStop) { jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame); fTransportState = JackTransportStopped; MakeAllStopping(table); } else if (fPendingPos) { jack_log("transport starting ==> starting frame = %d", ReadCurrentState()->frame); fTransportState = JackTransportStarting; MakeAllStartingLocating(table); SyncTimeout(frame_rate, buffer_size); } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up if (fNetworkSync) { jack_log("transport starting ==> netstarting frame = %d"); fTransportState = JackTransportNetStarting; } else { jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft); fTransportState = JackTransportRolling; } } break; case JackTransportRolling: if (cmd == TransportCommandStop) { jack_log("transport rolling ==> stopped"); fTransportState = JackTransportStopped; MakeAllStopping(table); } else if (fPendingPos) { jack_log("transport rolling ==> starting"); fTransportState = JackTransportStarting; MakeAllStartingLocating(table); SyncTimeout(frame_rate, buffer_size); } break; case JackTransportNetStarting: break; default: jack_error("Invalid JACK transport state: %d", fTransportState); } /* Update timebase, if needed. */ if (fTransportState == JackTransportRolling) { jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state pending->frame += buffer_size; WriteNextStateStop(1); } /* See if an asynchronous position request arrived during the last cycle. */ jack_position_t* request = WriteNextStateStart(2, &fPendingPos); if (fPendingPos) { jack_log("New pos = %ld", request->frame); jack_position_t* pending = WriteNextStateStart(1); CopyPosition(request, pending); WriteNextStateStop(1); } } // Client void JackTransportEngine::ReadCurrentPos(jack_position_t* pos) { UInt16 next_index = GetCurrentIndex(); UInt16 cur_index; do { cur_index = next_index; memcpy(pos, ReadCurrentState(), sizeof(jack_position_t)); next_index = GetCurrentIndex(); } while (cur_index != next_index); // Until a coherent state has been read } void JackTransportEngine::RequestNewPos(jack_position_t* pos) { jack_position_t* request = WriteNextStateStart(2); pos->unique_1 = pos->unique_2 = GenerateUniqueID(); CopyPosition(pos, request); jack_log("RequestNewPos pos = %ld", pos->frame); WriteNextStateStop(2); } jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos) { if (pos) ReadCurrentPos(pos); return GetState(); } jack_nframes_t JackTransportEngine::GetCurrentFrame() { jack_position_t pos; ReadCurrentPos(&pos); if (fTransportState == JackTransportRolling) { float usecs = GetMicroSeconds() - pos.usecs; jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs); return pos.frame + elapsed; } else { return pos.frame; } } // RT, client void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to) { int tries = 0; long timeout = 1000; do { /* throttle the busy wait if we don't get the answer * very quickly. See comment above about this * design. */ if (tries > 10) { JackSleep(20); tries = 0; /* debug code to avoid system hangs... */ if (--timeout == 0) { jack_error("hung in loop copying position B"); abort(); } } *to = *from; tries++; } while (to->unique_1 != to->unique_2); } } // end of namespace jack2-1.9.22/common/JackTransportEngine.h000066400000000000000000000126761436671425200202010ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JackTransportEngine__ #define __JackTransportEngine__ #include "JackAtomicArrayState.h" #include "JackCompilerDeps.h" #include "types.h" namespace Jack { typedef enum { TransportCommandNone = 0, TransportCommandStart = 1, TransportCommandStop = 2, } transport_command_t; /*! \brief The client transport structure. We have: - a "current" position - a "pending" position prepared by the server at each cycle - a "request" position wanted by a client At the beginning of a cycle the server needs to select a new current position. When a request and a pending position are available, the request takes precedence on the pending one. The server atomically switches to the new position. The current position can be read by clients. We use a JackAtomicArrayState pattern that allows to manage several "next" states independently. In jack1 implementation, transport code (jack_transport_cycle_end) was not called if the graph could not be locked (see jack_run_one_cycle). Here transport cycle (CycleBegin, CycleEnd) has to run in the RT thread concurrently with code executed from the "command" thread. Each client maintains a state in it's shared memory area defined by: - it's current transport state - a boolean that is "true" when slow-sync cb has to be called - a boolean that is "true" when timebase cb is called with new_pos on Several operations set the "slow-sync cb" flag to true: - setting a new cb (client) - activate (client) - transport start (server) - new pos (server) Slow-sync cb calls stops when: - the cb return true (client) - deactivate (client) - transport stop (server) Several operations set the "timebase cb" flag to true: - setting a new cb (client) - activate (client) - transport start (server) ?? - new pos (server) Timebase cb "new_pos" argument calls stops when: - after one cb call with "new_pos" argument true (client) - deactivate (client) - release (client) - transport stop (server) */ class JackClientInterface; PRE_PACKED_STRUCTURE class SERVER_EXPORT JackTransportEngine : public JackAtomicArrayState { private: jack_transport_state_t fTransportState; volatile transport_command_t fTransportCmd; transport_command_t fPreviousCmd; /* previous transport_cmd */ jack_time_t fSyncTimeout; int fSyncTimeLeft; int fTimeBaseMaster; bool fPendingPos; bool fNetworkSync; bool fConditionnal; alignas(SInt32) SInt32 fWriteCounter; bool CheckAllRolling(JackClientInterface** table); void MakeAllStartingLocating(JackClientInterface** table); void MakeAllStopping(JackClientInterface** table); void MakeAllLocating(JackClientInterface** table); void SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size); public: JackTransportEngine(); ~JackTransportEngine() {} void SetCommand(transport_command_t state) { fTransportCmd = state; } jack_transport_state_t GetState() const { return fTransportState; } void SetState(jack_transport_state_t state) { fTransportState = state; } /* \brief */ int ResetTimebase(int refnum); /* \brief */ int SetTimebaseMaster(int refnum, bool conditionnal); void GetTimebaseMaster(int& refnum, bool& conditionnal) { refnum = fTimeBaseMaster; conditionnal = fConditionnal; } /* \brief */ void CycleBegin(jack_nframes_t frame_rate, jack_time_t time); /* \brief */ void CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size); /* \brief */ void SetSyncTimeout(jack_time_t timeout) { fSyncTimeout = timeout; } void ReadCurrentPos(jack_position_t* pos); jack_unique_t GenerateUniqueID() { return (jack_unique_t)INC_ATOMIC(&fWriteCounter); } void RequestNewPos(jack_position_t* pos); jack_transport_state_t Query(jack_position_t* pos); jack_nframes_t GetCurrentFrame(); static void CopyPosition(jack_position_t* from, jack_position_t* to); bool GetNetworkSync() const { return fNetworkSync; } void SetNetworkSync(bool sync) { fNetworkSync = sync; } } POST_PACKED_STRUCTURE; } // end of namespace #endif jack2-1.9.22/common/JackTypes.h000066400000000000000000000025411436671425200161510ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. $Id: JackTypes.h,v 1.2.2.1 2006/06/20 14:44:00 letz Exp $ */ #ifndef __JackTypes__ #define __JackTypes__ #include "types.h" #include "JackCompilerDeps.h" typedef unsigned short UInt16; #if __LP64__ typedef unsigned int UInt32; typedef signed int SInt32; #else typedef unsigned long UInt32; typedef signed long SInt32; #endif #include "JackTypes_os.h" typedef uint16_t jack_int_t; // Internal type for ports and refnum typedef enum { JACK_TIMER_SYSTEM_CLOCK, JACK_TIMER_HPET, } jack_timer_type_t; typedef enum { NotTriggered, Triggered, Running, Finished, } jack_client_state_t; #endif jack2-1.9.22/common/JackWaitCallbackDriver.cpp000066400000000000000000000022551436671425200210770ustar00rootroot00000000000000/* Copyright (C) 2014 Cédric Schieli This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "JackWaitCallbackDriver.h" namespace Jack { JackWaitCallbackDriver::JackWaitCallbackDriver(JackRestarterDriver* driver) : JackWaitThreadedDriver(driver) { // Self register with the decorated driver so it can restart us assert(driver); driver->SetRestartDriver((JackDriver*)this); } bool JackWaitCallbackDriver::ExecuteReal() { // End the thread and let the callback driver do its job return false; } } // end of namespace jack2-1.9.22/common/JackWaitCallbackDriver.h000066400000000000000000000024741436671425200205470ustar00rootroot00000000000000/* Copyright (C) 2014 Cédric Schieli This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __JackWaitCallbackDriver__ #define __JackWaitCallbackDriver__ #include "JackWaitThreadedDriver.h" namespace Jack { /*! \brief Wrapper for a restartable non-threaded driver (e.g. JackProxyDriver). Simply ends its thread when the decorated driver Initialize method returns. Self register with the supplied JackRestarterDriver so it can restart the thread. */ class SERVER_EXPORT JackWaitCallbackDriver : public JackWaitThreadedDriver { public: JackWaitCallbackDriver(JackRestarterDriver* driver); protected: bool ExecuteReal(); }; } // end of namespace #endif jack2-1.9.22/common/JackWaitThreadedDriver.cpp000066400000000000000000000043301436671425200211170ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackSystemDeps.h" #include "JackWaitThreadedDriver.h" #include "JackGlobals.h" #include "JackClient.h" #include "JackEngineControl.h" #include "JackException.h" #include "JackError.h" #include "JackTools.h" namespace Jack { bool JackWaitThreadedDriver::Init() { return (fStarter.Start() == 0); } bool JackWaitThreadedDriver::Execute() { SetRealTime(); // Process a null cycle until NetDriver has started while (!fStarter.fRunning && fThread.GetStatus() == JackThread::kRunning) { // Use base class method assert(static_cast(fDriver)); static_cast(fDriver)->ProcessNull(); } return ExecuteReal(); } bool JackWaitThreadedDriver::ExecuteReal() { try { // Switch to keep running even in case of error while (fThread.GetStatus() == JackThread::kRunning) { fDriver->Process(); } return false; } catch (JackNetException& e) { e.PrintMessage(); jack_info("Driver is restarted"); fThread.DropSelfRealTime(); // Thread has been stopped... if (fThread.GetStatus() == JackThread::kIdle) { return false; } // Thread in kIniting status again... fThread.SetStatus(JackThread::kIniting); if (Init()) { // Thread in kRunning status again... fThread.SetStatus(JackThread::kRunning); return true; } return false; } } } // end of namespace jack2-1.9.22/common/JackWaitThreadedDriver.h000066400000000000000000000055751436671425200206000ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2008 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JackWaitThreadedDriver__ #define __JackWaitThreadedDriver__ #include "JackThreadedDriver.h" #include "JackTimedDriver.h" namespace Jack { /*! \brief Wrapper for a restartable threaded driver (e.g. JackNetDriver). The idea is to behave as the "dummy" driver, until the network connection is really started and processing starts. The Execute method will call the ProcessNull method from the base JackWaiterDriver, until the decorated driver Initialize method returns. A helper JackDriverStarter thread is used for that purpose. */ class SERVER_EXPORT JackWaitThreadedDriver : public JackThreadedDriver { private: struct JackDriverStarter : public JackRunnableInterface { JackDriver* fDriver; JackThread fThread; volatile bool fRunning; JackDriverStarter(JackDriver* driver) :fDriver(driver), fThread(this), fRunning(false) {} ~JackDriverStarter() { fThread.Kill(); } int Start() { fRunning = false; return fThread.Start(); } // JackRunnableInterface interface bool Execute() { // Blocks until decorated driver is started (that is when it's Initialize method returns). if (fDriver->Initialize()) { fRunning = true; } else { jack_error("Initing net driver fails..."); } return false; } }; JackDriverStarter fStarter; public: JackWaitThreadedDriver(JackDriver* net_driver) : JackThreadedDriver(net_driver), fStarter(net_driver) {} virtual ~JackWaitThreadedDriver() {} // JackRunnableInterface interface bool Init(); bool Execute(); protected: virtual bool ExecuteReal(); /*!< Real work to be done when the decorated driver has finish initializing */ }; } // end of namespace #endif jack2-1.9.22/common/JackWeakAPI.c000066400000000000000000000532101436671425200162600ustar00rootroot00000000000000//============================================================================= // // jackWeakAPI partly based on Julien Pommier (PianoTeq : http://www.pianoteq.com/) code. // // Copyright (C) 2002-2007 Werner Schweer and others // Copyright (C) 2009 Grame // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include #include #include #ifndef WIN32 #include #endif #include #include /* dynamically load libjack and forward all registered calls to libjack (similar to what relaytool is trying to do, but more portably..) */ typedef void (*print_function)(const char *); typedef void *(*thread_routine)(void*); static int libjack_is_present = 0; // public symbol, similar to what relaytool does. #ifdef WIN32 static HMODULE libjack_handle = 0; #else static void *libjack_handle = 0; #endif #ifndef WIN32 static void __attribute__((constructor)) tryload_libjack() #else void tryload_libjack() #endif { if (getenv("SKIP_LIBJACK") == 0) { // just in case libjack is causing troubles.. #ifdef __APPLE__ libjack_handle = dlopen("libjack.0.dylib", RTLD_LAZY); if (!libjack_handle) { fprintf(stderr, "dlopen error : %s \n", dlerror()); } libjack_handle = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY); if (!libjack_handle) { fprintf(stderr, "dlopen error : %s \n", dlerror()); } #elif defined(WIN32) #ifdef _WIN64 libjack_handle = LoadLibraryA("libjack64.dll"); #else libjack_handle = LoadLibraryA("libjack.dll"); #endif if (!libjack_handle) { char* lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL ); fprintf(stderr, "Failed to load libjack DLL: %d", lpMsgBuf); } #else libjack_handle = dlopen("libjack.so.0", RTLD_LAZY); #endif } libjack_is_present = (libjack_handle != 0); } void *load_jack_function(const char *fn_name) { void *fn = 0; if (!libjack_handle) { tryload_libjack(); if (!libjack_handle) { fprintf (stderr, "libjack not found, so do not try to load %s ffs !\n", fn_name); return 0; } } #ifdef WIN32 fn = (void*)GetProcAddress(libjack_handle, fn_name); #else fn = dlsym(libjack_handle, fn_name); #endif if (!fn) { #ifdef WIN32 char* lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL ); fprintf(stderr, "could not GetProcAddress( %s ), %s \n", fn_name, lpMsgBuf); #else fprintf(stderr, "could not dlsym( %s ), %s \n", fn_name, dlerror()); #endif } return fn; } #define DECL_FUNCTION(return_type, fn_name, arguments_types, arguments) \ typedef return_type (*fn_name##_ptr_t)arguments_types; \ return_type fn_name arguments_types { \ static fn_name##_ptr_t fn = 0; \ if (fn == 0) { fn = (fn_name##_ptr_t)load_jack_function(#fn_name); } \ if (fn) return (*fn)arguments; \ else return (return_type)-1; \ } #define DECL_FUNCTION_NULL(return_type, fn_name, arguments_types, arguments) \ typedef return_type (*fn_name##_ptr_t)arguments_types; \ return_type fn_name arguments_types { \ static fn_name##_ptr_t fn = 0; \ if (fn == 0) { fn = (fn_name##_ptr_t)load_jack_function(#fn_name); } \ if (fn) return (*fn)arguments; \ else return (return_type)0; \ } #define DECL_VOID_FUNCTION(fn_name, arguments_types, arguments) \ typedef void (*fn_name##_ptr_t)arguments_types; \ void fn_name arguments_types { \ static fn_name##_ptr_t fn = 0; \ if (fn == 0) { fn = (fn_name##_ptr_t)load_jack_function(#fn_name); } \ if (fn) (*fn)arguments; \ } DECL_VOID_FUNCTION(jack_get_version, (int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr), (major_ptr, minor_ptr, micro_ptr, proto_ptr)); DECL_FUNCTION_NULL(const char *, jack_get_version_string, (), ()); DECL_FUNCTION_NULL(jack_client_t *, jack_client_open, (const char *client_name, jack_options_t options, jack_status_t *status, ...), (client_name, options, status)); DECL_FUNCTION(int, jack_client_close, (jack_client_t *client), (client)); DECL_FUNCTION_NULL(jack_client_t *, jack_client_new, (const char *client_name), (client_name)); DECL_FUNCTION(int, jack_client_name_size, (), ()); DECL_FUNCTION_NULL(char*, jack_get_client_name, (jack_client_t *client), (client)); DECL_FUNCTION(int, jack_internal_client_new, (const char *client_name, const char *load_name, const char *load_init), (client_name, load_name, load_init)); DECL_VOID_FUNCTION(jack_internal_client_close, (const char *client_name), (client_name)); DECL_FUNCTION(int, jack_is_realtime, (jack_client_t *client), (client)); DECL_VOID_FUNCTION(jack_on_shutdown, (jack_client_t *client, JackShutdownCallback shutdown_callback, void *arg), (client, shutdown_callback, arg)); DECL_VOID_FUNCTION(jack_on_info_shutdown, (jack_client_t* client, JackInfoShutdownCallback shutdown_callback, void* arg), (client, shutdown_callback, arg)); DECL_FUNCTION(int, jack_set_process_callback, (jack_client_t *client, JackProcessCallback process_callback, void *arg), (client, process_callback, arg)); DECL_FUNCTION(jack_nframes_t, jack_thread_wait, (jack_client_t *client, int status), (client, status)); // DECL_FUNCTION(jack_nframes_t, jack_cycle_wait, (jack_client_t *client), (client)); DECL_VOID_FUNCTION(jack_cycle_signal, (jack_client_t *client, int status), (client, status)); DECL_FUNCTION(int, jack_set_process_thread, (jack_client_t *client, JackThreadCallback fun, void *arg), (client, fun, arg)); DECL_FUNCTION(int, jack_set_thread_init_callback, (jack_client_t *client, JackThreadInitCallback thread_init_callback, void *arg), (client, thread_init_callback, arg)); DECL_FUNCTION(int, jack_set_freewheel_callback, (jack_client_t *client, JackFreewheelCallback freewheel_callback, void *arg), (client, freewheel_callback, arg)); DECL_FUNCTION(int, jack_set_freewheel, (jack_client_t *client, int onoff), (client, onoff)); DECL_FUNCTION(int, jack_set_buffer_size, (jack_client_t *client, jack_nframes_t nframes), (client, nframes)); DECL_FUNCTION(int, jack_set_buffer_size_callback, (jack_client_t *client, JackBufferSizeCallback bufsize_callback, void *arg), (client, bufsize_callback, arg)); DECL_FUNCTION(int, jack_set_sample_rate_callback, (jack_client_t *client, JackSampleRateCallback srate_callback, void *arg), (client, srate_callback, arg)); DECL_FUNCTION(int, jack_set_client_registration_callback, (jack_client_t *client, JackClientRegistrationCallback registration_callback, void *arg), (client, registration_callback, arg)); DECL_FUNCTION(int, jack_set_port_registration_callback, (jack_client_t *client, JackPortRegistrationCallback registration_callback, void *arg), (client, registration_callback, arg)); DECL_FUNCTION(int, jack_set_port_connect_callback, (jack_client_t *client, JackPortConnectCallback connect_callback, void *arg), (client, connect_callback, arg)); DECL_FUNCTION(int, jack_set_port_rename_callback, (jack_client_t *client, JackPortRenameCallback rename_callback, void *arg), (client, rename_callback, arg)); DECL_FUNCTION(int, jack_set_graph_order_callback, (jack_client_t *client, JackGraphOrderCallback graph_callback, void *arg), (client, graph_callback, arg)); DECL_FUNCTION(int, jack_set_xrun_callback, (jack_client_t *client, JackXRunCallback xrun_callback, void *arg), (client, xrun_callback, arg)); DECL_FUNCTION(int, jack_set_latency_callback, (jack_client_t *client, JackLatencyCallback latency_callback, void *arg), (client, latency_callback, arg)); DECL_FUNCTION(int, jack_activate, (jack_client_t *client), (client)); DECL_FUNCTION(int, jack_deactivate, (jack_client_t *client), (client)); DECL_FUNCTION_NULL(jack_port_t *, jack_port_register, (jack_client_t *client, const char *port_name, const char *port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size)); DECL_FUNCTION(int, jack_port_unregister, (jack_client_t *client, jack_port_t* port), (client, port)); DECL_FUNCTION_NULL(void *, jack_port_get_buffer, (jack_port_t *port, jack_nframes_t nframes), (port, nframes)); DECL_FUNCTION_NULL(const char*, jack_port_name, (const jack_port_t *port), (port)); DECL_FUNCTION_NULL(const char*, jack_port_short_name, (const jack_port_t *port), (port)); DECL_FUNCTION(int, jack_port_flags, (const jack_port_t *port), (port)); DECL_FUNCTION_NULL(const char*, jack_port_type, (const jack_port_t *port), (port)); DECL_FUNCTION(jack_port_type_id_t, jack_port_type_id, (const jack_port_t *port), (port)); DECL_FUNCTION(int, jack_port_is_mine, (const jack_client_t *client, const jack_port_t* port), (client, port)); DECL_FUNCTION(int, jack_port_connected, (const jack_port_t *port), (port)); DECL_FUNCTION(int, jack_port_connected_to, (const jack_port_t *port, const char *port_name), (port, port_name)); DECL_FUNCTION_NULL(const char**, jack_port_get_connections, (const jack_port_t *port), (port)); DECL_FUNCTION_NULL(const char**, jack_port_get_all_connections, (const jack_client_t *client,const jack_port_t *port), (client, port)); DECL_FUNCTION(int, jack_port_tie, (jack_port_t *src, jack_port_t *dst), (src, dst)); DECL_FUNCTION(int, jack_port_untie, (jack_port_t *port), (port)); DECL_FUNCTION(jack_nframes_t, jack_port_get_latency, (jack_port_t *port), (port)); DECL_FUNCTION(jack_nframes_t, jack_port_get_total_latency ,(jack_client_t * client, jack_port_t *port), (client, port)); DECL_VOID_FUNCTION(jack_port_set_latency, (jack_port_t * port, jack_nframes_t frames), (port, frames)); DECL_FUNCTION(int, jack_recompute_total_latency, (jack_client_t* client, jack_port_t* port), (client, port)); DECL_VOID_FUNCTION(jack_port_get_latency_range, (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range), (port, mode, range)); DECL_VOID_FUNCTION(jack_port_set_latency_range, (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range), (port, mode, range)); DECL_FUNCTION(int, jack_recompute_total_latencies, (jack_client_t* client),(client)); DECL_FUNCTION(int, jack_port_set_name, (jack_port_t *port, const char *port_name), (port, port_name)); DECL_FUNCTION(int, jack_port_rename, (jack_client_t *client, jack_port_t *port, const char *port_name), (client, port, port_name)); DECL_FUNCTION(int, jack_port_set_alias, (jack_port_t *port, const char *alias), (port, alias)); DECL_FUNCTION(int, jack_port_unset_alias, (jack_port_t *port, const char *alias), (port, alias)); DECL_FUNCTION(int, jack_port_get_aliases, (const jack_port_t *port, char* const aliases[2]), (port,aliases)); DECL_FUNCTION(int, jack_port_request_monitor, (jack_port_t *port, int onoff), (port, onoff)); DECL_FUNCTION(int, jack_port_request_monitor_by_name, (jack_client_t *client, const char *port_name, int onoff), (client, port_name, onoff)); DECL_FUNCTION(int, jack_port_ensure_monitor, (jack_port_t *port, int onoff), (port, onoff)); DECL_FUNCTION(int, jack_port_monitoring_input, (jack_port_t *port) ,(port)); DECL_FUNCTION(int, jack_connect, (jack_client_t * client, const char *source_port, const char *destination_port), (client, source_port, destination_port)); DECL_FUNCTION(int, jack_disconnect, (jack_client_t * client, const char *source_port, const char *destination_port), (client, source_port, destination_port)); DECL_FUNCTION(int, jack_port_disconnect, (jack_client_t * client, jack_port_t * port), (client, port)); DECL_FUNCTION(int, jack_port_name_size,(),()); DECL_FUNCTION(int, jack_port_type_size,(),()); DECL_FUNCTION(size_t, jack_port_type_get_buffer_size, (jack_client_t *client, const char* port_type), (client, port_type)); DECL_FUNCTION(jack_nframes_t, jack_get_sample_rate, (jack_client_t *client), (client)); DECL_FUNCTION(jack_nframes_t, jack_get_buffer_size, (jack_client_t *client), (client)); DECL_FUNCTION_NULL(const char**, jack_get_ports, (jack_client_t *client, const char *port_name_pattern, const char * type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)); DECL_FUNCTION_NULL(jack_port_t *, jack_port_by_name, (jack_client_t * client, const char *port_name), (client, port_name)); DECL_FUNCTION_NULL(jack_port_t *, jack_port_by_id, (jack_client_t *client, jack_port_id_t port_id), (client, port_id)); DECL_FUNCTION(int, jack_engine_takeover_timebase, (jack_client_t * client), (client)); DECL_FUNCTION(jack_nframes_t, jack_frames_since_cycle_start, (const jack_client_t * client), (client)); DECL_FUNCTION(jack_time_t, jack_get_time, (), ()); DECL_FUNCTION(jack_nframes_t, jack_time_to_frames, (const jack_client_t *client, jack_time_t time), (client, time)); DECL_FUNCTION(jack_time_t, jack_frames_to_time, (const jack_client_t *client, jack_nframes_t frames), (client, frames)); DECL_FUNCTION(jack_nframes_t, jack_frame_time, (const jack_client_t *client), (client)); DECL_FUNCTION(jack_nframes_t, jack_last_frame_time, (const jack_client_t *client), (client)); DECL_FUNCTION(float, jack_cpu_load, (jack_client_t *client), (client)); DECL_FUNCTION_NULL(jack_native_thread_t, jack_client_thread_id, (jack_client_t *client), (client)); DECL_VOID_FUNCTION(jack_set_error_function, (print_function fun), (fun)); DECL_VOID_FUNCTION(jack_set_info_function, (print_function fun), (fun)); DECL_FUNCTION(float, jack_get_max_delayed_usecs, (jack_client_t *client), (client)); DECL_FUNCTION(float, jack_get_xrun_delayed_usecs, (jack_client_t *client), (client)); DECL_VOID_FUNCTION(jack_reset_max_delayed_usecs, (jack_client_t *client), (client)); DECL_FUNCTION(int, jack_release_timebase, (jack_client_t *client), (client)); DECL_FUNCTION(int, jack_set_sync_callback, (jack_client_t *client, JackSyncCallback sync_callback, void *arg), (client, sync_callback, arg)); DECL_FUNCTION(int, jack_set_sync_timeout, (jack_client_t *client, jack_time_t timeout), (client, timeout)); DECL_FUNCTION(int, jack_set_timebase_callback, (jack_client_t *client, int conditional, JackTimebaseCallback timebase_callback, void *arg), (client, conditional, timebase_callback, arg)); DECL_FUNCTION(int, jack_transport_locate, (jack_client_t *client, jack_nframes_t frame), (client, frame)); DECL_FUNCTION(jack_transport_state_t, jack_transport_query, (const jack_client_t *client, jack_position_t *pos), (client, pos)); DECL_FUNCTION(jack_nframes_t, jack_get_current_transport_frame, (const jack_client_t *client), (client)); DECL_FUNCTION(int, jack_transport_reposition, (jack_client_t *client, const jack_position_t *pos), (client, pos)); DECL_VOID_FUNCTION(jack_transport_start, (jack_client_t *client), (client)); DECL_VOID_FUNCTION(jack_transport_stop, (jack_client_t *client), (client)); DECL_VOID_FUNCTION(jack_get_transport_info, (jack_client_t *client, jack_transport_info_t *tinfo), (client,tinfo)); DECL_VOID_FUNCTION(jack_set_transport_info, (jack_client_t *client, jack_transport_info_t *tinfo), (client,tinfo)); DECL_FUNCTION(int, jack_client_real_time_priority, (jack_client_t* client), (client)); DECL_FUNCTION(int, jack_client_max_real_time_priority, (jack_client_t* client), (client)); DECL_FUNCTION(int, jack_acquire_real_time_scheduling, (jack_native_thread_t thread, int priority), (thread, priority)); DECL_FUNCTION(int, jack_client_create_thread, (jack_client_t* client, jack_native_thread_t *thread, int priority, int realtime, // boolean thread_routine routine, void *arg), (client, thread, priority, realtime, routine, arg)); DECL_FUNCTION(int, jack_drop_real_time_scheduling, (jack_native_thread_t thread), (thread)); DECL_FUNCTION(int, jack_client_stop_thread, (jack_client_t* client, jack_native_thread_t thread), (client, thread)); DECL_FUNCTION(int, jack_client_kill_thread, (jack_client_t* client, jack_native_thread_t thread), (client, thread)); #ifndef WIN32 DECL_VOID_FUNCTION(jack_set_thread_creator, (jack_thread_creator_t jtc), (jtc)); #endif DECL_FUNCTION(char *, jack_get_internal_client_name, (jack_client_t *client, jack_intclient_t intclient), (client, intclient)); DECL_FUNCTION(jack_intclient_t, jack_internal_client_handle, (jack_client_t *client, const char *client_name, jack_status_t *status), (client, client_name, status)); /* DECL_FUNCTION(jack_intclient_t, jack_internal_client_load, (jack_client_t *client, const char *client_name, jack_options_t options, jack_status_t *status , ...), (client, client_name, options, status, ...)); */ DECL_FUNCTION(jack_status_t, jack_internal_client_unload, (jack_client_t *client, jack_intclient_t intclient), (client, intclient)); DECL_VOID_FUNCTION(jack_free, (void* ptr), (ptr)); // session DECL_FUNCTION(int, jack_set_session_callback, (jack_client_t* ext_client, JackSessionCallback session_callback, void* arg), (ext_client, session_callback, arg)); DECL_FUNCTION(jack_session_command_t*, jack_session_notify, (jack_client_t* ext_client, const char* target, jack_session_event_type_t ev_type, const char* path), (ext_client, target, ev_type, path)); DECL_FUNCTION(int, jack_session_reply, (jack_client_t* ext_client, jack_session_event_t *event), (ext_client, event)); DECL_VOID_FUNCTION(jack_session_event_free, (jack_session_event_t* ev), (ev)); DECL_FUNCTION(char*, jack_client_get_uuid, (jack_client_t* ext_client),(ext_client)); DECL_FUNCTION(char*, jack_get_uuid_for_client_name, (jack_client_t* ext_client, const char* client_name),(ext_client, client_name)); DECL_FUNCTION(char*, jack_get_client_name_by_uuid, (jack_client_t* ext_client, const char* client_uuid),(ext_client, client_uuid)); DECL_FUNCTION(int, jack_reserve_client_name, (jack_client_t* ext_client, const char* name, const char* uuid),(ext_client, name, uuid)); DECL_VOID_FUNCTION(jack_session_commands_free, (jack_session_command_t *cmds),(cmds)); DECL_FUNCTION(int, jack_client_has_session_callback, (jack_client_t *client, const char* client_name),(client, client_name)); // MIDI DECL_FUNCTION(jack_nframes_t, jack_midi_get_event_count, (void* port_buffer), (port_buffer)); DECL_FUNCTION(int, jack_midi_event_get, (jack_midi_event_t* event, void* port_buffer, jack_nframes_t event_index), (event, port_buffer, event_index)) ; DECL_VOID_FUNCTION(jack_midi_clear_buffer, (void* port_buffer), (port_buffer)); DECL_FUNCTION(size_t, jack_midi_max_event_size, (void* port_buffer), (port_buffer)); DECL_FUNCTION_NULL(jack_midi_data_t*, jack_midi_event_reserve, (void* port_buffer, jack_nframes_t time, size_t data_size), (port_buffer, time, data_size)); DECL_FUNCTION(int, jack_midi_event_write, (void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size), (port_buffer, time, data, data_size)); DECL_FUNCTION(jack_nframes_t, jack_midi_get_lost_event_count, (void* port_buffer), (port_buffer)); jack2-1.9.22/common/Jackdmp.cpp000066400000000000000000000566471436671425200162000ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004-2013 Grame Copyright (C) 2016-2022 Filipe Coelho This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include "types.h" #include "jack.h" #include "control.h" #include "JackConstants.h" #include "JackPlatformPlug.h" #ifdef __ANDROID__ #include "JackControlAPIAndroid.h" #endif #if defined(JACK_DBUS) && defined(__linux__) #include #include #include "audio_reserve.h" #endif #if HAVE_SYSTEMD #include #endif /* This is a simple port of the old jackdmp.cpp file to use the new jack2 control API. Available options for the server are "hard-coded" in the source. A much better approach would be to use the control API to: - dynamically retrieve available server parameters and then prepare to parse them - get available drivers and their possible parameters, then prepare to parse them. */ #ifdef __APPLE__ #include #include static void notify_server_start(const char* server_name) { // Send notification to be used in the JackRouter plugin CFStringRef ref = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman); CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterGetDistributedCenter(), CFSTR("com.grame.jackserver.start"), ref, NULL, kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions); CFRelease(ref); } static void notify_server_stop(const char* server_name) { // Send notification to be used in the JackRouter plugin CFStringRef ref1 = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman); CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterGetDistributedCenter(), CFSTR("com.grame.jackserver.stop"), ref1, NULL, kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions); CFRelease(ref1); } #else static void notify_server_start(const char* server_name) {} static void notify_server_stop(const char* server_name) {} #endif static void copyright(FILE* file) { fprintf(file, "jackdmp " VERSION "\n" "Copyright 2001-2005 Paul Davis and others.\n" "Copyright 2004-2016 Grame.\n" "Copyright 2016-2023 Filipe Coelho.\n" "jackdmp comes with ABSOLUTELY NO WARRANTY\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions; see the file COPYING for details\n"); } static jackctl_driver_t * jackctl_server_get_driver(jackctl_server_t *server, const char *driver_name) { const JSList * node_ptr = jackctl_server_get_drivers_list(server); while (node_ptr) { if (strcmp(jackctl_driver_get_name((jackctl_driver_t *)node_ptr->data), driver_name) == 0) { return (jackctl_driver_t *)node_ptr->data; } node_ptr = jack_slist_next(node_ptr); } return NULL; } static jackctl_internal_t * jackctl_server_get_internal(jackctl_server_t *server, const char *internal_name) { const JSList * node_ptr = jackctl_server_get_internals_list(server); while (node_ptr) { if (strcmp(jackctl_internal_get_name((jackctl_internal_t *)node_ptr->data), internal_name) == 0) { return (jackctl_internal_t *)node_ptr->data; } node_ptr = jack_slist_next(node_ptr); } return NULL; } static jackctl_parameter_t * jackctl_get_parameter(const JSList * parameters_list, const char * parameter_name) { while (parameters_list) { if (strcmp(jackctl_parameter_get_name((jackctl_parameter_t *)parameters_list->data), parameter_name) == 0) { return (jackctl_parameter_t *)parameters_list->data; } parameters_list = jack_slist_next(parameters_list); } return NULL; } #ifdef __ANDROID__ static void jackctl_server_switch_master_dummy(jackctl_server_t * server_ctl, char * master_driver_name) { static bool is_dummy_driver = false; if(!strcmp(master_driver_name, "dummy")) { return; } jackctl_driver_t * driver_ctr; if(is_dummy_driver) { is_dummy_driver = false; driver_ctr = jackctl_server_get_driver(server_ctl, master_driver_name); } else { is_dummy_driver = true; driver_ctr = jackctl_server_get_driver(server_ctl, "dummy"); } jackctl_server_switch_master(server_ctl, driver_ctr); } #endif static void print_server_drivers(jackctl_server_t *server, FILE* file) { const JSList * node_ptr = jackctl_server_get_drivers_list(server); fprintf(file, "Available backends:\n"); while (node_ptr) { jackctl_driver_t* driver = (jackctl_driver_t *)node_ptr->data; fprintf(file, " %s (%s)\n", jackctl_driver_get_name(driver), (jackctl_driver_get_type(driver) == JackMaster) ? "master" : "slave"); node_ptr = jack_slist_next(node_ptr); } fprintf(file, "\n"); } static void print_server_internals(jackctl_server_t *server, FILE* file) { const JSList * node_ptr = jackctl_server_get_internals_list(server); fprintf(file, "Available internals:\n"); while (node_ptr) { jackctl_internal_t* internal = (jackctl_internal_t *)node_ptr->data; fprintf(file, " %s\n", jackctl_internal_get_name(internal)); node_ptr = jack_slist_next(node_ptr); } fprintf(file, "\n"); } static void usage(FILE* file, jackctl_server_t *server, bool full = true) { jackctl_parameter_t * param; const JSList * server_parameters; uint32_t i; union jackctl_parameter_value value; fprintf(file, "\n" "Usage: jackdmp [ --no-realtime OR -r ]\n" " [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n" " (the two previous arguments are mutually exclusive. The default is --realtime)\n" " [ --name OR -n server-name ]\n" " [ --timeout OR -t client-timeout-in-msecs ]\n" " [ --loopback OR -L loopback-port-number ]\n" " [ --port-max OR -p maximum-number-of-ports]\n" " [ --slave-backend OR -X slave-backend-name ]\n" " [ --internal-client OR -I internal-client-name ]\n" " [ --internal-session-file OR -C internal-session-file ]\n" " [ --verbose OR -v ]\n" #ifdef __linux__ " [ --clocksource OR -c [ h(pet) | s(ystem) ]\n" #endif " [ --autoconnect OR -a ]\n"); server_parameters = jackctl_server_get_parameters(server); param = jackctl_get_parameter(server_parameters, "self-connect-mode"); fprintf(file, " where is one of:\n"); for (i = 0; i < jackctl_parameter_get_enum_constraints_count(param); i++) { value = jackctl_parameter_get_enum_constraint_value(param, i); fprintf(file, " '%c' - %s", value.c, jackctl_parameter_get_enum_constraint_description(param, i)); if (value.c == JACK_DEFAULT_SELF_CONNECT_MODE) { fprintf(file, " (default)"); } fprintf(file, "\n"); } fprintf(file, " [ --replace-registry ]\n" " [ --silent OR -s ]\n" " [ --sync OR -S ]\n" " [ --temporary OR -T ]\n" " [ --version OR -V ]\n" " -d master-backend-name [ ... master-backend args ... ]\n" " jackdmp -d master-backend-name --help\n" " to display options for each master backend\n\n"); if (full) { print_server_drivers(server, file); print_server_internals(server, file); } } // Prototype to be found in libjackserver extern "C" void silent_jack_error_callback(const char *desc); void print_version() { printf( "jackdmp version " VERSION " tmpdir " jack_server_dir " protocol %d" "\n", JACK_PROTOCOL_VERSION); } int main(int argc, char** argv) { jackctl_server_t * server_ctl; const JSList * server_parameters; const char* server_name = JACK_DEFAULT_SERVER_NAME; jackctl_driver_t * master_driver_ctl; jackctl_driver_t * loopback_driver_ctl = NULL; int replace_registry = 0; for(int a = 1; a < argc; ++a) { if( !strcmp(argv[a], "--version") || !strcmp(argv[a], "-V") ) { print_version(); return 0; } } const char *options = "-d:X:I:P:uvshrRL:STFl:t:mn:p:C:" "a:" #ifdef __linux__ "c:" #endif ; struct option long_options[] = { #ifdef __linux__ { "clock-source", 1, 0, 'c' }, #endif { "internal-session-file", 1, 0, 'C' }, { "loopback-driver", 1, 0, 'L' }, { "audio-driver", 1, 0, 'd' }, { "midi-driver", 1, 0, 'X' }, { "internal-client", 1, 0, 'I' }, { "verbose", 0, 0, 'v' }, { "help", 0, 0, 'h' }, { "port-max", 1, 0, 'p' }, { "no-mlock", 0, 0, 'm' }, { "name", 1, 0, 'n' }, { "unlock", 0, 0, 'u' }, { "realtime", 0, 0, 'R' }, { "no-realtime", 0, 0, 'r' }, { "replace-registry", 0, &replace_registry, 0 }, { "loopback", 0, 0, 'L' }, { "realtime-priority", 1, 0, 'P' }, { "timeout", 1, 0, 't' }, { "temporary", 0, 0, 'T' }, { "silent", 0, 0, 's' }, { "sync", 0, 0, 'S' }, { "autoconnect", 1, 0, 'a' }, { 0, 0, 0, 0 } }; int i,opt = 0; int option_index = 0; char* internal_session_file = NULL; char* master_driver_name = NULL; char** master_driver_args = NULL; int master_driver_nargs = 1; int loopback = 0; jackctl_sigmask_t * sigmask; jackctl_parameter_t* param; union jackctl_parameter_value value; std::list internals_list; std::list slaves_list; std::list::iterator it; // Assume that we fail. int return_value = -1; bool notify_sent = false; copyright(stdout); #if defined(JACK_DBUS) && defined(__linux__) if (getenv("JACK_NO_AUDIO_RESERVATION")) server_ctl = jackctl_server_create2(NULL, NULL, NULL); else server_ctl = jackctl_server_create2(audio_acquire, audio_release, audio_reserve_loop); #else server_ctl = jackctl_server_create2(NULL, NULL, NULL); #endif if (server_ctl == NULL) { fprintf(stderr, "Failed to create server object\n"); return -1; } server_parameters = jackctl_server_get_parameters(server_ctl); opterr = 0; while (!master_driver_name && (opt = getopt_long(argc, argv, options, long_options, &option_index)) != EOF) { switch (opt) { #ifdef __linux__ case 'c': param = jackctl_get_parameter(server_parameters, "clock-source"); if (param != NULL) { if (tolower (optarg[0]) == 'h') { value.ui = JACK_TIMER_HPET; jackctl_parameter_set_value(param, &value); } else if (tolower (optarg[0]) == 'c') { /* For backwards compatibility with scripts, allow * the user to request the cycle clock on the * command line, but use the system clock instead */ value.ui = JACK_TIMER_SYSTEM_CLOCK; jackctl_parameter_set_value(param, &value); } else if (tolower (optarg[0]) == 's') { value.ui = JACK_TIMER_SYSTEM_CLOCK; jackctl_parameter_set_value(param, &value); } else { usage(stdout, server_ctl); goto destroy_server; } } break; #endif case 'a': param = jackctl_get_parameter(server_parameters, "self-connect-mode"); if (param != NULL) { bool value_valid = false; for (uint32_t k=0; k 0) { loopback_driver_ctl = jackctl_server_get_driver(server_ctl, "loopback"); if (loopback_driver_ctl != NULL) { const JSList * loopback_parameters = jackctl_driver_get_parameters(loopback_driver_ctl); param = jackctl_get_parameter(loopback_parameters, "channels"); if (param != NULL) { value.ui = loopback; jackctl_parameter_set_value(param, &value); } if (!jackctl_server_add_slave(server_ctl, loopback_driver_ctl)) { fprintf(stderr, "Driver \"loopback\" cannot be loaded\n"); goto close_server; } } else { fprintf(stderr, "Driver \"loopback\" not found\n"); goto close_server; } } // Start the server if (!jackctl_server_start(server_ctl)) { fprintf(stderr, "Failed to start server\n"); goto close_server; } // Internal clients for (it = internals_list.begin(); it != internals_list.end(); it++) { jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it); if (internal_driver_ctl == NULL) { fprintf(stderr, "Unknown internal \"%s\"\n", *it); goto stop_server; } if (!jackctl_server_load_internal(server_ctl, internal_driver_ctl)) { fprintf(stderr, "Internal client \"%s\" cannot be loaded\n", *it); goto stop_server; } } if (internal_session_file != NULL) { if (!jackctl_server_load_session_file(server_ctl, internal_session_file)) { fprintf(stderr, "Internal session file %s cannot be loaded!\n", internal_session_file); goto stop_server; } } notify_server_start(server_name); notify_sent = true; return_value = 0; #if HAVE_SYSTEMD sd_notify(0, "READY=1"); #endif // Waits for signal #ifdef __ANDROID__ //reserve SIGUSR2 signal for switching master driver while(1) { int signal = jackctl_wait_signals_and_return(sigmask); if (signal == SIGUSR2) { jackctl_server_switch_master_dummy(server_ctl, master_driver_name); } else { break; } } #else jackctl_wait_signals(sigmask); #endif #if HAVE_SYSTEMD sd_notify(0, "STOPPING=1"); #endif stop_server: if (!jackctl_server_stop(server_ctl)) { fprintf(stderr, "Cannot stop server...\n"); } close_server: if (loopback > 0 && loopback_driver_ctl) { jackctl_server_remove_slave(server_ctl, loopback_driver_ctl); } // Slave drivers for (it = slaves_list.begin(); it != slaves_list.end(); it++) { jackctl_driver_t * slave_driver_ctl = jackctl_server_get_driver(server_ctl, *it); if (slave_driver_ctl) { jackctl_server_remove_slave(server_ctl, slave_driver_ctl); } } // Internal clients for (it = internals_list.begin(); it != internals_list.end(); it++) { jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it); if (internal_driver_ctl) { jackctl_server_unload_internal(server_ctl, internal_driver_ctl); } } jackctl_server_close(server_ctl); destroy_server: jackctl_server_destroy(server_ctl); if (notify_sent) { notify_server_stop(server_name); } return return_value; } jack2-1.9.22/common/driver_interface.h000066400000000000000000000164401436671425200175720ustar00rootroot00000000000000/* Copyright (C) 2003 Bob Ham Copyright (C) 2008 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_driver_interface_h__ #define __jack_driver_interface_h__ #ifdef __cplusplus extern "C" { #endif #include #include "jslist.h" #include "JackCompilerDeps.h" #include "JackSystemDeps.h" #define JACK_DRIVER_NAME_MAX 15 #define JACK_DRIVER_PARAM_NAME_MAX 15 #define JACK_DRIVER_PARAM_STRING_MAX 127 #define JACK_DRIVER_PARAM_DESC 255 #define JACK_PATH_MAX 511 #define JACK_CONSTRAINT_FLAG_RANGE ((uint32_t)1) /**< if set, constraint is a range (min-max) */ #define JACK_CONSTRAINT_FLAG_STRICT ((uint32_t)2) /**< if set, constraint is strict, i.e. supplying non-matching value will not work */ #define JACK_CONSTRAINT_FLAG_FAKE_VALUE ((uint32_t)4) /**< if set, values have no user meaningful meaning */ /** Driver parameter types */ typedef enum { JackDriverParamInt = 1, JackDriverParamUInt, JackDriverParamChar, JackDriverParamString, JackDriverParamBool } jack_driver_param_type_t; /** Driver types */ typedef enum { JackDriverMaster = 1, JackDriverSlave, JackDriverNone, } jack_driver_type_t; /** Driver parameter value */ typedef union { uint32_t ui; int32_t i; char c; char str[JACK_DRIVER_PARAM_STRING_MAX + 1]; } jack_driver_param_value_t; typedef struct { jack_driver_param_value_t value; char short_desc[64]; /**< A short (~30 chars) description for the user */ } jack_driver_param_value_enum_t; struct jack_constraint_enum_uint32_descriptor { uint32_t value; const char * short_desc; }; struct jack_constraint_enum_sint32_descriptor { int32_t value; const char * short_desc; }; struct jack_constraint_enum_char_descriptor { char value; const char * short_desc; }; struct jack_constraint_enum_str_descriptor { const char * value; const char * short_desc; }; typedef struct { uint32_t flags; /**< JACK_CONSTRAINT_FLAG_XXX */ union { struct { jack_driver_param_value_t min; jack_driver_param_value_t max; } range; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is set */ struct { uint32_t count; jack_driver_param_value_enum_t * possible_values_array; } enumeration; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is not set */ } constraint; } jack_driver_param_constraint_desc_t; /** A driver parameter descriptor */ typedef struct { char name[JACK_DRIVER_NAME_MAX + 1]; /**< The parameter's name */ char character; /**< The parameter's character (for getopt, etc) */ jack_driver_param_type_t type; /**< The parameter's type */ jack_driver_param_value_t value; /**< The parameter's (default) value */ jack_driver_param_constraint_desc_t * constraint; /**< Pointer to parameter constraint descriptor. NULL if there is no constraint */ char short_desc[64]; /**< A short (~30 chars) description for the user */ char long_desc[1024]; /**< A longer description for the user */ } jack_driver_param_desc_t; /** A driver parameter */ typedef struct { char character; jack_driver_param_value_t value; } jack_driver_param_t; /** A struct for describing a jack driver */ typedef struct { char name[JACK_DRIVER_NAME_MAX + 1]; /**< The driver's canonical name */ jack_driver_type_t type; /**< The driver's type */ char desc[JACK_DRIVER_PARAM_DESC + 1]; /**< The driver's extended description */ #ifdef WIN32 wchar_t file[JACK_PATH_MAX + 1]; /**< The filename of the driver's shared object file */ #else char file[JACK_PATH_MAX + 1]; /**< The filename of the driver's shared object file */ #endif uint32_t nparams; /**< The number of parameters the driver has */ jack_driver_param_desc_t * params; /**< An array of parameter descriptors */ } jack_driver_desc_t; typedef struct { uint32_t size; /* size of the param array, in elements */ } jack_driver_desc_filler_t; int jack_parse_driver_params(jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr); // To be used by drivers SERVER_EXPORT jack_driver_desc_t * /* Newly allocated driver descriptor, NULL on failure */ jack_driver_descriptor_construct( const char * name, /* Driver name */ jack_driver_type_t type, /* Driver type */ const char * description, /* Driver description */ jack_driver_desc_filler_t * filler); /* Pointer to stack var to be supplied to jack_driver_descriptor_add_parameter() as well. Can be NULL for drivers that have no parameters. */ SERVER_EXPORT int /* 0 on failure */ jack_driver_descriptor_add_parameter( jack_driver_desc_t * driver_descr, /* Pointer to driver descriptor as returned by jack_driver_descriptor_construct() */ jack_driver_desc_filler_t * filler, /* Pointer to the stack var that was supplied to jack_driver_descriptor_add_parameter(). */ const char * name, /* Parameter's name */ char character, /* Parameter's character (for getopt, etc) */ jack_driver_param_type_t type, /* The parameter's type */ const jack_driver_param_value_t * value_ptr, /* Pointer to parameter's (default) value */ jack_driver_param_constraint_desc_t * constraint, /* Pointer to parameter constraint descriptor. NULL if there is no constraint */ const char * short_desc, /* A short (~30 chars) description for the user */ const char * long_desc); /* A longer description for the user, if NULL short_desc will be used */ SERVER_EXPORT int jack_constraint_add_enum( jack_driver_param_constraint_desc_t ** constraint_ptr_ptr, uint32_t * array_size_ptr, jack_driver_param_value_t * value_ptr, const char * short_desc); SERVER_EXPORT void jack_constraint_free(jack_driver_param_constraint_desc_t * constraint_ptr); #define JACK_CONSTRAINT_COMPOSE_ENUM(type) \ SERVER_EXPORT \ jack_driver_param_constraint_desc_t * \ jack_constraint_compose_enum_ ## type( \ uint32_t flags, \ struct jack_constraint_enum_ ## type ## _descriptor * descr_array_ptr) JACK_CONSTRAINT_COMPOSE_ENUM(uint32); JACK_CONSTRAINT_COMPOSE_ENUM(sint32); JACK_CONSTRAINT_COMPOSE_ENUM(char); JACK_CONSTRAINT_COMPOSE_ENUM(str); typedef jack_driver_desc_t * (*JackDriverDescFunction) (); #ifdef __cplusplus } #endif #endif /* __jack_driver_interface_h__ */ jack2-1.9.22/common/jack/000077500000000000000000000000001436671425200150115ustar00rootroot00000000000000jack2-1.9.22/common/jack/control.h000066400000000000000000000421011436671425200166400ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* JACK control API Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2008 GRAME This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** * @file jack/control.h * @ingroup publicheader * @brief JACK control API * */ #ifndef JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED #define JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED #include #include #include #if !defined(sun) && !defined(__sun__) #include #endif /** Parameter types, intentionally similar to jack_driver_param_type_t */ typedef enum { JackParamInt = 1, /**< @brief value type is a signed integer */ JackParamUInt, /**< @brief value type is an unsigned integer */ JackParamChar, /**< @brief value type is a char */ JackParamString, /**< @brief value type is a string with max size of ::JACK_PARAM_STRING_MAX+1 chars */ JackParamBool, /**< @brief value type is a boolean */ } jackctl_param_type_t; /** Driver types */ typedef enum { JackMaster = 1, /**< @brief master driver */ JackSlave /**< @brief slave driver */ } jackctl_driver_type_t; /** @brief Max value that jackctl_param_type_t type can have */ #define JACK_PARAM_MAX (JackParamBool + 1) /** @brief Max length of string parameter value, excluding terminating null char */ #define JACK_PARAM_STRING_MAX 127 /** @brief Type for parameter value */ /* intentionally similar to jack_driver_param_value_t */ union jackctl_parameter_value { uint32_t ui; /**< @brief member used for ::JackParamUInt */ int32_t i; /**< @brief member used for ::JackParamInt */ char c; /**< @brief member used for ::JackParamChar */ char str[JACK_PARAM_STRING_MAX + 1]; /**< @brief member used for ::JackParamString */ bool b; /**< @brief member used for ::JackParamBool */ }; /** opaque type for server object */ typedef struct jackctl_server jackctl_server_t; /** opaque type for driver object */ typedef struct jackctl_driver jackctl_driver_t; /** opaque type for internal client object */ typedef struct jackctl_internal jackctl_internal_t; /** opaque type for parameter object */ typedef struct jackctl_parameter jackctl_parameter_t; /** opaque type for sigmask object */ typedef struct jackctl_sigmask jackctl_sigmask_t; #ifdef __cplusplus extern "C" { #endif #if 0 } /* Adjust editor indent */ #endif /** * @defgroup ControlAPI The API for starting and controlling a JACK server * @{ */ /** * Call this function to setup process signal handling. As a general * rule, it is required for proper operation for the server object. * * @param flags signals setup flags, use 0 for none. Currently no * flags are defined * * @return the configurated signal set. */ jackctl_sigmask_t * jackctl_setup_signals( unsigned int flags); /** * Call this function to wait on a signal set. * * @param signals signals set to wait on */ void jackctl_wait_signals( jackctl_sigmask_t * signals); /** * \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN * NEW JACK PROJECTS * * @deprecated Please use jackctl_server_create2(). */ jackctl_server_t * jackctl_server_create( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name)); /** * Call this function to create server object. * * @param on_device_acquire - Optional callback to be called before device is acquired. If false is returned, device usage will fail * @param on_device_release - Optional callback to be called after device is released. * @param on_device_reservation_loop - Optional callback to be called when looping/idling the reservation. * * @return server object handle, NULL if creation of server object * failed. Successfully created server object must be destroyed with * paired call to ::jackctl_server_destroy */ jackctl_server_t * jackctl_server_create2( bool (* on_device_acquire)(const char * device_name), void (* on_device_release)(const char * device_name), void (* on_device_reservation_loop)(void)); /** * Call this function to destroy server object. * * @param server server object handle to destroy */ void jackctl_server_destroy( jackctl_server_t * server); /** * Call this function to open JACK server * * @param server server object handle * @param driver driver to use * * @return success status: true - success, false - fail */ bool jackctl_server_open( jackctl_server_t * server, jackctl_driver_t * driver); /** * Call this function to start JACK server * * @param server server object handle * * @return success status: true - success, false - fail */ bool jackctl_server_start( jackctl_server_t * server); /** * Call this function to stop JACK server * * @param server server object handle * * @return success status: true - success, false - fail */ bool jackctl_server_stop( jackctl_server_t * server); /** * Call this function to close JACK server * * @param server server object handle * * @return success status: true - success, false - fail */ bool jackctl_server_close( jackctl_server_t * server); /** * Call this function to get list of available drivers. List node data * pointers is a driver object handle (::jackctl_driver_t). * * @param server server object handle to get drivers for * * @return Single linked list of driver object handles. Must not be * modified. Always same for same server object. */ const JSList * jackctl_server_get_drivers_list( jackctl_server_t * server); /** * Call this function to get list of server parameters. List node data * pointers is a parameter object handle (::jackctl_parameter_t). * * @param server server object handle to get parameters for * * @return Single linked list of parameter object handles. Must not be * modified. Always same for same server object. */ const JSList * jackctl_server_get_parameters( jackctl_server_t * server); /** * Call this function to get list of available internal clients. List node data * pointers is a internal client object handle (::jackctl_internal_t). * * @param server server object handle to get internal clients for * * @return Single linked list of internal client object handles. Must not be * modified. Always same for same server object. */ const JSList * jackctl_server_get_internals_list( jackctl_server_t * server); /** * Call this function to load one internal client. * (can be used when the server is running) * * @param server server object handle * @param internal internal to use * * @return success status: true - success, false - fail */ bool jackctl_server_load_internal( jackctl_server_t * server, jackctl_internal_t * internal); /** * Call this function to unload one internal client. * (can be used when the server is running) * * @param server server object handle * @param internal internal to unload * * @return success status: true - success, false - fail */ bool jackctl_server_unload_internal( jackctl_server_t * server, jackctl_internal_t * internal); /** * Call this function to load a session file. * (can be used when the server is running) * * @param server server object handle * @param file the session file to load, containing a list of * internal clients and connections to be made. * * @return success status: true - success, false - fail */ bool jackctl_server_load_session_file( jackctl_server_t * server_ptr, const char * file); /** * Call this function to add a slave in the driver slave list. * (cannot be used when the server is running that is between * jackctl_server_start and jackctl_server_stop) * * @param server server object handle * @param driver driver to add in the driver slave list. * * @return success status: true - success, false - fail */ bool jackctl_server_add_slave(jackctl_server_t * server, jackctl_driver_t * driver); /** * Call this function to remove a slave from the driver slave list. * (cannot be used when the server is running that is between * jackctl_server_start and jackctl_server_stop) * * @param server server object handle * @param driver driver to remove from the driver slave list. * * @return success status: true - success, false - fail */ bool jackctl_server_remove_slave(jackctl_server_t * server, jackctl_driver_t * driver); /** * Call this function to switch master driver. * * @param server server object handle * @param driver driver to switch to * * @return success status: true - success, false - fail */ bool jackctl_server_switch_master(jackctl_server_t * server, jackctl_driver_t * driver); /** * Call this function to get name of driver. * * @param driver driver object handle to get name of * * @return driver name. Must not be modified. Always same for same * driver object. */ const char * jackctl_driver_get_name( jackctl_driver_t * driver); /** * Call this function to get type of driver. * * @param driver driver object handle to get name of * * @return driver type. Must not be modified. Always same for same * driver object. */ jackctl_driver_type_t jackctl_driver_get_type( jackctl_driver_t * driver); /** * Call this function to get list of driver parameters. List node data * pointers is a parameter object handle (::jackctl_parameter_t). * * @param driver driver object handle to get parameters for * * @return Single linked list of parameter object handles. Must not be * modified. Always same for same driver object. */ const JSList * jackctl_driver_get_parameters( jackctl_driver_t * driver); /** * Call this function to parse parameters for a driver. * * @param driver driver object handle * @param argc parameter list len * @param argv parameter list, as an array of char* * * @return success status: true - success, false - fail */ int jackctl_driver_params_parse( jackctl_driver_t * driver, int argc, char* argv[]); /** * Call this function to get name of internal client. * * @param internal internal object handle to get name of * * @return internal name. Must not be modified. Always same for same * internal object. */ const char * jackctl_internal_get_name( jackctl_internal_t * internal); /** * Call this function to get list of internal parameters. List node data * pointers is a parameter object handle (::jackctl_parameter_t). * * @param internal internal object handle to get parameters for * * @return Single linked list of parameter object handles. Must not be * modified. Always same for same internal object. */ const JSList * jackctl_internal_get_parameters( jackctl_internal_t * internal); /** * Call this function to get parameter name. * * @param parameter parameter object handle to get name of * * @return parameter name. Must not be modified. Always same for same * parameter object. */ const char * jackctl_parameter_get_name( jackctl_parameter_t * parameter); /** * Call this function to get parameter short description. * * @param parameter parameter object handle to get short description of * * @return parameter short description. Must not be modified. Always * same for same parameter object. */ const char * jackctl_parameter_get_short_description( jackctl_parameter_t * parameter); /** * Call this function to get parameter long description. * * @param parameter parameter object handle to get long description of * * @return parameter long description. Must not be modified. Always * same for same parameter object. */ const char * jackctl_parameter_get_long_description( jackctl_parameter_t * parameter); /** * Call this function to get parameter type. * * @param parameter parameter object handle to get type of * * @return parameter type. Always same for same parameter object. */ jackctl_param_type_t jackctl_parameter_get_type( jackctl_parameter_t * parameter); /** * Call this function to get parameter character. * * @param parameter parameter object handle to get character of * * @return character. */ char jackctl_parameter_get_id( jackctl_parameter_t * parameter); /** * Call this function to check whether parameter has been set, or its * default value is being used. * * @param parameter parameter object handle to check * * @return true - parameter is set, false - parameter is using default * value. */ bool jackctl_parameter_is_set( jackctl_parameter_t * parameter); /** * Call this function to reset parameter to its default value. * * @param parameter parameter object handle to reset value of * * @return success status: true - success, false - fail */ bool jackctl_parameter_reset( jackctl_parameter_t * parameter); /** * Call this function to get parameter value. * * @param parameter parameter object handle to get value of * * @return parameter value. */ union jackctl_parameter_value jackctl_parameter_get_value( jackctl_parameter_t * parameter); /** * Call this function to set parameter value. * * @param parameter parameter object handle to get value of * @param value_ptr pointer to variable containing parameter value * * @return success status: true - success, false - fail */ bool jackctl_parameter_set_value( jackctl_parameter_t * parameter, const union jackctl_parameter_value * value_ptr); /** * Call this function to get parameter default value. * * @param parameter parameter object handle to get default value of * * @return parameter default value. */ union jackctl_parameter_value jackctl_parameter_get_default_value( jackctl_parameter_t * parameter); /** * Call this function check whether parameter has range constraint. * * @param parameter object handle of parameter to check * * @return whether parameter has range constraint. */ bool jackctl_parameter_has_range_constraint( jackctl_parameter_t * parameter); /** * Call this function check whether parameter has enumeration constraint. * * @param parameter object handle of parameter to check * * @return whether parameter has enumeration constraint. */ bool jackctl_parameter_has_enum_constraint( jackctl_parameter_t * parameter); /** * Call this function get how many enumeration values parameter has. * * @param parameter object handle of parameter * * @return number of enumeration values */ uint32_t jackctl_parameter_get_enum_constraints_count( jackctl_parameter_t * parameter); /** * Call this function to get parameter enumeration value. * * @param parameter object handle of parameter * @param index index of parameter enumeration value * * @return enumeration value. */ union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value( jackctl_parameter_t * parameter, uint32_t index); /** * Call this function to get parameter enumeration value description. * * @param parameter object handle of parameter * @param index index of parameter enumeration value * * @return enumeration value description. */ const char * jackctl_parameter_get_enum_constraint_description( jackctl_parameter_t * parameter, uint32_t index); /** * Call this function to get parameter range. * * @param parameter object handle of parameter * @param min_ptr pointer to variable receiving parameter minimum value * @param max_ptr pointer to variable receiving parameter maximum value */ void jackctl_parameter_get_range_constraint( jackctl_parameter_t * parameter, union jackctl_parameter_value * min_ptr, union jackctl_parameter_value * max_ptr); /** * Call this function to check whether parameter constraint is strict, * i.e. whether supplying non-matching value will not work for sure. * * @param parameter parameter object handle to check * * @return whether parameter constraint is strict. */ bool jackctl_parameter_constraint_is_strict( jackctl_parameter_t * parameter); /** * Call this function to check whether parameter has fake values, * i.e. values have no user meaningful meaning and only value * description is meaningful to user. * * @param parameter parameter object handle to check * * @return whether parameter constraint is strict. */ bool jackctl_parameter_constraint_is_fake_value( jackctl_parameter_t * parameter); /** * Call this function to log an error message. * * @param format string */ void jack_error( const char *format, ...); /** * Call this function to log an information message. * * @param format string */ void jack_info( const char *format, ...); /** * Call this function to log an information message but only when * verbose mode is enabled. * * @param format string */ void jack_log( const char *format, ...); /**@}*/ #if 0 { /* Adjust editor indent */ #endif #ifdef __cplusplus } /* extern "C" */ #endif #endif /* #ifndef JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED */ jack2-1.9.22/common/jack/intclient.h000066400000000000000000000113341436671425200171550ustar00rootroot00000000000000/* * Copyright (C) 2004 Jack O'Quin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifndef __jack_intclient_h__ #define __jack_intclient_h__ #ifdef __cplusplus extern "C" { #endif #include /** * Get an internal client's name. This is useful when @ref * JackUseExactName was not specified on jack_internal_client_load() * and @ref JackNameNotUnique status was returned. In that case, the * actual name will differ from the @a client_name requested. * * @param client requesting JACK client's handle. * * @param intclient handle returned from jack_internal_client_load() * or jack_internal_client_handle(). * * @return NULL if unsuccessful, otherwise pointer to the internal * client name obtained from the heap via malloc(). The caller should * jack_free() this storage when no longer needed. */ char *jack_get_internal_client_name (jack_client_t *client, jack_intclient_t intclient); /** * Return the @ref jack_intclient_t handle for an internal client * running in the JACK server. * * @param client requesting JACK client's handle. * * @param client_name for the internal client of no more than * jack_client_name_size() characters. The name scope is local to the * current server. * * @param status (if non-NULL) an address for JACK to return * information from this operation. This status word is formed by * OR-ing together the relevant @ref JackStatus bits. * * @return Opaque internal client handle if successful. If 0, the * internal client was not found, and @a *status includes the @ref * JackNoSuchClient and @ref JackFailure bits. */ jack_intclient_t jack_internal_client_handle (jack_client_t *client, const char *client_name, jack_status_t *status); /** * Load an internal client into the JACK server. * * Internal clients run inside the JACK server process. They can use * most of the same functions as external clients. Each internal * client is built as a shared object module, which must declare * jack_initialize() and jack_finish() entry points called at load and * unload times. See @ref inprocess.c for an example. * * @param client loading JACK client's handle. * * @param client_name of at most jack_client_name_size() characters * for the internal client to load. The name scope is local to the * current server. * * @param options formed by OR-ing together @ref JackOptions bits. * Only the @ref JackLoadOptions bits are valid. * * @param status (if non-NULL) an address for JACK to return * information from the load operation. This status word is formed by * OR-ing together the relevant @ref JackStatus bits. * * Optional parameters: depending on corresponding [@a options * bits] additional parameters may follow @a status (in this order). * * @arg [@ref JackLoadName] (char *) load_name is the shared * object file from which to load the new internal client (otherwise * use the @a client_name). * * @arg [@ref JackLoadInit] (char *) load_init an arbitrary * string passed to the internal client's jack_initialize() routine * (otherwise NULL), of no more than @ref JACK_LOAD_INIT_LIMIT bytes. * * @return Opaque internal client handle if successful. If this is 0, * the load operation failed, the internal client was not loaded, and * @a *status includes the @ref JackFailure bit. */ jack_intclient_t jack_internal_client_load (jack_client_t *client, const char *client_name, jack_options_t options, jack_status_t *status, ...); /** * Unload an internal client from a JACK server. This calls the * intclient's jack_finish() entry point then removes it. See @ref * inprocess.c for an example. * * @param client unloading JACK client's handle. * * @param intclient handle returned from jack_internal_client_load() or * jack_internal_client_handle(). * * @return 0 if successful, otherwise @ref JackStatus bits. */ jack_status_t jack_internal_client_unload (jack_client_t *client, jack_intclient_t intclient); #ifdef __cplusplus } #endif #endif /* __jack_intclient_h__ */ jack2-1.9.22/common/jack/jack.h000066400000000000000000001553641436671425200161100ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004 Jack O'Quin This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_h__ #define __jack_h__ #ifdef __cplusplus extern "C" { #endif #include #include #include /** * Note: More documentation can be found in jack/types.h. */ /************************************************************* * NOTE: JACK_WEAK_EXPORT ***MUST*** be used on every function * added to the JACK API after the 0.116.2 release. * * Functions that predate this release are marked with * JACK_WEAK_OPTIONAL_EXPORT which can be defined at compile * time in a variety of ways. The default definition is empty, * so that these symbols get normal linkage. If you wish to * use all JACK symbols with weak linkage, include * before jack.h. *************************************************************/ #include /** * Call this function to get version of the JACK, in form of several numbers * * @param major_ptr pointer to variable receiving major version of JACK. * * @param minor_ptr pointer to variable receiving minor version of JACK. * * @param major_ptr pointer to variable receiving micro version of JACK. * * @param major_ptr pointer to variable receiving protocol version of JACK. * */ void jack_get_version( int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr) JACK_OPTIONAL_WEAK_EXPORT; /** * Call this function to get version of the JACK, in form of a string * * @return Human readable string describing JACK version being used. * */ const char * jack_get_version_string(void) JACK_OPTIONAL_WEAK_EXPORT; /** * @defgroup ClientFunctions Creating & manipulating clients * @{ */ /** * Open an external client session with a JACK server. This interface * is more complex but more powerful than jack_client_new(). With it, * clients may choose which of several servers to connect, and control * whether and how to start the server automatically, if it was not * already running. There is also an option for JACK to generate a * unique client name, when necessary. * * @param client_name of at most jack_client_name_size() characters. * The name scope is local to each server. Unless forbidden by the * @ref JackUseExactName option, the server will modify this name to * create a unique variant, if needed. * * @param options formed by OR-ing together @ref JackOptions bits. * Only the @ref JackOpenOptions bits are allowed. * * @param status (if non-NULL) an address for JACK to return * information from the open operation. This status word is formed by * OR-ing together the relevant @ref JackStatus bits. * * * Optional parameters: depending on corresponding [@a options * bits] additional parameters may follow @a status (in this order). * * @arg [@ref JackServerName] (char *) server_name selects * from among several possible concurrent server instances. Server * names are unique to each user. If unspecified, use "default" * unless \$JACK_DEFAULT_SERVER is defined in the process environment. * * @return Opaque client handle if successful. If this is NULL, the * open operation failed, @a *status includes @ref JackFailure and the * caller is not a JACK client. */ jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) JACK_OPTIONAL_WEAK_EXPORT; /** * \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN * NEW JACK CLIENTS * * @deprecated Please use jack_client_open(). */ jack_client_t * jack_client_new (const char *client_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Disconnects an external client from a JACK server. * * @return 0 on success, otherwise a non-zero error code */ int jack_client_close (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the maximum number of characters in a JACK client name * including the final NULL character. This value is a constant. */ int jack_client_name_size (void) JACK_OPTIONAL_WEAK_EXPORT; /** * @return pointer to actual client name. This is useful when @ref * JackUseExactName is not specified on open and @ref * JackNameNotUnique status was returned. In that case, the actual * name will differ from the @a client_name requested. */ char * jack_get_client_name (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * Get the session ID for a client name. * * The session manager needs this to reassociate a client name to the session_id. * * The caller is responsible for calling jack_free(3) on any non-NULL * returned value. */ char *jack_get_uuid_for_client_name (jack_client_t *client, const char *client_name) JACK_WEAK_EXPORT; /** * Get the client name for a session_id. * * In order to snapshot the graph connections, the session manager needs to map * session_ids to client names. * * The caller is responsible for calling jack_free(3) on any non-NULL * returned value. */ char *jack_get_client_name_by_uuid (jack_client_t *client, const char *client_uuid ) JACK_WEAK_EXPORT; /** * Load an internal client into the Jack server. * * Internal clients run inside the JACK server process. They can use * most of the same functions as external clients. Each internal * client must declare jack_initialize() and jack_finish() entry * points, called at load and unload times. See inprocess.c for an * example of how to write an internal client. * * @deprecated Please use jack_internal_client_load(). * * @param client_name of at most jack_client_name_size() characters. * * @param load_name of a shared object file containing the code for * the new client. * * @param load_init an arbitrary string passed to the jack_initialize() * routine of the new client (may be NULL). * * @return 0 if successful. */ int jack_internal_client_new (const char *client_name, const char *load_name, const char *load_init) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Remove an internal client from a JACK server. * * @deprecated Please use jack_internal_client_unload(). */ void jack_internal_client_close (const char *client_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Tell the Jack server that the program is ready to start processing * audio. * * @return 0 on success, otherwise a non-zero error code */ int jack_activate (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the Jack server to remove this @a client from the process * graph. Also, disconnect all ports belonging to it, since inactive * clients have no port connections. * * @return 0 on success, otherwise a non-zero error code */ int jack_deactivate (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * @return pid of client. If not available, 0 will be returned. */ int jack_get_client_pid (const char *name) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the pthread ID of the thread running the JACK client side * real-time code. */ jack_native_thread_t jack_client_thread_id (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ /** * @param client pointer to JACK client structure. * * Check if the JACK subsystem is running with -R (--realtime). * * @return 1 if JACK is running realtime, 0 otherwise */ int jack_is_realtime (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * @defgroup NonCallbackAPI The non-callback API * @{ */ /** * \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN * NEW JACK CLIENTS. * * @deprecated Please use jack_cycle_wait() and jack_cycle_signal() functions. */ jack_nframes_t jack_thread_wait (jack_client_t *client, int status) JACK_OPTIONAL_WEAK_EXPORT; /** * Wait until this JACK client should process data. * * @param client - pointer to a JACK client structure * * @return the number of frames of data to process */ jack_nframes_t jack_cycle_wait (jack_client_t* client) JACK_OPTIONAL_WEAK_EXPORT; /** * Signal next clients in the graph. * * @param client - pointer to a JACK client structure * @param status - if non-zero, calling thread should exit */ void jack_cycle_signal (jack_client_t* client, int status) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the Jack server to call @a thread_callback in the RT thread. * Typical use are in conjunction with @a jack_cycle_wait and @a jack_cycle_signal functions. * The code in the supplied function must be suitable for real-time * execution. That means that it cannot call functions that might * block for a long time. This includes malloc, free, printf, * pthread_mutex_lock, sleep, wait, poll, select, pthread_join, * pthread_cond_wait, etc, etc. See * http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000 * for more information. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code. */ int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ /** * @defgroup ClientCallbacks Setting Client Callbacks * @{ */ /** * Tell JACK to call @a thread_init_callback once just after * the creation of the thread in which all other callbacks * will be handled. * * The code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code, causing JACK * to remove that client from the process() graph. */ int jack_set_thread_init_callback (jack_client_t *client, JackThreadInitCallback thread_init_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * @param client pointer to JACK client structure. * @param function The jack_shutdown function pointer. * @param arg The arguments for the jack_shutdown function. * * Register a function (and argument) to be called if and when the * JACK server shuts down the client thread. The function must * be written as if it were an asynchonrous POSIX signal * handler --- use only async-safe functions, and remember that it * is executed from another thread. A typical function might * set a flag or write to a pipe so that the rest of the * application knows that the JACK client thread has shut * down. * * NOTE: clients do not need to call this. It exists only * to help more complex clients understand what is going * on. It should be called before jack_client_activate(). * * NOTE: if a client calls this AND jack_on_info_shutdown(), then * in case of a client thread shutdown, the callback * passed to this function will not be called, and the one passed to * jack_on_info_shutdown() will. * * NOTE: application should typically signal another thread to correctly * finish cleanup, that is by calling "jack_client_close" * (since "jack_client_close" cannot be called directly in the context * of the thread that calls the shutdown callback). */ void jack_on_shutdown (jack_client_t *client, JackShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * @param client pointer to JACK client structure. * @param function The jack_info_shutdown function pointer. * @param arg The arguments for the jack_info_shutdown function. * * Register a function (and argument) to be called if and when the * JACK server shuts down the client thread. The function must * be written as if it were an asynchonrous POSIX signal * handler --- use only async-safe functions, and remember that it * is executed from another thread. A typical function might * set a flag or write to a pipe so that the rest of the * application knows that the JACK client thread has shut * down. * * NOTE: clients do not need to call this. It exists only * to help more complex clients understand what is going * on. It should be called before jack_client_activate(). * * NOTE: if a client calls this AND jack_on_shutdown(), then * in case of a client thread shutdown, the callback passed to * jack_on_info_shutdown() will be called. * * NOTE: application should typically signal another thread to correctly * finish cleanup, that is by calling "jack_client_close" * (since "jack_client_close" cannot be called directly in the context * of the thread that calls the shutdown callback). */ void jack_on_info_shutdown (jack_client_t *client, JackInfoShutdownCallback shutdown_callback, void *arg) JACK_WEAK_EXPORT; /** * Tell the Jack server to call @a process_callback whenever there is * work be done, passing @a arg as the second argument. * * The code in the supplied function must be suitable for real-time * execution. That means that it cannot call functions that might * block for a long time. This includes malloc, free, printf, * pthread_mutex_lock, sleep, wait, poll, select, pthread_join, * pthread_cond_wait, etc, etc. See * http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000 * for more information. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code. */ int jack_set_process_callback (jack_client_t *client, JackProcessCallback process_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the Jack server to call @a freewheel_callback * whenever we enter or leave "freewheel" mode, passing @a * arg as the second argument. The first argument to the * callback will be non-zero if JACK is entering freewheel * mode, and zero otherwise. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code. */ int jack_set_freewheel_callback (jack_client_t *client, JackFreewheelCallback freewheel_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell JACK to call @a bufsize_callback whenever the size of the the * buffer that will be passed to the @a process_callback is about to * change. Clients that depend on knowing the buffer size must supply * a @a bufsize_callback before activating themselves. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @param client pointer to JACK client structure. * @param bufsize_callback function to call when the buffer size changes. * @param arg argument for @a bufsize_callback. * * @return 0 on success, otherwise a non-zero error code */ int jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback bufsize_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the Jack server to call @a srate_callback whenever the system * sample rate changes. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code */ int jack_set_sample_rate_callback (jack_client_t *client, JackSampleRateCallback srate_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the JACK server to call @a client_registration_callback whenever a * client is registered or unregistered, passing @a arg as a parameter. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code */ int jack_set_client_registration_callback (jack_client_t *client, JackClientRegistrationCallback registration_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the JACK server to call @a registration_callback whenever a * port is registered or unregistered, passing @a arg as a parameter. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code */ int jack_set_port_registration_callback (jack_client_t *client, JackPortRegistrationCallback registration_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the JACK server to call @a connect_callback whenever a * port is connected or disconnected, passing @a arg as a parameter. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code */ int jack_set_port_connect_callback (jack_client_t *client, JackPortConnectCallback connect_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the JACK server to call @a rename_callback whenever a * port is renamed, passing @a arg as a parameter. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code */ int jack_set_port_rename_callback (jack_client_t *client, JackPortRenameCallback rename_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the JACK server to call @a graph_callback whenever the * processing graph is reordered, passing @a arg as a parameter. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code */ int jack_set_graph_order_callback (jack_client_t *client, JackGraphOrderCallback graph_callback, void *) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the JACK server to call @a xrun_callback whenever there is a * xrun, passing @a arg as a parameter. * * All "notification events" are received in a separated non RT thread, * the code in the supplied function does not need to be * suitable for real-time execution. * * NOTE: this function cannot be called while the client is activated * (after jack_activate has been called.) * * @return 0 on success, otherwise a non-zero error code */ int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Tell the Jack server to call @a latency_callback whenever it * is necessary to recompute the latencies for some or all * Jack ports. * * @a latency_callback will be called twice each time it is * needed, once being passed JackCaptureLatency and once * JackPlaybackLatency. See @ref LatencyFunctions for * the definition of each type of latency and related functions. * * IMPORTANT: Most JACK clients do NOT need to register a latency * callback. * * Clients that meet any of the following conditions do NOT * need to register a latency callback: * * - have only input ports * - have only output ports * - their output is totally unrelated to their input * - their output is not delayed relative to their input * (i.e. data that arrives in a given process() * callback is processed and output again in the * same callback) * * Clients NOT registering a latency callback MUST also * satisfy this condition: * * - have no multiple distinct internal signal pathways * * This means that if your client has more than 1 input and * output port, and considers them always "correlated" * (e.g. as a stereo pair), then there is only 1 (e.g. stereo) * signal pathway through the client. This would be true, * for example, of a stereo FX rack client that has a * left/right input pair and a left/right output pair. * * However, this is somewhat a matter of perspective. The * same FX rack client could be connected so that its * two input ports were connected to entirely separate * sources. Under these conditions, the fact that the client * does not register a latency callback MAY result * in port latency values being incorrect. * * Clients that do not meet any of those conditions SHOULD * register a latency callback. * * Another case is when a client wants to use * @ref jack_port_get_latency_range(), which only returns meaningful * values when ports get connected and latency values change. * * See the documentation for @ref jack_port_set_latency_range() * on how the callback should operate. Remember that the @a mode * argument given to the latency callback will need to be * passed into @ref jack_port_set_latency_range() * * @return 0 on success, otherwise a non-zero error code */ int jack_set_latency_callback (jack_client_t *client, JackLatencyCallback latency_callback, void *) JACK_WEAK_EXPORT; /**@}*/ /** * @defgroup ServerClientControl Controlling & querying JACK server operation * @{ */ /** * Start/Stop JACK's "freewheel" mode. * * When in "freewheel" mode, JACK no longer waits for * any external event to begin the start of the next process * cycle. * * As a result, freewheel mode causes "faster than realtime" * execution of a JACK graph. If possessed, real-time * scheduling is dropped when entering freewheel mode, and * if appropriate it is reacquired when stopping. * * IMPORTANT: on systems using capabilities to provide real-time * scheduling (i.e. Linux kernel 2.4), if onoff is zero, this function * must be called from the thread that originally called jack_activate(). * This restriction does not apply to other systems (e.g. Linux kernel 2.6 * or OS X). * * @param client pointer to JACK client structure * @param onoff if non-zero, freewheel mode starts. Otherwise * freewheel mode ends. * * @return 0 on success, otherwise a non-zero error code. */ int jack_set_freewheel(jack_client_t* client, int onoff) JACK_OPTIONAL_WEAK_EXPORT; /** * Change the buffer size passed to the @a process_callback. * * This operation stops the JACK engine process cycle, then calls all * registered @a bufsize_callback functions before restarting the * process cycle. This will cause a gap in the audio flow, so it * should only be done at appropriate stopping points. * * @see jack_set_buffer_size_callback() * * @param client pointer to JACK client structure. * @param nframes new buffer size. Must be a power of two. * * @return 0 on success, otherwise a non-zero error code */ int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the sample rate of the jack system, as set by the user when * jackd was started. */ jack_nframes_t jack_get_sample_rate (jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the current maximum size that will ever be passed to the @a * process_callback. It should only be used *before* the client has * been activated. This size may change, clients that depend on it * must register a @a bufsize_callback so they will be notified if it * does. * * @see jack_set_buffer_size_callback() */ jack_nframes_t jack_get_buffer_size (jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT; /** * Old-style interface to become the timebase for the entire JACK * subsystem. * * @deprecated This function still exists for compatibility with the * earlier transport interface, but it does nothing. Instead, see * transport.h and use jack_set_timebase_callback(). * * @return ENOSYS, function not implemented. */ int jack_engine_takeover_timebase (jack_client_t *) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * @return the current CPU load estimated by JACK. This is a running * average of the time it takes to execute a full process cycle for * all clients as a percentage of the real time available per cycle * determined by the buffer size and sample rate. */ float jack_cpu_load (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ /** * @defgroup PortFunctions Creating & manipulating ports * @{ */ /** * Create a new port for the client. This is an object used for moving * data of any type in or out of the client. Ports may be connected * in various ways. * * Each port has a short name. The port's full name contains the name * of the client concatenated with a colon (:) followed by its short * name. The jack_port_name_size() is the maximum length of this full * name. Exceeding that will cause the port registration to fail and * return NULL. * * The @a port_name must be unique among all ports owned by this client. * If the name is not unique, the registration will fail. * * All ports have a type, which may be any non-NULL and non-zero * length string, passed as an argument. Some port types are built * into the JACK API, currently only JACK_DEFAULT_AUDIO_TYPE. * * @param client pointer to JACK client structure. * @param port_name non-empty short name for the new port (not * including the leading @a "client_name:"). Must be unique. * @param port_type port type name. If longer than * jack_port_type_size(), only that many characters are significant. * @param flags @ref JackPortFlags bit mask. * @param buffer_size must be non-zero if this is not a built-in @a * port_type. Otherwise, it is ignored. * * @return jack_port_t pointer on success, otherwise NULL. */ jack_port_t * jack_port_register (jack_client_t *client, const char *port_name, const char *port_type, unsigned long flags, unsigned long buffer_size) JACK_OPTIONAL_WEAK_EXPORT; /** * Remove the port from the client, disconnecting any existing * connections. * * @return 0 on success, otherwise a non-zero error code */ int jack_port_unregister (jack_client_t *client, jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * This returns a pointer to the memory area associated with the * specified port. For an output port, it will be a memory area * that can be written to; for an input port, it will be an area * containing the data from the port's connection(s), or * zero-filled. if there are multiple inbound connections, the data * will be mixed appropriately. * * FOR OUTPUT PORTS ONLY : DEPRECATED in Jack 2.0 !! * --------------------------------------------------- * You may cache the value returned, but only between calls to * your "blocksize" callback. For this reason alone, you should * either never cache the return value or ensure you have * a "blocksize" callback and be sure to invalidate the cached * address from there. * * Caching output ports is DEPRECATED in Jack 2.0, due to some new optimization (like "pipelining"). * Port buffers have to be retrieved in each callback for proper functioning. */ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the UUID of the jack_port_t * * @see jack_uuid_to_string() to convert into a string representation */ jack_uuid_t jack_port_uuid (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the full name of the jack_port_t (including the @a * "client_name:" prefix). * * @see jack_port_name_size(). */ const char * jack_port_name (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the short name of the jack_port_t (not including the @a * "client_name:" prefix). * * @see jack_port_name_size(). */ const char * jack_port_short_name (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the @ref JackPortFlags of the jack_port_t. */ int jack_port_flags (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the @a port type, at most jack_port_type_size() characters * including a final NULL. */ const char * jack_port_type (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the @a port type id. */ jack_port_type_id_t jack_port_type_id (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return TRUE if the jack_port_t belongs to the jack_client_t. */ int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return number of connections to or from @a port. * * @pre The calling client must own @a port. */ int jack_port_connected (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return TRUE if the locally-owned @a port is @b directly connected * to the @a port_name. * * @see jack_port_name_size() */ int jack_port_connected_to (const jack_port_t *port, const char *port_name) JACK_OPTIONAL_WEAK_EXPORT; /** * @return a null-terminated array of full port names to which the @a * port is connected. If none, returns NULL. * * The caller is responsible for calling jack_free() on any non-NULL * returned value. * * @param port locally owned jack_port_t pointer. * * @see jack_port_name_size(), jack_port_get_all_connections() */ const char ** jack_port_get_connections (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return a null-terminated array of full port names to which the @a * port is connected. If none, returns NULL. * * The caller is responsible for calling jack_free() on any non-NULL * returned value. * * This differs from jack_port_get_connections() in two important * respects: * * 1) You may not call this function from code that is * executed in response to a JACK event. For example, * you cannot use it in a GraphReordered handler. * * 2) You need not be the owner of the port to get information * about its connections. * * @see jack_port_name_size() */ const char ** jack_port_get_all_connections (const jack_client_t *client, const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * * @deprecated This function will be removed from a future version * of JACK. Do not use it. There is no replacement. It has * turned out to serve essentially no purpose in real-life * JACK clients. */ int jack_port_tie (jack_port_t *src, jack_port_t *dst) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * * @deprecated This function will be removed from a future version * of JACK. Do not use it. There is no replacement. It has * turned out to serve essentially no purpose in real-life * JACK clients. */ int jack_port_untie (jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN * NEW JACK CLIENTS * * Modify a port's short name. May be called at any time. If the * resulting full name (including the @a "client_name:" prefix) is * longer than jack_port_name_size(), it will be truncated. * * @return 0 on success, otherwise a non-zero error code. */ int jack_port_set_name (jack_port_t *port, const char *port_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Modify a port's short name. May NOT be called from a callback handling a server event. * If the resulting full name (including the @a "client_name:" prefix) is * longer than jack_port_name_size(), it will be truncated. * * @return 0 on success, otherwise a non-zero error code. * * This differs from jack_port_set_name() by triggering PortRename notifications to * clients that have registered a port rename handler. */ int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) JACK_OPTIONAL_WEAK_EXPORT; /** * Set @a alias as an alias for @a port. May be called at any time. * If the alias is longer than jack_port_name_size(), it will be truncated. * * After a successful call, and until JACK exits or * @function jack_port_unset_alias() is called, @alias may be * used as a alternate name for the port. * * Ports can have up to two aliases - if both are already * set, this function will return an error. * * @return 0 on success, otherwise a non-zero error code. */ int jack_port_set_alias (jack_port_t *port, const char *alias) JACK_OPTIONAL_WEAK_EXPORT; /** * Remove @a alias as an alias for @a port. May be called at any time. * * After a successful call, @a alias can no longer be * used as a alternate name for the port. * * @return 0 on success, otherwise a non-zero error code. */ int jack_port_unset_alias (jack_port_t *port, const char *alias) JACK_OPTIONAL_WEAK_EXPORT; /** * Get any aliases known for @port. * * @return the number of aliases discovered for the port */ int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) JACK_OPTIONAL_WEAK_EXPORT; /** * If @ref JackPortCanMonitor is set for this @a port, turn input * monitoring on or off. Otherwise, do nothing. */ int jack_port_request_monitor (jack_port_t *port, int onoff) JACK_OPTIONAL_WEAK_EXPORT; /** * If @ref JackPortCanMonitor is set for this @a port_name, turn input * monitoring on or off. Otherwise, do nothing. * * @return 0 on success, otherwise a non-zero error code. * * @see jack_port_name_size() */ int jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff) JACK_OPTIONAL_WEAK_EXPORT; /** * If @ref JackPortCanMonitor is set for a port, this function turns * on input monitoring if it was off, and turns it off if only one * request has been made to turn it on. Otherwise it does nothing. * * @return 0 on success, otherwise a non-zero error code */ int jack_port_ensure_monitor (jack_port_t *port, int onoff) JACK_OPTIONAL_WEAK_EXPORT; /** * @return TRUE if input monitoring has been requested for @a port. */ int jack_port_monitoring_input (jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * Establish a connection between two ports. * * When a connection exists, data written to the source port will * be available to be read at the destination port. * * @pre The port types must be identical. * * @pre The @ref JackPortFlags of the @a source_port must include @ref * JackPortIsOutput. * * @pre The @ref JackPortFlags of the @a destination_port must include * @ref JackPortIsInput. * * @return 0 on success, EEXIST if the connection is already made, * otherwise a non-zero error code */ int jack_connect (jack_client_t *client, const char *source_port, const char *destination_port) JACK_OPTIONAL_WEAK_EXPORT; /** * Remove a connection between two ports. * * @pre The port types must be identical. * * @pre The @ref JackPortFlags of the @a source_port must include @ref * JackPortIsOutput. * * @pre The @ref JackPortFlags of the @a destination_port must include * @ref JackPortIsInput. * * @return 0 on success, otherwise a non-zero error code */ int jack_disconnect (jack_client_t *client, const char *source_port, const char *destination_port) JACK_OPTIONAL_WEAK_EXPORT; /** * Perform the same function as jack_disconnect() using port handles * rather than names. This avoids the name lookup inherent in the * name-based version. * * Clients connecting their own ports are likely to use this function, * while generic connection clients (e.g. patchbays) would use * jack_disconnect(). */ int jack_port_disconnect (jack_client_t *client, jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the maximum number of characters in a full JACK port name * including the final NULL character. This value is a constant. * * A port's full name contains the owning client name concatenated * with a colon (:) followed by its short name and a NULL * character. */ int jack_port_name_size(void) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the maximum number of characters in a JACK port type name * including the final NULL character. This value is a constant. */ int jack_port_type_size(void) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the buffersize of a port of type @arg port_type. * * this function may only be called in a buffer_size callback. */ size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) JACK_WEAK_EXPORT; /**@}*/ /** * @defgroup LatencyFunctions Managing and determining latency * @{ * * The purpose of JACK's latency API is to allow clients to * easily answer two questions: * * - How long has it been since the data read from a port arrived * at the edge of the JACK graph (either via a physical port * or being synthesized from scratch)? * * - How long will it be before the data written to a port arrives * at the edge of a JACK graph? * To help answering these two questions, all JACK ports have two * latency values associated with them, both measured in frames: * * capture latency: how long since the data read from * the buffer of a port arrived at * a port marked with JackPortIsTerminal. * The data will have come from the "outside * world" if the terminal port is also * marked with JackPortIsPhysical, or * will have been synthesized by the client * that owns the terminal port. * * playback latency: how long until the data * written to the buffer of port will reach a port * marked with JackPortIsTerminal. * * Both latencies might potentially have more than one value * because there may be multiple pathways to/from a given port * and a terminal port. Latency is therefore generally * expressed a min/max pair. * * In most common setups, the minimum and maximum latency * are the same, but this design accommodates more complex * routing, and allows applications (and thus users) to * detect cases where routing is creating an anomalous * situation that may either need fixing or more * sophisticated handling by clients that care about * latency. * * See also @ref jack_set_latency_callback for details on how * clients that add latency to the signal path should interact * with JACK to ensure that the correct latency figures are * used. */ /** * The port latency is zero by default. Clients that control * physical hardware with non-zero latency should call this * to set the latency to its correct value. Note that the value * should include any systemic latency present "outside" the * physical hardware controlled by the client. For example, * for a client controlling a digital audio interface connected * to an external digital converter, the latency setting should * include both buffering by the audio interface *and* the converter. * * @deprecated This method will be removed in the next major * release of JACK. It should not be used in new code, and should * be replaced by a latency callback that calls @ref * jack_port_set_latency_range(). */ void jack_port_set_latency (jack_port_t *port, jack_nframes_t) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * return the latency range defined by @a mode for * @a port, in frames. * * See @ref LatencyFunctions for the definition of each latency value. * * This function is best used from callbacks, specifically the latency callback. * Before a port is connected, this returns the default latency: zero. * Therefore it only makes sense to call jack_port_get_latency_range() when * the port is connected, and that gets signalled by the latency callback. * See @ref jack_set_latency_callback() for details. */ void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) JACK_WEAK_EXPORT; /** * set the minimum and maximum latencies defined by * @a mode for @a port, in frames. * * See @ref LatencyFunctions for the definition of each latency value. * * This function should ONLY be used inside a latency * callback. The client should determine the current * value of the latency using @ref jack_port_get_latency_range() * (called using the same mode as @a mode) * and then add some number of frames to that reflects * latency added by the client. * * How much latency a client adds will vary * dramatically. For most clients, the answer is zero * and there is no reason for them to register a latency * callback and thus they should never call this * function. * * More complex clients that take an input signal, * transform it in some way and output the result but * not during the same process() callback will * generally know a single constant value to add * to the value returned by @ref jack_port_get_latency_range(). * * Such clients would register a latency callback (see * @ref jack_set_latency_callback) and must know what input * ports feed which output ports as part of their * internal state. Their latency callback will update * the ports' latency values appropriately. * * A pseudo-code example will help. The @a mode argument to the latency * callback will determine whether playback or capture * latency is being set. The callback will use * @ref jack_port_set_latency_range() as follows: * * \code * jack_latency_range_t range; * if (mode == JackPlaybackLatency) { * foreach input_port in (all self-registered port) { * jack_port_get_latency_range (port_feeding_input_port, JackPlaybackLatency, &range); * range.min += min_delay_added_as_signal_flows_from port_feeding to input_port; * range.max += max_delay_added_as_signal_flows_from port_feeding to input_port; * jack_port_set_latency_range (input_port, JackPlaybackLatency, &range); * } * } else if (mode == JackCaptureLatency) { * foreach output_port in (all self-registered port) { * jack_port_get_latency_range (port_fed_by_output_port, JackCaptureLatency, &range); * range.min += min_delay_added_as_signal_flows_from_output_port_to_fed_by_port; * range.max += max_delay_added_as_signal_flows_from_output_port_to_fed_by_port; * jack_port_set_latency_range (output_port, JackCaptureLatency, &range); * } * } * \endcode * * In this relatively simple pseudo-code example, it is assumed that * each input port or output is connected to only 1 output or input * port respectively. * * If a port is connected to more than 1 other port, then the * range.min and range.max values passed to @ref * jack_port_set_latency_range() should reflect the minimum and * maximum values across all connected ports. * * See the description of @ref jack_set_latency_callback for more * information. */ void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) JACK_WEAK_EXPORT; /** * Request a complete recomputation of all port latencies. This * can be called by a client that has just changed the internal * latency of its port using jack_port_set_latency * and wants to ensure that all signal pathways in the graph * are updated with respect to the values that will be returned * by jack_port_get_total_latency. It allows a client * to change multiple port latencies without triggering a * recompute for each change. * * @return zero for successful execution of the request. non-zero * otherwise. */ int jack_recompute_total_latencies (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the time (in frames) between data being available or * delivered at/to a port, and the time at which it arrived at or is * delivered to the "other side" of the port. E.g. for a physical * audio output port, this is the time between writing to the port and * when the signal will leave the connector. For a physical audio * input port, this is the time between the sound arriving at the * connector and the corresponding frames being readable from the * port. * * @deprecated This method will be removed in the next major * release of JACK. It should not be used in new code, and should * be replaced by jack_port_get_latency_range() in any existing * use cases. */ jack_nframes_t jack_port_get_latency (jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * The maximum of the sum of the latencies in every * connection path that can be drawn between the port and other * ports with the @ref JackPortIsTerminal flag set. * * @deprecated This method will be removed in the next major * release of JACK. It should not be used in new code, and should * be replaced by jack_port_get_latency_range() in any existing * use cases. */ jack_nframes_t jack_port_get_total_latency (jack_client_t *client, jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Request a complete recomputation of a port's total latency. This * can be called by a client that has just changed the internal * latency of its port using jack_port_set_latency * and wants to ensure that all signal pathways in the graph * are updated with respect to the values that will be returned * by jack_port_get_total_latency. * * @return zero for successful execution of the request. non-zero * otherwise. * * @deprecated This method will be removed in the next major * release of JACK. It should not be used in new code, and should * be replaced by jack_recompute_total_latencies() in any existing * use cases. */ int jack_recompute_total_latency (jack_client_t*, jack_port_t* port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /**@}*/ /** * @defgroup PortSearching Looking up ports * @{ */ /** * @param port_name_pattern A regular expression used to select * ports by name. If NULL or of zero length, no selection based * on name will be carried out. * @param type_name_pattern A regular expression used to select * ports by type. If NULL or of zero length, no selection based * on type will be carried out. * @param flags A value used to select ports by their flags. * If zero, no selection based on flags will be carried out. * * @return a NULL-terminated array of ports that match the specified * arguments. The caller is responsible for calling jack_free() any * non-NULL returned value. * * @see jack_port_name_size(), jack_port_type_size() */ const char ** jack_get_ports (jack_client_t *client, const char *port_name_pattern, const char *type_name_pattern, unsigned long flags) JACK_OPTIONAL_WEAK_EXPORT; /** * @return address of the jack_port_t named @a port_name. * * @see jack_port_name_size() */ jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name) JACK_OPTIONAL_WEAK_EXPORT; /** * @return address of the jack_port_t of a @a port_id. */ jack_port_t * jack_port_by_id (jack_client_t *client, jack_port_id_t port_id) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ /** * @defgroup TimeFunctions Handling time * @{ * * JACK time is in units of 'frames', according to the current sample rate. * The absolute value of frame times is meaningless, frame times have meaning * only relative to each other. */ /** * @return the estimated time in frames that has passed since the JACK * server began the current process cycle. */ jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the estimated current time in frames. * This function is intended for use in other threads (not the process * callback). The return value can be compared with the value of * jack_last_frame_time to relate time in other threads to JACK time. */ jack_nframes_t jack_frame_time (const jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the precise time at the start of the current process cycle. * This function may only be used from the process callback, and can * be used to interpret timestamps generated by jack_frame_time() in * other threads with respect to the current process cycle. * * This is the only jack time function that returns exact time: * when used during the process callback it always returns the same * value (until the next process callback, where it will return * that value + nframes, etc). The return value is guaranteed to be * monotonic and linear in this fashion unless an xrun occurs. * If an xrun occurs, clients must check this value again, as time * may have advanced in a non-linear way (e.g. cycles may have been skipped). */ jack_nframes_t jack_last_frame_time (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * This function may only be used from the process callback. * It provides the internal cycle timing information as used by * most of the other time related functions. This allows the * caller to map between frame counts and microseconds with full * precision (i.e. without rounding frame times to integers), * and also provides e.g. the microseconds time of the start of * the current cycle directly (it has to be computed otherwise). * * If the return value is zero, the following information is * provided in the variables pointed to by the arguments: * * current_frames: the frame time counter at the start of the * current cycle, same as jack_last_frame_time(). * current_usecs: the microseconds time at the start of the * current cycle. * next_usecs: the microseconds time of the start of the next * next cycle as computed by the DLL. * period_usecs: the current best estimate of the period time in * microseconds. * * NOTES: * * Because of the types used, all the returned values except period_usecs * are unsigned. In computations mapping between frames and microseconds * *signed* differences are required. The easiest way is to compute those * separately and assign them to the appropriate signed variables, * int32_t for frames and int64_t for usecs. See the implementation of * jack_frames_to_time() and Jack_time_to_frames() for an example. * * Unless there was an xrun, skipped cycles, or the current cycle is the * first after freewheeling or starting Jack, the value of current_usecs * will always be the value of next_usecs of the previous cycle. * * The value of period_usecs will in general NOT be exactly equal to * the difference of next_usecs and current_usecs. This is because to * ensure stability of the DLL and continuity of the mapping, a fraction * of the loop error must be included in next_usecs. For an accurate * mapping between frames and microseconds, the difference of next_usecs * and current_usecs should be used, and not period_usecs. * * @return zero if OK, non-zero otherwise. */ int jack_get_cycle_times(const jack_client_t *client, jack_nframes_t *current_frames, jack_time_t *current_usecs, jack_time_t *next_usecs, float *period_usecs) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the estimated time in microseconds of the specified frame time */ jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t) JACK_OPTIONAL_WEAK_EXPORT; /** * @return the estimated time in frames for the specified system time. */ jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t) JACK_OPTIONAL_WEAK_EXPORT; /** * @return return JACK's current system time in microseconds, * using the JACK clock source. * * The value returned is guaranteed to be monotonic, but not linear. */ jack_time_t jack_get_time(void) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ /** * @defgroup ErrorOutput Controlling error/information output * @{ */ /** * Display JACK error message. * * Set via jack_set_error_function(), otherwise a JACK-provided * default will print @a msg (plus a newline) to stderr. * * @param msg error message text (no newline at end). */ extern void (*jack_error_callback)(const char *msg) JACK_OPTIONAL_WEAK_EXPORT; /** * Set the @ref jack_error_callback for error message display. * Set it to NULL to restore default_jack_error_callback function. * * The JACK library provides two built-in callbacks for this purpose: * default_jack_error_callback() and silent_jack_error_callback(). */ void jack_set_error_function (void (*func)(const char *)) JACK_OPTIONAL_WEAK_EXPORT; /** * Display JACK info message. * * Set via jack_set_info_function(), otherwise a JACK-provided * default will print @a msg (plus a newline) to stdout. * * @param msg info message text (no newline at end). */ extern void (*jack_info_callback)(const char *msg) JACK_OPTIONAL_WEAK_EXPORT; /** * Set the @ref jack_info_callback for info message display. * Set it to NULL to restore default_jack_info_callback function. * * The JACK library provides two built-in callbacks for this purpose: * default_jack_info_callback() and silent_jack_info_callback(). */ void jack_set_info_function (void (*func)(const char *)) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ /** * The free function to be used on memory returned by jack_port_get_connections, * jack_port_get_all_connections, jack_get_ports and jack_get_internal_client_name functions. * This is MANDATORY on Windows when otherwise all nasty runtime version related crashes can occur. * Developers are strongly encouraged to use this function instead of the standard "free" function in new code. * * @param ptr the memory pointer to be deallocated. */ void jack_free(void* ptr) JACK_OPTIONAL_WEAK_EXPORT; #ifdef __cplusplus } #endif #endif /* __jack_h__ */ jack2-1.9.22/common/jack/jslist.h000066400000000000000000000126361436671425200165020ustar00rootroot00000000000000/* Based on gslist.c from glib-1.2.9 (LGPL). Adaption to JACK, Copyright (C) 2002 Kai Vehmanen. - replaced use of gtypes with normal ANSI C types - glib's memory allocation routines replaced with malloc/free calls This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_jslist_h__ #define __jack_jslist_h__ #include #include #ifdef sun #define __inline__ #endif typedef struct _JSList JSList; typedef int (*JCompareFunc) (void* a, void* b); struct _JSList { void *data; JSList *next; }; static __inline__ JSList* jack_slist_alloc (void) { JSList *new_list; new_list = (JSList*)malloc(sizeof(JSList)); if (new_list) { new_list->data = NULL; new_list->next = NULL; } return new_list; } static __inline__ JSList* jack_slist_prepend (JSList* list, void* data) { JSList *new_list; new_list = (JSList*)malloc(sizeof(JSList)); if (new_list) { new_list->data = data; new_list->next = list; } return new_list; } #define jack_slist_next(slist) ((slist) ? (((JSList *)(slist))->next) : NULL) static __inline__ JSList* jack_slist_last (JSList *list) { if (list) { while (list->next) list = list->next; } return list; } static __inline__ JSList* jack_slist_remove_link (JSList *list, JSList *link) { JSList *tmp; JSList *prev; prev = NULL; tmp = list; while (tmp) { if (tmp == link) { if (prev) prev->next = tmp->next; if (list == tmp) list = list->next; tmp->next = NULL; break; } prev = tmp; tmp = tmp->next; } return list; } static __inline__ void jack_slist_free (JSList *list) { while (list) { JSList *next = list->next; free(list); list = next; } } static __inline__ void jack_slist_free_1 (JSList *list) { if (list) { free(list); } } static __inline__ JSList* jack_slist_remove (JSList *list, void *data) { JSList *tmp; JSList *prev; prev = NULL; tmp = list; while (tmp) { if (tmp->data == data) { if (prev) prev->next = tmp->next; if (list == tmp) list = list->next; tmp->next = NULL; jack_slist_free (tmp); break; } prev = tmp; tmp = tmp->next; } return list; } static __inline__ unsigned int jack_slist_length (JSList *list) { unsigned int length; length = 0; while (list) { length++; list = list->next; } return length; } static __inline__ JSList* jack_slist_find (JSList *list, void *data) { while (list) { if (list->data == data) break; list = list->next; } return list; } static __inline__ JSList* jack_slist_copy (JSList *list) { JSList *new_list = NULL; if (list) { JSList *last; new_list = jack_slist_alloc (); new_list->data = list->data; last = new_list; list = list->next; while (list) { last->next = jack_slist_alloc (); last = last->next; last->data = list->data; list = list->next; } } return new_list; } static __inline__ JSList* jack_slist_append (JSList *list, void *data) { JSList *new_list; JSList *last; new_list = jack_slist_alloc (); new_list->data = data; if (list) { last = jack_slist_last (list); last->next = new_list; return list; } else return new_list; } static __inline__ JSList* jack_slist_sort_merge (JSList *l1, JSList *l2, JCompareFunc compare_func) { JSList list, *l; l = &list; while (l1 && l2) { if (compare_func(l1->data, l2->data) < 0) { l = l->next = l1; l1 = l1->next; } else { l = l->next = l2; l2 = l2->next; } } l->next = l1 ? l1 : l2; return list.next; } static __inline__ JSList* jack_slist_sort (JSList *list, JCompareFunc compare_func) { JSList *l1, *l2; if (!list) return NULL; if (!list->next) return list; l1 = list; l2 = list->next; while ((l2 = l2->next) != NULL) { if ((l2 = l2->next) == NULL) break; l1 = l1->next; } l2 = l1->next; l1->next = NULL; return jack_slist_sort_merge (jack_slist_sort (list, compare_func), jack_slist_sort (l2, compare_func), compare_func); } #endif /* __jack_jslist_h__ */ jack2-1.9.22/common/jack/metadata.h000066400000000000000000000257721436671425200167570ustar00rootroot00000000000000/* Copyright (C) 2011-2014 David Robillard Copyright (C) 2013 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file jack/metadata.h * @ingroup publicheader * @brief JACK Metadata API * */ #ifndef __jack_metadata_h__ #define __jack_metadata_h__ #include #ifdef __cplusplus extern "C" { #endif /** * @defgroup Metadata Metadata API. * @{ */ /** * A single property (key:value pair). * * Although there is no semantics imposed on metadata keys and values, it is * much less useful to use it to associate highly structured data with a port * (or client), since this then implies the need for some (presumably * library-based) code to parse the structure and be able to use it. * * The real goal of the metadata API is to be able to tag ports (and clients) * with small amounts of data that is outside of the core JACK API but * nevertheless useful. */ typedef struct { /** The key of this property (URI string). */ const char* key; /** The property value (null-terminated string). */ const char* data; /** * Type of data, either a MIME type or URI. * * If type is NULL or empty, the data is assumed to be a UTF-8 encoded * string (text/plain). The data is a null-terminated string regardless of * type, so values can always be copied, but clients should not try to * interpret values of an unknown type. * * Example values: * - image/png;base64 (base64 encoded PNG image) * - http://www.w3.org/2001/XMLSchema#int (integer) * * Official types are preferred, but clients may use any syntactically * valid MIME type (which start with a type and slash, like "text/..."). * If a URI type is used, it must be a complete absolute URI * (which start with a scheme and colon, like "http:"). */ const char* type; } jack_property_t; /** * Set a property on @p subject. * * See the above documentation for rules about @p subject and @p key. * @param subject The subject to set the property on. * @param key The key of the property. * @param value The value of the property. * @param type The type of the property. See the discussion of * types in the definition of jack_property_t above. * @return 0 on success. */ int jack_set_property(jack_client_t*, jack_uuid_t subject, const char* key, const char* value, const char* type); /** * Get a property on @p subject. * * @param subject The subject to get the property from. * @param key The key of the property. * @param value Set to the value of the property if found, or NULL otherwise. * The caller must free this value with jack_free(). * @param type The type of the property if set, or NULL. See the discussion * of types in the definition of jack_property_t above. * If non-null, the caller must free this value with jack_free(). * * @return 0 on success, -1 if the @p subject has no @p key property. */ int jack_get_property(jack_uuid_t subject, const char* key, char** value, char** type); /** * A description of a subject (a set of properties). */ typedef struct { jack_uuid_t subject; /**< Subject being described. */ uint32_t property_cnt; /**< Number of elements in "properties". */ jack_property_t* properties; /**< Array of properties. */ uint32_t property_size; /**< Private, do not use. */ } jack_description_t; /** * Free a description. * * @param desc a jack_description_t whose associated memory will all be released * @param free_description_itself if non-zero, then @param desc will also be passed to free() */ void jack_free_description (jack_description_t* desc, int free_description_itself); /** * Get a description of @p subject. * @param subject The subject to get all properties of. * @param desc Set to the description of subject if found, or NULL otherwise. * The caller must free this value with jack_free_description(). * @return the number of properties, -1 if no @p subject with any properties exists. */ int jack_get_properties (jack_uuid_t subject, jack_description_t* desc); /** * Get descriptions for all subjects with metadata. * @param descs Set to an array of descriptions. * The caller must free each of these with jack_free_description(), * and the array itself with jack_free(). * @return the number of descriptions, or -1 on error. */ int jack_get_all_properties (jack_description_t** descs); /** * Remove a single property on a subject. * * @param client The JACK client making the request to remove the property. * @param subject The subject to remove the property from. * @param key The key of the property to be removed. * * @return 0 on success, -1 otherwise */ int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key); /** * Remove all properties on a subject. * * @param client The JACK client making the request to remove some properties. * @param subject The subject to remove all properties from. * * @return a count of the number of properties removed, or -1 on error. */ int jack_remove_properties (jack_client_t* client, jack_uuid_t subject); /** * Remove all properties. * * WARNING!! This deletes all metadata managed by a running JACK server. * Data lost cannot be recovered (though it can be recreated by new calls * to jack_set_property()). * * @param client The JACK client making the request to remove all properties * * @return 0 on success, -1 otherwise */ int jack_remove_all_properties (jack_client_t* client); typedef enum { PropertyCreated, PropertyChanged, PropertyDeleted } jack_property_change_t; /** * Prototype for the client supplied function that is called by the * engine anytime a property or properties have been modified. * * Note that when the key is empty, it means all properties have been * modified. This is often used to indicate that the removal of all keys. * * @param subject The subject the change relates to, this can be either a client or port * @param key The key of the modified property (URI string) * @param change Wherever the key has been created, changed or deleted * @param arg pointer to a client supplied structure */ typedef void (*JackPropertyChangeCallback)(jack_uuid_t subject, const char* key, jack_property_change_t change, void* arg); /** * Arrange for @p client to call @p callback whenever a property is created, * changed or deleted. * * @param client the JACK client making the request * @param callback the function to be invoked when a property change occurs * @param arg the argument to be passed to @param callback when it is invoked * * @return 0 success, -1 otherwise. */ int jack_set_property_change_callback (jack_client_t* client, JackPropertyChangeCallback callback, void* arg); /** * A value that identifies what the hardware port is connected to (an external * device of some kind). Possible values might be "E-Piano" or "Master 2 Track". */ extern const char* JACK_METADATA_CONNECTED; /** * The supported event types of an event port. * * This is a kludge around Jack only supporting MIDI, particularly for OSC. * This property is a comma-separated list of event types, currently "MIDI" or * "OSC". If this contains "OSC", the port may carry OSC bundles (first byte * '#') or OSC messages (first byte '/'). Note that the "status byte" of both * OSC events is not a valid MIDI status byte, so MIDI clients that check the * status byte will gracefully ignore OSC messages if the user makes an * inappropriate connection. */ extern const char* JACK_METADATA_EVENT_TYPES; /** * A value that should be shown when attempting to identify the * specific hardware outputs of a client. Typical values might be * "ADAT1", "S/PDIF L" or "MADI 43". */ extern const char* JACK_METADATA_HARDWARE; /** * A value with a MIME type of "image/png;base64" that is an encoding of an * NxN (with 32 < N <= 128) image to be used when displaying a visual * representation of that client or port. */ extern const char* JACK_METADATA_ICON_LARGE; /** * The name of the icon for the subject (typically client). * * This is used for looking up icons on the system, possibly with many sizes or * themes. Icons should be searched for according to the freedesktop Icon * * Theme Specification: * https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html */ extern const char* JACK_METADATA_ICON_NAME; /** * A value with a MIME type of "image/png;base64" that is an encoding of an * NxN (with N <=32) image to be used when displaying a visual representation * of that client or port. */ extern const char* JACK_METADATA_ICON_SMALL; /** * Order for a port. * * This is used to specify the best order to show ports in user interfaces. * The value MUST be an integer. There are no other requirements, so there may * be gaps in the orders for several ports. Applications should compare the * orders of ports to determine their relative order, but must not assign any * other relevance to order values. * * It is encouraged to use http://www.w3.org/2001/XMLSchema#int as the type. */ extern const char* JACK_METADATA_ORDER; /** * A value that should be shown to the user when displaying a port to the user, * unless the user has explicitly overridden that a request to show the port * name, or some other key value. */ extern const char* JACK_METADATA_PRETTY_NAME; /** */ extern const char* JACK_METADATA_PORT_GROUP; /** * The type of an audio signal. * * This property allows audio ports to be tagged with a "meaning". The value * is a simple string. Currently, the only type is "CV", for "control voltage" * ports. Hosts SHOULD be take care to not treat CV ports as audibile and send * their output directly to speakers. In particular, CV ports are not * necessarily periodic at all and may have very high DC. */ extern const char* JACK_METADATA_SIGNAL_TYPE; /** * @} */ #ifdef __cplusplus } /* namespace */ #endif #endif /* __jack_metadata_h__ */ jack2-1.9.22/common/jack/midiport.h000066400000000000000000000153011436671425200170110ustar00rootroot00000000000000/* Copyright (C) 2004 Ian Esten This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __JACK_MIDIPORT_H #define __JACK_MIDIPORT_H #ifdef __cplusplus extern "C" { #endif #include #include #include /** Type for raw event data contained in @ref jack_midi_event_t. */ typedef unsigned char jack_midi_data_t; /** A Jack MIDI event. */ typedef struct _jack_midi_event { jack_nframes_t time; /**< Sample index at which event is valid */ size_t size; /**< Number of bytes of data in \a buffer */ jack_midi_data_t *buffer; /**< Raw MIDI data */ } jack_midi_event_t; /** * @defgroup MIDIAPI Reading and writing MIDI data * @{ */ /** Get number of events in a port buffer. * * @param port_buffer Port buffer from which to retrieve event. * @return number of events inside @a port_buffer */ uint32_t jack_midi_get_event_count(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT; /** Get a MIDI event from an event port buffer. * * Jack MIDI is normalised, the MIDI event returned by this function is * guaranteed to be a complete MIDI event (the status byte will always be * present, and no realtime events will interspered with the event). * * This rule does not apply to System Exclusive MIDI messages * since they can be of arbitrary length. * To maintain smooth realtime operation such events CAN be deliverd * as multiple, non-normalised events. * The maximum size of one event "chunk" depends on the MIDI backend in use. * For example the midiseq driver will create chunks of 256 bytes. * The first SysEx "chunked" event starts with 0xF0 and the last * delivered chunk ends with 0xF7. * To receive the full SysEx message, a caller of jack_midi_event_get() * must concatenate chunks until a chunk ends with 0xF7. * * @param event Event structure to store retrieved event in. * @param port_buffer Port buffer from which to retrieve event. * @param event_index Index of event to retrieve. * @return 0 on success, ENODATA if buffer is empty. */ int jack_midi_event_get(jack_midi_event_t *event, void *port_buffer, uint32_t event_index) JACK_OPTIONAL_WEAK_EXPORT; /** Clear an event buffer. * * This should be called at the beginning of each process cycle before calling * @ref jack_midi_event_reserve or @ref jack_midi_event_write. This * function may not be called on an input port's buffer. * * @param port_buffer Port buffer to clear (must be an output port buffer). */ void jack_midi_clear_buffer(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT; /** Reset an event buffer (from data allocated outside of JACK). * * This should be called at the beginning of each process cycle before calling * @ref jack_midi_event_reserve or @ref jack_midi_event_write. This * function may not be called on an input port's buffer. * * @deprecated Please use jack_midi_clear_buffer(). * * @param port_buffer Port buffer to reset. */ void jack_midi_reset_buffer(void *port_buffer) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** Get the size of the largest event that can be stored by the port. * * This function returns the current space available, taking into account * events already stored in the port. * * @param port_buffer Port buffer to check size of. */ size_t jack_midi_max_event_size(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT; /** Allocate space for an event to be written to an event port buffer. * * Clients are to write the actual event data to be written starting at the * pointer returned by this function. Clients must not write more than * @a data_size bytes into this buffer. Clients must write normalised * MIDI data to the port - no running status and no (1-byte) realtime * messages interspersed with other messages (realtime messages are fine * when they occur on their own, like other messages). * * Events must be written in order, sorted by their sample offsets. * JACK will not sort the events for you, and will refuse to store * out-of-order events. * * @param port_buffer Buffer to write event to. * @param time Sample offset of event. * @param data_size Length of event's raw data in bytes. * @return Pointer to the beginning of the reserved event's data buffer, or * NULL on error (ie not enough space). */ jack_midi_data_t* jack_midi_event_reserve(void *port_buffer, jack_nframes_t time, size_t data_size) JACK_OPTIONAL_WEAK_EXPORT; /** Write an event into an event port buffer. * * This function is simply a wrapper for @ref jack_midi_event_reserve * which writes the event data into the space reserved in the buffer. * * Clients must not write more than * @a data_size bytes into this buffer. Clients must write normalised * MIDI data to the port - no running status and no (1-byte) realtime * messages interspersed with other messages (realtime messages are fine * when they occur on their own, like other messages). * * Events must be written in order, sorted by their sample offsets. * JACK will not sort the events for you, and will refuse to store * out-of-order events. * * @param port_buffer Buffer to write event to. * @param time Sample offset of event. * @param data Message data to be written. * @param data_size Length of @a data in bytes. * @return 0 on success, ENOBUFS if there's not enough space in buffer for event. */ int jack_midi_event_write(void *port_buffer, jack_nframes_t time, const jack_midi_data_t *data, size_t data_size) JACK_OPTIONAL_WEAK_EXPORT; /** Get the number of events that could not be written to @a port_buffer. * * This function returning a non-zero value implies @a port_buffer is full. * Currently the only way this can happen is if events are lost on port mixdown. * * @param port_buffer Port to receive count for. * @returns Number of events that could not be written to @a port_buffer. */ uint32_t jack_midi_get_lost_event_count(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ #ifdef __cplusplus } #endif #endif /* __JACK_MIDIPORT_H */ jack2-1.9.22/common/jack/net.h000066400000000000000000000357431436671425200157640ustar00rootroot00000000000000/* Copyright (C) 2009-2010 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __net_h__ #define __net_h__ #ifdef __cplusplus extern "C" { #endif #include #include #include #define DEFAULT_MULTICAST_IP "225.3.19.154" #define DEFAULT_PORT 19000 #define DEFAULT_MTU 1500 #define MASTER_NAME_SIZE 256 // Possible error codes #define NO_ERROR 0 #define SOCKET_ERROR -1 #define SYNC_PACKET_ERROR -2 #define DATA_PACKET_ERROR -3 #define RESTART_CB_API 1 enum JackNetEncoder { JackFloatEncoder = 0, // samples are transmitted as float JackIntEncoder = 1, // samples are transmitted as 16 bits integer JackCeltEncoder = 2, // samples are transmitted using CELT codec (http://www.celt-codec.org/) JackOpusEncoder = 3, // samples are transmitted using OPUS codec (http://www.opus-codec.org/) }; typedef struct { int audio_input; // from master or to slave (-1 to take master audio physical inputs) int audio_output; // to master or from slave (-1 to take master audio physical outputs) int midi_input; // from master or to slave (-1 to take master MIDI physical inputs) int midi_output; // to master or from slave (-1 to take master MIDI physical outputs) int mtu; // network Maximum Transmission Unit int time_out; // in second, -1 means infinite int encoder; // encoder type (one of JackNetEncoder) int kbps; // KB per second for CELT or OPUS codec int latency; // network latency in number of buffers } jack_slave_t; typedef struct { int audio_input; // master audio physical outputs (-1 to take slave wanted audio inputs) int audio_output; // master audio physical inputs (-1 to take slave wanted audio outputs) int midi_input; // master MIDI physical outputs (-1 to take slave wanted MIDI inputs) int midi_output; // master MIDI physical inputs (-1 to take slave wanted MIDI outputs) jack_nframes_t buffer_size; // master buffer size jack_nframes_t sample_rate; // master sample rate char master_name[MASTER_NAME_SIZE]; // master machine name int time_out; // in second, -1 means infinite int partial_cycle; // if 'true', partial buffers will be used } jack_master_t; /** * jack_net_slave_t is an opaque type. You may only access it using the * API provided. */ typedef struct _jack_net_slave jack_net_slave_t; /** * Open a network connection with the master machine. * * @param ip the multicast address of the master * @param port the connection port * @param name the JACK client name * @param request a connection request structure * @param result a connection result structure * * @return Opaque net handle if successful or NULL in case of error. */ jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result); /** * Close the network connection with the master machine. * * @param net the network connection to be closed * * @return 0 on success, otherwise a non-zero error code */ int jack_net_slave_close(jack_net_slave_t* net); /** * Prototype for Process callback. * * @param nframes buffer size * @param audio_input number of audio inputs * @param audio_input_buffer an array of audio input buffers (from master) * @param midi_input number of MIDI inputs * @param midi_input_buffer an array of MIDI input buffers (from master) * @param audio_output number of audio outputs * @param audio_output_buffer an array of audio output buffers (to master) * @param midi_output number of MIDI outputs * @param midi_output_buffer an array of MIDI output buffers (to master) * @param arg pointer to a client supplied structure supplied by jack_set_net_process_callback() * * @return zero on success, non-zero on error */ typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, void* data); /** * Set network process callback. * * @param net the network connection * @param net_callback the process callback * @param arg pointer to a client supplied structure * * @return 0 on success, otherwise a non-zero error code */ int jack_set_net_slave_process_callback(jack_net_slave_t * net, JackNetSlaveProcessCallback net_callback, void *arg); /** * Start processing thread, the net_callback will start to be called. * * @param net the network connection * * @return 0 on success, otherwise a non-zero error code */ int jack_net_slave_activate(jack_net_slave_t* net); /** * Stop processing thread. * * @param net the network connection * * @return 0 on success, otherwise a non-zero error code */ int jack_net_slave_deactivate(jack_net_slave_t* net); /** * Test if slave is still active. * * @param net the network connection * * @return a boolean */ int jack_net_slave_is_active(jack_net_slave_t* net); /** * Prototype for BufferSize callback. * * @param nframes buffer size * @param arg pointer to a client supplied structure supplied by jack_set_net_buffer_size_callback() * * @return zero on success, non-zero on error */ typedef int (*JackNetSlaveBufferSizeCallback)(jack_nframes_t nframes, void *arg); /** * Set network buffer size callback. * * @param net the network connection * @param bufsize_callback the buffer size callback * @param arg pointer to a client supplied structure * * @return 0 on success, otherwise a non-zero error code */ int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg); /** * Prototype for SampleRate callback. * * @param nframes sample rate * @param arg pointer to a client supplied structure supplied by jack_set_net_sample_rate_callback() * * @return zero on success, non-zero on error */ typedef int (*JackNetSlaveSampleRateCallback)(jack_nframes_t nframes, void *arg); /** * Set network sample rate callback. * * @param net the network connection * @param samplerate_callback the sample rate callback * @param arg pointer to a client supplied structure * * @return 0 on success, otherwise a non-zero error code */ int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg); /** * Prototype for server Shutdown callback (if not set, the client will just restart, waiting for an available master again). * * @param arg pointer to a client supplied structure supplied by jack_set_net_shutdown_callback() */ typedef void (*JackNetSlaveShutdownCallback)(void* arg); /** * Set network shutdown callback. * * @param net the network connection * @param shutdown_callback the shutdown callback * @param arg pointer to a client supplied structure * * @return 0 on success, otherwise a non-zero error code */ int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Prototype for server Restart callback : this is the new preferable way to be notified when the master has disappeared. * The client may want to retry connecting a certain number of time (which will be done using the time_out value given in jack_net_slave_open) * by returning 0. Otherwise returning a non-zero error code will definively close the connection * (and jack_net_slave_is_active will later on return false). * If both Shutdown and Restart are supplied, Restart callback will be used. * * @param arg pointer to a client supplied structure supplied by jack_set_net_restart_callback() * * @return 0 on success, otherwise a non-zero error code */ typedef int (*JackNetSlaveRestartCallback)(void* arg); /** * Set network restart callback. * * @param net the network connection * @param restart_callback the shutdown callback * @param arg pointer to a client supplied structure * * @return 0 on success, otherwise a non-zero error code */ int jack_set_net_slave_restart_callback(jack_net_slave_t *net, JackNetSlaveRestartCallback restart_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Prototype for server Error callback. * * @param error_code an error code (see "Possible error codes") * @param arg pointer to a client supplied structure supplied by jack_set_net_error_callback() */ typedef void (*JackNetSlaveErrorCallback) (int error_code, void* arg); /** * Set error restart callback. * * @param net the network connection * @param error_callback the error callback * @param arg pointer to a client supplied structure * * @return 0 on success, otherwise a non-zero error code */ int jack_set_net_slave_error_callback(jack_net_slave_t *net, JackNetSlaveErrorCallback error_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * jack_net_master_t is an opaque type, you may only access it using the API provided. */ typedef struct _jack_net_master jack_net_master_t; /** * Open a network connection with the slave machine. * * @param ip the multicast address of the master * @param port the connection port * @param request a connection request structure * @param result a connection result structure * * @return Opaque net handle if successful or NULL in case of error. */ jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result); /** * Close the network connection with the slave machine. * * @param net the network connection to be closed * * @return 0 on success, otherwise a non-zero error code */ int jack_net_master_close(jack_net_master_t* net); /** * Receive sync and data from the network (complete buffer). * * @param net the network connection * @param audio_input number of audio inputs * @param audio_input_buffer an array of audio input buffers * @param midi_input number of MIDI inputs * @param midi_input_buffer an array of MIDI input buffers * * @return zero on success, non-zero on error */ int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer); /** * Receive sync and data from the network (incomplete buffer). * * @param net the network connection * @param audio_input number of audio inputs * @param audio_input_buffer an array of audio input buffers * @param midi_input number of MIDI inputs * @param midi_input_buffer an array of MIDI input buffers * @param frames the number of frames to receive * * @return zero on success, non-zero on error */ int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames); /** * Send sync and data to the network (complete buffer). * * @param net the network connection * @param audio_output number of audio outputs * @param audio_output_buffer an array of audio output buffers * @param midi_output number of MIDI outputs * @param midi_output_buffer an array of MIDI output buffers * * @return zero on success, non-zero on error */ int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer); /** * Send sync and data to the network (incomplete buffer). * * @param net the network connection * @param audio_output number of audio outputs * @param audio_output_buffer an array of audio output buffers * @param midi_output number of MIDI outputs * @param midi_output_buffer an array of MIDI output buffers * @param frames the number of frames to send * * @return zero on success, non-zero on error */ int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames); // Experimental Adapter API /** * jack_adapter_t is an opaque type, you may only access it using the API provided. */ typedef struct _jack_adapter jack_adapter_t; /** * Create an adapter. * * @param input number of audio inputs * @param output of audio outputs * @param host_buffer_size the host buffer size in frames * @param host_sample_rate the host buffer sample rate * @param adapted_buffer_size the adapted buffer size in frames * @param adapted_sample_rate the adapted buffer sample rate * * @return 0 on success, otherwise a non-zero error code */ jack_adapter_t* jack_create_adapter(int input, int output, jack_nframes_t host_buffer_size, jack_nframes_t host_sample_rate, jack_nframes_t adapted_buffer_size, jack_nframes_t adapted_sample_rate); /** * Destroy an adapter. * * @param adapter the adapter to be destroyed * * @return 0 on success, otherwise a non-zero error code */ int jack_destroy_adapter(jack_adapter_t* adapter); /** * Flush internal state of an adapter. * * @param adapter the adapter to be flushed * * @return 0 on success, otherwise a non-zero error code */ void jack_flush_adapter(jack_adapter_t* adapter); /** * Push input to and pull output from adapter ringbuffer. * * @param adapter the adapter * @param input an array of audio input buffers * @param output an array of audio output buffers * @param frames number of frames * * @return 0 on success, otherwise a non-zero error code */ int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); /** * Pull input from and push output to adapter ringbuffer. * * @param adapter the adapter * @param input an array of audio input buffers * @param output an array of audio output buffers * @param frames number of frames * * @return 0 on success, otherwise a non-zero error code */ int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames); #ifdef __cplusplus } #endif #endif /* __net_h__ */ jack2-1.9.22/common/jack/ringbuffer.h000066400000000000000000000174201436671425200173170ustar00rootroot00000000000000/* Copyright (C) 2000 Paul Davis Copyright (C) 2003 Rohan Drape This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _RINGBUFFER_H #define _RINGBUFFER_H #ifdef __cplusplus extern "C" { #endif #include /** @file ringbuffer.h * * A set of library functions to make lock-free ringbuffers available * to JACK clients. The `capture_client.c' (in the example_clients * directory) is a fully functioning user of this API. * * The key attribute of a ringbuffer is that it can be safely accessed * by two threads simultaneously -- one reading from the buffer and * the other writing to it -- without using any synchronization or * mutual exclusion primitives. For this to work correctly, there can * only be a single reader and a single writer thread. Their * identities cannot be interchanged. */ typedef struct { char *buf; size_t len; } jack_ringbuffer_data_t ; typedef struct { char *buf; size_t write_ptr; size_t read_ptr; size_t size; size_t size_mask; int mlocked; } jack_ringbuffer_t ; /** * Allocates a ringbuffer data structure of a specified size. The * caller must arrange for a call to jack_ringbuffer_free() to release * the memory associated with the ringbuffer. * * @param sz the ringbuffer size in bytes. * * @return a pointer to a new jack_ringbuffer_t, if successful; NULL * otherwise. */ jack_ringbuffer_t *jack_ringbuffer_create(size_t sz); /** * Frees the ringbuffer data structure allocated by an earlier call to * jack_ringbuffer_create(). * * @param rb a pointer to the ringbuffer structure. */ void jack_ringbuffer_free(jack_ringbuffer_t *rb); /** * Fill a data structure with a description of the current readable * data held in the ringbuffer. This description is returned in a two * element array of jack_ringbuffer_data_t. Two elements are needed * because the data to be read may be split across the end of the * ringbuffer. * * The first element will always contain a valid @a len field, which * may be zero or greater. If the @a len field is non-zero, then data * can be read in a contiguous fashion using the address given in the * corresponding @a buf field. * * If the second element has a non-zero @a len field, then a second * contiguous stretch of data can be read from the address given in * its corresponding @a buf field. * * @param rb a pointer to the ringbuffer structure. * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t. * */ void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); /** * Fill a data structure with a description of the current writable * space in the ringbuffer. The description is returned in a two * element array of jack_ringbuffer_data_t. Two elements are needed * because the space available for writing may be split across the end * of the ringbuffer. * * The first element will always contain a valid @a len field, which * may be zero or greater. If the @a len field is non-zero, then data * can be written in a contiguous fashion using the address given in * the corresponding @a buf field. * * If the second element has a non-zero @a len field, then a second * contiguous stretch of data can be written to the address given in * the corresponding @a buf field. * * @param rb a pointer to the ringbuffer structure. * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t. */ void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); /** * Read data from the ringbuffer. * * @param rb a pointer to the ringbuffer structure. * @param dest a pointer to a buffer where data read from the * ringbuffer will go. * @param cnt the number of bytes to read. * * @return the number of bytes read, which may range from 0 to cnt. */ size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt); /** * Read data from the ringbuffer. Opposed to jack_ringbuffer_read() * this function does not move the read pointer. Thus it's * a convenient way to inspect data in the ringbuffer in a * continuous fashion. The price is that the data is copied * into a user provided buffer. For "raw" non-copy inspection * of the data in the ringbuffer use jack_ringbuffer_get_read_vector(). * * @param rb a pointer to the ringbuffer structure. * @param dest a pointer to a buffer where data read from the * ringbuffer will go. * @param cnt the number of bytes to read. * * @return the number of bytes read, which may range from 0 to cnt. */ size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt); /** * Advance the read pointer. * * After data have been read from the ringbuffer using the pointers * returned by jack_ringbuffer_get_read_vector(), use this function to * advance the buffer pointers, making that space available for future * write operations. * * @param rb a pointer to the ringbuffer structure. * @param cnt the number of bytes read. */ void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt); /** * Return the number of bytes available for reading. * * @param rb a pointer to the ringbuffer structure. * * @return the number of bytes available to read. */ size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb); /** * Lock a ringbuffer data block into memory. * * Uses the mlock() system call. This is not a realtime operation. * * @param rb a pointer to the ringbuffer structure. */ int jack_ringbuffer_mlock(jack_ringbuffer_t *rb); /** * Reset the read and write pointers, making an empty buffer. * * This is not thread safe. * * @param rb a pointer to the ringbuffer structure. */ void jack_ringbuffer_reset(jack_ringbuffer_t *rb); /** * Reset the internal "available" size, and read and write pointers, making an empty buffer. * * This is not thread safe. * * @param rb a pointer to the ringbuffer structure. * @param sz the new size, that must be less than allocated size. */ void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz); /** * Write data into the ringbuffer. * * @param rb a pointer to the ringbuffer structure. * @param src a pointer to the data to be written to the ringbuffer. * @param cnt the number of bytes to write. * * @return the number of bytes write, which may range from 0 to cnt */ size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, size_t cnt); /** * Advance the write pointer. * * After data have been written the ringbuffer using the pointers * returned by jack_ringbuffer_get_write_vector(), use this function * to advance the buffer pointer, making the data available for future * read operations. * * @param rb a pointer to the ringbuffer structure. * @param cnt the number of bytes written. */ void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt); /** * Return the number of bytes available for writing. * * @param rb a pointer to the ringbuffer structure. * * @return the amount of free space (in bytes) available for writing. */ size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb); #ifdef __cplusplus } #endif #endif jack2-1.9.22/common/jack/session.h000066400000000000000000000227411436671425200166530ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004 Jack O'Quin Copyright (C) 2010 Torben Hohn This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_session_h__ #define __jack_session_h__ #ifdef __cplusplus extern "C" { #endif #include #include /** * @defgroup SessionClientFunctions Session API for clients. * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. * See https://new-session-manager.jackaudio.org/ * @{ */ /** * Session event type. * * If a client can't save templates, i might just do a normal save. * * There is no "quit without saving" event because a client might refuse to * quit when it has unsaved data, but other clients may have already quit. * This results in too much confusion, so it is unsupported. */ enum JackSessionEventType { /** * Save the session completely. * * The client may save references to data outside the provided directory, * but it must do so by creating a link inside the provided directory and * referring to that in any save files. The client must not refer to data * files outside the provided directory directly in save files, because * this makes it impossible for the session manager to create a session * archive for distribution or archival. */ JackSessionSave = 1, /** * Save the session completely, then quit. * * The rules for saving are exactly the same as for JackSessionSave. */ JackSessionSaveAndQuit = 2, /** * Save a session template. * * A session template is a "skeleton" of the session, but without any data. * Clients must save a session that, when restored, will create the same * ports as a full save would have. However, the actual data contained in * the session may not be saved (e.g. a DAW would create the necessary * tracks, but not save the actual recorded data). */ JackSessionSaveTemplate = 3 }; typedef enum JackSessionEventType jack_session_event_type_t; /** * @ref jack_session_flags_t bits */ enum JackSessionFlags { /** * An error occurred while saving. */ JackSessionSaveError = 0x01, /** * Client needs to be run in a terminal. */ JackSessionNeedTerminal = 0x02 }; /** * Session flags. */ typedef enum JackSessionFlags jack_session_flags_t; struct _jack_session_event { /** * The type of this session event. */ jack_session_event_type_t type; /** * Session directory path, with trailing separator. * * This directory is exclusive to the client; when saving the client may * create any files it likes in this directory. */ const char *session_dir; /** * Client UUID which must be passed to jack_client_open on session load. * * The client can specify this in the returned command line, or save it * in a state file within the session directory. */ const char *client_uuid; /** * Reply (set by client): the command line needed to restore the client. * * This is a platform dependent command line. It must contain * ${SESSION_DIR} instead of the actual session directory path. More * generally, just as in session files, clients should not include any * paths outside the session directory here as this makes * archival/distribution impossible. * * This field is set to NULL by Jack when the event is delivered to the * client. The client must set to allocated memory that is safe to * free(). This memory will be freed by jack_session_event_free. */ char *command_line; /** * Reply (set by client): Session flags. */ jack_session_flags_t flags; /** * Future flags. Set to zero for now. */ uint32_t future; }; typedef struct _jack_session_event jack_session_event_t; /** * Prototype for the client supplied function that is called * whenever a session notification is sent via jack_session_notify(). * * Ownership of the memory of @a event is passed to the application. * It must be freed using jack_session_event_free when its not used anymore. * * The client must promptly call jack_session_reply for this event. * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. * See https://github.com/linuxaudio/new-session-manager * * @param event The event structure. * @param arg Pointer to a client supplied structure. */ typedef void (*JackSessionCallback)(jack_session_event_t *event, void *arg); /** * Tell the JACK server to call @a session_callback when a session event * is to be delivered. * * setting more than one session_callback per process is probably a design * error. if you have a multiclient application its more sensible to create * a jack_client with only a session callback set. * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. * See https://github.com/linuxaudio/new-session-manager * * @return 0 on success, otherwise a non-zero error code */ int jack_set_session_callback (jack_client_t *client, JackSessionCallback session_callback, void *arg) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Reply to a session event. * * This can either be called directly from the callback, or later from a * different thread. For example, it is possible to push the event through a * queue and execute the save code from the GUI thread. * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. * See https://github.com/linuxaudio/new-session-manager * * @return 0 on success, otherwise a non-zero error code */ int jack_session_reply (jack_client_t *client, jack_session_event_t *event) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Free memory used by a jack_session_event_t. * * This also frees the memory used by the command_line pointer, if its non NULL. * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. * See https://github.com/linuxaudio/new-session-manager */ void jack_session_event_free (jack_session_event_t *event) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Get the assigned uuid for client. * Safe to call from callback and all other threads. * * The caller is responsible for calling jack_free(3) on any non-NULL * returned value. */ char *jack_client_get_uuid (jack_client_t *client) JACK_WEAK_EXPORT; /** * @} */ /** * @defgroup JackSessionManagerAPI API for a session manager. * * @{ */ typedef struct { const char *uuid; const char *client_name; const char *command; jack_session_flags_t flags; } jack_session_command_t; /** * Send an event to all clients listening for session callbacks. * * The returned strings of the clients are accumulated and returned as an array * of jack_session_command_t. its terminated by ret[i].uuid == NULL target == * NULL means send to all interested clients. otherwise a clientname */ jack_session_command_t *jack_session_notify ( jack_client_t* client, const char *target, jack_session_event_type_t type, const char *path) JACK_WEAK_EXPORT; /** * Free the memory allocated by a session command. * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. * See https://github.com/linuxaudio/new-session-manager */ void jack_session_commands_free (jack_session_command_t *cmds) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * Reserve a client name and associate it with a UUID. * * When a client later calls jack_client_open() and specifies the UUID, jackd * will assign the reserved name. This allows a session manager to know in * advance under which client name its managed clients will appear. * * @return 0 on success, otherwise a non-zero error code */ int jack_reserve_client_name (jack_client_t *client, const char *name, const char *uuid) JACK_WEAK_EXPORT; /** * Find out whether a client has set up a session callback. * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. * See https://github.com/linuxaudio/new-session-manager * * @return 0 when the client has no session callback, 1 when it has one. * -1 on error. */ int jack_client_has_session_callback (jack_client_t *client, const char *client_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; /** * @} */ #ifdef __cplusplus } #endif #endif jack2-1.9.22/common/jack/statistics.h000066400000000000000000000034541436671425200173620ustar00rootroot00000000000000/* * Copyright (C) 2004 Rui Nuno Capela, Lee Revell * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef __statistics_h__ #define __statistics_h__ #ifdef __cplusplus extern "C" { #endif #include /** * @return the maximum delay reported by the backend since * startup or reset. When compared to the period size in usecs, this * can be used to estimate the ideal period size for a given setup. */ float jack_get_max_delayed_usecs (jack_client_t *client); /** * @return the delay in microseconds due to the most recent XRUN * occurrence. This probably only makes sense when called from a @ref * JackXRunCallback defined using jack_set_xrun_callback(). */ float jack_get_xrun_delayed_usecs (jack_client_t *client); /** * Reset the maximum delay counter. This would be useful * to estimate the effect that a change to the configuration of a running * system (e.g. toggling kernel preemption) has on the delay * experienced by JACK, without having to restart the JACK engine. */ void jack_reset_max_delayed_usecs (jack_client_t *client); #ifdef __cplusplus } #endif #endif /* __statistics_h__ */ jack2-1.9.22/common/jack/systemdeps.h000066400000000000000000000116071436671425200173670ustar00rootroot00000000000000/* Copyright (C) 2004-2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __jack_systemdeps_h__ #define __jack_systemdeps_h__ #ifndef POST_PACKED_STRUCTURE #ifdef __GNUC__ /* POST_PACKED_STRUCTURE needs to be a macro which expands into a compiler directive. The directive must tell the compiler to arrange the preceding structure declaration so that it is packed on byte-boundaries rather than use the natural alignment of the processor and/or compiler. */ #define PRE_PACKED_STRUCTURE #define POST_PACKED_STRUCTURE __attribute__((__packed__)) #else #ifdef _MSC_VER #define PRE_PACKED_STRUCTURE1 __pragma(pack(push,1)) #define PRE_PACKED_STRUCTURE PRE_PACKED_STRUCTURE1 /* PRE_PACKED_STRUCTURE needs to be a macro which expands into a compiler directive. The directive must tell the compiler to arrange the following structure declaration so that it is packed on byte-boundaries rather than use the natural alignment of the processor and/or compiler. */ #define POST_PACKED_STRUCTURE ;__pragma(pack(pop)) /* and POST_PACKED_STRUCTURE needs to be a macro which restores the packing to its previous setting */ #else #define PRE_PACKED_STRUCTURE #define POST_PACKED_STRUCTURE #endif /* _MSC_VER */ #endif /* __GNUC__ */ #endif #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(GNU_WIN32) #ifdef __MINGW32__ # include // mingw gives warning if we include windows.h before winsock2.h #endif #include #ifdef _MSC_VER /* Microsoft compiler */ #define __inline__ inline #if (!defined(int8_t) && !defined(_STDINT_H)) #define __int8_t_defined typedef INT8 int8_t; typedef UINT8 uint8_t; typedef INT16 int16_t; typedef UINT16 uint16_t; typedef INT32 int32_t; typedef UINT32 uint32_t; typedef INT64 int64_t; typedef UINT64 uint64_t; #endif #elif __MINGW32__ /* MINGW */ #include #include #else /* other compilers ...*/ #include #include #include #endif #if !defined(_PTHREAD_H) && !defined(PTHREAD_WIN32) /** * to make jack API independent of different thread implementations, * we define jack_native_thread_t to HANDLE here. */ typedef HANDLE jack_native_thread_t; #else #ifdef PTHREAD_WIN32 // Added by JE - 10-10-2011 #include // Makes sure we #include the ptw32 version ! #endif /** * to make jack API independent of different thread implementations, * we define jack_native_thread_t to pthread_t here. */ typedef pthread_t jack_native_thread_t; #endif #endif /* _WIN32 && !__CYGWIN__ && !GNU_WIN32 */ #if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) #if defined(__CYGWIN__) || defined(GNU_WIN32) #include #endif #include #include #include /** * to make jack API independent of different thread implementations, * we define jack_native_thread_t to pthread_t here. */ typedef pthread_t jack_native_thread_t; #endif /* __APPLE__ || __linux__ || __sun__ || sun */ #if (defined(__arm__) || defined(__aarch64__) || defined(__mips__) || defined(__ppc__) || defined(__powerpc__)) && !defined(__APPLE__) #undef POST_PACKED_STRUCTURE #define POST_PACKED_STRUCTURE #endif /* __arm__ || __aarch64__ || __mips__ || __ppc__ || __powerpc__ */ /** define JACK_LIB_EXPORT, useful for internal clients */ #if defined(_WIN32) #define JACK_LIB_EXPORT __declspec(dllexport) #elif defined(__GNUC__) #define JACK_LIB_EXPORT __attribute__((visibility("default"))) #else #define JACK_LIB_EXPORT #endif #endif /* __jack_systemdeps_h__ */ jack2-1.9.22/common/jack/thread.h000066400000000000000000000121251436671425200164320ustar00rootroot00000000000000/* Copyright (C) 2004 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_thread_h__ #define __jack_thread_h__ #ifdef __cplusplus extern "C" { #endif #include #include /* use 512KB stack per thread - the default is way too high to be feasible * with mlockall() on many systems */ #define THREAD_STACK 524288 /** @file thread.h * * Library functions to standardize thread creation for JACK and its * clients. These interfaces hide some system variations in the * handling of realtime scheduling and associated privileges. */ /** * @defgroup ClientThreads Creating and managing client threads * @{ */ /** * @returns if JACK is running with realtime scheduling, this returns * the priority that any JACK-created client threads will run at. * Otherwise returns -1. */ int jack_client_real_time_priority (jack_client_t*) JACK_OPTIONAL_WEAK_EXPORT; /** * @returns if JACK is running with realtime scheduling, this returns * the maximum priority that a JACK client thread should use if the thread * is subject to realtime scheduling. Otherwise returns -1. */ int jack_client_max_real_time_priority (jack_client_t*) JACK_OPTIONAL_WEAK_EXPORT; /** * Attempt to enable realtime scheduling for a thread. On some * systems that may require special privileges. * * @param thread POSIX thread ID. * @param priority requested thread priority. * * @returns 0, if successful; EPERM, if the calling process lacks * required realtime privileges; otherwise some other error number. */ int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority) JACK_OPTIONAL_WEAK_EXPORT; /** * Create a thread for JACK or one of its clients. The thread is * created executing @a start_routine with @a arg as its sole * argument. * * @param client the JACK client for whom the thread is being created. May be * NULL if the client is being created within the JACK server. * @param thread place to return POSIX thread ID. * @param priority thread priority, if realtime. * @param realtime true for the thread to use realtime scheduling. On * some systems that may require special privileges. * @param start_routine function the thread calls when it starts. * @param arg parameter passed to the @a start_routine. * * @returns 0, if successful; otherwise some error number. */ int jack_client_create_thread (jack_client_t* client, jack_native_thread_t *thread, int priority, int realtime, /* boolean */ void *(*start_routine)(void*), void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Drop realtime scheduling for a thread. * * @param thread POSIX thread ID. * * @returns 0, if successful; otherwise an error number. */ int jack_drop_real_time_scheduling (jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT; /** * Stop the thread, waiting for the thread handler to terminate. * * @param thread POSIX thread ID. * * @returns 0, if successful; otherwise an error number. */ int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT; /** * Kill the thread. * * @param thread POSIX thread ID. * * @returns 0, if successful; otherwise an error number. */ int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT; #ifndef _WIN32 typedef int (*jack_thread_creator_t)(pthread_t*, const pthread_attr_t*, void* (*function)(void*), void* arg); /** * This function can be used in very very specialized cases * where it is necessary that client threads created by JACK * are created by something other than pthread_create(). After * it is used, any threads that JACK needs for the client will * will be created by calling the function passed to this * function. * * No normal application/client should consider calling this. * The specific case for which it was created involves running * win32/x86 plugins under Wine on Linux, where it is necessary * that all threads that might call win32 functions are known * to Wine. * * Set it to NULL to restore thread creation function. * * @param creator a function that creates a new thread * */ void jack_set_thread_creator (jack_thread_creator_t creator) JACK_OPTIONAL_WEAK_EXPORT; #endif /**@}*/ #ifdef __cplusplus } #endif #endif /* __jack_thread_h__ */ jack2-1.9.22/common/jack/transport.h000066400000000000000000000207051436671425200172220ustar00rootroot00000000000000/* Copyright (C) 2002 Paul Davis Copyright (C) 2003 Jack O'Quin This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_transport_h__ #define __jack_transport_h__ #ifdef __cplusplus extern "C" { #endif #include #include /** * @defgroup TransportControl Transport and Timebase control * @{ */ /** * Called by the timebase master to release itself from that * responsibility. * * If the timebase master releases the timebase or leaves the JACK * graph for any reason, the JACK engine takes over at the start of * the next process cycle. The transport state does not change. If * rolling, it continues to play, with frame numbers as the only * available position information. * * @see jack_set_timebase_callback * * @param client the JACK client structure. * * @return 0 on success, otherwise a non-zero error code. */ int jack_release_timebase (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * Register (or unregister) as a slow-sync client, one that cannot * respond immediately to transport position changes. * * The @a sync_callback will be invoked at the first available * opportunity after its registration is complete. If the client is * currently active this will be the following process cycle, * otherwise it will be the first cycle after calling jack_activate(). * After that, it runs according to the ::JackSyncCallback rules. * Clients that don't set a @a sync_callback are assumed to be ready * immediately any time the transport wants to start. * * @param client the JACK client structure. * @param sync_callback is a realtime function that returns TRUE when * the client is ready. Setting @a sync_callback to NULL declares that * this client no longer requires slow-sync processing. * @param arg an argument for the @a sync_callback function. * * @return 0 on success, otherwise a non-zero error code. */ int jack_set_sync_callback (jack_client_t *client, JackSyncCallback sync_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Set the timeout value for slow-sync clients. * * This timeout prevents unresponsive slow-sync clients from * completely halting the transport mechanism. The default is two * seconds. When the timeout expires, the transport starts rolling, * even if some slow-sync clients are still unready. The @a * sync_callbacks of these clients continue being invoked, giving them * a chance to catch up. * * @see jack_set_sync_callback * * @param client the JACK client structure. * @param timeout is delay (in microseconds) before the timeout expires. * * @return 0 on success, otherwise a non-zero error code. */ int jack_set_sync_timeout (jack_client_t *client, jack_time_t timeout) JACK_OPTIONAL_WEAK_EXPORT; /** * Register as timebase master for the JACK subsystem. * * The timebase master registers a callback that updates extended * position information such as beats or timecode whenever necessary. * Without this extended information, there is no need for this * function. * * There is never more than one master at a time. When a new client * takes over, the former @a timebase_callback is no longer called. * Taking over the timebase may be done conditionally, so it fails if * there was a master already. * * @param client the JACK client structure. * @param conditional non-zero for a conditional request. * @param timebase_callback is a realtime function that returns * position information. * @param arg an argument for the @a timebase_callback function. * * @return * - 0 on success; * - EBUSY if a conditional request fails because there was already a * timebase master; * - other non-zero error code. */ int jack_set_timebase_callback (jack_client_t *client, int conditional, JackTimebaseCallback timebase_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; /** * Reposition the transport to a new frame number. * * May be called at any time by any client. The new position takes * effect in two process cycles. If there are slow-sync clients and * the transport is already rolling, it will enter the * ::JackTransportStarting state and begin invoking their @a * sync_callbacks until ready. This function is realtime-safe. * * @see jack_transport_reposition, jack_set_sync_callback * * @param client the JACK client structure. * @param frame frame number of new transport position. * * @return 0 if valid request, non-zero otherwise. */ int jack_transport_locate (jack_client_t *client, jack_nframes_t frame) JACK_OPTIONAL_WEAK_EXPORT; /** * Query the current transport state and position. * * This function is realtime-safe, and can be called from any thread. * If called from the process thread, @a pos corresponds to the first * frame of the current cycle and the state returned is valid for the * entire cycle. * * @param client the JACK client structure. * @param pos pointer to structure for returning current transport * position; @a pos->valid will show which fields contain valid data. * If @a pos is NULL, do not return position information. * * @return Current transport state. */ jack_transport_state_t jack_transport_query (const jack_client_t *client, jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT; /** * Return an estimate of the current transport frame, * including any time elapsed since the last transport * positional update. * * @param client the JACK client structure */ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * Request a new transport position. * * May be called at any time by any client. The new position takes * effect in two process cycles. If there are slow-sync clients and * the transport is already rolling, it will enter the * ::JackTransportStarting state and begin invoking their @a * sync_callbacks until ready. This function is realtime-safe. * * @see jack_transport_locate, jack_set_sync_callback * * @param client the JACK client structure. * @param pos requested new transport position. * * @return 0 if valid request, EINVAL if position structure rejected. */ int jack_transport_reposition (jack_client_t *client, const jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT; /** * Start the JACK transport rolling. * * Any client can make this request at any time. It takes effect no * sooner than the next process cycle, perhaps later if there are * slow-sync clients. This function is realtime-safe. * * @see jack_set_sync_callback * * @param client the JACK client structure. */ void jack_transport_start (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * Stop the JACK transport. * * Any client can make this request at any time. It takes effect on * the next process cycle. This function is realtime-safe. * * @param client the JACK client structure. */ void jack_transport_stop (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; /** * Gets the current transport info structure (deprecated). * * @param client the JACK client structure. * @param tinfo current transport info structure. The "valid" field * describes which fields contain valid data. * * @deprecated This is for compatibility with the earlier transport * interface. Use jack_transport_query(), instead. * * @pre Must be called from the process thread. */ void jack_get_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT; /** * Set the transport info structure (deprecated). * * @deprecated This function still exists for compatibility with the * earlier transport interface, but it does nothing. Instead, define * a ::JackTimebaseCallback. */ void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT; /**@}*/ #ifdef __cplusplus } #endif #endif /* __jack_transport_h__ */ jack2-1.9.22/common/jack/types.h000066400000000000000000000607421436671425200163370ustar00rootroot00000000000000/* Copyright (C) 2001 Paul Davis Copyright (C) 2004 Jack O'Quin This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_types_h__ #define __jack_types_h__ #include typedef uint64_t jack_uuid_t; typedef int32_t jack_shmsize_t; /** * Type used to represent sample frame counts. */ typedef uint32_t jack_nframes_t; /** * Maximum value that can be stored in jack_nframes_t */ #define JACK_MAX_FRAMES (4294967295U) /* This should be UINT32_MAX, but C++ has a problem with that. */ /** * Type used to represent the value of free running * monotonic clock with units of microseconds. */ typedef uint64_t jack_time_t; /** * Maximum size of @a load_init string passed to an internal client * jack_initialize() function via jack_internal_client_load(). */ #define JACK_LOAD_INIT_LIMIT 1024 /** * jack_intclient_t is an opaque type representing a loaded internal * client. You may only access it using the API provided in @ref * intclient.h "". */ typedef uint64_t jack_intclient_t; /** * jack_port_t is an opaque type. You may only access it using the * API provided. */ typedef struct _jack_port jack_port_t; /** * jack_client_t is an opaque type. You may only access it using the * API provided. */ typedef struct _jack_client jack_client_t; /** * Ports have unique ids. A port registration callback is the only * place you ever need to know their value. */ typedef uint32_t jack_port_id_t; typedef uint32_t jack_port_type_id_t; /** * @ref jack_options_t bits */ enum JackOptions { /** * Null value to use when no option bits are needed. */ JackNullOption = 0x00, /** * Do not automatically start the JACK server when it is not * already running. This option is always selected if * \$JACK_NO_START_SERVER is defined in the calling process * environment. */ JackNoStartServer = 0x01, /** * Use the exact client name requested. Otherwise, JACK * automatically generates a unique one, if needed. */ JackUseExactName = 0x02, /** * Open with optional (char *) server_name parameter. */ JackServerName = 0x04, /** * Load internal client from optional (char *) * load_name. Otherwise use the @a client_name. */ JackLoadName = 0x08, /** * Pass optional (char *) load_init string to the * jack_initialize() entry point of an internal client. */ JackLoadInit = 0x10, /** * pass a SessionID Token this allows the sessionmanager to identify the client again. */ JackSessionID = 0x20 }; /** Valid options for opening an external client. */ #define JackOpenOptions (JackSessionID|JackServerName|JackNoStartServer|JackUseExactName) /** Valid options for loading an internal client. */ #define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName) /** * Options for several JACK operations, formed by OR-ing together the * relevant @ref JackOptions bits. */ typedef enum JackOptions jack_options_t; /** * @ref jack_status_t bits */ enum JackStatus { /** * Overall operation failed. */ JackFailure = 0x01, /** * The operation contained an invalid or unsupported option. */ JackInvalidOption = 0x02, /** * The desired client name was not unique. With the @ref * JackUseExactName option this situation is fatal. Otherwise, * the name was modified by appending a dash and a two-digit * number in the range "-01" to "-99". The * jack_get_client_name() function will return the exact string * that was used. If the specified @a client_name plus these * extra characters would be too long, the open fails instead. */ JackNameNotUnique = 0x04, /** * The JACK server was started as a result of this operation. * Otherwise, it was running already. In either case the caller * is now connected to jackd, so there is no race condition. * When the server shuts down, the client will find out. */ JackServerStarted = 0x08, /** * Unable to connect to the JACK server. */ JackServerFailed = 0x10, /** * Communication error with the JACK server. */ JackServerError = 0x20, /** * Requested client does not exist. */ JackNoSuchClient = 0x40, /** * Unable to load internal client */ JackLoadFailure = 0x80, /** * Unable to initialize client */ JackInitFailure = 0x100, /** * Unable to access shared memory */ JackShmFailure = 0x200, /** * Client's protocol version does not match */ JackVersionError = 0x400, /** * Backend error */ JackBackendError = 0x800, /** * Client zombified failure */ JackClientZombie = 0x1000 }; /** * Status word returned from several JACK operations, formed by * OR-ing together the relevant @ref JackStatus bits. */ typedef enum JackStatus jack_status_t; /** * @ref jack_latency_callback_mode_t */ enum JackLatencyCallbackMode { /** * Latency Callback for Capture Latency. * Input Ports have their latency value setup. * In the Callback the client needs to set the latency of the output ports */ JackCaptureLatency, /** * Latency Callback for Playback Latency. * Output Ports have their latency value setup. * In the Callback the client needs to set the latency of the input ports */ JackPlaybackLatency }; /** * Type of Latency Callback (Capture or Playback) */ typedef enum JackLatencyCallbackMode jack_latency_callback_mode_t; /** * Prototype for the client supplied function that is called * by the engine when port latencies need to be recalculated * * @param mode playback or capture latency * @param arg pointer to a client supplied data * * @return zero on success, non-zero on error */ typedef void (*JackLatencyCallback)(jack_latency_callback_mode_t mode, void *arg); /** * the new latency API operates on Ranges. */ PRE_PACKED_STRUCTURE struct _jack_latency_range { /** * minimum latency */ jack_nframes_t min; /** * maximum latency */ jack_nframes_t max; } POST_PACKED_STRUCTURE; typedef struct _jack_latency_range jack_latency_range_t; /** * Prototype for the client supplied function that is called * by the engine anytime there is work to be done. * * @pre nframes == jack_get_buffer_size() * @pre nframes == pow(2,x) * * @param nframes number of frames to process * @param arg pointer to a client supplied structure * * @return zero on success, non-zero on error */ typedef int (*JackProcessCallback)(jack_nframes_t nframes, void *arg); /** * Prototype for the client thread routine called * by the engine when the client is inserted in the graph. * * @param arg pointer to a client supplied structure * */ typedef void *(*JackThreadCallback)(void* arg); /** * Prototype for the client supplied function that is called * once after the creation of the thread in which other * callbacks will be made. Special thread characteristics * can be set from this callback, for example. This is a * highly specialized callback and most clients will not * and should not use it. * * @param arg pointer to a client supplied structure * * @return void */ typedef void (*JackThreadInitCallback)(void *arg); /** * Prototype for the client supplied function that is called * whenever the processing graph is reordered. * * @param arg pointer to a client supplied structure * * @return zero on success, non-zero on error */ typedef int (*JackGraphOrderCallback)(void *arg); /** * Prototype for the client-supplied function that is called whenever * an xrun has occurred. * * @see jack_get_xrun_delayed_usecs() * * @param arg pointer to a client supplied structure * * @return zero on success, non-zero on error */ typedef int (*JackXRunCallback)(void *arg); /** * Prototype for the @a bufsize_callback that is invoked whenever the * JACK engine buffer size changes. Although this function is called * in the JACK process thread, the normal process cycle is suspended * during its operation, causing a gap in the audio flow. So, the @a * bufsize_callback can allocate storage, touch memory not previously * referenced, and perform other operations that are not realtime * safe. * * @param nframes buffer size * @param arg pointer supplied by jack_set_buffer_size_callback(). * * @return zero on success, non-zero on error */ typedef int (*JackBufferSizeCallback)(jack_nframes_t nframes, void *arg); /** * Prototype for the client supplied function that is called * when the engine sample rate changes. * * @param nframes new engine sample rate * @param arg pointer to a client supplied structure * * @return zero on success, non-zero on error */ typedef int (*JackSampleRateCallback)(jack_nframes_t nframes, void *arg); /** * Prototype for the client supplied function that is called * whenever a port is registered or unregistered. * * @param port the ID of the port * @param arg pointer to a client supplied data * @param register non-zero if the port is being registered, * zero if the port is being unregistered */ typedef void (*JackPortRegistrationCallback)(jack_port_id_t port, int /* register */, void *arg); /** * Prototype for the client supplied function that is called * whenever a client is registered or unregistered. * * @param name a null-terminated string containing the client name * @param register non-zero if the client is being registered, * zero if the client is being unregistered * @param arg pointer to a client supplied structure */ typedef void (*JackClientRegistrationCallback)(const char* name, int /* register */, void *arg); /** * Prototype for the client supplied function that is called * whenever a port is connected or disconnected. * * @param a one of two ports connected or disconnected * @param b one of two ports connected or disconnected * @param connect non-zero if ports were connected * zero if ports were disconnected * @param arg pointer to a client supplied data */ typedef void (*JackPortConnectCallback)(jack_port_id_t a, jack_port_id_t b, int connect, void* arg); /** * Prototype for the client supplied function that is called * whenever the port name has been changed. * * @param port the port that has been renamed * @param new_name the new name * @param arg pointer to a client supplied structure */ typedef void (*JackPortRenameCallback)(jack_port_id_t port, const char* old_name, const char* new_name, void *arg); /** * Prototype for the client supplied function that is called * whenever jackd starts or stops freewheeling. * * @param starting non-zero if we start starting to freewheel, zero otherwise * @param arg pointer to a client supplied structure */ typedef void (*JackFreewheelCallback)(int starting, void *arg); /** * Prototype for the client supplied function that is called * whenever jackd is shutdown. Note that after server shutdown, * the client pointer is *not* deallocated by libjack, * the application is responsible to properly use jack_client_close() * to release client resources. Warning: jack_client_close() cannot be * safely used inside the shutdown callback and has to be called outside of * the callback context. * * @param arg pointer to a client supplied structure */ typedef void (*JackShutdownCallback)(void *arg); /** * Prototype for the client supplied function that is called * whenever jackd is shutdown. Note that after server shutdown, * the client pointer is *not* deallocated by libjack, * the application is responsible to properly use jack_client_close() * to release client resources. Warning: jack_client_close() cannot be * safely used inside the shutdown callback and has to be called outside of * the callback context. * @param code a status word, formed by OR-ing together the relevant @ref JackStatus bits. * @param reason a string describing the shutdown reason (backend failure, server crash... etc...). * Note that this string will not be available anymore after the callback returns, so possibly copy it. * @param arg pointer to a client supplied structure */ typedef void (*JackInfoShutdownCallback)(jack_status_t code, const char* reason, void *arg); /** * Used for the type argument of jack_port_register() for default * audio ports and midi ports. */ #define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" #define JACK_DEFAULT_MIDI_TYPE "8 bit raw midi" /** * For convenience, use this typedef if you want to be able to change * between float and double. You may want to typedef sample_t to * jack_default_audio_sample_t in your application. */ typedef float jack_default_audio_sample_t; /** * A port has a set of flags that are formed by AND-ing together the * desired values from the list below. The flags "JackPortIsInput" and * "JackPortIsOutput" are mutually exclusive and it is an error to use * them both. */ enum JackPortFlags { /** * if JackPortIsInput is set, then the port can receive * data. */ JackPortIsInput = 0x1, /** * if JackPortIsOutput is set, then data can be read from * the port. */ JackPortIsOutput = 0x2, /** * if JackPortIsPhysical is set, then the port corresponds * to some kind of physical I/O connector. */ JackPortIsPhysical = 0x4, /** * if JackPortCanMonitor is set, then a call to * jack_port_request_monitor() makes sense. * * Precisely what this means is dependent on the client. A typical * result of it being called with TRUE as the second argument is * that data that would be available from an output port (with * JackPortIsPhysical set) is sent to a physical output connector * as well, so that it can be heard/seen/whatever. * * Clients that do not control physical interfaces * should never create ports with this bit set. */ JackPortCanMonitor = 0x8, /** * JackPortIsTerminal means: * * for an input port: the data received by the port * will not be passed on or made * available at any other port * * for an output port: the data available at the port * does not originate from any other port * * Audio synthesizers, I/O hardware interface clients, HDR * systems are examples of clients that would set this flag for * their ports. */ JackPortIsTerminal = 0x10, }; /** * Transport states. */ typedef enum { /* the order matters for binary compatibility */ JackTransportStopped = 0, /**< Transport halted */ JackTransportRolling = 1, /**< Transport playing */ JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */ JackTransportStarting = 3, /**< Waiting for sync ready */ JackTransportNetStarting = 4, /**< Waiting for sync ready on the network*/ } jack_transport_state_t; typedef uint64_t jack_unique_t; /**< Unique ID (opaque) */ /** * Optional struct jack_position_t fields. */ typedef enum { JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ JackPositionTimecode = 0x20, /**< External timecode */ JackBBTFrameOffset = 0x40, /**< Frame offset of BBT information */ JackAudioVideoRatio = 0x80, /**< audio frames per video frame */ JackVideoFrameOffset = 0x100, /**< frame offset of first video frame */ JackTickDouble = 0x200, /**< double-resolution tick */ } jack_position_bits_t; /** all valid position bits */ #define JACK_POSITION_MASK (JackPositionBBT|JackPositionTimecode) #define EXTENDED_TIME_INFO /** transport tick_double member is available for use */ #define JACK_TICK_DOUBLE PRE_PACKED_STRUCTURE struct _jack_position { /* these four cannot be set from clients: the server sets them */ jack_unique_t unique_1; /**< unique ID */ jack_time_t usecs; /**< monotonic, free-rolling */ jack_nframes_t frame_rate; /**< current frame rate (per second) */ jack_nframes_t frame; /**< frame number, always present */ jack_position_bits_t valid; /**< which other fields are valid */ /* JackPositionBBT fields: */ int32_t bar; /**< current bar */ int32_t beat; /**< current beat-within-bar */ int32_t tick; /**< current tick-within-beat */ double bar_start_tick; float beats_per_bar; /**< time signature "numerator" */ float beat_type; /**< time signature "denominator" */ double ticks_per_beat; double beats_per_minute; /* JackPositionTimecode fields: (EXPERIMENTAL: could change) */ double frame_time; /**< current time in seconds */ double next_time; /**< next sequential frame_time (unless repositioned) */ /* JackBBTFrameOffset fields: */ jack_nframes_t bbt_offset; /**< frame offset for the BBT fields (the given bar, beat, and tick values actually refer to a time frame_offset frames before the start of the cycle), should be assumed to be 0 if JackBBTFrameOffset is not set. If JackBBTFrameOffset is set and this value is zero, the BBT time refers to the first frame of this cycle. If the value is positive, the BBT time refers to a frame that many frames before the start of the cycle. */ /* JACK video positional data (experimental) */ float audio_frames_per_video_frame; /**< number of audio frames per video frame. Should be assumed zero if JackAudioVideoRatio is not set. If JackAudioVideoRatio is set and the value is zero, no video data exists within the JACK graph */ jack_nframes_t video_offset; /**< audio frame at which the first video frame in this cycle occurs. Should be assumed to be 0 if JackVideoFrameOffset is not set. If JackVideoFrameOffset is set, but the value is zero, there is no video frame within this cycle. */ /* JACK extra transport fields */ double tick_double; /**< current tick-within-beat in double resolution. Should be assumed zero if JackTickDouble is not set. Since older versions of JACK do not expose this variable, the macro JACK_TICK_DOUBLE is provided, which can be used as build-time detection. */ /* For binary compatibility, new fields should be allocated from * this padding area with new valid bits controlling access, so * the existing structure size and offsets are preserved. */ int32_t padding[5]; /* When (unique_1 == unique_2) the contents are consistent. */ jack_unique_t unique_2; /**< unique ID */ } POST_PACKED_STRUCTURE; typedef struct _jack_position jack_position_t; /** * Prototype for the @a sync_callback defined by slow-sync clients. * When the client is active, this callback is invoked just before * process() in the same thread. This occurs once after registration, * then subsequently whenever some client requests a new position, or * the transport enters the ::JackTransportStarting state. This * realtime function must not wait. * * The transport @a state will be: * * - ::JackTransportStopped when a new position is requested; * - ::JackTransportStarting when the transport is waiting to start; * - ::JackTransportRolling when the timeout has expired, and the * position is now a moving target. * * @param state current transport state. * @param pos new transport position. * @param arg the argument supplied by jack_set_sync_callback(). * * @return TRUE (non-zero) when ready to roll. */ typedef int (*JackSyncCallback)(jack_transport_state_t state, jack_position_t *pos, void *arg); /** * Prototype for the @a timebase_callback used to provide extended * position information. Its output affects all of the following * process cycle. This realtime function must not wait. * * This function is called immediately after process() in the same * thread whenever the transport is rolling, or when any client has * requested a new position in the previous cycle. The first cycle * after jack_set_timebase_callback() is also treated as a new * position, or the first cycle after jack_activate() if the client * had been inactive. * * The timebase master may not use its @a pos argument to set @a * pos->frame. To change position, use jack_transport_reposition() or * jack_transport_locate(). These functions are realtime-safe, the @a * timebase_callback can call them directly. * * @param state current transport state. * @param nframes number of frames in current period. * @param pos address of the position structure for the next cycle; @a * pos->frame will be its frame number. If @a new_pos is FALSE, this * structure contains extended position information from the current * cycle. If TRUE, it contains whatever was set by the requester. * The @a timebase_callback's task is to update the extended * information here. * @param new_pos TRUE (non-zero) for a newly requested @a pos, or for * the first cycle after the @a timebase_callback is defined. * @param arg the argument supplied by jack_set_timebase_callback(). */ typedef void (*JackTimebaseCallback)(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *arg); /********************************************************************* * The following interfaces are DEPRECATED. They are only provided * for compatibility with the earlier JACK transport implementation. *********************************************************************/ /** * Optional struct jack_transport_info_t fields. * * @see jack_position_bits_t. */ typedef enum { JackTransportState = 0x1, /**< Transport state */ JackTransportPosition = 0x2, /**< Frame number */ JackTransportLoop = 0x4, /**< Loop boundaries (ignored) */ JackTransportSMPTE = 0x8, /**< SMPTE (ignored) */ JackTransportBBT = 0x10 /**< Bar, Beat, Tick */ } jack_transport_bits_t; /** * Deprecated struct for transport position information. * * @deprecated This is for compatibility with the earlier transport * interface. Use the jack_position_t struct, instead. */ typedef struct { /* these two cannot be set from clients: the server sets them */ jack_nframes_t frame_rate; /**< current frame rate (per second) */ jack_time_t usecs; /**< monotonic, free-rolling */ jack_transport_bits_t valid; /**< which fields are legal to read */ jack_transport_state_t transport_state; jack_nframes_t frame; jack_nframes_t loop_start; jack_nframes_t loop_end; long smpte_offset; /**< SMPTE offset (from frame 0) */ float smpte_frame_rate; /**< 29.97, 30, 24 etc. */ int bar; int beat; int tick; double bar_start_tick; float beats_per_bar; float beat_type; double ticks_per_beat; double beats_per_minute; } jack_transport_info_t; #endif /* __jack_types_h__ */ jack2-1.9.22/common/jack/uuid.h000066400000000000000000000031551436671425200161340ustar00rootroot00000000000000/* Copyright (C) 2013 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_uuid_h__ #define __jack_uuid_h__ #include #ifdef __cplusplus extern "C" { #endif #define JACK_UUID_SIZE 36 #define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1) /* includes trailing null */ #define JACK_UUID_EMPTY_INITIALIZER 0 extern jack_uuid_t jack_client_uuid_generate (); extern jack_uuid_t jack_port_uuid_generate (uint32_t port_id); extern uint32_t jack_uuid_to_index (jack_uuid_t); extern int jack_uuid_compare (jack_uuid_t, jack_uuid_t); extern void jack_uuid_copy (jack_uuid_t* dst, jack_uuid_t src); extern void jack_uuid_clear (jack_uuid_t*); extern int jack_uuid_parse (const char *buf, jack_uuid_t*); extern void jack_uuid_unparse (jack_uuid_t, char buf[JACK_UUID_STRING_SIZE]); extern int jack_uuid_empty (jack_uuid_t); #ifdef __cplusplus } /* namespace */ #endif #endif /* __jack_uuid_h__ */ jack2-1.9.22/common/jack/weakjack.h000066400000000000000000000123641436671425200167500ustar00rootroot00000000000000/* Copyright (C) 2010 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __weakjack_h__ #define __weakjack_h__ /** * @defgroup WeakLinkage Managing support for newer/older versions of JACK * @{ One challenge faced by developers is that of taking * advantage of new features introduced in new versions * of [ JACK ] while still supporting older versions of * the system. Normally, if an application uses a new * feature in a library/API, it is unable to run on * earlier versions of the library/API that do not * support that feature. Such applications would either * fail to launch or crash when an attempt to use the * feature was made. This problem cane be solved using * weakly-linked symbols. * * When a symbol in a framework is defined as weakly * linked, the symbol does not have to be present at * runtime for a process to continue running. The static * linker identifies a weakly linked symbol as such in * any code module that references the symbol. The * dynamic linker uses this same information at runtime * to determine whether a process can continue * running. If a weakly linked symbol is not present in * the framework, the code module can continue to run as * long as it does not reference the symbol. However, if * the symbol is present, the code can use it normally. * * (adapted from: http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html) * * A concrete example will help. Suppose that someone uses a version * of a JACK client we'll call "Jill". Jill was linked against a version * of JACK that contains a newer part of the API (say, jack_set_latency_callback()) * and would like to use it if it is available. * * When Jill is run on a system that has a suitably "new" version of * JACK, this function will be available entirely normally. But if Jill * is run on a system with an old version of JACK, the function isn't * available. * * With normal symbol linkage, this would create a startup error whenever * someone tries to run Jill with the "old" version of JACK. However, functions * added to JACK after version 0.116.2 are all declared to have "weak" linkage * which means that their absence doesn't cause an error during program * startup. Instead, Jill can test whether or not the symbol jack_set_latency_callback * is null or not. If its null, it means that the JACK installed on this machine * is too old to support this function. If its not null, then Jill can use it * just like any other function in the API. For example: * * \code * if (jack_set_latency_callback) { * jack_set_latency_callback (jill_client, jill_latency_callback, arg); * } * \endcode * * However, there are clients that may want to use this approach to parts of the * the JACK API that predate 0.116.2. For example, they might want to see if even * really old basic parts of the API like jack_client_open() exist at runtime. * * Such clients should include before any other JACK header. * This will make the \b entire JACK API be subject to weak linkage, so that any * and all functions can be checked for existence at runtime. It is important * to understand that very few clients need to do this - if you use this * feature you should have a clear reason to do so. * * */ #ifdef __APPLE__ #define WEAK_ATTRIBUTE weak_import #else #define WEAK_ATTRIBUTE __weak__ #endif #ifndef JACK_OPTIONAL_WEAK_EXPORT /* JACK_OPTIONAL_WEAK_EXPORT needs to be a macro which expands into a compiler directive. If non-null, the directive must tell the compiler to arrange for weak linkage of the symbol it used with. For this to work fully may require linker arguments for the client as well. */ #ifdef __GNUC__ #define JACK_OPTIONAL_WEAK_EXPORT __attribute__((WEAK_ATTRIBUTE)) #else /* Add other things here for non-gcc platforms */ #endif #endif #ifndef JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT /* JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT needs to be a macro which expands into a compiler directive. If non-null, the directive must tell the compiler to arrange for weak linkage of the symbol it is used with AND optionally to mark the symbol as deprecated. For this to work fully may require linker arguments for the client as well. */ #ifdef __GNUC__ #define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((WEAK_ATTRIBUTE,__deprecated__)) #else /* Add other things here for non-gcc platforms */ #endif #endif /**@}*/ #endif /* weakjack */ jack2-1.9.22/common/jack/weakmacros.h000066400000000000000000000053121436671425200173170ustar00rootroot00000000000000/* Copyright (C) 2010 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __weakmacros_h__ #define __weakmacros_h__ /************************************************************* * NOTE: JACK_WEAK_EXPORT ***MUST*** be used on every function * added to the JACK API after the 0.116.2 release. * * Functions that predate this release are marked with * JACK_WEAK_OPTIONAL_EXPORT which can be defined at compile * time in a variety of ways. The default definition is empty, * so that these symbols get normal linkage. If you wish to * use all JACK symbols with weak linkage, include * before jack.h. *************************************************************/ #ifdef __APPLE__ #define WEAK_ATTRIBUTE weak_import #else #define WEAK_ATTRIBUTE __weak__ #endif #ifndef JACK_WEAK_EXPORT #ifdef __GNUC__ /* JACK_WEAK_EXPORT needs to be a macro which expands into a compiler directive. If non-null, the directive must tell the compiler to arrange for weak linkage of the symbol it used with. For this to work full may require linker arguments in the client as well. */ #ifdef _WIN32 /* Not working with __declspec(dllexport) so normal linking Linking with JackWeakAPI.cpp will be the preferred way. */ #define JACK_WEAK_EXPORT #else #define JACK_WEAK_EXPORT __attribute__((WEAK_ATTRIBUTE)) #endif #else /* Add other things here for non-gcc platforms */ #ifdef _WIN32 #define JACK_WEAK_EXPORT #endif #endif #endif #ifndef JACK_WEAK_EXPORT #define JACK_WEAK_EXPORT #endif #ifndef JACK_OPTIONAL_WEAK_EXPORT #define JACK_OPTIONAL_WEAK_EXPORT #endif #ifndef JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT #ifdef __GNUC__ #define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((__deprecated__)) #else /* Add other things here for non-gcc platforms */ #ifdef _WIN32 #define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT #endif #endif /* __GNUC__ */ #ifndef JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT #define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT #endif #endif #endif /* __weakmacros_h__ */ jack2-1.9.22/common/memops.c000066400000000000000000001256551436671425200155630ustar00rootroot00000000000000/* Copyright (C) 2000 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _ISOC9X_SOURCE 1 #define _ISOC99_SOURCE 1 #define __USE_ISOC9X 1 #define __USE_ISOC99 1 #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include "memops.h" #if defined (__SSE2__) && !defined (__sun__) #include #ifdef __SSE4_1__ #include #endif #endif #if defined (__ARM_NEON__) || defined (__ARM_NEON) #include #endif /* Notes about these *_SCALING values. the MAX_BIT values are floating point. when multiplied by a full-scale normalized floating point sample value (-1.0..+1.0) they should give the maximum value representable with an integer sample type of N bits. Note that this is asymmetric. Sample ranges for signed integer, 2's complement values are -(2^(N-1) to +(2^(N-1)-1) Complications ------------- If we use +2^(N-1) for the scaling factors, we run into a problem: if we start with a normalized float value of -1.0, scaling to 24 bits would give -8388608 (-2^23), which is ideal. But with +1.0, we get +8388608, which is technically out of range. We never multiply a full range normalized value by this constant, but we could multiply it by a positive value that is close enough to +1.0 to produce a value > +(2^(N-1)-1. There is no way around this paradox without wasting CPU cycles to determine which scaling factor to use (i.e. determine if its negative or not, use the right factor). So, for now (October 2008) we use 2^(N-1)-1 as the scaling factor. */ #define SAMPLE_32BIT_SCALING 2147483647.0 #define SAMPLE_24BIT_SCALING 8388607.0f #define SAMPLE_16BIT_SCALING 32767.0f /* these are just values to use if the floating point value was out of range advice from Fons Adriaensen: make the limits symmetrical */ #define SAMPLE_32BIT_MAX 2147483647 #define SAMPLE_32BIT_MIN -2147483647 #define SAMPLE_32BIT_MAX_D 2147483647.0 #define SAMPLE_32BIT_MIN_D -2147483647.0 #define SAMPLE_24BIT_MAX 8388607 #define SAMPLE_24BIT_MIN -8388607 #define SAMPLE_24BIT_MAX_F 8388607.0f #define SAMPLE_24BIT_MIN_F -8388607.0f #define SAMPLE_16BIT_MAX 32767 #define SAMPLE_16BIT_MIN -32767 #define SAMPLE_16BIT_MAX_F 32767.0f #define SAMPLE_16BIT_MIN_F -32767.0f /* these mark the outer edges of the range considered "within" range for a floating point sample value. values outside (and on the boundaries) of this range will be clipped before conversion; values within this range will be scaled to appropriate values for the target sample type. */ #define NORMALIZED_FLOAT_MIN -1.0f #define NORMALIZED_FLOAT_MAX 1.0f /* define this in case we end up on a platform that is missing the real lrintf functions */ #define f_round(f) lrintf(f) #define d_round(f) lrint(f) #define float_16(s, d)\ if ((s) <= NORMALIZED_FLOAT_MIN) {\ (d) = SAMPLE_16BIT_MIN;\ } else if ((s) >= NORMALIZED_FLOAT_MAX) {\ (d) = SAMPLE_16BIT_MAX;\ } else {\ (d) = f_round ((s) * SAMPLE_16BIT_SCALING);\ } /* call this when "s" has already been scaled (e.g. when dithering) */ #define float_16_scaled(s, d)\ if ((s) <= SAMPLE_16BIT_MIN_F) {\ (d) = SAMPLE_16BIT_MIN_F;\ } else if ((s) >= SAMPLE_16BIT_MAX_F) { \ (d) = SAMPLE_16BIT_MAX;\ } else {\ (d) = f_round ((s));\ } #define float_24u32(s, d) \ if ((s) <= NORMALIZED_FLOAT_MIN) {\ (d) = SAMPLE_24BIT_MIN << 8;\ } else if ((s) >= NORMALIZED_FLOAT_MAX) {\ (d) = SAMPLE_24BIT_MAX << 8;\ } else {\ (d) = f_round ((s) * SAMPLE_24BIT_SCALING) << 8;\ } #define float_24l32(s, d) \ if ((s) <= NORMALIZED_FLOAT_MIN) {\ (d) = SAMPLE_24BIT_MIN; \ } else if ((s) >= NORMALIZED_FLOAT_MAX) {\ (d) = SAMPLE_24BIT_MAX; \ } else {\ (d) = f_round ((s) * SAMPLE_24BIT_SCALING); \ } #define float_32(s, d) \ do { \ double clipped = fmin(NORMALIZED_FLOAT_MAX, \ fmax((double)(s), NORMALIZED_FLOAT_MIN)); \ double scaled = clipped * SAMPLE_32BIT_MAX_D; \ (d) = d_round(scaled); \ } \ while (0) /* call this when "s" has already been scaled (e.g. when dithering) */ #define float_24u32_scaled(s, d)\ if ((s) <= SAMPLE_24BIT_MIN_F) {\ (d) = SAMPLE_24BIT_MIN << 8;\ } else if ((s) >= SAMPLE_24BIT_MAX_F) { \ (d) = SAMPLE_24BIT_MAX << 8; \ } else {\ (d) = f_round ((s)) << 8; \ } #define float_24(s, d) \ if ((s) <= NORMALIZED_FLOAT_MIN) {\ (d) = SAMPLE_24BIT_MIN;\ } else if ((s) >= NORMALIZED_FLOAT_MAX) {\ (d) = SAMPLE_24BIT_MAX;\ } else {\ (d) = f_round ((s) * SAMPLE_24BIT_SCALING);\ } /* call this when "s" has already been scaled (e.g. when dithering) */ #define float_24_scaled(s, d)\ if ((s) <= SAMPLE_24BIT_MIN_F) {\ (d) = SAMPLE_24BIT_MIN;\ } else if ((s) >= SAMPLE_24BIT_MAX_F) { \ (d) = SAMPLE_24BIT_MAX; \ } else {\ (d) = f_round ((s)); \ } #if defined (__SSE2__) && !defined (__sun__) /* generates same as _mm_set_ps(1.f, 1.f, 1f., 1f) but faster */ static inline __m128 gen_one(void) { volatile __m128i x = { 0 }; /* shut up, GCC */ __m128i ones = _mm_cmpeq_epi32(x, x); return (__m128)_mm_slli_epi32 (_mm_srli_epi32(ones, 25), 23); } static inline __m128 clip(__m128 s, __m128 min, __m128 max) { return _mm_min_ps(max, _mm_max_ps(s, min)); } static inline __m128d clip_double(__m128d s, __m128d min, __m128d max) { return _mm_min_pd(max, _mm_max_pd(s, min)); } static inline __m128i float_24_sse(__m128 s) { const __m128 upper_bound = gen_one(); /* NORMALIZED_FLOAT_MAX */ const __m128 lower_bound = _mm_sub_ps(_mm_setzero_ps(), upper_bound); __m128 clipped = clip(s, lower_bound, upper_bound); __m128 scaled = _mm_mul_ps(clipped, _mm_set1_ps(SAMPLE_24BIT_SCALING)); return _mm_cvtps_epi32(scaled); } #endif #if defined (__ARM_NEON__) || defined (__ARM_NEON) static inline float32x4_t clip(float32x4_t s, float32x4_t min, float32x4_t max) { return vminq_f32(max, vmaxq_f32(s, min)); } static inline int32x4_t float_24_neon(float32x4_t s) { const float32x4_t upper_bound = vdupq_n_f32(NORMALIZED_FLOAT_MAX); const float32x4_t lower_bound = vdupq_n_f32(NORMALIZED_FLOAT_MIN); float32x4_t clipped = clip(s, lower_bound, upper_bound); float32x4_t scaled = vmulq_f32(clipped, vdupq_n_f32(SAMPLE_24BIT_SCALING)); return vcvtq_s32_f32(scaled); } static inline int16x4_t float_16_neon(float32x4_t s) { const float32x4_t upper_bound = vdupq_n_f32(NORMALIZED_FLOAT_MAX); const float32x4_t lower_bound = vdupq_n_f32(NORMALIZED_FLOAT_MIN); float32x4_t clipped = clip(s, lower_bound, upper_bound); float32x4_t scaled = vmulq_f32(clipped, vdupq_n_f32(SAMPLE_16BIT_SCALING)); return vmovn_s32(vcvtq_s32_f32(scaled)); } #endif /* Linear Congruential noise generator. From the music-dsp list * less random than rand(), but good enough and 10x faster */ static unsigned int seed = 22222; static inline unsigned int fast_rand() { seed = (seed * 196314165) + 907633515; return seed; } /* functions for native float sample data */ void sample_move_floatLE_sSs (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { while (nsamples--) { *dst = *((float *) src); dst++; src += src_skip; } } void sample_move_dS_floatLE (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { while (nsamples--) { *((float *) dst) = *src; dst += dst_skip; src++; } } /* NOTES on function naming: foo_bar_d_s the "d" component defines the destination type for the operation the "s" component defines the source type for the operation TYPE can be one of: S - sample is a jack_default_audio_sample_t, currently (October 2008) a 32 bit floating point value Ss - like S but reverse endian from the host CPU 32 - sample is a signed 32 bit integer value 32u24 - sample is a signed 32 bit integer value, but data is in upper 24 bits only 32u24s - like 32u24 but reverse endian from the host CPU 32l24 - sample is a signed 32 bit integer value, but data is in lower 24 bits only 32l24s - like 32l24 but reverse endian from the host CPU 24 - sample is a signed 24 bit integer value 24s - like 24 but reverse endian from the host CPU 16 - sample is a signed 16 bit integer value 16s - like 16 but reverse endian from the host CPU For obvious reasons, the reverse endian versions only show as source types. This covers all known sample formats at 16 bits or larger. */ /* functions for native integer sample data */ void sample_move_d32_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { while (nsamples--) { int32_t z; float_32(*src, z); #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(z>>24); dst[1]=(char)(z>>16); dst[2]=(char)(z>>8); dst[3]=(char)(z); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(z); dst[1]=(char)(z>>8); dst[2]=(char)(z>>16); dst[3]=(char)(z>>24); #endif dst += dst_skip; src++; } } void sample_move_d32_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { while (nsamples--) { float_32(*src, *(int32_t *)dst); dst += dst_skip; src++; } } void sample_move_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { float32x4_t samples = vld1q_f32(src); int32x4_t converted = float_24_neon(samples); int32x4_t shifted = vshlq_n_s32(converted, 8); shifted = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(shifted))); switch(dst_skip) { case 4: vst1q_s32((int32_t*)dst, shifted); break; default: vst1q_lane_s32((int32_t*)(dst), shifted, 0); vst1q_lane_s32((int32_t*)(dst+dst_skip), shifted, 1); vst1q_lane_s32((int32_t*)(dst+2*dst_skip), shifted, 2); vst1q_lane_s32((int32_t*)(dst+3*dst_skip), shifted, 3); break; } dst += 4*dst_skip; src+= 4; } #endif int32_t z; while (nsamples--) { float_24u32 (*src, z); #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(z>>24); dst[1]=(char)(z>>16); dst[2]=(char)(z>>8); dst[3]=(char)(z); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(z); dst[1]=(char)(z>>8); dst[2]=(char)(z>>16); dst[3]=(char)(z>>24); #endif dst += dst_skip; src++; } } void sample_move_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__SSE2__) && !defined (__sun__) __m128 int_max = _mm_set1_ps(SAMPLE_24BIT_MAX_F); __m128 int_min = _mm_sub_ps(_mm_setzero_ps(), int_max); __m128 factor = int_max; unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { __m128 in = _mm_load_ps(src); __m128 scaled = _mm_mul_ps(in, factor); __m128 clipped = clip(scaled, int_min, int_max); __m128i y = _mm_cvttps_epi32(clipped); __m128i shifted = _mm_slli_epi32(y, 8); #ifdef __SSE4_1__ *(int32_t*)dst = _mm_extract_epi32(shifted, 0); *(int32_t*)(dst+dst_skip) = _mm_extract_epi32(shifted, 1); *(int32_t*)(dst+2*dst_skip) = _mm_extract_epi32(shifted, 2); *(int32_t*)(dst+3*dst_skip) = _mm_extract_epi32(shifted, 3); #else __m128i shuffled1 = _mm_shuffle_epi32(shifted, _MM_SHUFFLE(0, 3, 2, 1)); __m128i shuffled2 = _mm_shuffle_epi32(shifted, _MM_SHUFFLE(1, 0, 3, 2)); __m128i shuffled3 = _mm_shuffle_epi32(shifted, _MM_SHUFFLE(2, 1, 0, 3)); _mm_store_ss((float*)dst, (__m128)shifted); _mm_store_ss((float*)(dst+dst_skip), (__m128)shuffled1); _mm_store_ss((float*)(dst+2*dst_skip), (__m128)shuffled2); _mm_store_ss((float*)(dst+3*dst_skip), (__m128)shuffled3); #endif dst += 4*dst_skip; src+= 4; } while (nsamples--) { __m128 in = _mm_load_ss(src); __m128 scaled = _mm_mul_ss(in, factor); __m128 clipped = _mm_min_ss(int_max, _mm_max_ss(scaled, int_min)); int y = _mm_cvttss_si32(clipped); *((int *) dst) = y<<8; dst += dst_skip; src++; } #elif defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { float32x4_t samples = vld1q_f32(src); int32x4_t converted = float_24_neon(samples); int32x4_t shifted = vshlq_n_s32(converted, 8); switch(dst_skip) { case 4: vst1q_s32((int32_t*)dst, shifted); break; default: vst1q_lane_s32((int32_t*)(dst), shifted, 0); vst1q_lane_s32((int32_t*)(dst+dst_skip), shifted, 1); vst1q_lane_s32((int32_t*)(dst+2*dst_skip), shifted, 2); vst1q_lane_s32((int32_t*)(dst+3*dst_skip), shifted, 3); break; } dst += 4*dst_skip; src+= 4; } #endif #if !defined (__SSE2__) while (nsamples--) { float_24u32 (*src, *((int32_t*) dst)); dst += dst_skip; src++; } #endif } void sample_move_dS_s32u24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { #if defined (__ARM_NEON__) || defined (__ARM_NEON) float32x4_t factor = vdupq_n_f32(1.0 / SAMPLE_24BIT_SCALING); unsigned long unrolled = nsamples / 4; while (unrolled--) { int32x4_t src128; switch(src_skip) { case 4: src128 = vld1q_s32((int32_t*)src); break; case 8: src128 = vld2q_s32((int32_t*)src).val[0]; break; default: src128 = vld1q_lane_s32((int32_t*)src, src128, 0); src128 = vld1q_lane_s32((int32_t*)(src+src_skip), src128, 1); src128 = vld1q_lane_s32((int32_t*)(src+2*src_skip), src128, 2); src128 = vld1q_lane_s32((int32_t*)(src+3*src_skip), src128, 3); break; } src128 = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(src128))); int32x4_t shifted = vshrq_n_s32(src128, 8); float32x4_t as_float = vcvtq_f32_s32(shifted); float32x4_t divided = vmulq_f32(as_float, factor); vst1q_f32(dst, divided); src += 4*src_skip; dst += 4; } nsamples = nsamples & 3; #endif /* ALERT: signed sign-extension portability !!! */ const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; while (nsamples--) { int x; #if __BYTE_ORDER == __LITTLE_ENDIAN x = (unsigned char)(src[0]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[2]); x <<= 8; x |= (unsigned char)(src[3]); #elif __BYTE_ORDER == __BIG_ENDIAN x = (unsigned char)(src[3]); x <<= 8; x |= (unsigned char)(src[2]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[0]); #endif *dst = (x >> 8) * scaling; dst++; src += src_skip; } } void sample_move_dS_s32u24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { #if defined (__SSE2__) && !defined (__sun__) unsigned long unrolled = nsamples / 4; static float inv_sample_max_24bit = 1.0 / SAMPLE_24BIT_SCALING; __m128 factor = _mm_set1_ps(inv_sample_max_24bit); while (unrolled--) { int i1 = *((int *) src); src+= src_skip; int i2 = *((int *) src); src+= src_skip; int i3 = *((int *) src); src+= src_skip; int i4 = *((int *) src); src+= src_skip; __m128i src = _mm_set_epi32(i4, i3, i2, i1); __m128i shifted = _mm_srai_epi32(src, 8); __m128 as_float = _mm_cvtepi32_ps(shifted); __m128 divided = _mm_mul_ps(as_float, factor); _mm_storeu_ps(dst, divided); dst += 4; } nsamples = nsamples & 3; #elif defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; float32x4_t factor = vdupq_n_f32(1.0 / SAMPLE_24BIT_SCALING); while (unrolled--) { int32x4_t src128; switch(src_skip) { case 4: src128 = vld1q_s32((int32_t*)src); break; case 8: src128 = vld2q_s32((int32_t*)src).val[0]; break; default: src128 = vld1q_lane_s32((int32_t*)src, src128, 0); src128 = vld1q_lane_s32((int32_t*)(src+src_skip), src128, 1); src128 = vld1q_lane_s32((int32_t*)(src+2*src_skip), src128, 2); src128 = vld1q_lane_s32((int32_t*)(src+3*src_skip), src128, 3); break; } int32x4_t shifted = vshrq_n_s32(src128, 8); float32x4_t as_float = vcvtq_f32_s32(shifted); float32x4_t divided = vmulq_f32(as_float, factor); vst1q_f32(dst, divided); src += 4*src_skip; dst += 4; } nsamples = nsamples & 3; #endif /* ALERT: signed sign-extension portability !!! */ const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; while (nsamples--) { *dst = (*((int *) src) >> 8) * scaling; dst++; src += src_skip; } } void sample_move_d32l24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { float32x4_t samples = vld1q_f32(src); int32x4_t converted = float_24_neon(samples); converted = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(converted))); switch(dst_skip) { case 4: vst1q_s32((int32_t*)dst, converted); break; default: vst1q_lane_s32((int32_t*)(dst), converted, 0); vst1q_lane_s32((int32_t*)(dst+dst_skip), converted, 1); vst1q_lane_s32((int32_t*)(dst+2*dst_skip), converted, 2); vst1q_lane_s32((int32_t*)(dst+3*dst_skip), converted, 3); break; } dst += 4*dst_skip; src+= 4; } #endif int32_t z; while (nsamples--) { float_24l32 (*src, z); #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(z>>24); dst[1]=(char)(z>>16); dst[2]=(char)(z>>8); dst[3]=(char)(z); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(z); dst[1]=(char)(z>>8); dst[2]=(char)(z>>16); dst[3]=(char)(z>>24); #endif dst += dst_skip; src++; } } void sample_move_d32l24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__SSE2__) && !defined (__sun__) __m128 int_max = _mm_set1_ps(SAMPLE_24BIT_MAX_F); __m128 int_min = _mm_sub_ps(_mm_setzero_ps(), int_max); __m128 factor = int_max; unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { __m128 in = _mm_load_ps(src); __m128 scaled = _mm_mul_ps(in, factor); __m128 clipped = clip(scaled, int_min, int_max); __m128i shifted = _mm_cvttps_epi32(clipped); #ifdef __SSE4_1__ *(int32_t*)dst = _mm_extract_epi32(shifted, 0); *(int32_t*)(dst+dst_skip) = _mm_extract_epi32(shifted, 1); *(int32_t*)(dst+2*dst_skip) = _mm_extract_epi32(shifted, 2); *(int32_t*)(dst+3*dst_skip) = _mm_extract_epi32(shifted, 3); #else __m128i shuffled1 = _mm_shuffle_epi32(shifted, _MM_SHUFFLE(0, 3, 2, 1)); __m128i shuffled2 = _mm_shuffle_epi32(shifted, _MM_SHUFFLE(1, 0, 3, 2)); __m128i shuffled3 = _mm_shuffle_epi32(shifted, _MM_SHUFFLE(2, 1, 0, 3)); _mm_store_ss((float*)dst, (__m128)shifted); _mm_store_ss((float*)(dst+dst_skip), (__m128)shuffled1); _mm_store_ss((float*)(dst+2*dst_skip), (__m128)shuffled2); _mm_store_ss((float*)(dst+3*dst_skip), (__m128)shuffled3); #endif dst += 4*dst_skip; src+= 4; } while (nsamples--) { __m128 in = _mm_load_ss(src); __m128 scaled = _mm_mul_ss(in, factor); __m128 clipped = _mm_min_ss(int_max, _mm_max_ss(scaled, int_min)); int y = _mm_cvttss_si32(clipped); *((int *) dst) = y<<8; dst += dst_skip; src++; } #elif defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { float32x4_t samples = vld1q_f32(src); int32x4_t converted = float_24_neon(samples); switch(dst_skip) { case 4: vst1q_s32((int32_t*)dst, converted); break; default: vst1q_lane_s32((int32_t*)(dst), converted, 0); vst1q_lane_s32((int32_t*)(dst+dst_skip), converted, 1); vst1q_lane_s32((int32_t*)(dst+2*dst_skip), converted, 2); vst1q_lane_s32((int32_t*)(dst+3*dst_skip), converted, 3); break; } dst += 4*dst_skip; src+= 4; } #endif #if !defined (__SSE2__) while (nsamples--) { float_24l32 (*src, *((int32_t*) dst)); dst += dst_skip; src++; } #endif } void sample_move_dS_s32s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { const jack_default_audio_sample_t scaling = 1.0/SAMPLE_32BIT_SCALING; while (nsamples--) { int32_t x; #if __BYTE_ORDER == __LITTLE_ENDIAN x = (unsigned char)(src[0]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[2]); x <<= 8; x |= (unsigned char)(src[3]); #elif __BYTE_ORDER == __BIG_ENDIAN x = (unsigned char)(src[3]); x <<= 8; x |= (unsigned char)(src[2]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[0]); #endif double extended = x * scaling; *dst = (float)extended; dst++; src += src_skip; } } void sample_move_dS_s32l24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { #if defined (__ARM_NEON__) || defined (__ARM_NEON) float32x4_t factor = vdupq_n_f32(1.0 / SAMPLE_24BIT_SCALING); unsigned long unrolled = nsamples / 4; while (unrolled--) { uint32x4_t src128; switch(src_skip) { case 4: src128 = vld1q_u32((uint32_t*)src); break; case 8: src128 = vld2q_u32((uint32_t*)src).val[0]; break; default: src128 = vld1q_lane_u32((uint32_t*)src, src128, 0); src128 = vld1q_lane_u32((uint32_t*)(src+src_skip), src128, 1); src128 = vld1q_lane_u32((uint32_t*)(src+2*src_skip), src128, 2); src128 = vld1q_lane_u32((uint32_t*)(src+3*src_skip), src128, 3); break; } src128 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(src128))); uint32x4_t toupper = vshlq_n_u32(src128, 8); int32x4_t shifted = vshrq_n_s32((int32x4_t)toupper, 8); float32x4_t as_float = vcvtq_f32_s32(shifted); float32x4_t divided = vmulq_f32(as_float, factor); vst1q_f32(dst, divided); src += 4*src_skip; dst += 4; } nsamples = nsamples & 3; #endif /* ALERT: signed sign-extension portability !!! */ const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; while (nsamples--) { int32_t x; #if __BYTE_ORDER == __LITTLE_ENDIAN x = (unsigned char)(src[0]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[2]); x <<= 8; x |= (unsigned char)(src[3]); #elif __BYTE_ORDER == __BIG_ENDIAN x = (unsigned char)(src[3]); x <<= 8; x |= (unsigned char)(src[2]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[0]); #endif *dst = (x >> 0) * scaling; dst++; src += src_skip; } } void sample_move_dS_s32 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { const double scaling = 1.0 / SAMPLE_32BIT_SCALING; while (nsamples--) { int32_t val=(*((int32_t*)src)); double extended = val * scaling; *dst = (float)extended; dst++; src += src_skip; } } void sample_move_dS_s32l24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { #if defined (__SSE2__) && !defined (__sun__) unsigned long unrolled = nsamples / 4; static float inv_sample_max_24bit = 1.0 / SAMPLE_24BIT_SCALING; __m128 factor = _mm_set1_ps(inv_sample_max_24bit); while (unrolled--) { int i1 = *((int *) src); src+= src_skip; int i2 = *((int *) src); src+= src_skip; int i3 = *((int *) src); src+= src_skip; int i4 = *((int *) src); src+= src_skip; __m128i shifted = _mm_set_epi32(i4, i3, i2, i1); __m128 as_float = _mm_cvtepi32_ps(shifted); __m128 divided = _mm_mul_ps(as_float, factor); _mm_storeu_ps(dst, divided); dst += 4; } nsamples = nsamples & 3; #elif defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; float32x4_t factor = vdupq_n_f32(1.0 / SAMPLE_24BIT_SCALING); while (unrolled--) { uint32x4_t src128; switch(src_skip) { case 4: src128 = vld1q_u32((uint32_t*)src); break; case 8: src128 = vld2q_u32((uint32_t*)src).val[0]; break; default: src128 = vld1q_lane_u32((uint32_t*)src, src128, 0); src128 = vld1q_lane_u32((uint32_t*)(src+src_skip), src128, 1); src128 = vld1q_lane_u32((uint32_t*)(src+2*src_skip), src128, 2); src128 = vld1q_lane_u32((uint32_t*)(src+3*src_skip), src128, 3); break; } // Sign extension by moving to upper as unsigned, then down uint32x4_t toupper = vshlq_n_u32(src128, 8); int32x4_t shifted = vshrq_n_s32((int32x4_t)toupper, 8); float32x4_t as_float = vcvtq_f32_s32(shifted); float32x4_t divided = vmulq_f32(as_float, factor); vst1q_f32(dst, divided); src += 4*src_skip; dst += 4; } nsamples = nsamples & 3; #endif /* ALERT: signed sign-extension portability !!! */ const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; while (nsamples--) { uint32_t val=(*((uint32_t*)src)); if (val & 0x800000u) val|=0xFF000000u; *dst = (*((int32_t *) &val)) * scaling; dst++; src += src_skip; } } void sample_move_d24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; while (unrolled--) { int i; int32_t z[4]; float32x4_t samples = vld1q_f32(src); int32x4_t converted = float_24_neon(samples); converted = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(converted))); vst1q_s32(z, converted); for (i = 0; i != 4; ++i) { memcpy (dst, ((char*)(z+i))+1, 3); dst += dst_skip; } src += 4; } nsamples = nsamples & 3; #endif int32_t z; while (nsamples--) { float_24 (*src, z); #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(z>>16); dst[1]=(char)(z>>8); dst[2]=(char)(z); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(z); dst[1]=(char)(z>>8); dst[2]=(char)(z>>16); #endif dst += dst_skip; src++; } } void sample_move_d24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__SSE2__) && !defined (__sun__) _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); while (nsamples >= 4) { int i; int32_t z[4]; __m128 samples = _mm_loadu_ps(src); __m128i converted = float_24_sse(samples); #ifdef __SSE4_1__ z[0] = _mm_extract_epi32(converted, 0); z[1] = _mm_extract_epi32(converted, 1); z[2] = _mm_extract_epi32(converted, 2); z[3] = _mm_extract_epi32(converted, 3); #else __m128i shuffled1 = _mm_shuffle_epi32(converted, _MM_SHUFFLE(0, 3, 2, 1)); __m128i shuffled2 = _mm_shuffle_epi32(converted, _MM_SHUFFLE(1, 0, 3, 2)); __m128i shuffled3 = _mm_shuffle_epi32(converted, _MM_SHUFFLE(2, 1, 0, 3)); _mm_store_ss((float*)z, (__m128)converted); _mm_store_ss((float*)z+1, (__m128)shuffled1); _mm_store_ss((float*)z+2, (__m128)shuffled2); _mm_store_ss((float*)z+3, (__m128)shuffled3); #endif for (i = 0; i != 4; ++i) { memcpy (dst, z+i, 3); dst += dst_skip; } nsamples -= 4; src += 4; } #elif defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; while (unrolled--) { int i; int32_t z[4]; float32x4_t samples = vld1q_f32(src); int32x4_t converted = float_24_neon(samples); vst1q_s32(z, converted); for (i = 0; i != 4; ++i) { memcpy (dst, z+i, 3); dst += dst_skip; } src += 4; } nsamples = nsamples & 3; #endif int32_t z; while (nsamples--) { float_24 (*src, z); #if __BYTE_ORDER == __LITTLE_ENDIAN memcpy (dst, &z, 3); #elif __BYTE_ORDER == __BIG_ENDIAN memcpy (dst, (char *)&z + 1, 3); #endif dst += dst_skip; src++; } } void sample_move_dS_s24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; #if defined (__ARM_NEON__) || defined (__ARM_NEON) // we shift 8 to the right by dividing by 256.0 -> no sign extra handling const float32x4_t vscaling = vdupq_n_f32(scaling/256.0); int32_t x[4]; memset(x, 0, sizeof(x)); unsigned long unrolled = nsamples / 4; while (unrolled--) { #if __BYTE_ORDER == __BIG_ENDIAN /* ARM big endian?? */ // right aligned / inverse sequence below -> *256 memcpy(((char*)&x[0])+1, src, 3); memcpy(((char*)&x[1])+1, src+src_skip, 3); memcpy(((char*)&x[2])+1, src+2*src_skip, 3); memcpy(((char*)&x[3])+1, src+3*src_skip, 3); #else memcpy(&x[0], src, 3); memcpy(&x[1], src+src_skip, 3); memcpy(&x[2], src+2*src_skip, 3); memcpy(&x[3], src+3*src_skip, 3); #endif src += 4 * src_skip; int32x4_t source = vld1q_s32(x); source = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(source))); float32x4_t converted = vcvtq_f32_s32(source); float32x4_t scaled = vmulq_f32(converted, vscaling); vst1q_f32(dst, scaled); dst += 4; } nsamples = nsamples & 3; #endif /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { int x; #if __BYTE_ORDER == __LITTLE_ENDIAN x = (unsigned char)(src[0]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[2]); /* correct sign bit and the rest of the top byte */ if (src[0] & 0x80) { x |= 0xff << 24; } #elif __BYTE_ORDER == __BIG_ENDIAN x = (unsigned char)(src[2]); x <<= 8; x |= (unsigned char)(src[1]); x <<= 8; x |= (unsigned char)(src[0]); /* correct sign bit and the rest of the top byte */ if (src[2] & 0x80) { x |= 0xff << 24; } #endif *dst = x * scaling; dst++; src += src_skip; } } void sample_move_dS_s24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { const jack_default_audio_sample_t scaling = 1.f/SAMPLE_24BIT_SCALING; #if defined (__SSE2__) && !defined (__sun__) const __m128 scaling_block = _mm_set_ps1(scaling); while (nsamples >= 4) { int x0, x1, x2, x3; memcpy((char*)&x0 + 1, src, 3); memcpy((char*)&x1 + 1, src+src_skip, 3); memcpy((char*)&x2 + 1, src+2*src_skip, 3); memcpy((char*)&x3 + 1, src+3*src_skip, 3); src += 4 * src_skip; const __m128i block_i = _mm_set_epi32(x3, x2, x1, x0); const __m128i shifted = _mm_srai_epi32(block_i, 8); const __m128 converted = _mm_cvtepi32_ps (shifted); const __m128 scaled = _mm_mul_ps(converted, scaling_block); _mm_storeu_ps(dst, scaled); dst += 4; nsamples -= 4; } #elif defined (__ARM_NEON__) || defined (__ARM_NEON) // we shift 8 to the right by dividing by 256.0 -> no sign extra handling const float32x4_t vscaling = vdupq_n_f32(scaling/256.0); int32_t x[4]; memset(x, 0, sizeof(x)); unsigned long unrolled = nsamples / 4; while (unrolled--) { #if __BYTE_ORDER == __BIG_ENDIAN /* ARM big endian?? */ // left aligned -> *256 memcpy(&x[0], src, 3); memcpy(&x[1], src+src_skip, 3); memcpy(&x[2], src+2*src_skip, 3); memcpy(&x[3], src+3*src_skip, 3); #else memcpy(((char*)&x[0])+1, src, 3); memcpy(((char*)&x[1])+1, src+src_skip, 3); memcpy(((char*)&x[2])+1, src+2*src_skip, 3); memcpy(((char*)&x[3])+1, src+3*src_skip, 3); #endif src += 4 * src_skip; int32x4_t source = vld1q_s32(x); float32x4_t converted = vcvtq_f32_s32(source); float32x4_t scaled = vmulq_f32(converted, vscaling); vst1q_f32(dst, scaled); dst += 4; } nsamples = nsamples & 3; #endif while (nsamples--) { int x; #if __BYTE_ORDER == __LITTLE_ENDIAN memcpy((char*)&x + 1, src, 3); #elif __BYTE_ORDER == __BIG_ENDIAN memcpy(&x, src, 3); #endif x >>= 8; *dst = x * scaling; dst++; src += src_skip; } } void sample_move_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { float32x4_t samples = vld1q_f32(src); int16x4_t converted = float_16_neon(samples); converted = vreinterpret_s16_u8(vrev16_u8(vreinterpret_u8_s16(converted))); switch(dst_skip) { case 2: vst1_s16((int16_t*)dst, converted); break; default: vst1_lane_s16((int16_t*)(dst), converted, 0); vst1_lane_s16((int16_t*)(dst+dst_skip), converted, 1); vst1_lane_s16((int16_t*)(dst+2*dst_skip), converted, 2); vst1_lane_s16((int16_t*)(dst+3*dst_skip), converted, 3); break; } dst += 4*dst_skip; src+= 4; } #endif int16_t tmp; while (nsamples--) { // float_16 (*src, tmp); if (*src <= NORMALIZED_FLOAT_MIN) { tmp = SAMPLE_16BIT_MIN; } else if (*src >= NORMALIZED_FLOAT_MAX) { tmp = SAMPLE_16BIT_MAX; } else { tmp = (int16_t) f_round (*src * SAMPLE_16BIT_SCALING); } #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(tmp>>8); dst[1]=(char)(tmp); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(tmp); dst[1]=(char)(tmp>>8); #endif dst += dst_skip; src++; } } void sample_move_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { #if defined (__ARM_NEON__) || defined (__ARM_NEON) unsigned long unrolled = nsamples / 4; nsamples = nsamples & 3; while (unrolled--) { float32x4_t samples = vld1q_f32(src); int16x4_t converted = float_16_neon(samples); switch(dst_skip) { case 2: vst1_s16((int16_t*)dst, converted); break; default: vst1_lane_s16((int16_t*)(dst), converted, 0); vst1_lane_s16((int16_t*)(dst+dst_skip), converted, 1); vst1_lane_s16((int16_t*)(dst+2*dst_skip), converted, 2); vst1_lane_s16((int16_t*)(dst+3*dst_skip), converted, 3); break; } dst += 4*dst_skip; src+= 4; } #endif while (nsamples--) { float_16 (*src, *((int16_t*) dst)); dst += dst_skip; src++; } } void sample_move_dither_rect_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { jack_default_audio_sample_t val; int16_t tmp; while (nsamples--) { val = (*src * SAMPLE_16BIT_SCALING) + fast_rand() / (float) UINT_MAX - 0.5f; float_16_scaled (val, tmp); #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(tmp>>8); dst[1]=(char)(tmp); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(tmp); dst[1]=(char)(tmp>>8); #endif dst += dst_skip; src++; } } void sample_move_dither_rect_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { jack_default_audio_sample_t val; while (nsamples--) { val = (*src * SAMPLE_16BIT_SCALING) + fast_rand() / (float)UINT_MAX - 0.5f; float_16_scaled (val, *((int16_t*) dst)); dst += dst_skip; src++; } } void sample_move_dither_tri_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { jack_default_audio_sample_t val; int16_t tmp; while (nsamples--) { val = (*src * SAMPLE_16BIT_SCALING) + ((float)fast_rand() + (float)fast_rand()) / (float)UINT_MAX - 1.0f; float_16_scaled (val, tmp); #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(tmp>>8); dst[1]=(char)(tmp); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(tmp); dst[1]=(char)(tmp>>8); #endif dst += dst_skip; src++; } } void sample_move_dither_tri_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { jack_default_audio_sample_t val; while (nsamples--) { val = (*src * SAMPLE_16BIT_SCALING) + ((float)fast_rand() + (float)fast_rand()) / (float)UINT_MAX - 1.0f; float_16_scaled (val, *((int16_t*) dst)); dst += dst_skip; src++; } } void sample_move_dither_shaped_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { jack_default_audio_sample_t x; jack_default_audio_sample_t xe; /* the innput sample - filtered error */ jack_default_audio_sample_t xp; /* x' */ float r; float rm1 = state->rm1; unsigned int idx = state->idx; int16_t tmp; while (nsamples--) { x = *src * SAMPLE_16BIT_SCALING; r = ((float)fast_rand() + (float)fast_rand()) / (float)UINT_MAX - 1.0f; /* Filter the error with Lipshitz's minimally audible FIR: [2.033 -2.165 1.959 -1.590 0.6149] */ xe = x - state->e[idx] * 2.033f + state->e[(idx - 1) & DITHER_BUF_MASK] * 2.165f - state->e[(idx - 2) & DITHER_BUF_MASK] * 1.959f + state->e[(idx - 3) & DITHER_BUF_MASK] * 1.590f - state->e[(idx - 4) & DITHER_BUF_MASK] * 0.6149f; xp = xe + r - rm1; rm1 = r; float_16_scaled (xp, tmp); /* Intrinsic z^-1 delay */ idx = (idx + 1) & DITHER_BUF_MASK; state->e[idx] = xp - xe; #if __BYTE_ORDER == __LITTLE_ENDIAN dst[0]=(char)(tmp>>8); dst[1]=(char)(tmp); #elif __BYTE_ORDER == __BIG_ENDIAN dst[0]=(char)(tmp); dst[1]=(char)(tmp>>8); #endif dst += dst_skip; src++; } state->rm1 = rm1; state->idx = idx; } void sample_move_dither_shaped_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { jack_default_audio_sample_t x; jack_default_audio_sample_t xe; /* the innput sample - filtered error */ jack_default_audio_sample_t xp; /* x' */ float r; float rm1 = state->rm1; unsigned int idx = state->idx; while (nsamples--) { x = *src * SAMPLE_16BIT_SCALING; r = ((float)fast_rand() + (float)fast_rand()) / (float)UINT_MAX - 1.0f; /* Filter the error with Lipshitz's minimally audible FIR: [2.033 -2.165 1.959 -1.590 0.6149] */ xe = x - state->e[idx] * 2.033f + state->e[(idx - 1) & DITHER_BUF_MASK] * 2.165f - state->e[(idx - 2) & DITHER_BUF_MASK] * 1.959f + state->e[(idx - 3) & DITHER_BUF_MASK] * 1.590f - state->e[(idx - 4) & DITHER_BUF_MASK] * 0.6149f; xp = xe + r - rm1; rm1 = r; float_16_scaled (xp, *((int16_t*) dst)); /* Intrinsic z^-1 delay */ idx = (idx + 1) & DITHER_BUF_MASK; state->e[idx] = *((int16_t*) dst) - xe; dst += dst_skip; src++; } state->rm1 = rm1; state->idx = idx; } void sample_move_dS_s16s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { short z; const jack_default_audio_sample_t scaling = 1.0/SAMPLE_16BIT_SCALING; #if defined (__ARM_NEON__) || defined (__ARM_NEON) const float32x4_t vscaling = vdupq_n_f32(scaling); unsigned long unrolled = nsamples / 4; while (unrolled--) { int16x4_t source16x4; switch(src_skip) { case 2: source16x4 = vld1_s16((int16_t*)src); break; case 4: source16x4 = vld2_s16((int16_t*)src).val[0]; break; default: source16x4 = vld1_lane_s16((int16_t*)src, source16x4, 0); source16x4 = vld1_lane_s16((int16_t*)(src+src_skip), source16x4, 1); source16x4 = vld1_lane_s16((int16_t*)(src+2*src_skip), source16x4, 2); source16x4 = vld1_lane_s16((int16_t*)(src+3*src_skip), source16x4, 3); break; } source16x4 = vreinterpret_s16_u8(vrev16_u8(vreinterpret_u8_s16(source16x4))); int32x4_t source32x4 = vmovl_s16(source16x4); src += 4 * src_skip; float32x4_t converted = vcvtq_f32_s32(source32x4); float32x4_t scaled = vmulq_f32(converted, vscaling); vst1q_f32(dst, scaled); dst += 4; } nsamples = nsamples & 3; #endif /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { #if __BYTE_ORDER == __LITTLE_ENDIAN z = (unsigned char)(src[0]); z <<= 8; z |= (unsigned char)(src[1]); #elif __BYTE_ORDER == __BIG_ENDIAN z = (unsigned char)(src[1]); z <<= 8; z |= (unsigned char)(src[0]); #endif *dst = z * scaling; dst++; src += src_skip; } } void sample_move_dS_s16 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { /* ALERT: signed sign-extension portability !!! */ const jack_default_audio_sample_t scaling = 1.0/SAMPLE_16BIT_SCALING; #if defined (__ARM_NEON__) || defined (__ARM_NEON) const float32x4_t vscaling = vdupq_n_f32(scaling); unsigned long unrolled = nsamples / 4; while (unrolled--) { int16x4_t source16x4; switch(src_skip) { case 2: source16x4 = vld1_s16((int16_t*)src); break; case 4: source16x4 = vld2_s16((int16_t*)src).val[0]; break; default: source16x4 = vld1_lane_s16((int16_t*)src, source16x4, 0); source16x4 = vld1_lane_s16((int16_t*)(src+src_skip), source16x4, 1); source16x4 = vld1_lane_s16((int16_t*)(src+2*src_skip), source16x4, 2); source16x4 = vld1_lane_s16((int16_t*)(src+3*src_skip), source16x4, 3); break; } int32x4_t source32x4 = vmovl_s16(source16x4); src += 4 * src_skip; float32x4_t converted = vcvtq_f32_s32(source32x4); float32x4_t scaled = vmulq_f32(converted, vscaling); vst1q_f32(dst, scaled); dst += 4; } nsamples = nsamples & 3; #endif while (nsamples--) { *dst = (*((short *) src)) * scaling; dst++; src += src_skip; } } void memset_interleave (char *dst, char val, unsigned long bytes, unsigned long unit_bytes, unsigned long skip_bytes) { switch (unit_bytes) { case 1: while (bytes--) { *dst = val; dst += skip_bytes; } break; case 2: while (bytes) { *((short *) dst) = (short) val; dst += skip_bytes; bytes -= 2; } break; case 4: while (bytes) { *((int *) dst) = (int) val; dst += skip_bytes; bytes -= 4; } break; default: while (bytes) { memset(dst, val, unit_bytes); dst += skip_bytes; bytes -= unit_bytes; } break; } } /* COPY FUNCTIONS: used to move data from an input channel to an output channel. Note that we assume that the skip distance is the same for both channels. This is completely fine unless the input and output were on different audio interfaces that were interleaved differently. We don't try to handle that. */ void memcpy_fake (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar) { memcpy (dst, src, src_bytes); } void memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes) { while (src_bytes) { *((short *) dst) = *((short *) src); dst += dst_skip_bytes; src += src_skip_bytes; src_bytes -= 2; } } void memcpy_interleave_d24_s24 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes) { while (src_bytes) { memcpy(dst, src, 3); dst += dst_skip_bytes; src += src_skip_bytes; src_bytes -= 3; } } void memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes) { while (src_bytes) { *((int *) dst) = *((int *) src); dst += dst_skip_bytes; src += src_skip_bytes; src_bytes -= 4; } } jack2-1.9.22/common/memops.h000066400000000000000000000222261436671425200155560ustar00rootroot00000000000000/* Copyright (C) 1999-2000 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __jack_memops_h__ #define __jack_memops_h__ #include "types.h" #ifdef __cplusplus extern "C" { #endif #ifdef sun #define __inline__ #endif typedef enum { None, Rectangular, Triangular, Shaped } DitherAlgorithm; #define DITHER_BUF_SIZE 8 #define DITHER_BUF_MASK 7 typedef struct { unsigned int depth; float rm1; unsigned int idx; float e[DITHER_BUF_SIZE]; } dither_state_t; /* float functions */ void sample_move_floatLE_sSs (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long dst_skip); void sample_move_dS_floatLE (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); /* integer functions */ void sample_move_d32_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d32_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d32l24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d32l24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_shaped_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_shaped_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_shaped_d24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_shaped_d24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_shaped_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_shaped_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dS_s32s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s32 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s32u24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s32u24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s32l24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s32l24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s16s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s16 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_merge_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_merge_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); static __inline__ void sample_merge (jack_default_audio_sample_t *dst, jack_default_audio_sample_t *src, unsigned long cnt) { while (cnt--) { *dst += *src; dst++; src++; } } static __inline__ void sample_memcpy (jack_default_audio_sample_t *dst, jack_default_audio_sample_t *src, unsigned long cnt) { memcpy (dst, src, cnt * sizeof (jack_default_audio_sample_t)); } void memset_interleave (char *dst, char val, unsigned long bytes, unsigned long unit_bytes, unsigned long skip_bytes); void memcpy_fake (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar); void memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void memcpy_interleave_d24_s24 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void merge_memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void merge_memcpy_interleave_d24_s24 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void merge_memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void merge_memcpy_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar); void merge_memcpy_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar); #ifdef __cplusplus } #endif #endif /* __jack_memops_h__ */ jack2-1.9.22/common/netjack.c000066400000000000000000000762241436671425200156770ustar00rootroot00000000000000 /* -*- mode: c; c-file-style: "linux"; -*- */ /* NetJack Abstraction. Copyright (C) 2018 Karl Linden Copyright (C) 2008 Pieter Palmers Copyright (C) 2006 Torben Hohn Copyright (C) 2003 Robert Ham Copyright (C) 2001 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $ */ #include #include #include #include #include #include #include #include #include #include "jack/jslist.h" #include #ifdef WIN32 #include #include #define socklen_t int #else #include #include #endif #if defined(HAVE_CONFIG_H) #include "config.h" #endif #if HAVE_SAMPLERATE #include #endif #include "netjack.h" #include "netjack_packet.h" #include "JackError.h" #define MIN(x,y) ((x)<(y) ? (x) : (y)) static int sync_state = 1; static jack_transport_state_t last_transport_state; static int net_driver_sync_cb(jack_transport_state_t state, jack_position_t *pos, void *data) { int retval = sync_state; if (state == JackTransportStarting && last_transport_state != JackTransportStarting) { retval = 0; } // if (state == JackTransportStarting) // jack_info("Starting sync_state = %d", sync_state); last_transport_state = state; return retval; } int netjack_wait( netjack_driver_state_t *netj ) { int we_have_the_expected_frame = 0; jack_nframes_t next_frame_avail; jack_time_t packet_recv_time_stamp; jacknet_packet_header *pkthdr; if( !netj->next_deadline_valid ) { netj->next_deadline = jack_get_time() + netj->period_usecs; netj->next_deadline_valid = 1; } // Increment expected frame here. if( netj->expected_framecnt_valid ) { netj->expected_framecnt += 1; } else { // starting up.... lets look into the packetcache, and fetch the highest packet. packet_cache_drain_socket( netj->packcache, netj->sockfd ); if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail ) ) { netj->expected_framecnt = next_frame_avail; netj->expected_framecnt_valid = 1; } else { // no packets there... start normally. netj->expected_framecnt = 0; netj->expected_framecnt_valid = 1; } } //jack_log( "expect %d", netj->expected_framecnt ); // Now check if required packet is already in the cache. // then poll (have deadline calculated) // then drain socket, rinse and repeat. while(1) { if( packet_cache_get_next_available_framecnt( netj->packcache, netj->expected_framecnt, &next_frame_avail) ) { if( next_frame_avail == netj->expected_framecnt ) { we_have_the_expected_frame = 1; if( !netj->always_deadline ) break; } } if( ! netjack_poll_deadline( netj->sockfd, netj->next_deadline ) ) { break; } packet_cache_drain_socket( netj->packcache, netj->sockfd ); } // check if we know who to send our packets too. if (!netj->srcaddress_valid) if( netj->packcache->master_address_valid ) { memcpy (&(netj->syncsource_address), &(netj->packcache->master_address), sizeof( struct sockaddr_in ) ); netj->srcaddress_valid = 1; } // XXX: switching mode unconditionally is stupid. // if we were running free perhaps we like to behave differently // ie. fastforward one packet etc. // well... this is the first packet we see. hmm.... dunno ;S // it works... so... netj->running_free = 0; //if( !we_have_the_expected_frame ) // jack_error( "netxrun... %d", netj->expected_framecnt ); if( we_have_the_expected_frame ) { jack_time_t now = jack_get_time(); if( now < netj->next_deadline ) netj->time_to_deadline = netj->next_deadline - now; else netj->time_to_deadline = 0; packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) & (netj->rx_buf), netj->rx_bufsize , &packet_recv_time_stamp); pkthdr = (jacknet_packet_header *) netj->rx_buf; packet_header_ntoh(pkthdr); netj->deadline_goodness = (int)pkthdr->sync_state; netj->packet_data_valid = 1; int want_deadline; if( netj->jitter_val != 0 ) want_deadline = netj->jitter_val; else if( netj->latency < 4 ) want_deadline = -netj->period_usecs / 2; else want_deadline = (netj->period_usecs / 4 + 10 * (int)netj->period_usecs * netj->latency / 100); if( netj->deadline_goodness != MASTER_FREEWHEELS ) { if( netj->deadline_goodness < want_deadline ) { netj->next_deadline -= netj->period_usecs / 100; //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); } if( netj->deadline_goodness > want_deadline ) { netj->next_deadline += netj->period_usecs / 100; //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); } } // if( netj->next_deadline < (netj->period_usecs*70/100) ) { // jack_error( "master is forcing deadline_offset to below 70%% of period_usecs... increase latency setting on master" ); // netj->deadline_offset = (netj->period_usecs*90/100); // } netj->next_deadline += netj->period_usecs; } else { netj->time_to_deadline = 0; netj->next_deadline += netj->period_usecs; // bah... the packet is not there. // either // - it got lost. // - its late // - sync source is not sending anymore. // lets check if we have the next packets, we will just run a cycle without data. // in that case. if( packet_cache_get_next_available_framecnt( netj->packcache, netj->expected_framecnt, &next_frame_avail) ) { jack_nframes_t offset = next_frame_avail - netj->expected_framecnt; //XXX: hmm... i need to remember why resync_threshold wasn't right. //if( offset < netj->resync_threshold ) if( offset < 10 ) { // ok. don't do nothing. we will run without data. // this seems to be one or 2 lost packets. // // this can also be reordered packet jitter. // (maybe this is not happening in real live) // but it happens in netem. netj->packet_data_valid = 0; // I also found this happening, when the packet queue, is too full. // but wtf ? use a smaller latency. this link can handle that ;S if( packet_cache_get_fill( netj->packcache, netj->expected_framecnt ) > 80.0 ) netj->next_deadline -= netj->period_usecs / 2; } else { // the diff is too high. but we have a packet in the future. // lets resync. netj->expected_framecnt = next_frame_avail; packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) & (netj->rx_buf), netj->rx_bufsize, NULL ); pkthdr = (jacknet_packet_header *) netj->rx_buf; packet_header_ntoh(pkthdr); //netj->deadline_goodness = 0; netj->deadline_goodness = (int)pkthdr->sync_state - (int)netj->period_usecs * offset; netj->next_deadline_valid = 0; netj->packet_data_valid = 1; } } else { // no packets in buffer. netj->packet_data_valid = 0; //printf( "frame %d No Packet in queue. num_lost_packets = %d \n", netj->expected_framecnt, netj->num_lost_packets ); if( netj->num_lost_packets < 5 ) { // ok. No Packet in queue. The packet was either lost, // or we are running too fast. // // Adjusting the deadline unconditionally resulted in // too many xruns on master. // But we need to adjust for the case we are running too fast. // So lets check if the last packet is there now. // // It would not be in the queue anymore, if it had been // retrieved. This might break for redundancy, but // i will make the packet cache drop redundant packets, // that have already been retrieved. // if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { if( next_frame_avail == (netj->expected_framecnt - 1) ) { // Ok. the last packet is there now. // and it had not been retrieved. // // TODO: We are still dropping 2 packets. // perhaps we can adjust the deadline // when (num_packets lost == 0) // This might still be too much. netj->next_deadline += netj->period_usecs; } } } else if( (netj->num_lost_packets <= 100) ) { // lets try adjusting the deadline harder, for some packets, we might have just ran 2 fast. netj->next_deadline += netj->period_usecs * netj->latency / 8; } else { // But now we can check for any new frame available. // if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { netj->expected_framecnt = next_frame_avail; packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) & (netj->rx_buf), netj->rx_bufsize, NULL ); pkthdr = (jacknet_packet_header *) netj->rx_buf; packet_header_ntoh(pkthdr); netj->deadline_goodness = pkthdr->sync_state; netj->next_deadline_valid = 0; netj->packet_data_valid = 1; netj->running_free = 0; jack_info( "resync after freerun... %d", netj->expected_framecnt ); } else { if( netj->num_lost_packets == 101 ) { jack_info( "master seems gone... entering freerun mode", netj->expected_framecnt ); } netj->running_free = 1; // when we really don't see packets. // reset source address. and open possibility for new master. // maybe dsl reconnect. Also restart of netsource without fix // reply address changes port. if (netj->num_lost_packets > 200 ) { netj->srcaddress_valid = 0; packet_cache_reset_master_address( netj->packcache ); } } } } } int retval = 0; if( !netj->packet_data_valid ) { netj->num_lost_packets += 1; if( netj->num_lost_packets == 1 ) retval = netj->period_usecs; } else { if( (netj->num_lost_packets > 1) && !netj->running_free ) retval = (netj->num_lost_packets - 1) * netj->period_usecs; netj->num_lost_packets = 0; } return retval; } void netjack_send_silence( netjack_driver_state_t *netj, int syncstate ) { int tx_size = get_sample_size(netj->bitdepth) * netj->playback_channels * netj->net_period_up + sizeof(jacknet_packet_header); unsigned int *packet_buf, *packet_bufX; packet_buf = alloca( tx_size); jacknet_packet_header *tx_pkthdr = (jacknet_packet_header *)packet_buf; jacknet_packet_header *rx_pkthdr = (jacknet_packet_header *)netj->rx_buf; //framecnt = rx_pkthdr->framecnt; netj->reply_port = rx_pkthdr->reply_port; // offset packet_bufX by the packetheader. packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); tx_pkthdr->sync_state = syncstate; tx_pkthdr->framecnt = netj->expected_framecnt; // memset 0 the payload. int payload_size = get_sample_size(netj->bitdepth) * netj->playback_channels * netj->net_period_up; memset(packet_bufX, 0, payload_size); packet_header_hton(tx_pkthdr); if (netj->srcaddress_valid) { int r; if (netj->reply_port) netj->syncsource_address.sin_port = htons(netj->reply_port); for( r = 0; r < netj->redundancy; r++ ) netjack_sendto(netj->outsockfd, (char *)packet_buf, tx_size, 0, (struct sockaddr*) & (netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu); } } void netjack_attach( netjack_driver_state_t *netj ) { //puts ("net_driver_attach"); jack_port_t * port; char buf[32]; unsigned int chn; int port_flags; if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT #if HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 celt_int32 lookahead; netj->celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); #else celt_int32_t lookahead; netj->celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); #endif celt_mode_info( netj->celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); netj->codec_latency = 2 * lookahead; #endif } if( netj->bitdepth == OPUS_MODE ) { #if HAVE_OPUS netj->opus_mode = opus_custom_mode_create(netj->sample_rate, netj->period_size, NULL); #endif } if (netj->handle_transport_sync) jack_set_sync_callback(netj->client, (JackSyncCallback) net_driver_sync_cb, NULL); port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; for (chn = 0; chn < netj->capture_channels_audio; chn++) { snprintf (buf, sizeof(buf) - 1, "capture_%u", chn + 1); port = jack_port_register (netj->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); if (!port) { jack_error ("NET: cannot register port for %s", buf); break; } netj->capture_ports = jack_slist_append (netj->capture_ports, port); if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT #if HAVE_CELT_API_0_11 netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create_custom( netj->celt_mode, 1, NULL ) ); #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode, 1, NULL ) ); #else netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode ) ); #endif #endif } else if( netj->bitdepth == OPUS_MODE ) { #if HAVE_OPUS OpusCustomDecoder *decoder = opus_custom_decoder_create( netj->opus_mode, 1, NULL ); netj->capture_srcs = jack_slist_append(netj->capture_srcs, decoder ); #endif } else { #if HAVE_SAMPLERATE netj->capture_srcs = jack_slist_append(netj->capture_srcs, src_new(SRC_LINEAR, 1, NULL)); #endif } } for (chn = netj->capture_channels_audio; chn < netj->capture_channels; chn++) { snprintf (buf, sizeof(buf) - 1, "capture_%u", chn + 1); port = jack_port_register (netj->client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); if (!port) { jack_error ("NET: cannot register port for %s", buf); break; } netj->capture_ports = jack_slist_append (netj->capture_ports, port); } port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; for (chn = 0; chn < netj->playback_channels_audio; chn++) { snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1); port = jack_port_register (netj->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); if (!port) { jack_error ("NET: cannot register port for %s", buf); break; } netj->playback_ports = jack_slist_append (netj->playback_ports, port); if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT #if HAVE_CELT_API_0_11 CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_decoder_create_custom( celt_mode, 1, NULL ) ); #elif HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); #else CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); #endif #endif } else if( netj->bitdepth == OPUS_MODE ) { #if HAVE_OPUS const int kbps = netj->resample_factor; jack_log( "OPUS %dkbps\n", kbps); OpusCustomMode *opus_mode = opus_custom_mode_create( netj->sample_rate, netj->period_size, NULL ); // XXX free me in the end OpusCustomEncoder *oe = opus_custom_encoder_create( opus_mode, 1, NULL ); opus_custom_encoder_ctl(oe, OPUS_SET_BITRATE(kbps*1024)); // bits per second opus_custom_encoder_ctl(oe, OPUS_SET_COMPLEXITY(10)); opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY)); netj->playback_srcs = jack_slist_append(netj->playback_srcs, oe ); #endif } else { #if HAVE_SAMPLERATE netj->playback_srcs = jack_slist_append(netj->playback_srcs, src_new(SRC_LINEAR, 1, NULL)); #endif } } for (chn = netj->playback_channels_audio; chn < netj->playback_channels; chn++) { snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1); port = jack_port_register (netj->client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); if (!port) { jack_error ("NET: cannot register port for %s", buf); break; } netj->playback_ports = jack_slist_append (netj->playback_ports, port); } jack_activate (netj->client); } void netjack_detach( netjack_driver_state_t *netj ) { JSList * node; for (node = netj->capture_ports; node; node = jack_slist_next (node)) jack_port_unregister (netj->client, ((jack_port_t *) node->data)); jack_slist_free (netj->capture_ports); netj->capture_ports = NULL; for (node = netj->capture_srcs; node; node = jack_slist_next (node)) { #if HAVE_CELT if( netj->bitdepth == CELT_MODE ) { CELTDecoder * decoder = node->data; celt_decoder_destroy(decoder); } else #endif #if HAVE_OPUS if ( netj->bitdepth == OPUS_MODE ) { OpusCustomDecoder * decoder = node->data; opus_custom_decoder_destroy(decoder); } else #endif { #if HAVE_SAMPLERATE SRC_STATE * src = node->data; src_delete(src); #endif } } jack_slist_free (netj->capture_srcs); netj->playback_srcs = NULL; for (node = netj->playback_ports; node; node = jack_slist_next (node)) jack_port_unregister (netj->client, ((jack_port_t *) node->data)); jack_slist_free (netj->playback_ports); netj->playback_ports = NULL; for (node = netj->playback_srcs; node; node = jack_slist_next (node)) { #if HAVE_CELT if( netj->bitdepth == CELT_MODE ) { CELTEncoder * encoder = node->data; celt_encoder_destroy(encoder); } else #endif #if HAVE_OPUS if ( netj->bitdepth == OPUS_MODE ) { OpusCustomEncoder * encoder = node->data; opus_custom_encoder_destroy(encoder); } else #endif { #if HAVE_SAMPLERATE SRC_STATE * src = node->data; src_delete(src); #endif } } jack_slist_free (netj->playback_srcs); netj->playback_srcs = NULL; #if HAVE_CELT if( netj->bitdepth == CELT_MODE ) celt_mode_destroy(netj->celt_mode); #endif #if HAVE_OPUS if( netj->bitdepth == OPUS_MODE ) opus_custom_mode_destroy(netj->opus_mode); #endif } netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, jack_client_t * client, const char *name, unsigned int capture_ports, unsigned int playback_ports, unsigned int capture_ports_midi, unsigned int playback_ports_midi, jack_nframes_t sample_rate, jack_nframes_t period_size, unsigned int listen_port, unsigned int transport_sync, unsigned int resample_factor, unsigned int resample_factor_up, unsigned int bitdepth, unsigned int use_autoconfig, unsigned int latency, unsigned int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val ) { // Fill in netj values. // might be subject to autoconfig... // so don't calculate anything with them... netj->sample_rate = sample_rate; netj->period_size = period_size; netj->dont_htonl_floats = dont_htonl_floats; netj->listen_port = listen_port; netj->capture_channels = capture_ports + capture_ports_midi; netj->capture_channels_audio = capture_ports; netj->capture_channels_midi = capture_ports_midi; netj->capture_ports = NULL; netj->playback_channels = playback_ports + playback_ports_midi; netj->playback_channels_audio = playback_ports; netj->playback_channels_midi = playback_ports_midi; netj->playback_ports = NULL; netj->codec_latency = 0; netj->handle_transport_sync = transport_sync; netj->mtu = 1400; netj->latency = latency; netj->redundancy = redundancy; netj->use_autoconfig = use_autoconfig; netj->always_deadline = always_deadline; netj->client = client; if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != CELT_MODE) && (bitdepth != OPUS_MODE)) { jack_info ("Invalid bitdepth: %d (8, 16 or 0 for float) !!!", bitdepth); return NULL; } netj->bitdepth = bitdepth; if (resample_factor_up == 0) { resample_factor_up = resample_factor; } netj->resample_factor = resample_factor; netj->resample_factor_up = resample_factor_up; netj->jitter_val = jitter_val; netj->playback_srcs = NULL; netj->capture_srcs = NULL; return netj; } void netjack_release( netjack_driver_state_t *netj ) { close( netj->sockfd ); close( netj->outsockfd ); packet_cache_free( netj->packcache ); netj->packcache = NULL; } int netjack_startup( netjack_driver_state_t *netj ) { int first_pack_len; struct sockaddr_in address; // Now open the socket, and wait for the first packet to arrive... netj->sockfd = socket (AF_INET, SOCK_DGRAM, 0); #ifdef WIN32 if (netj->sockfd == INVALID_SOCKET) #else if (netj->sockfd == -1) #endif { jack_info ("socket error"); return -1; } address.sin_family = AF_INET; address.sin_port = htons(netj->listen_port); address.sin_addr.s_addr = htonl(INADDR_ANY); if (bind (netj->sockfd, (struct sockaddr *) &address, sizeof (address)) < 0) { jack_info("bind error"); return -1; } netj->outsockfd = socket (AF_INET, SOCK_DGRAM, 0); #ifdef WIN32 if (netj->outsockfd == INVALID_SOCKET) #else if (netj->outsockfd == -1) #endif { jack_info ("socket error"); return -1; } netj->srcaddress_valid = 0; if (netj->use_autoconfig) { jacknet_packet_header *first_packet = alloca (sizeof (jacknet_packet_header)); #ifdef WIN32 int address_size = sizeof( struct sockaddr_in ); #else socklen_t address_size = sizeof (struct sockaddr_in); #endif //jack_info ("Waiting for an incoming packet !!!"); //jack_info ("*** IMPORTANT *** Don't connect a client to jackd until the driver is attached to a clock source !!!"); while(1) { if( ! netjack_poll( netj->sockfd, 1000 ) ) { jack_info ("Waiting aborted"); return -1; } first_pack_len = recvfrom (netj->sockfd, (char *)first_packet, sizeof (jacknet_packet_header), 0, (struct sockaddr*) & netj->syncsource_address, &address_size); #ifdef WIN32 if( first_pack_len == -1 ) { first_pack_len = sizeof(jacknet_packet_header); break; } #else if (first_pack_len == sizeof (jacknet_packet_header)) break; #endif } netj->srcaddress_valid = 1; if (first_pack_len == sizeof (jacknet_packet_header)) { packet_header_ntoh (first_packet); jack_info ("AutoConfig Override !!!"); if (netj->sample_rate != first_packet->sample_rate) { jack_info ("AutoConfig Override: Master JACK sample rate = %d", first_packet->sample_rate); netj->sample_rate = first_packet->sample_rate; } if (netj->period_size != first_packet->period_size) { jack_info ("AutoConfig Override: Master JACK period size is %d", first_packet->period_size); netj->period_size = first_packet->period_size; } if (netj->capture_channels_audio != first_packet->capture_channels_audio) { jack_info ("AutoConfig Override: capture_channels_audio = %d", first_packet->capture_channels_audio); netj->capture_channels_audio = first_packet->capture_channels_audio; } if (netj->capture_channels_midi != first_packet->capture_channels_midi) { jack_info ("AutoConfig Override: capture_channels_midi = %d", first_packet->capture_channels_midi); netj->capture_channels_midi = first_packet->capture_channels_midi; } if (netj->playback_channels_audio != first_packet->playback_channels_audio) { jack_info ("AutoConfig Override: playback_channels_audio = %d", first_packet->playback_channels_audio); netj->playback_channels_audio = first_packet->playback_channels_audio; } if (netj->playback_channels_midi != first_packet->playback_channels_midi) { jack_info ("AutoConfig Override: playback_channels_midi = %d", first_packet->playback_channels_midi); netj->playback_channels_midi = first_packet->playback_channels_midi; } netj->mtu = first_packet->mtu; jack_info ("MTU is set to %d bytes", first_packet->mtu); netj->latency = first_packet->latency; } } netj->capture_channels = netj->capture_channels_audio + netj->capture_channels_midi; netj->playback_channels = netj->playback_channels_audio + netj->playback_channels_midi; if( (netj->capture_channels * netj->period_size * netj->latency * 4) > 100000000 ) { jack_error( "autoconfig requests more than 100MB packet cache... bailing out" ); exit(1); } if( netj->playback_channels > 1000 ) { jack_error( "autoconfig requests more than 1000 playback channels... bailing out" ); exit(1); } if( netj->mtu < (2 * sizeof( jacknet_packet_header )) ) { jack_error( "bullshit mtu requested by autoconfig" ); exit(1); } if( netj->sample_rate == 0 ) { jack_error( "sample_rate 0 requested by autoconfig" ); exit(1); } // After possible Autoconfig: do all calculations... netj->period_usecs = (jack_time_t) floor ((((float) netj->period_size) / (float)netj->sample_rate) * 1000000.0f); if( netj->latency == 0 ) netj->deadline_offset = 50 * netj->period_usecs; else netj->deadline_offset = netj->period_usecs + 10 * netj->latency * netj->period_usecs / 100; if( netj->bitdepth == CELT_MODE ) { // celt mode. // TODO: this is a hack. But i don't want to change the packet header. netj->resample_factor = (netj->resample_factor * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); netj->resample_factor_up = (netj->resample_factor_up * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); netj->net_period_down = netj->resample_factor; netj->net_period_up = netj->resample_factor_up; } else if( netj->bitdepth == OPUS_MODE ) { // Opus mode. // TODO: this is a hack. But i don't want to change the packet header, either netj->net_period_down = (netj->resample_factor * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); netj->net_period_up = (netj->resample_factor_up * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); } else { netj->net_period_down = (float) netj->period_size / (float) netj->resample_factor; netj->net_period_up = (float) netj->period_size / (float) netj->resample_factor_up; } netj->rx_bufsize = sizeof (jacknet_packet_header) + netj->net_period_down * netj->capture_channels * get_sample_size (netj->bitdepth); netj->packcache = packet_cache_new (netj->latency + 50, netj->rx_bufsize, netj->mtu); netj->expected_framecnt_valid = 0; netj->num_lost_packets = 0; netj->next_deadline_valid = 0; netj->deadline_goodness = 0; netj->time_to_deadline = 0; // Special handling for latency=0 if( netj->latency == 0 ) netj->resync_threshold = 0; else netj->resync_threshold = MIN( 15, netj->latency - 1 ); netj->running_free = 0; return 0; } jack2-1.9.22/common/netjack.h000066400000000000000000000122311436671425200156700ustar00rootroot00000000000000 /* Copyright (C) 2003 Robert Ham Copyright (C) 2005 Torben Hohn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __NETJACK_H__ #define __NETJACK_H__ #include #include #include #include #include "jack/jslist.h" #if HAVE_CELT #include #endif #if HAVE_OPUS #include #include #endif #ifdef __cplusplus extern "C" { #endif struct _packet_cache; typedef struct _netjack_driver_state netjack_driver_state_t; struct _netjack_driver_state { jack_nframes_t net_period_up; jack_nframes_t net_period_down; jack_nframes_t sample_rate; jack_nframes_t bitdepth; jack_nframes_t period_size; jack_time_t period_usecs; int dont_htonl_floats; int always_deadline; jack_nframes_t codec_latency; unsigned int listen_port; unsigned int capture_channels; unsigned int playback_channels; unsigned int capture_channels_audio; unsigned int playback_channels_audio; unsigned int capture_channels_midi; unsigned int playback_channels_midi; JSList *capture_ports; JSList *playback_ports; JSList *playback_srcs; JSList *capture_srcs; jack_client_t *client; #ifdef WIN32 SOCKET sockfd; SOCKET outsockfd; #else int sockfd; int outsockfd; #endif struct sockaddr_in syncsource_address; int reply_port; int srcaddress_valid; int sync_state; unsigned int handle_transport_sync; unsigned int *rx_buf; unsigned int rx_bufsize; //unsigned int tx_bufsize; unsigned int mtu; unsigned int latency; unsigned int redundancy; jack_nframes_t expected_framecnt; int expected_framecnt_valid; unsigned int num_lost_packets; jack_time_t next_deadline; jack_time_t deadline_offset; int next_deadline_valid; int packet_data_valid; int resync_threshold; int running_free; int deadline_goodness; jack_time_t time_to_deadline; unsigned int use_autoconfig; unsigned int resample_factor; unsigned int resample_factor_up; int jitter_val; struct _packet_cache * packcache; #if HAVE_CELT CELTMode *celt_mode; #endif #if HAVE_OPUS OpusCustomMode* opus_mode; #endif }; int netjack_wait( netjack_driver_state_t *netj ); void netjack_send_silence( netjack_driver_state_t *netj, int syncstate ); void netjack_read( netjack_driver_state_t *netj, jack_nframes_t nframes ) ; void netjack_write( netjack_driver_state_t *netj, jack_nframes_t nframes, int syncstate ); void netjack_attach( netjack_driver_state_t *netj ); void netjack_detach( netjack_driver_state_t *netj ); netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, jack_client_t * client, const char *name, unsigned int capture_ports, unsigned int playback_ports, unsigned int capture_ports_midi, unsigned int playback_ports_midi, jack_nframes_t sample_rate, jack_nframes_t period_size, unsigned int listen_port, unsigned int transport_sync, unsigned int resample_factor, unsigned int resample_factor_up, unsigned int bitdepth, unsigned int use_autoconfig, unsigned int latency, unsigned int redundancy, int dont_htonl_floats, int always_deadline, int jitter_val ); void netjack_release( netjack_driver_state_t *netj ); int netjack_startup( netjack_driver_state_t *netj ); #ifdef __cplusplus } #endif #endif jack2-1.9.22/common/netjack_packet.c000066400000000000000000001356621436671425200172300ustar00rootroot00000000000000 /* * NetJack - Packet Handling functions * * used by the driver and the jacknet_client * * Copyright (C) 2019 Karl Linden * Copyright (C) 2008 Marc-Olivier Barre * Copyright (C) 2008 Pieter Palmers * Copyright (C) 2006 Torben Hohn * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $ * */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #ifdef __APPLE__ #define _DARWIN_C_SOURCE #endif #if HAVE_PPOLL #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #ifdef WIN32 #include #include #define socklen_t int #else #include #include #include #endif #include #include #if HAVE_SAMPLERATE #include #endif #if HAVE_CELT #include #endif #if HAVE_OPUS #include #include #endif #include "netjack_packet.h" #include "JackError.h" #ifdef NO_JACK_ERROR #define jack_error printf #endif int fraggo = 0; void packet_header_hton (jacknet_packet_header *pkthdr) { pkthdr->capture_channels_audio = htonl(pkthdr->capture_channels_audio); pkthdr->playback_channels_audio = htonl(pkthdr->playback_channels_audio); pkthdr->capture_channels_midi = htonl(pkthdr->capture_channels_midi); pkthdr->playback_channels_midi = htonl(pkthdr->playback_channels_midi); pkthdr->period_size = htonl(pkthdr->period_size); pkthdr->sample_rate = htonl(pkthdr->sample_rate); pkthdr->sync_state = htonl(pkthdr->sync_state); pkthdr->transport_frame = htonl(pkthdr->transport_frame); pkthdr->transport_state = htonl(pkthdr->transport_state); pkthdr->framecnt = htonl(pkthdr->framecnt); pkthdr->latency = htonl(pkthdr->latency); pkthdr->reply_port = htonl(pkthdr->reply_port); pkthdr->mtu = htonl(pkthdr->mtu); pkthdr->fragment_nr = htonl(pkthdr->fragment_nr); } void packet_header_ntoh (jacknet_packet_header *pkthdr) { pkthdr->capture_channels_audio = ntohl(pkthdr->capture_channels_audio); pkthdr->playback_channels_audio = ntohl(pkthdr->playback_channels_audio); pkthdr->capture_channels_midi = ntohl(pkthdr->capture_channels_midi); pkthdr->playback_channels_midi = ntohl(pkthdr->playback_channels_midi); pkthdr->period_size = ntohl(pkthdr->period_size); pkthdr->sample_rate = ntohl(pkthdr->sample_rate); pkthdr->sync_state = ntohl(pkthdr->sync_state); pkthdr->transport_frame = ntohl(pkthdr->transport_frame); pkthdr->transport_state = ntohl(pkthdr->transport_state); pkthdr->framecnt = ntohl(pkthdr->framecnt); pkthdr->latency = ntohl(pkthdr->latency); pkthdr->reply_port = ntohl(pkthdr->reply_port); pkthdr->mtu = ntohl(pkthdr->mtu); pkthdr->fragment_nr = ntohl(pkthdr->fragment_nr); } int get_sample_size (int bitdepth) { if (bitdepth == 8) return sizeof (int8_t); if (bitdepth == 16) return sizeof (int16_t); //JN: why? is this for buffer sizes before or after encoding? //JN: if the former, why not int16_t, if the latter, shouldn't it depend on -c N? if( bitdepth == CELT_MODE ) return sizeof( unsigned char ); if( bitdepth == OPUS_MODE ) return sizeof( unsigned char ); return sizeof (int32_t); } int jack_port_is_audio(const char *porttype) { return (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0); } int jack_port_is_midi(const char *porttype) { return (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0); } // fragment management functions. packet_cache *packet_cache_new (int num_packets, int pkt_size, int mtu) { int fragment_payload_size = mtu - sizeof (jacknet_packet_header); int i, fragment_number; if( pkt_size == sizeof(jacknet_packet_header) ) fragment_number = 1; else fragment_number = (pkt_size - sizeof (jacknet_packet_header) - 1) / fragment_payload_size + 1; packet_cache *pcache = malloc (sizeof (packet_cache)); if (pcache == NULL) { jack_error ("could not allocate packet cache (1)"); return NULL; } pcache->size = num_packets; pcache->packets = malloc (sizeof (cache_packet) * num_packets); pcache->master_address_valid = 0; pcache->last_framecnt_retreived = 0; pcache->last_framecnt_retreived_valid = 0; if (pcache->packets == NULL) { jack_error ("could not allocate packet cache (2)"); return NULL; } for (i = 0; i < num_packets; i++) { pcache->packets[i].valid = 0; pcache->packets[i].num_fragments = fragment_number; pcache->packets[i].packet_size = pkt_size; pcache->packets[i].mtu = mtu; pcache->packets[i].framecnt = 0; pcache->packets[i].fragment_array = malloc (sizeof (char) * fragment_number); pcache->packets[i].packet_buf = malloc (pkt_size); if ((pcache->packets[i].fragment_array == NULL) || (pcache->packets[i].packet_buf == NULL)) { jack_error ("could not allocate packet cache (3)"); return NULL; } } pcache->mtu = mtu; return pcache; } void packet_cache_free (packet_cache *pcache) { int i; if( pcache == NULL ) return; for (i = 0; i < pcache->size; i++) { free (pcache->packets[i].fragment_array); free (pcache->packets[i].packet_buf); } free (pcache->packets); free (pcache); } cache_packet *packet_cache_get_packet (packet_cache *pcache, jack_nframes_t framecnt) { int i; cache_packet *retval; for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) return &(pcache->packets[i]); } // The Packet is not in the packet cache. // find a free packet. retval = packet_cache_get_free_packet (pcache); if (retval != NULL) { cache_packet_set_framecnt (retval, framecnt); return retval; } // No Free Packet available // Get The Oldest packet and reset it. retval = packet_cache_get_oldest_packet (pcache); //printf( "Dropping %d from Cache :S\n", retval->framecnt ); cache_packet_reset (retval); cache_packet_set_framecnt (retval, framecnt); return retval; } // TODO: fix wrapping case... need to pass // current expected frame here. // // or just save framecount into packet_cache. cache_packet *packet_cache_get_oldest_packet (packet_cache *pcache) { jack_nframes_t minimal_frame = JACK_MAX_FRAMES; cache_packet *retval = &(pcache->packets[0]); int i; for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid && (pcache->packets[i].framecnt < minimal_frame)) { minimal_frame = pcache->packets[i].framecnt; retval = &(pcache->packets[i]); } } return retval; } cache_packet *packet_cache_get_free_packet (packet_cache *pcache) { int i; for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid == 0) return &(pcache->packets[i]); } return NULL; } void cache_packet_reset (cache_packet *pack) { int i; pack->valid = 0; // XXX: i don't think this is necessary here... // fragment array is cleared in _set_framecnt() for (i = 0; i < pack->num_fragments; i++) pack->fragment_array[i] = 0; } void cache_packet_set_framecnt (cache_packet *pack, jack_nframes_t framecnt) { int i; pack->framecnt = framecnt; for (i = 0; i < pack->num_fragments; i++) pack->fragment_array[i] = 0; pack->valid = 1; } void cache_packet_add_fragment (cache_packet *pack, char *packet_buf, int rcv_len) { jacknet_packet_header *pkthdr = (jacknet_packet_header *) packet_buf; int fragment_payload_size = pack->mtu - sizeof (jacknet_packet_header); char *packet_bufX = pack->packet_buf + sizeof (jacknet_packet_header); char *dataX = packet_buf + sizeof (jacknet_packet_header); jack_nframes_t fragment_nr = ntohl (pkthdr->fragment_nr); jack_nframes_t framecnt = ntohl (pkthdr->framecnt); if (framecnt != pack->framecnt) { jack_error ("error. framecnts don't match"); return; } if (fragment_nr == 0) { memcpy (pack->packet_buf, packet_buf, rcv_len); pack->fragment_array[0] = 1; return; } if ((fragment_nr < pack->num_fragments) && (fragment_nr > 0)) { if ((fragment_nr * fragment_payload_size + rcv_len - sizeof (jacknet_packet_header)) <= (pack->packet_size - sizeof (jacknet_packet_header))) { memcpy (packet_bufX + fragment_nr * fragment_payload_size, dataX, rcv_len - sizeof (jacknet_packet_header)); pack->fragment_array[fragment_nr] = 1; } else jack_error ("too long packet received..."); } } int cache_packet_is_complete (cache_packet *pack) { int i; for (i = 0; i < pack->num_fragments; i++) if (pack->fragment_array[i] == 0) return 0; return 1; } #ifndef WIN32 // new poll using nanoseconds resolution and // not waiting forever. int netjack_poll_deadline (int sockfd, jack_time_t deadline) { struct pollfd fds; int poll_err = 0; #if HAVE_PPOLL struct timespec timeout_spec = { 0, 0 }; #else int timeout; #endif jack_time_t now = jack_get_time(); if( now >= deadline ) return 0; if( (deadline - now) >= 1000000 ) { jack_error( "deadline more than 1 second in the future, trimming it." ); deadline = now + 500000; } #if HAVE_PPOLL timeout_spec.tv_nsec = (deadline - now) * 1000; #else timeout = lrintf( (float)(deadline - now) / 1000.0 ); #endif fds.fd = sockfd; fds.events = POLLIN; #if HAVE_PPOLL poll_err = ppoll (&fds, 1, &timeout_spec, NULL); #else poll_err = poll (&fds, 1, timeout); #endif if (poll_err == -1) { switch (errno) { case EBADF: jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); break; case EFAULT: jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); break; case EINTR: jack_error ("Error %d: A signal occurred before any requested event", errno); break; case EINVAL: jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); break; case ENOMEM: jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); break; } } return poll_err; } int netjack_poll (int sockfd, int timeout) { struct pollfd fds; int i, poll_err = 0; sigset_t sigmask, rsigmask; struct sigaction action; sigemptyset(&sigmask); sigaddset(&sigmask, SIGHUP); sigaddset(&sigmask, SIGINT); sigaddset(&sigmask, SIGQUIT); sigaddset(&sigmask, SIGPIPE); sigaddset(&sigmask, SIGTERM); sigaddset(&sigmask, SIGUSR1); sigaddset(&sigmask, SIGUSR2); action.sa_handler = SIG_DFL; action.sa_mask = sigmask; action.sa_flags = SA_RESTART; for (i = 1; i < NSIG; i++) if (sigismember (&sigmask, i)) sigaction (i, &action, 0); fds.fd = sockfd; fds.events = POLLIN; sigprocmask(SIG_UNBLOCK, &sigmask, &rsigmask); while (poll_err == 0) { poll_err = poll (&fds, 1, timeout); } sigprocmask(SIG_SETMASK, &rsigmask, NULL); if (poll_err == -1) { switch (errno) { case EBADF: jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); break; case EFAULT: jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); break; case EINTR: jack_error ("Error %d: A signal occurred before any requested event", errno); break; case EINVAL: jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); break; case ENOMEM: jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); break; } return 0; } return 1; } #else int netjack_poll (int sockfd, int timeout) { jack_error( "netjack_poll not implemented" ); return 0; } int netjack_poll_deadline (int sockfd, jack_time_t deadline) { fd_set fds; FD_ZERO( &fds ); FD_SET( sockfd, &fds ); struct timeval timeout; while( 1 ) { jack_time_t now = jack_get_time(); if( now >= deadline ) return 0; int timeout_usecs = (deadline - now); //jack_error( "timeout = %d", timeout_usecs ); timeout.tv_sec = 0; timeout.tv_usec = (timeout_usecs < 500) ? 500 : timeout_usecs; timeout.tv_usec = (timeout_usecs > 1000000) ? 500000 : timeout_usecs; int poll_err = select (0, &fds, NULL, NULL, &timeout); if( poll_err != 0 ) return poll_err; } return 0; } #endif // This now reads all a socket has into the cache. // replacing netjack_recv functions. void packet_cache_drain_socket( packet_cache *pcache, int sockfd ) { char *rx_packet = alloca (pcache->mtu); jacknet_packet_header *pkthdr = (jacknet_packet_header *) rx_packet; int rcv_len; jack_nframes_t framecnt; cache_packet *cpack; struct sockaddr_in sender_address; #ifdef WIN32 int senderlen = sizeof( struct sockaddr_in ); u_long parm = 1; ioctlsocket( sockfd, FIONBIO, &parm ); #else unsigned int senderlen = sizeof( struct sockaddr_in ); #endif while (1) { #ifdef WIN32 rcv_len = recvfrom (sockfd, rx_packet, pcache->mtu, 0, (struct sockaddr*) &sender_address, &senderlen); #else rcv_len = recvfrom (sockfd, rx_packet, pcache->mtu, MSG_DONTWAIT, (struct sockaddr*) &sender_address, &senderlen); #endif if (rcv_len < 0) return; if (pcache->master_address_valid) { // Verify its from our master. if (memcmp (&sender_address, &(pcache->master_address), senderlen) != 0) continue; } else { // Setup this one as master //printf( "setup master...\n" ); memcpy ( &(pcache->master_address), &sender_address, senderlen ); pcache->master_address_valid = 1; } framecnt = ntohl (pkthdr->framecnt); if( pcache->last_framecnt_retreived_valid && (framecnt <= pcache->last_framecnt_retreived )) continue; cpack = packet_cache_get_packet (pcache, framecnt); cache_packet_add_fragment (cpack, rx_packet, rcv_len); cpack->recv_timestamp = jack_get_time(); } } void packet_cache_reset_master_address( packet_cache *pcache ) { pcache->master_address_valid = 0; pcache->last_framecnt_retreived = 0; pcache->last_framecnt_retreived_valid = 0; } void packet_cache_clear_old_packets (packet_cache *pcache, jack_nframes_t framecnt ) { int i; for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid && (pcache->packets[i].framecnt < framecnt)) { cache_packet_reset (&(pcache->packets[i])); } } } int packet_cache_retreive_packet_pointer( packet_cache *pcache, jack_nframes_t framecnt, char **packet_buf, int pkt_size, jack_time_t *timestamp ) { int i; cache_packet *cpack = NULL; for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) { cpack = &(pcache->packets[i]); break; } } if( cpack == NULL ) { //printf( "retrieve packet: %d....not found\n", framecnt ); return -1; } if( !cache_packet_is_complete( cpack ) ) { return -1; } // ok. cpack is the one we want and its complete. *packet_buf = cpack->packet_buf; if( timestamp ) *timestamp = cpack->recv_timestamp; pcache->last_framecnt_retreived_valid = 1; pcache->last_framecnt_retreived = framecnt; return pkt_size; } int packet_cache_release_packet( packet_cache *pcache, jack_nframes_t framecnt ) { int i; cache_packet *cpack = NULL; for (i = 0; i < pcache->size; i++) { if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) { cpack = &(pcache->packets[i]); break; } } if( cpack == NULL ) { //printf( "retrieve packet: %d....not found\n", framecnt ); return -1; } if( !cache_packet_is_complete( cpack ) ) { return -1; } cache_packet_reset (cpack); packet_cache_clear_old_packets( pcache, framecnt ); return 0; } float packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt ) { int num_packets_before_us = 0; int i; for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); if (cpack->valid && cache_packet_is_complete( cpack )) if( cpack->framecnt >= expected_framecnt ) num_packets_before_us += 1; } return 100.0 * (float)num_packets_before_us / (float)( pcache->size ); } // Returns 0 when no valid packet is inside the cache. int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ) { int i; jack_nframes_t best_offset = JACK_MAX_FRAMES / 2 - 1; int retval = 0; for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); if (!cpack->valid || !cache_packet_is_complete( cpack )) { //printf( "invalid\n" ); continue; } if( cpack->framecnt < expected_framecnt ) continue; if( (cpack->framecnt - expected_framecnt) > best_offset ) { continue; } best_offset = cpack->framecnt - expected_framecnt; retval = 1; if (best_offset == 0) break; } if (retval && framecnt) *framecnt = expected_framecnt + best_offset; return retval; } int packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_t *framecnt ) { int i; jack_nframes_t best_value = 0; int retval = 0; for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); if (!cpack->valid || !cache_packet_is_complete( cpack )) { //printf( "invalid\n" ); continue; } if (cpack->framecnt < best_value) { continue; } best_value = cpack->framecnt; retval = 1; } if (retval && framecnt) *framecnt = best_value; return retval; } // Returns 0 when no valid packet is inside the cache. int packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ) { int i; jack_nframes_t best_offset = 0; int retval = 0; for (i = 0; i < pcache->size; i++) { cache_packet *cpack = &(pcache->packets[i]); //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); if (!cpack->valid || !cache_packet_is_complete( cpack )) { //printf( "invalid\n" ); continue; } if ((cpack->framecnt - expected_framecnt) < best_offset) { continue; } best_offset = cpack->framecnt - expected_framecnt; retval = 1; if( best_offset == 0 ) break; } if (retval && framecnt) *framecnt = JACK_MAX_FRAMES - best_offset; return retval; } // fragmented packet IO void netjack_sendto (int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu) { int frag_cnt = 0; char *tx_packet, *dataX; jacknet_packet_header *pkthdr; tx_packet = alloca (mtu + 10); dataX = tx_packet + sizeof (jacknet_packet_header); pkthdr = (jacknet_packet_header *) tx_packet; int fragment_payload_size = mtu - sizeof (jacknet_packet_header); if (pkt_size <= mtu) { int err; pkthdr = (jacknet_packet_header *) packet_buf; pkthdr->fragment_nr = htonl (0); err = sendto(sockfd, packet_buf, pkt_size, flags, addr, addr_size); if( err < 0 ) { //printf( "error in send\n" ); perror( "send" ); } } else { int err; // Copy the packet header to the tx pack first. memcpy(tx_packet, packet_buf, sizeof (jacknet_packet_header)); // Now loop and send all char *packet_bufX = packet_buf + sizeof (jacknet_packet_header); while (packet_bufX < (packet_buf + pkt_size - fragment_payload_size)) { pkthdr->fragment_nr = htonl (frag_cnt++); memcpy (dataX, packet_bufX, fragment_payload_size); sendto (sockfd, tx_packet, mtu, flags, addr, addr_size); packet_bufX += fragment_payload_size; } int last_payload_size = packet_buf + pkt_size - packet_bufX; memcpy (dataX, packet_bufX, last_payload_size); pkthdr->fragment_nr = htonl (frag_cnt); //jack_log("last fragment_count = %d, payload_size = %d\n", fragment_count, last_payload_size); // sendto(last_pack_size); err = sendto(sockfd, tx_packet, last_payload_size + sizeof(jacknet_packet_header), flags, addr, addr_size); if( err < 0 ) { //printf( "error in send\n" ); perror( "send" ); } } } void decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf) { int i; jack_midi_clear_buffer (buf); for (i = 0; i < buffer_size_uint32 - 3;) { uint32_t payload_size; payload_size = buffer_uint32[i]; payload_size = ntohl (payload_size); if (payload_size) { jack_midi_event_t event; event.time = ntohl (buffer_uint32[i + 1]); event.size = ntohl (buffer_uint32[i + 2]); event.buffer = (jack_midi_data_t*) (&(buffer_uint32[i + 3])); jack_midi_event_write (buf, event.time, event.buffer, event.size); // skip to the next event unsigned int nb_data_quads = (((event.size - 1) & ~0x3) >> 2) + 1; i += 3 + nb_data_quads; } else break; // no events can follow an empty event, we're done } } void encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf) { int i; unsigned int written = 0; // midi port, encode midi events unsigned int nevents = jack_midi_get_event_count (buf); for (i = 0; i < nevents; ++i) { jack_midi_event_t event; jack_midi_event_get (&event, buf, i); unsigned int nb_data_quads = (((event.size - 1) & ~0x3) >> 2) + 1; unsigned int payload_size = 3 + nb_data_quads; // only write if we have sufficient space for the event // otherwise drop it if (written + payload_size < buffer_size_uint32 - 1) { // write header buffer_uint32[written] = htonl (payload_size); written++; buffer_uint32[written] = htonl (event.time); written++; buffer_uint32[written] = htonl (event.size); written++; // write data jack_midi_data_t* tmpbuff = (jack_midi_data_t*)(&(buffer_uint32[written])); memcpy (tmpbuff, event.buffer, event.size); written += nb_data_quads; } else { // buffer overflow jack_error ("midi buffer overflow"); break; } } // now put a netjack_midi 'no-payload' event, signaling EOF buffer_uint32[written] = 0; } // render functions for float void render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) { int chn = 0; JSList *node = capture_ports; #if HAVE_SAMPLERATE JSList *src_node = capture_srcs; #endif uint32_t *packet_bufX = (uint32_t *)packet_payload; if (!packet_payload) return; while (node != NULL) { int i; int_float_t val; #if HAVE_SAMPLERATE SRC_DATA src; #endif jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary if (net_period_down != nframes) { SRC_STATE *src_state = src_node->data; for (i = 0; i < net_period_down; i++) { packet_bufX[i] = ntohl (packet_bufX[i]); } src.data_in = (float *) packet_bufX; src.input_frames = net_period_down; src.data_out = buf; src.output_frames = nframes; src.src_ratio = (float) nframes / (float) net_period_down; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); src_node = jack_slist_next (src_node); } else #endif { if( dont_htonl_floats ) { memcpy( buf, packet_bufX, net_period_down * sizeof(jack_default_audio_sample_t)); } else { for (i = 0; i < net_period_down; i++) { val.i = packet_bufX[i]; val.i = ntohl (val.i); buf[i] = val.f; } } } } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down; uint32_t * buffer_uint32 = (uint32_t*)packet_bufX; decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats ) { int chn = 0; JSList *node = playback_ports; #if HAVE_SAMPLERATE JSList *src_node = playback_srcs; #endif uint32_t *packet_bufX = (uint32_t *) packet_payload; while (node != NULL) { #if HAVE_SAMPLERATE SRC_DATA src; #endif int i; int_float_t val; jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { // audio port, resample if necessary #if HAVE_SAMPLERATE if (net_period_up != nframes) { SRC_STATE *src_state = src_node->data; src.data_in = buf; src.input_frames = nframes; src.data_out = (float *) packet_bufX; src.output_frames = net_period_up; src.src_ratio = (float) net_period_up / (float) nframes; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); for (i = 0; i < net_period_up; i++) { packet_bufX[i] = htonl (packet_bufX[i]); } src_node = jack_slist_next (src_node); } else #endif { if( dont_htonl_floats ) { memcpy( packet_bufX, buf, net_period_up * sizeof(jack_default_audio_sample_t) ); } else { for (i = 0; i < net_period_up; i++) { val.f = buf[i]; val.i = htonl (val.i); packet_bufX[i] = val.i; } } } } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } // render functions for 16bit void render_payload_to_jack_ports_16bit (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) { int chn = 0; JSList *node = capture_ports; #if HAVE_SAMPLERATE JSList *src_node = capture_srcs; #endif uint16_t *packet_bufX = (uint16_t *)packet_payload; if( !packet_payload ) return; while (node != NULL) { int i; //uint32_t val; #if HAVE_SAMPLERATE SRC_DATA src; #endif jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); #if HAVE_SAMPLERATE float *floatbuf = alloca (sizeof(float) * net_period_down); #endif const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { // audio port, resample if necessary #if HAVE_SAMPLERATE if (net_period_down != nframes) { SRC_STATE *src_state = src_node->data; for (i = 0; i < net_period_down; i++) { floatbuf[i] = ((float) ntohs(packet_bufX[i])) / 32767.0 - 1.0; } src.data_in = floatbuf; src.input_frames = net_period_down; src.data_out = buf; src.output_frames = nframes; src.src_ratio = (float) nframes / (float) net_period_down; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); src_node = jack_slist_next (src_node); } else #endif for (i = 0; i < net_period_down; i++) buf[i] = ((float) ntohs (packet_bufX[i])) / 32768.0 - 1.0; } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void render_jack_ports_to_payload_16bit (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) { int chn = 0; JSList *node = playback_ports; #if HAVE_SAMPLERATE JSList *src_node = playback_srcs; #endif uint16_t *packet_bufX = (uint16_t *)packet_payload; while (node != NULL) { #if HAVE_SAMPLERATE SRC_DATA src; #endif int i; jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { // audio port, resample if necessary #if HAVE_SAMPLERATE if (net_period_up != nframes) { SRC_STATE *src_state = src_node->data; float *floatbuf = alloca (sizeof(float) * net_period_up); src.data_in = buf; src.input_frames = nframes; src.data_out = floatbuf; src.output_frames = net_period_up; src.src_ratio = (float) net_period_up / (float) nframes; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); for (i = 0; i < net_period_up; i++) { packet_bufX[i] = htons (((uint16_t)((floatbuf[i] + 1.0) * 32767.0))); } src_node = jack_slist_next (src_node); } else #endif for (i = 0; i < net_period_up; i++) packet_bufX[i] = htons(((uint16_t)((buf[i] + 1.0) * 32767.0))); } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } // render functions for 8bit void render_payload_to_jack_ports_8bit (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) { int chn = 0; JSList *node = capture_ports; #if HAVE_SAMPLERATE JSList *src_node = capture_srcs; #endif int8_t *packet_bufX = (int8_t *)packet_payload; if (!packet_payload) return; while (node != NULL) { int i; //uint32_t val; #if HAVE_SAMPLERATE SRC_DATA src; #endif jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); #if HAVE_SAMPLERATE float *floatbuf = alloca (sizeof (float) * net_period_down); #endif const char *porttype = jack_port_type (port); if (jack_port_is_audio(porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary if (net_period_down != nframes) { SRC_STATE *src_state = src_node->data; for (i = 0; i < net_period_down; i++) floatbuf[i] = ((float) packet_bufX[i]) / 127.0; src.data_in = floatbuf; src.input_frames = net_period_down; src.data_out = buf; src.output_frames = nframes; src.src_ratio = (float) nframes / (float) net_period_down; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); src_node = jack_slist_next (src_node); } else #endif for (i = 0; i < net_period_down; i++) buf[i] = ((float) packet_bufX[i]) / 127.0; } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void render_jack_ports_to_payload_8bit (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) { int chn = 0; JSList *node = playback_ports; #if HAVE_SAMPLERATE JSList *src_node = playback_srcs; #endif int8_t *packet_bufX = (int8_t *)packet_payload; while (node != NULL) { #if HAVE_SAMPLERATE SRC_DATA src; #endif int i; jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary if (net_period_up != nframes) { SRC_STATE *src_state = src_node->data; float *floatbuf = alloca (sizeof (float) * net_period_up); src.data_in = buf; src.input_frames = nframes; src.data_out = floatbuf; src.output_frames = net_period_up; src.src_ratio = (float) net_period_up / (float) nframes; src.end_of_input = 0; src_set_ratio (src_state, src.src_ratio); src_process (src_state, &src); for (i = 0; i < net_period_up; i++) packet_bufX[i] = floatbuf[i] * 127.0; src_node = jack_slist_next (src_node); } else #endif for (i = 0; i < net_period_up; i++) packet_bufX[i] = buf[i] * 127.0; } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 4; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } #if HAVE_CELT // render functions for celt. void render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) { int chn = 0; JSList *node = capture_ports; JSList *src_node = capture_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { // audio port, decode celt data. CELTDecoder *decoder = src_node->data; #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 if( !packet_payload ) celt_decode_float( decoder, NULL, net_period_down, buf, nframes ); else celt_decode_float( decoder, packet_bufX, net_period_down, buf, nframes ); #else if( !packet_payload ) celt_decode_float( decoder, NULL, net_period_down, buf ); else celt_decode_float( decoder, packet_bufX, net_period_down, buf ); #endif src_node = jack_slist_next (src_node); } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; if( packet_payload ) decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) { int chn = 0; JSList *node = playback_ports; JSList *src_node = playback_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { // audio port, encode celt data. int encoded_bytes; float *floatbuf = alloca (sizeof(float) * nframes ); memcpy( floatbuf, buf, nframes * sizeof(float) ); CELTEncoder *encoder = src_node->data; #if HAVE_CELT_API_0_8 || HAVE_CELT_API_0_11 encoded_bytes = celt_encode_float( encoder, floatbuf, nframes, packet_bufX, net_period_up ); #else encoded_bytes = celt_encode_float( encoder, floatbuf, NULL, packet_bufX, net_period_up ); #endif if( encoded_bytes != net_period_up ) printf( "something in celt changed. netjack needs to be changed to handle this.\n" ); src_node = jack_slist_next( src_node ); } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } #endif #if HAVE_OPUS #define CDO (sizeof(short)) ///< compressed data offset (first 2 bytes are length) // render functions for Opus. void render_payload_to_jack_ports_opus (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) { int chn = 0; JSList *node = capture_ports; JSList *src_node = capture_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { // audio port, decode opus data. OpusCustomDecoder *decoder = (OpusCustomDecoder*) src_node->data; if( !packet_payload ) memset(buf, 0, nframes * sizeof(float)); else { unsigned short len; memcpy(&len, packet_bufX, CDO); len = ntohs(len); opus_custom_decode_float( decoder, packet_bufX + CDO, len, buf, nframes ); } src_node = jack_slist_next (src_node); } else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; if( packet_payload ) decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); chn++; } } void render_jack_ports_to_payload_opus (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) { int chn = 0; JSList *node = playback_ports; JSList *src_node = playback_srcs; unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *porttype = jack_port_type (port); if (jack_port_is_audio (porttype)) { // audio port, encode opus data. int encoded_bytes; float *floatbuf = alloca (sizeof(float) * nframes ); memcpy( floatbuf, buf, nframes * sizeof(float) ); OpusCustomEncoder *encoder = (OpusCustomEncoder*) src_node->data; encoded_bytes = opus_custom_encode_float( encoder, floatbuf, nframes, packet_bufX + CDO, net_period_up - CDO ); unsigned short len = htons(encoded_bytes); memcpy(packet_bufX, &len, CDO); src_node = jack_slist_next( src_node ); } else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_up / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_up); node = jack_slist_next (node); chn++; } } #endif /* Wrapper functions with bitdepth argument... */ void render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) { if (bitdepth == 8) render_payload_to_jack_ports_8bit (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); else if (bitdepth == 16) render_payload_to_jack_ports_16bit (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); #if HAVE_CELT else if (bitdepth == CELT_MODE) render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); #endif #if HAVE_OPUS else if (bitdepth == OPUS_MODE) render_payload_to_jack_ports_opus (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); #endif else render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); } void render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats) { if (bitdepth == 8) render_jack_ports_to_payload_8bit (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); else if (bitdepth == 16) render_jack_ports_to_payload_16bit (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); #if HAVE_CELT else if (bitdepth == CELT_MODE) render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); #endif #if HAVE_OPUS else if (bitdepth == OPUS_MODE) render_jack_ports_to_payload_opus (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); #endif else render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); } jack2-1.9.22/common/netjack_packet.h000066400000000000000000000130431436671425200172210ustar00rootroot00000000000000 /* * NetJack - Packet Handling functions * * used by the driver and the jacknet_client * * Copyright (C) 2006 Torben Hohn * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $ * */ #ifndef __JACK_NET_PACKET_H__ #define __JACK_NET_PACKET_H__ #ifdef __cplusplus extern "C" { #endif #include #include #include #include // The Packet Header. #define CELT_MODE 1000 // Magic bitdepth value that indicates CELT compression #define OPUS_MODE 999 // Magic bitdepth value that indicates OPUS compression #define MASTER_FREEWHEELS 0x80000000 typedef struct _jacknet_packet_header jacknet_packet_header; struct _jacknet_packet_header { // General AutoConf Data jack_nframes_t capture_channels_audio; jack_nframes_t playback_channels_audio; jack_nframes_t capture_channels_midi; jack_nframes_t playback_channels_midi; jack_nframes_t period_size; jack_nframes_t sample_rate; // Transport Sync jack_nframes_t sync_state; jack_nframes_t transport_frame; jack_nframes_t transport_state; // Packet loss Detection, and latency reduction jack_nframes_t framecnt; jack_nframes_t latency; jack_nframes_t reply_port; jack_nframes_t mtu; jack_nframes_t fragment_nr; }; typedef union _int_float int_float_t; union _int_float { uint32_t i; float f; }; // fragment reorder cache. typedef struct _cache_packet cache_packet; struct _cache_packet { int valid; int num_fragments; int packet_size; int mtu; jack_time_t recv_timestamp; jack_nframes_t framecnt; char * fragment_array; char * packet_buf; }; typedef struct _packet_cache packet_cache; struct _packet_cache { int size; cache_packet *packets; int mtu; struct sockaddr_in master_address; int master_address_valid; jack_nframes_t last_framecnt_retreived; int last_framecnt_retreived_valid; }; // fragment cache function prototypes // XXX: Some of these are private. packet_cache *packet_cache_new(int num_packets, int pkt_size, int mtu); void packet_cache_free(packet_cache *pkt_cache); cache_packet *packet_cache_get_packet(packet_cache *pkt_cache, jack_nframes_t framecnt); cache_packet *packet_cache_get_oldest_packet(packet_cache *pkt_cache); cache_packet *packet_cache_get_free_packet(packet_cache *pkt_cache); void cache_packet_reset(cache_packet *pack); void cache_packet_set_framecnt(cache_packet *pack, jack_nframes_t framecnt); void cache_packet_add_fragment(cache_packet *pack, char *packet_buf, int rcv_len); int cache_packet_is_complete(cache_packet *pack); void packet_cache_drain_socket( packet_cache *pcache, int sockfd ); void packet_cache_reset_master_address( packet_cache *pcache ); float packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt ); int packet_cache_retreive_packet_pointer( packet_cache *pcache, jack_nframes_t framecnt, char **packet_buf, int pkt_size, jack_time_t *timestamp ); int packet_cache_release_packet( packet_cache *pcache, jack_nframes_t framecnt ); int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); int packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_t *framecnt ); int packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); // Function Prototypes int netjack_poll_deadline (int sockfd, jack_time_t deadline); void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu); int get_sample_size(int bitdepth); void packet_header_hton(jacknet_packet_header *pkthdr); void packet_header_ntoh(jacknet_packet_header *pkthdr); void render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats ); void render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats ); // XXX: This is sort of deprecated: // This one waits forever. an is not using ppoll int netjack_poll(int sockfd, int timeout); void decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); void encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); #ifdef __cplusplus } #endif #endif jack2-1.9.22/common/promiscuous.c000066400000000000000000000046371436671425200166470ustar00rootroot00000000000000/* Copyright (C) 2014-2017 Cédric Schieli This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef WIN32 #include #include #include #include #include #include #include #ifdef __APPLE__ #include #endif #include "JackError.h" #endif int jack_group2gid(const char* group) { #ifdef WIN32 return -1; #else size_t buflen; char *buf; int ret; struct group grp; struct group *result; if (!group || !*group) return -1; ret = strtol(group, &buf, 10); if (!*buf) return ret; /* MacOSX only defines _SC_GETGR_R_SIZE_MAX starting from 10.4 */ #if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 buflen = 4096; #else buflen = sysconf(_SC_GETGR_R_SIZE_MAX); if (buflen == -1) buflen = 4096; #endif buf = (char*)malloc(buflen); while (buf && ((ret = getgrnam_r(group, &grp, buf, buflen, &result)) == ERANGE)) { buflen *= 2; buf = (char*)realloc(buf, buflen); } if (!buf) return -1; free(buf); if (ret || !result) return -1; return grp.gr_gid; #endif } #ifndef WIN32 int jack_promiscuous_perms(int fd, const char* path, gid_t gid) { mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; if (gid >= 0) { if (((fd < 0) ? chown(path, -1, gid) : fchown(fd, -1, gid)) < 0) { jack_log("Cannot chgrp %s: %s. Falling back to permissive perms.", path, strerror(errno)); } else { mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; } } if (((fd < 0) ? chmod(path, mode) : fchmod(fd, mode)) < 0) { jack_log("Cannot chmod %s: %s. Falling back to default (umask) perms.", path, strerror(errno)); return -1; } return 0; } #endif jack2-1.9.22/common/promiscuous.h000066400000000000000000000022101436671425200166350ustar00rootroot00000000000000/* Copyright (C) 2014-2017 Cédric Schieli This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_gid_h__ #define __jack_gid_h__ #ifdef __cplusplus extern "C" { #endif int jack_group2gid (const char *group); /*!< Lookup gid for a UNIX group in a thread-safe way */ #ifndef WIN32 int jack_promiscuous_perms (int fd, const char *path, gid_t gid); /*!< Set promiscuous permissions on object referenced by fd and/or path */ #endif #ifdef __cplusplus } #endif #endif /* __jack_gid_h__ */ jack2-1.9.22/common/ringbuffer.c000066400000000000000000000254261436671425200164070ustar00rootroot00000000000000/* Copyright (C) 2000 Paul Davis Copyright (C) 2003 Rohan Drape This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. This is safe for the case of one read thread and one write thread. */ #include #include #ifdef USE_MLOCK #include #endif /* USE_MLOCK */ #include "JackCompilerDeps.h" /* Portable definitions for acquire and release fences */ #if defined(_MSC_VER) #if defined(_M_AMD64) || defined(_M_IX86) || defined(_M_X64) /* Only compiler fences are needed on x86. In fact, GCC * will generate no instructions for acq/rel fences on * x86 */ #include #define JACK_ACQ_FENCE() _ReadBarrier() #define JACK_REL_FENCE() _WriteBarrier() #else /* Use full memory fence for non-x86 systems with msvc */ #include #define JACK_ACQ_FENCE() MemoryBarrier() #define JACK_REL_FENCE() MemoryBarrier() #endif #elif defined(__GNUC__) #ifdef __ATOMIC_ACQUIRE #define JACK_ACQ_FENCE() __atomic_thread_fence(__ATOMIC_ACQUIRE) #define JACK_REL_FENCE() __atomic_thread_fence(__ATOMIC_RELEASE) #else /* Fallback to the legacy __sync builtin (full memory fence) */ #define JACK_ACQ_FENCE() __sync_synchronize() #define JACK_REL_FENCE() __sync_synchronize() #endif #else #define JACK_ACQ_FENCE() #define JACK_REL_FENCE() #endif typedef struct { char *buf; size_t len; } jack_ringbuffer_data_t ; typedef struct { char *buf; size_t write_ptr; size_t read_ptr; size_t size; size_t size_mask; int mlocked; } jack_ringbuffer_t ; LIB_EXPORT jack_ringbuffer_t *jack_ringbuffer_create(size_t sz); LIB_EXPORT void jack_ringbuffer_free(jack_ringbuffer_t *rb); LIB_EXPORT void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); LIB_EXPORT void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); LIB_EXPORT size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt); LIB_EXPORT size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt); LIB_EXPORT void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt); LIB_EXPORT size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb); LIB_EXPORT int jack_ringbuffer_mlock(jack_ringbuffer_t *rb); LIB_EXPORT void jack_ringbuffer_reset(jack_ringbuffer_t *rb); LIB_EXPORT void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz); LIB_EXPORT size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, size_t cnt); void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt); size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb); /* Create a new ringbuffer to hold at least `sz' bytes of data. The actual buffer size is rounded up to the next power of two. */ LIB_EXPORT jack_ringbuffer_t * jack_ringbuffer_create (size_t sz) { int power_of_two; jack_ringbuffer_t *rb; if ((rb = (jack_ringbuffer_t *) malloc (sizeof (jack_ringbuffer_t))) == NULL) { return NULL; } for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); rb->size = 1 << power_of_two; rb->size_mask = rb->size; rb->size_mask -= 1; rb->write_ptr = 0; rb->read_ptr = 0; if ((rb->buf = (char *) malloc (rb->size)) == NULL) { free (rb); return NULL; } rb->mlocked = 0; return rb; } /* Free all data associated with the ringbuffer `rb'. */ LIB_EXPORT void jack_ringbuffer_free (jack_ringbuffer_t * rb) { #ifdef USE_MLOCK if (rb->mlocked) { munlock (rb->buf, rb->size); } #endif /* USE_MLOCK */ free (rb->buf); free (rb); } /* Lock the data block of `rb' using the system call 'mlock'. */ LIB_EXPORT int jack_ringbuffer_mlock (jack_ringbuffer_t * rb) { #ifdef USE_MLOCK if (mlock (rb->buf, rb->size)) { return -1; } #endif /* USE_MLOCK */ rb->mlocked = 1; return 0; } /* Reset the read and write pointers to zero. This is not thread safe. */ LIB_EXPORT void jack_ringbuffer_reset (jack_ringbuffer_t * rb) { rb->read_ptr = 0; rb->write_ptr = 0; memset(rb->buf, 0, rb->size); } /* Reset the read and write pointers to zero. This is not thread safe. */ LIB_EXPORT void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz) { rb->size = sz; rb->size_mask = rb->size; rb->size_mask -= 1; rb->read_ptr = 0; rb->write_ptr = 0; } /* Return the number of bytes available for reading. This is the number of bytes in front of the read pointer and behind the write pointer. */ LIB_EXPORT size_t jack_ringbuffer_read_space (const jack_ringbuffer_t * rb) { size_t w, r; w = rb->write_ptr; JACK_ACQ_FENCE(); r = rb->read_ptr; return (w - r) & rb->size_mask; } /* Return the number of bytes available for writing. This is the number of bytes in front of the write pointer and behind the read pointer. */ LIB_EXPORT size_t jack_ringbuffer_write_space (const jack_ringbuffer_t * rb) { size_t w, r; w = rb->write_ptr; r = rb->read_ptr; JACK_ACQ_FENCE(); return (r - w - 1) & rb->size_mask; } /* The copying data reader. Copy at most `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ LIB_EXPORT size_t jack_ringbuffer_read (jack_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_read; size_t n1, n2; if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) { return 0; } to_read = cnt > free_cnt ? free_cnt : cnt; /* note: relaxed load here, rb->read_ptr cannot be * modified from writing thread */ cnt2 = rb->read_ptr + to_read; if (cnt2 > rb->size) { n1 = rb->size - rb->read_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_read; n2 = 0; } memcpy (dest, &(rb->buf[rb->read_ptr]), n1); JACK_REL_FENCE(); /* ensure pointer increment happens after copy */ rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; if (n2) { memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); JACK_REL_FENCE(); /* ensure pointer increment happens after copy */ rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; } return to_read; } /* The copying data reader w/o read pointer advance. Copy at most `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ LIB_EXPORT size_t jack_ringbuffer_peek (jack_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_read; size_t n1, n2; size_t tmp_read_ptr; tmp_read_ptr = rb->read_ptr; if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) { return 0; } to_read = cnt > free_cnt ? free_cnt : cnt; cnt2 = tmp_read_ptr + to_read; if (cnt2 > rb->size) { n1 = rb->size - tmp_read_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_read; n2 = 0; } memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask; if (n2) { memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); } return to_read; } /* The copying data writer. Copy at most `cnt' bytes to `rb' from `src'. Returns the actual number of bytes copied. */ LIB_EXPORT size_t jack_ringbuffer_write (jack_ringbuffer_t * rb, const char *src, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_write; size_t n1, n2; if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0) { return 0; } to_write = cnt > free_cnt ? free_cnt : cnt; /* note: relaxed load here, rb->write_ptr cannot be * modified from reading thread */ cnt2 = rb->write_ptr + to_write; if (cnt2 > rb->size) { n1 = rb->size - rb->write_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_write; n2 = 0; } memcpy (&(rb->buf[rb->write_ptr]), src, n1); JACK_REL_FENCE(); /* ensure pointer increment happens after copy */ rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; if (n2) { memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); JACK_REL_FENCE(); /* ensure pointer increment happens after copy */ rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; } return to_write; } /* Advance the read pointer `cnt' places. */ LIB_EXPORT void jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt) { size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; JACK_REL_FENCE(); /* ensure pointer increment happens after copy (by user) */ rb->read_ptr = tmp; } /* Advance the write pointer `cnt' places. */ LIB_EXPORT void jack_ringbuffer_write_advance (jack_ringbuffer_t * rb, size_t cnt) { size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; JACK_REL_FENCE(); /* ensure pointer increment happens after copy (by user) */ rb->write_ptr = tmp; } /* The non-copying data reader. `vec' is an array of two places. Set the values at `vec' to hold the current readable data at `rb'. If the readable data is in one segment the second segment has zero length. */ LIB_EXPORT void jack_ringbuffer_get_read_vector (const jack_ringbuffer_t * rb, jack_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; size_t r; r = rb->read_ptr; free_cnt = jack_ringbuffer_read_space(rb); cnt2 = r + free_cnt; if (cnt2 > rb->size) { /* Two part vector: the rest of the buffer after the current write ptr, plus some from the start of the buffer. */ vec[0].buf = &(rb->buf[r]); vec[0].len = rb->size - r; vec[1].buf = rb->buf; vec[1].len = cnt2 & rb->size_mask; } else { /* Single part vector: just the rest of the buffer */ vec[0].buf = &(rb->buf[r]); vec[0].len = free_cnt; vec[1].len = 0; } } /* The non-copying data writer. `vec' is an array of two places. Set the values at `vec' to hold the current writeable data at `rb'. If the writeable data is in one segment the second segment has zero length. */ LIB_EXPORT void jack_ringbuffer_get_write_vector (const jack_ringbuffer_t * rb, jack_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; size_t w; w = rb->write_ptr; free_cnt = jack_ringbuffer_write_space(rb); cnt2 = w + free_cnt; if (cnt2 > rb->size) { /* Two part vector: the rest of the buffer after the current write ptr, plus some from the start of the buffer. */ vec[0].buf = &(rb->buf[w]); vec[0].len = rb->size - w; vec[1].buf = rb->buf; vec[1].len = cnt2 & rb->size_mask; } else { vec[0].buf = &(rb->buf[w]); vec[0].len = free_cnt; vec[1].len = 0; } } jack2-1.9.22/common/shm.c000066400000000000000000000772561436671425200150550ustar00rootroot00000000000000/* This module provides a set of abstract shared memory interfaces * with support using both System V and POSIX shared memory * implementations. The code is divided into three sections: * * - common (interface-independent) code * - POSIX implementation * - System V implementation * - Windows implementation * * The implementation used is determined by whether USE_POSIX_SHM was * set in the ./configure step. */ /* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2005-2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackConstants.h" #ifdef WIN32 #include #include #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "promiscuous.h" #endif #include "shm.h" #include "JackError.h" static int GetUID() { #ifdef WIN32 return _getpid(); //#error "No getuid function available" #else return geteuid(); #endif } static int GetPID() { #ifdef WIN32 return _getpid(); #else return getpid(); #endif } #ifdef USE_POSIX_SHM static const jack_shmtype_t jack_shmtype = shm_POSIX; #elif WIN32 static const jack_shmtype_t jack_shmtype = shm_WIN32; #else static const jack_shmtype_t jack_shmtype = shm_SYSV; #endif /* interface-dependent forward declarations */ static int jack_access_registry (jack_shm_info_t *ri); static int jack_create_registry (jack_shm_info_t *ri); static void jack_remove_shm (const jack_shm_id_t id); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * common interface-independent section * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* The JACK SHM registry is a chunk of memory for keeping track of the * shared memory used by each active JACK server. This allows the * server to clean up shared memory when it exits. To avoid memory * leakage due to kill -9, crashes or debugger-driven exits, this * cleanup is also done when a new instance of that server starts. */ /* per-process global data for the SHM interfaces */ #ifdef USE_POSIX_SHM static const jack_shm_id_t registry_id = "/jack-shm-registry"; #elif WIN32 static const jack_shm_id_t registry_id = "jack-shm-registry"; #else static jack_shm_id_t registry_id; /* SHM id for the registry */ #endif #ifdef WIN32 static jack_shm_info_t registry_info = {/* SHM info for the registry */ JACK_SHM_NULL_INDEX, 0, { NULL } }; #else static jack_shm_info_t registry_info = { /* SHM info for the registry */ .index = JACK_SHM_NULL_INDEX, .ptr.attached_at = MAP_FAILED }; #endif /* pointers to registry header and array */ static jack_shm_header_t *jack_shm_header = NULL; static jack_shm_registry_t *jack_shm_registry = NULL; /* jack_shm_lock_registry() serializes updates to the shared memory * segment JACK uses to keep track of the SHM segments allocated to * all its processes, including multiple servers. * * This is not a high-contention lock, but it does need to work across * multiple processes. High transaction rates and realtime safety are * not required. Any solution needs to at least be portable to POSIX * and POSIX-like systems. * * We must be particularly careful to ensure that the lock be released * if the owning process terminates abnormally. Otherwise, a segfault * or kill -9 at the wrong moment could prevent JACK from ever running * again on that machine until after a reboot. */ #define JACK_SEMAPHORE_KEY 0x282929 #ifndef USE_POSIX_SHM #define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY #endif static int semid = -1; #ifdef WIN32 #include #include static BOOL check_process_running(DWORD process_id) { DWORD aProcesses[2048], cbNeeded, cProcesses; unsigned int i; // Enumerate all processes if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) { return FALSE; } // Calculate how many process identifiers were returned. cProcesses = cbNeeded / sizeof(DWORD); for (i = 0; i < cProcesses; i++) { if (aProcesses[i] == process_id) { // Process process_id is running... return TRUE; } } return FALSE; } static int semaphore_init () {return 0;} static int semaphore_add (int value) {return 0;} #else /* all semaphore errors are fatal -- issue message, but do not return */ static void semaphore_error (char *msg) { jack_error ("JACK semaphore error: %s (%s)", msg, strerror (errno)); } static int semaphore_init () { key_t semkey = JACK_SEMAPHORE_KEY; struct sembuf sbuf; int create_flags = IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* Get semaphore ID associated with this key. */ if ((semid = semget(semkey, 0, 0)) == -1) { /* Semaphore does not exist - Create. */ if ((semid = semget(semkey, 1, create_flags)) != -1) { /* Initialize the semaphore, allow one owner. */ sbuf.sem_num = 0; sbuf.sem_op = 1; sbuf.sem_flg = 0; if (semop(semid, &sbuf, 1) == -1) { semaphore_error ("semop"); return -1; } } else if (errno == EEXIST) { if ((semid = semget(semkey, 0, 0)) == -1) { semaphore_error ("semget"); return -1; } } else { semaphore_error ("semget creation"); return -1; } } return 0; } static inline int semaphore_add (int value) { struct sembuf sbuf; sbuf.sem_num = 0; sbuf.sem_op = value; sbuf.sem_flg = SEM_UNDO; if (semop(semid, &sbuf, 1) == -1) { semaphore_error ("semop"); return -1; } return 0; } #endif static int jack_shm_lock_registry (void) { if (semid == -1) { if (semaphore_init () < 0) return -1; } return semaphore_add (-1); } static void jack_shm_unlock_registry (void) { semaphore_add (1); } static void jack_shm_init_registry () { /* registry must be locked */ int i; memset (jack_shm_header, 0, JACK_SHM_REGISTRY_SIZE); jack_shm_header->magic = JACK_SHM_MAGIC; //jack_shm_header->protocol = JACK_PROTOCOL_VERSION; jack_shm_header->type = jack_shmtype; jack_shm_header->size = JACK_SHM_REGISTRY_SIZE; jack_shm_header->hdr_len = sizeof (jack_shm_header_t); jack_shm_header->entry_len = sizeof (jack_shm_registry_t); for (i = 0; i < MAX_SHM_ID; ++i) { jack_shm_registry[i].index = i; } } static int jack_shm_validate_registry () { /* registry must be locked */ if ((jack_shm_header->magic == JACK_SHM_MAGIC) //&& (jack_shm_header->protocol == JACK_PROTOCOL_VERSION) && (jack_shm_header->type == jack_shmtype) && (jack_shm_header->size == JACK_SHM_REGISTRY_SIZE) && (jack_shm_header->hdr_len == sizeof (jack_shm_header_t)) && (jack_shm_header->entry_len == sizeof (jack_shm_registry_t))) { return 0; /* registry OK */ } return -1; } /* set a unique per-user, per-server shm prefix string * * According to the POSIX standard: * * "The name argument conforms to the construction rules for a * pathname. If name begins with the slash character, then processes * calling shm_open() with the same value of name refer to the same * shared memory object, as long as that name has not been * removed. If name does not begin with the slash character, the * effect is implementation-defined. The interpretation of slash * characters other than the leading slash character in name is * implementation-defined." * * Since the Linux implementation does not allow slashes *within* the * name, in the interest of portability we use colons instead. */ static void jack_get_server_prefix (const char* const server_name, char* const prefix, const size_t size) { #ifdef WIN32 char buffer[UNLEN+1]={0}; DWORD len = UNLEN+1; GetUserName(buffer, &len); snprintf (prefix, size, "jack-%s:%s:", buffer, server_name); #else snprintf (prefix, size, "jack-%d:%s:", GetUID(), server_name); #endif } /* gain server addressability to shared memory registration segment * * returns: 0 if successful */ static int jack_server_initialize_shm (int new_registry) { int rc; if (jack_shm_header) return 0; /* already initialized */ if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } rc = jack_access_registry (®istry_info); if (new_registry) { jack_remove_shm (registry_id); rc = ENOENT; } switch (rc) { case ENOENT: /* registry does not exist */ rc = jack_create_registry (®istry_info); break; case 0: /* existing registry */ if (jack_shm_validate_registry () == 0) break; /* else it was invalid, so fall through */ case EINVAL: /* bad registry */ /* Apparently, this registry was created by an older * JACK version. Delete it so we can try again. */ jack_release_shm (®istry_info); jack_remove_shm (registry_id); if ((rc = jack_create_registry (®istry_info)) != 0) { jack_error ("incompatible shm registry (%s)", strerror (errno)); #ifndef USE_POSIX_SHM jack_error ("to delete, use `ipcrm -M 0x%0.8x'", JACK_SHM_REGISTRY_KEY); #endif } break; default: /* failure return code */ break; } jack_shm_unlock_registry (); return rc; } /* gain client addressability to shared memory registration segment * * NOTE: this function is no longer used for server initialization, * instead it calls jack_register_server(). * * returns: 0 if successful */ int jack_initialize_shm (const char *server_name) { int rc; char server_prefix[JACK_SERVER_NAME_SIZE+1]; if (jack_shm_header) return 0; /* already initialized */ jack_get_server_prefix (server_name, server_prefix, sizeof(server_prefix)); if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } if ((rc = jack_access_registry (®istry_info)) == 0) { if ((rc = jack_shm_validate_registry ()) != 0) { jack_error ("Incompatible shm registry, " "are jackd and libjack in sync?"); } } jack_shm_unlock_registry (); return rc; } char* jack_shm_addr (jack_shm_info_t* si) { return (char*)si->ptr.attached_at; } void jack_destroy_shm (jack_shm_info_t* si) { /* must NOT have the registry locked */ if (si->index == JACK_SHM_NULL_INDEX) return; /* segment not allocated */ jack_remove_shm (jack_shm_registry[si->index].id); jack_release_shm_info (si->index); } jack_shm_registry_t * jack_get_free_shm_info () { /* registry must be locked */ jack_shm_registry_t* si = NULL; int i; for (i = 0; i < MAX_SHM_ID; ++i) { if (jack_shm_registry[i].size == 0) { break; } } if (i < MAX_SHM_ID) { si = &jack_shm_registry[i]; } return si; } static void jack_release_shm_entry (jack_shm_registry_index_t index) { /* the registry must be locked */ jack_shm_registry[index].size = 0; jack_shm_registry[index].allocator = 0; memset (&jack_shm_registry[index].id, 0, sizeof (jack_shm_registry[index].id)); } int jack_release_shm_info (jack_shm_registry_index_t index) { /* must NOT have the registry locked */ if (jack_shm_registry[index].allocator == GetPID()) { if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } jack_release_shm_entry (index); jack_shm_unlock_registry (); } return 0; } /* Claim server_name for this process. * * returns 0 if successful * EEXIST if server_name was already active for this user * ENOSPC if server registration limit reached * ENOMEM if unable to access shared memory registry */ int jack_register_server (const char *server_name, int new_registry) { int i, res = 0; char server_prefix[JACK_SERVER_NAME_SIZE+1]; jack_get_server_prefix (server_name, server_prefix, sizeof(server_prefix)); if (jack_server_initialize_shm (new_registry)) return ENOMEM; if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } /* See if server_name already registered. Since server names * are per-user, we register the unique server prefix string. */ for (i = 0; i < MAX_SERVERS; i++) { if (strncmp (jack_shm_header->server[i].name, server_prefix, sizeof(server_prefix)) != 0) continue; /* no match */ if (jack_shm_header->server[i].pid == GetPID()){ res = 0; /* it's me */ goto unlock; } /* see if server still exists */ #ifdef WIN32 if (check_process_running(jack_shm_header->server[i].pid)) { res = EEXIST; /* other server running */ goto unlock; } #else if (kill (jack_shm_header->server[i].pid, 0) == 0) { res = EEXIST; /* other server running */ goto unlock; } #endif /* it's gone, reclaim this entry */ memset (&jack_shm_header->server[i], 0, sizeof (jack_shm_server_t)); } /* find a free entry */ for (i = 0; i < MAX_SERVERS; i++) { if (jack_shm_header->server[i].pid == 0) break; } if (i >= MAX_SERVERS){ res = ENOSPC; /* out of space */ goto unlock; } /* claim it */ jack_shm_header->server[i].pid = GetPID(); strncpy (jack_shm_header->server[i].name, server_prefix, sizeof(server_prefix)); unlock: jack_shm_unlock_registry (); return res; } /* release server_name registration */ int jack_unregister_server (const char *server_name /* unused */) { int i; if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } for (i = 0; i < MAX_SERVERS; i++) { if (jack_shm_header->server[i].pid == GetPID()) { memset (&jack_shm_header->server[i], 0, sizeof (jack_shm_server_t)); } } jack_shm_unlock_registry (); return 0; } /* called for server startup and termination */ int jack_cleanup_shm () { int i; int destroy; jack_shm_info_t copy; if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } for (i = 0; i < MAX_SHM_ID; i++) { jack_shm_registry_t* r; r = &jack_shm_registry[i]; memcpy (©, r, sizeof (jack_shm_info_t)); destroy = FALSE; /* ignore unused entries */ if (r->allocator == 0) continue; /* is this my shm segment? */ if (r->allocator == GetPID()) { /* allocated by this process, so unattach and destroy. */ jack_release_shm (©); destroy = TRUE; } else { /* see if allocator still exists */ #ifdef WIN32 //jack_info("TODO: kill API not available !!"); #else if (kill (r->allocator, 0)) { if (errno == ESRCH) { /* allocator no longer exists, * so destroy */ destroy = TRUE; } } #endif } if (destroy) { int index = copy.index; if ((index >= 0) && (index < MAX_SHM_ID)) { jack_remove_shm (jack_shm_registry[index].id); jack_release_shm_entry (index); } r->size = 0; r->allocator = 0; } } jack_shm_unlock_registry (); return TRUE; } /* resize a shared memory segment * * There is no way to resize a System V shm segment. Resizing is * possible with POSIX shm, but not with the non-conformant Mac OS X * implementation. Since POSIX shm is mainly used on that platform, * it's simpler to treat them both the same. * * So, we always resize by deleting and reallocating. This is * tricky, because the old segment will not disappear until * all the clients have released it. We only do what we can * from here. * * This is not done under a single lock. I don't even want to think * about all the things that could possibly go wrong if multiple * processes tried to resize the same segment concurrently. That * probably doesn't happen. */ int jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { jack_shm_id_t id; /* The underlying type of `id' differs for SYSV and POSIX */ memcpy (&id, &jack_shm_registry[si->index].id, sizeof (id)); jack_release_shm (si); jack_destroy_shm (si); if (jack_shmalloc ((char *) id, size, si)) { return -1; } return jack_attach_shm (si); } int jack_attach_lib_shm (jack_shm_info_t* si) { int res = jack_attach_shm(si); if (res == 0) si->size = jack_shm_registry[si->index].size; // Keep size in si struct return res; } int jack_attach_lib_shm_read (jack_shm_info_t* si) { int res = jack_attach_shm_read(si); if (res == 0) si->size = jack_shm_registry[si->index].size; // Keep size in si struct return res; } #ifdef USE_POSIX_SHM /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * POSIX interface-dependent functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* gain addressability to existing SHM registry segment * * sets up global registry pointers, if successful * * returns: 0 if existing registry accessed successfully * ENOENT if registry does not exist * EINVAL if registry exists, but has the wrong size */ static int jack_access_registry (jack_shm_info_t *ri) { /* registry must be locked */ int shm_fd; /* try to open an existing segment */ if ((shm_fd = shm_open (registry_id, O_RDWR, 0666)) < 0) { int rc = errno; if (errno != ENOENT) { jack_error ("Cannot open existing shm registry segment" " (%s)", strerror (errno)); } close (shm_fd); return rc; } if ((ri->ptr.attached_at = mmap (0, JACK_SHM_REGISTRY_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { jack_error ("Cannot mmap shm registry segment (%s)", strerror (errno)); close (shm_fd); return EINVAL; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = ri->ptr.attached_at; jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); close (shm_fd); return 0; } /* create a new SHM registry segment * * sets up global registry pointers, if successful * * returns: 0 if registry created successfully * nonzero error code if unable to allocate a new registry */ static int jack_create_registry (jack_shm_info_t *ri) { /* registry must be locked */ int shm_fd; if ((shm_fd = shm_open (registry_id, O_RDWR|O_CREAT, 0666)) < 0) { int rc = errno; jack_error ("Cannot create shm registry segment (%s)", strerror (errno)); return rc; } /* Previous shm_open result depends of the actual value of umask, force correct file permission here */ if (fchmod(shm_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) < 0) { jack_log("Cannot chmod jack-shm-registry (%s) %d %d", strerror (errno)); } /* Set the desired segment size. NOTE: the non-conformant Mac * OS X POSIX shm only allows ftruncate() on segment creation. */ if (ftruncate (shm_fd, JACK_SHM_REGISTRY_SIZE) < 0) { int rc = errno; jack_error ("Cannot set registry size (%s)", strerror (errno)); jack_remove_shm (registry_id); close (shm_fd); return rc; } if ((ri->ptr.attached_at = mmap (0, JACK_SHM_REGISTRY_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { jack_error ("Cannot mmap shm registry segment (%s)", strerror (errno)); jack_remove_shm (registry_id); close (shm_fd); return EINVAL; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = ri->ptr.attached_at; jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); /* initialize registry contents */ jack_shm_init_registry (); close (shm_fd); return 0; } static void jack_remove_shm (const jack_shm_id_t id) { /* registry may or may not be locked */ shm_unlink (id); } void jack_release_shm (jack_shm_info_t* si) { /* registry may or may not be locked */ if (si->ptr.attached_at != MAP_FAILED) { munmap (si->ptr.attached_at, jack_shm_registry[si->index].size); } } void jack_release_lib_shm (jack_shm_info_t* si) { /* registry may or may not be locked */ if (si->ptr.attached_at != MAP_FAILED) { munmap (si->ptr.attached_at, si->size); } } /* allocate a POSIX shared memory segment */ int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si) { jack_shm_registry_t* registry; int shm_fd; int rc = -1; char name[SHM_NAME_MAX+1]; const char* promiscuous; if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } if ((registry = jack_get_free_shm_info ()) == NULL) { jack_error ("shm registry full"); goto unlock; } /* On Mac OS X, the maximum length of a shared memory segment * name is SHM_NAME_MAX (instead of NAME_MAX or PATH_MAX as * defined by the standard). Unfortunately, Apple sets this * value so small (about 31 bytes) that it is useless for * actual names. So, we construct a short name from the * registry index for uniqueness and ignore the shm_name * parameter. Bah! */ snprintf (name, sizeof (name), "/jack-%d-%d", GetUID(), registry->index); if (strlen (name) >= sizeof (registry->id)) { jack_error ("shm segment name too long %s", name); goto unlock; } if ((shm_fd = shm_open (name, O_RDWR|O_CREAT, 0666)) < 0) { jack_error ("Cannot create shm segment %s (%s)", name, strerror (errno)); goto unlock; } if (ftruncate (shm_fd, size) < 0) { jack_error ("Cannot set size of engine shm " "registry 0 (%s)", strerror (errno)); close (shm_fd); goto unlock; } promiscuous = getenv("JACK_PROMISCUOUS_SERVER"); if ((promiscuous != NULL) && (jack_promiscuous_perms(shm_fd, name, jack_group2gid(promiscuous)) < 0)) goto unlock; close (shm_fd); registry->size = size; strncpy (registry->id, name, sizeof (registry->id)); registry->allocator = GetPID(); si->index = registry->index; si->ptr.attached_at = MAP_FAILED; /* not attached */ rc = 0; /* success */ unlock: jack_shm_unlock_registry (); return rc; } int jack_attach_shm (jack_shm_info_t* si) { int shm_fd; jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if ((shm_fd = shm_open (registry->id, O_RDWR, 0666)) < 0) { jack_error ("Cannot open shm segment %s (%s)", registry->id, strerror (errno)); return -1; } if ((si->ptr.attached_at = mmap (0, registry->size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { jack_error ("Cannot mmap shm segment %s (%s)", registry->id, strerror (errno)); close (shm_fd); return -1; } close (shm_fd); return 0; } int jack_attach_shm_read (jack_shm_info_t* si) { int shm_fd; jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if ((shm_fd = shm_open (registry->id, O_RDONLY, 0666)) < 0) { jack_error ("Cannot open shm segment %s (%s)", registry->id, strerror (errno)); return -1; } if ((si->ptr.attached_at = mmap (0, registry->size, PROT_READ, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { jack_error ("Cannot mmap shm segment %s (%s)", registry->id, strerror (errno)); close (shm_fd); return -1; } close (shm_fd); return 0; } #elif WIN32 static int jack_access_registry (jack_shm_info_t *ri) { /* registry must be locked */ HANDLE shm_fd; /* try to open an existing segment */ if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry_id)) == NULL) { int rc = GetLastError(); if (rc != ERROR_FILE_NOT_FOUND) { jack_error ("Cannot open existing shm registry segment (%ld)", rc); } return rc; } if ((ri->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, JACK_SHM_REGISTRY_SIZE)) == NULL) { jack_error ("Cannot mmap shm registry segment (%ld)", GetLastError()); jack_remove_shm (registry_id); CloseHandle (shm_fd); return EINVAL; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = ri->ptr.attached_at; jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); //CloseHandle(shm_fd); // TO CHECK return 0; } static int jack_create_registry (jack_shm_info_t *ri) { /* registry must be locked */ HANDLE shm_fd; if ((shm_fd = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, JACK_SHM_REGISTRY_SIZE, registry_id)) == NULL || (shm_fd == INVALID_HANDLE_VALUE)) { int rc = GetLastError(); jack_error ("Cannot create shm registry segment (%ld)", rc); return rc; } if ((ri->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, JACK_SHM_REGISTRY_SIZE)) == NULL) { jack_error ("Cannot mmap shm registry segment (%ld)", GetLastError()); jack_remove_shm (registry_id); CloseHandle (shm_fd); return EINVAL; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = ri->ptr.attached_at; jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); /* initialize registry contents */ jack_shm_init_registry (); //CloseHandle(shm_fd); // TO CHECK return 0; } static void jack_remove_shm (const jack_shm_id_t id) { /* nothing to do */ } void jack_release_shm (jack_shm_info_t* si) { /* registry may or may not be locked */ if (si->ptr.attached_at != NULL) { UnmapViewOfFile (si->ptr.attached_at); } } void jack_release_lib_shm (jack_shm_info_t* si) { jack_release_shm(si); } int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si) { jack_shm_registry_t* registry; HANDLE shm_fd; int rc = -1; char name[SHM_NAME_MAX+1]; if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } if ((registry = jack_get_free_shm_info ()) == NULL) { jack_error ("shm registry full"); goto unlock; } snprintf (name, sizeof (name), "jack-%d-%d", GetUID(), registry->index); if (strlen (name) >= sizeof (registry->id)) { jack_error ("shm segment name too long %s", name); goto unlock; } if ((shm_fd = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, size, name)) == NULL || (shm_fd == INVALID_HANDLE_VALUE)) { int rc = GetLastError(); jack_error ("Cannot create shm segment (%ld)",rc); goto unlock; } //CloseHandle (shm_fd); // TO CHECK registry->size = size; strncpy (registry->id, name, sizeof (registry->id)); registry->allocator = _getpid(); si->index = registry->index; si->ptr.attached_at = NULL; /* not attached */ rc = 0; /* success */ unlock: jack_shm_unlock_registry (); return rc; } int jack_attach_shm (jack_shm_info_t* si) { HANDLE shm_fd; jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry->id)) == NULL) { int rc = GetLastError(); jack_error ("Cannot open shm segment (%ld)",rc); return -1; } if ((si->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, registry->size)) == NULL) { jack_error ("Cannot mmap shm segment (%ld)", GetLastError()); jack_remove_shm (registry_id); CloseHandle (shm_fd); return -1; } //CloseHandle (shm_fd); // TO CHECK return 0; } int jack_attach_shm_read (jack_shm_info_t* si) { HANDLE shm_fd; jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry->id)) == NULL) { int rc = GetLastError(); jack_error ("Cannot open shm segment (%ld)",rc); return -1; } if ((si->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_READ, 0, 0, registry->size)) == NULL) { jack_error("Cannot mmap shm segment (%ld)", GetLastError()); jack_remove_shm(registry_id); CloseHandle(shm_fd); return -1; } //CloseHandle (shm_fd); // TO CHECK return 0; } #else /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * System V interface-dependent functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* gain addressability to existing SHM registry segment * * sets up global registry pointers, if successful * * returns: 0 if existing registry accessed successfully * ENOENT if registry does not exist * EINVAL if registry exists, but has the wrong size * other nonzero error code if unable to access registry */ static int jack_access_registry (jack_shm_info_t *ri) { /* registry must be locked */ /* try without IPC_CREAT to get existing segment */ if ((registry_id = shmget (JACK_SHM_REGISTRY_KEY, JACK_SHM_REGISTRY_SIZE, 0666)) < 0) { switch (errno) { case ENOENT: /* segment does not exist */ return ENOENT; case EINVAL: /* segment exists, but too small */ /* attempt minimum size access */ registry_id = shmget (JACK_SHM_REGISTRY_KEY, 1, 0666); return EINVAL; default: /* or other error */ jack_error ("unable to access shm registry (%s)", strerror (errno)); return errno; } } if ((ri->ptr.attached_at = shmat (registry_id, 0, 0)) < 0) { jack_error ("Cannot attach shm registry segment (%s)", strerror (errno)); return EINVAL; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = ri->ptr.attached_at; jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); return 0; } /* create a new SHM registry segment * * sets up global registry pointers, if successful * * returns: 0 if registry created successfully * nonzero error code if unable to allocate a new registry */ static int jack_create_registry (jack_shm_info_t *ri) { /* registry must be locked */ if ((registry_id = shmget (JACK_SHM_REGISTRY_KEY, JACK_SHM_REGISTRY_SIZE, 0666|IPC_CREAT)) < 0) { jack_error ("Cannot create shm registry segment (%s)", strerror (errno)); return errno; } if ((ri->ptr.attached_at = shmat (registry_id, 0, 0)) < 0) { jack_error ("Cannot attach shm registry segment (%s)", strerror (errno)); return EINVAL; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = ri->ptr.attached_at; jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); /* initialize registry contents */ jack_shm_init_registry (); return 0; } static void jack_remove_shm (jack_shm_id_t id) { /* registry may or may not be locked */ shmctl (id, IPC_RMID, NULL); } void jack_release_shm (jack_shm_info_t* si) { /* registry may or may not be locked */ if (si->ptr.attached_at != MAP_FAILED) { shmdt (si->ptr.attached_at); } } void jack_release_lib_shm (jack_shm_info_t* si) { jack_release_shm(si); } int jack_shmalloc (const char* name_not_used, jack_shmsize_t size, jack_shm_info_t* si) { int shmflags; int shmid; int rc = -1; jack_shm_registry_t* registry; if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } if ((registry = jack_get_free_shm_info ())) { shmflags = 0666 | IPC_CREAT | IPC_EXCL; if ((shmid = shmget (IPC_PRIVATE, size, shmflags)) >= 0) { registry->size = size; registry->id = shmid; registry->allocator = getpid(); si->index = registry->index; si->ptr.attached_at = MAP_FAILED; /* not attached */ rc = 0; } else { jack_error ("Cannot create shm segment %s (%s)", name_not_used, strerror (errno)); } } jack_shm_unlock_registry (); return rc; } int jack_attach_shm (jack_shm_info_t* si) { if ((si->ptr.attached_at = shmat (jack_shm_registry[si->index].id, 0, 0)) < 0) { jack_error ("Cannot attach shm segment (%s)", strerror (errno)); jack_release_shm_info (si->index); return -1; } return 0; } int jack_attach_shm_read (jack_shm_info_t* si) { if ((si->ptr.attached_at = shmat (jack_shm_registry[si->index].id, 0, SHM_RDONLY)) < 0) { jack_error ("Cannot attach shm segment (%s)", strerror (errno)); jack_release_shm_info (si->index); return -1; } return 0; } #endif /* !USE_POSIX_SHM */ jack2-1.9.22/common/shm.h000066400000000000000000000153611436671425200150470ustar00rootroot00000000000000/* This module provides a set of abstract shared memory interfaces * with support using both System V and POSIX shared memory * implementations. The code is divided into three sections: * * - common (interface-independent) code * - POSIX implementation * - System V implementation * - Windows implementation * * The implementation used is determined by whether USE_POSIX_SHM was * set in the ./configure step. */ /* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2005-2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_shm_h__ #define __jack_shm_h__ #include #include #include "types.h" #include "JackCompilerDeps.h" #include "JackConstants.h" #define TRUE 1 #define FALSE 0 #ifdef __cplusplus extern "C" { #endif #define MAX_SERVERS 8 /* maximum concurrent servers */ #define MAX_SHM_ID 256 /* generally about 16 per server */ #define JACK_SHM_MAGIC 0x4a41434b /* shm magic number: "JACK" */ #define JACK_SHM_NULL_INDEX -1 /* NULL SHM index */ #define JACK_SHM_REGISTRY_INDEX -2 /* pseudo SHM index for registry */ /* On Mac OS X, SHM_NAME_MAX is the maximum length of a shared memory * segment name (instead of NAME_MAX or PATH_MAX as defined by the * standard). */ #ifdef USE_POSIX_SHM #ifndef NAME_MAX #define NAME_MAX 255 #endif #ifndef SHM_NAME_MAX #define SHM_NAME_MAX NAME_MAX #endif typedef char shm_name_t[SHM_NAME_MAX]; typedef shm_name_t jack_shm_id_t; #elif WIN32 #define NAME_MAX 255 #ifndef SHM_NAME_MAX #define SHM_NAME_MAX NAME_MAX #endif typedef char shm_name_t[SHM_NAME_MAX]; typedef shm_name_t jack_shm_id_t; #elif __ANDROID__ #ifndef NAME_MAX #define NAME_MAX 255 #endif #ifndef SHM_NAME_MAX #define SHM_NAME_MAX NAME_MAX #endif typedef char shm_name_t[SHM_NAME_MAX]; typedef shm_name_t jack_shm_id_t; typedef int jack_shm_fd_t; #else /* System V SHM */ typedef int jack_shm_id_t; #endif /* SHM type */ /* shared memory type */ typedef enum { shm_POSIX = 1, /* POSIX shared memory */ shm_SYSV = 2, /* System V shared memory */ shm_WIN32 = 3, /* Windows 32 shared memory */ shm_ANDROID = 4 /* Android shared memory */ } jack_shmtype_t; #ifdef __APPLE__ /* we need to align and pack data to 32bit so that x86_64 and arm64 work together */ typedef int32_t jack_shm_registry_index_t; #else typedef int16_t jack_shm_registry_index_t; #endif /** * A structure holding information about shared memory allocated by * JACK. this persists across invocations of JACK, and can be used by * multiple JACK servers. It contains no pointers and is valid across * address spaces. * * The registry consists of two parts: a header including an array of * server names, followed by an array of segment registry entries. */ typedef struct _jack_shm_server { #ifdef WIN32 int pid; /* process ID */ #else pid_t pid; /* process ID */ #endif char name[JACK_SERVER_NAME_SIZE+1]; } jack_shm_server_t; typedef struct _jack_shm_header { uint32_t magic; /* magic number */ uint16_t protocol; /* JACK protocol version */ jack_shmtype_t type; /* shm type */ jack_shmsize_t size; /* total registry segment size */ jack_shmsize_t hdr_len; /* size of header */ jack_shmsize_t entry_len; /* size of registry entry */ jack_shm_server_t server[MAX_SERVERS]; /* current server array */ } jack_shm_header_t; typedef struct _jack_shm_registry { jack_shm_registry_index_t index; /* offset into the registry */ #ifdef WIN32 int allocator; /* PID that created shm segment */ #else pid_t allocator; /* PID that created shm segment */ #endif jack_shmsize_t size; /* for POSIX unattach */ jack_shm_id_t id; /* API specific, see above */ #ifdef __ANDROID__ jack_shm_fd_t fd; #endif } jack_shm_registry_t; #define JACK_SHM_REGISTRY_SIZE (sizeof (jack_shm_header_t) \ + sizeof (jack_shm_registry_t) * MAX_SHM_ID) /** * a structure holding information about shared memory * allocated by JACK. this version is valid only * for a given address space. It contains a pointer * indicating where the shared memory has been * attached to the address space. */ PRE_PACKED_STRUCTURE struct _jack_shm_info { jack_shm_registry_index_t index; /* offset into the registry */ uint32_t size; #ifdef __ANDROID__ jack_shm_fd_t fd; #endif union { void *attached_at; /* address where attached */ char ptr_size[8]; } ptr; /* a "pointer" that has the same 8 bytes size when compiling in 32 or 64 bits */ } POST_PACKED_STRUCTURE; typedef struct _jack_shm_info jack_shm_info_t; /* utility functions used only within JACK */ void jack_shm_copy_from_registry (jack_shm_info_t*, jack_shm_registry_index_t); void jack_shm_copy_to_registry (jack_shm_info_t*, jack_shm_registry_index_t*); int jack_release_shm_info (jack_shm_registry_index_t); char* jack_shm_addr (jack_shm_info_t* si); /* here begin the API */ int jack_register_server (const char *server_name, int new_registry); int jack_unregister_server (const char *server_name); int jack_initialize_shm (const char *server_name); int jack_initialize_shm_server (void); int jack_initialize_shm_client (void); int jack_cleanup_shm (void); int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result); void jack_release_shm (jack_shm_info_t*); void jack_release_lib_shm (jack_shm_info_t*); void jack_destroy_shm (jack_shm_info_t*); int jack_attach_shm (jack_shm_info_t*); int jack_attach_lib_shm (jack_shm_info_t*); int jack_attach_shm_read (jack_shm_info_t*); int jack_attach_lib_shm_read (jack_shm_info_t*); int jack_resize_shm (jack_shm_info_t*, jack_shmsize_t size); #ifdef __cplusplus } #endif #endif /* __jack_shm_h__ */ jack2-1.9.22/common/timestamps.c000066400000000000000000000037421436671425200164410ustar00rootroot00000000000000/* Copyright (C) 2002-2003 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "timestamps.h" #include "JackTime.h" typedef struct { jack_time_t when; const char *what; } jack_timestamp_t; static jack_timestamp_t *timestamps = 0; static unsigned long timestamp_cnt = 0; static unsigned long timestamp_index; void jack_init_timestamps (unsigned long howmany) { if (timestamps) { free (timestamps); } timestamps = (jack_timestamp_t *) malloc (howmany * sizeof(jack_timestamp_t)); timestamp_cnt = howmany; memset (timestamps, 0, sizeof(jack_timestamp_t) * howmany); timestamp_index = 0; } void jack_timestamp (const char *what) { if (timestamp_index < timestamp_cnt) { timestamps[timestamp_index].when = GetMicroSeconds(); timestamps[timestamp_index].what = what; ++timestamp_index; } } void jack_dump_timestamps (FILE *out) { unsigned long i; for (i = 0; i < timestamp_index; ++i) { fprintf (out, "%-.32s %" PRIu64 " %" PRIu64, timestamps[i].what, timestamps[i].when, timestamps[i].when - timestamps[0].when); if (i > 0) { fprintf (out, " %" PRIu64, timestamps[i].when - timestamps[i-1].when); } fputc ('\n', out); } } void jack_reset_timestamps () { timestamp_index = 0; } jack2-1.9.22/common/timestamps.h000066400000000000000000000022001436671425200164320ustar00rootroot00000000000000/* Copyright (C) 2002 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __jack_timestamps_h__ #define __jack_timestamps_h__ #include #ifdef __cplusplus extern "C" { #endif void jack_init_timestamps (unsigned long howmany); void jack_timestamp (const char *what); void jack_dump_timestamps (FILE *out); void jack_reset_timestamps (); #ifdef __cplusplus } #endif #endif /* __jack_timestamps_h__ */ jack2-1.9.22/common/varargs.h000066400000000000000000000047501436671425200157250ustar00rootroot00000000000000/* * Copyright (C) 2004 Jack O'Quin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifndef __jack_varargs_h__ #define __jack_varargs_h__ #include #include #include #include "types.h" #ifdef __cplusplus extern "C" { #endif /* variable argument structure */ typedef struct { char *server_name; /* server name */ char *load_name; /* load module name */ char *load_init; /* initialization string */ jack_uuid_t session_id; /* requested session_id */ } jack_varargs_t; static const char* jack_default_server_name (void) { const char *server_name; if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL) server_name = "default"; return server_name; } static inline void jack_varargs_init (jack_varargs_t *va) { memset (va, 0, sizeof(jack_varargs_t)); va->server_name = (char*)jack_default_server_name(); } static inline void jack_varargs_parse (jack_options_t options, va_list ap, jack_varargs_t *va) { // initialize default settings jack_varargs_init (va); if ((options & JackServerName)) { char *sn = va_arg(ap, char *); if (sn) va->server_name = sn; } if ((options & JackLoadName)) va->load_name = va_arg(ap, char *); if ((options & JackLoadInit)) va->load_init = va_arg(ap, char *); if ((options & JackSessionID)) { char *sid = va_arg(ap, char *); if (sid) { const long long id = atoll( sid ); if (id > 0) va->session_id = id; } } } #ifdef __cplusplus } #endif #endif /* __jack_varargs_h__ */ jack2-1.9.22/common/wscript000066400000000000000000000454021436671425200155240ustar00rootroot00000000000000#! /usr/bin/python3 # encoding: utf-8 import os def configure(conf): conf.env['BUILD_NETLIB'] = conf.env['SAMPLERATE'] conf.env['BUILD_ADAPTER'] = conf.env['SAMPLERATE'] if conf.env['IS_WINDOWS']: try: conf.check(header_name='regex.h', lib='regex', uselib_store='REGEX', define_name='HAVE_REGEX_H') except Exception: conf.check(header_name='tre/regex.h', lib='tre', uselib_store='REGEX', define_name='HAVE_TRE_REGEX_H') conf.check(header_name='winsock2.h', lib='ws2_32', uselib_store='WS2_32', define_name='HAVE_WINSOCK2_H') conf.check( header_name=['windows.h', 'mmsystem.h'], lib='winmm', uselib_store='WINMM', define_name='HAVE_MMSYSTEM_H' ) conf.check(header_name=['windows.h', 'psapi.h'], lib='psapi', uselib_store='PSAPI', define_name='HAVE_PSAPI_H') def create_jack_process_obj(bld, target, sources, uselib=None, framework=None): process = bld(features=['cxx', 'cxxshlib']) if not bld.env['IS_WINDOWS']: process.env['cxxshlib_PATTERN'] = '%s.so' process.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE'] if bld.env['IS_MACOSX']: if framework: process.framework = framework env_includes = ['../macosx', '../posix', '../macosx/coreaudio'] if bld.env['IS_LINUX']: env_includes = ['../linux', '../posix', '../linux/alsa'] if bld.env['IS_FREEBSD']: env_includes = ['../freebsd', '../posix', '../solaris/oss'] if bld.env['IS_SUN']: env_includes = ['../solaris', '../posix', '../solaris/oss'] if bld.env['IS_WINDOWS']: env_includes = ['../windows', '../windows/portaudio'] process.includes = ['.'] + env_includes + ['jack', '..'] process.name = target process.target = target process.source = sources if bld.env['IS_LINUX'] or bld.env['IS_MACOSX'] or bld.env['IS_FREEBSD']: process.env.append_value('CPPFLAGS', '-fvisibility=hidden') process.install_path = '${ADDON_DIR}/' process.use = [uselib.name] return process def build(bld): common_libsources = [ 'JackActivationCount.cpp', 'JackAPI.cpp', 'JackClient.cpp', 'JackConnectionManager.cpp', 'ringbuffer.c', 'JackError.cpp', 'JackException.cpp', 'JackFrameTimer.cpp', 'JackGraphManager.cpp', 'JackPort.cpp', 'JackPortType.cpp', 'JackAudioPort.cpp', 'JackMidiPort.cpp', 'JackMidiAPI.cpp', 'JackEngineControl.cpp', 'JackShmMem.cpp', 'JackGenericClientChannel.cpp', 'shm.c', 'JackGlobals.cpp', 'JackTransportEngine.cpp', 'JackTools.cpp', 'JackMessageBuffer.cpp', 'JackEngineProfiling.cpp', ] includes = ['.', './jack'] if not bld.variant: includes.append('..') else: includes.append('../..') uselib = ['CELT', 'OPUS', 'DB', 'PTHREAD', 'SAMPLERATE'] if bld.env['IS_LINUX']: common_libsources += [ 'JackDebugClient.cpp', 'timestamps.c', 'promiscuous.c', '../posix/JackPosixThread.cpp', '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixMutex.cpp', '../posix/JackSocket.cpp', '../linux/JackLinuxFutex.cpp', '../linux/JackLinuxTime.c', ] includes = ['../linux', '../posix'] + includes uselib.append('RT') uselib.append('DL') if bld.env['IS_FREEBSD']: common_libsources += [ 'JackDebugClient.cpp', 'timestamps.c', 'promiscuous.c', '../posix/JackPosixThread.cpp', '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixMutex.cpp', '../posix/JackPosixSemaphore.cpp', '../posix/JackSocket.cpp', '../posix/JackPosixTime.c', ] includes = ['../freebsd', '../posix'] + includes if bld.env['IS_SUN']: common_libsources += [ 'JackDebugClient.cpp', 'timestamps.c', 'promiscuous.c', '../posix/JackPosixThread.cpp', '../posix/JackFifo.cpp', '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixMutex.cpp', '../posix/JackSocket.cpp', '../solaris/JackSolarisTime.c', ] includes = ['../solaris', '../posix'] + includes uselib.append('RT') if bld.env['IS_MACOSX']: common_libsources += [ 'JackDebugClient.cpp', 'timestamps.c', 'promiscuous.c', '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../macosx/JackMachThread.mm', '../macosx/JackMachSemaphore.mm', '../macosx/JackMachSemaphoreServer.mm', '../posix/JackSocket.cpp', '../macosx/JackMachTime.c', ] includes = ['../macosx', '../macosx/RPC', '../posix'] + includes if bld.env['IS_WINDOWS']: common_libsources += [ '../windows/JackWinMutex.cpp', '../windows/JackWinProcessSync.cpp', '../windows/JackWinSemaphore.cpp', '../windows/JackWinThread.cpp', '../windows/JackWinTime.c', ] includes = ['../windows'] + includes libsuffix = "64" if (bld.env['DEST_CPU'] == "x86_64" and not bld.variant) else "" buildbindir = os.path.join('..', bld.path.get_bld().srcpath().rstrip(bld.path.srcpath())) staticbuild = bool('BUILD_STATIC' in bld.env and bld.env['BUILD_STATIC']) uselib.append('REGEX') uselib.append('WS2_32') uselib.append('PSAPI') uselib.append('WINMM') else: libsuffix = "" clientlib = bld(features=['c', 'cxx', 'cxxshlib', 'cshlib']) if bld.env['IS_MACOSX']: clientlib.framework = ['CoreAudio', 'Accelerate'] clientlib.defines = 'HAVE_CONFIG_H' clientlib.includes = includes clientlib.name = 'clientlib' clientlib.target = 'jack'+libsuffix clientlib.install_path = '${LIBDIR}' clientlib.use = uselib if bld.env['IS_WINDOWS']: clientlib.env['cxxshlib_PATTERN'] = 'lib%s.dll' clientlib.env['cxxstlib_PATTERN'] = 'lib%s.a' clientlib.env['implib_PATTERN'] = 'lib%s.dll.a' if staticbuild: clientlib.env['SHLIB_MARKER'] = '' clientlib.env.append_value('LINKFLAGS', ['-static-libstdc++', '--disable-auto-import']) clientlib.env.append_value('LINKFLAGS', ['-Wl,--output-def,lib%s.def' % clientlib.target]) bld.install_files(clientlib.install_path, [os.path.join(buildbindir, 'lib%s.def' % clientlib.target)]) if bld.env['AUTOSTART_METHOD'] == 'dbus': clientlib.use.append('DBUS-1') clientlib.source = [] + common_libsources clientlib.source += [ 'JackLibClient.cpp', 'JackLibAPI.cpp', 'JackMetadata.cpp', ] if bld.env['IS_LINUX']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', '../posix/JackPosixServerLaunch.cpp', ] if bld.env['IS_FREEBSD']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', '../posix/JackPosixServerLaunch.cpp', ] if bld.env['IS_SUN']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', '../posix/JackPosixServerLaunch.cpp', ] if bld.env['IS_MACOSX']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', '../posix/JackPosixServerLaunch.cpp', ] if bld.env['IS_WINDOWS']: clientlib.source += [ '../windows/JackWinNamedPipe.cpp', '../windows/JackWinNamedPipeClientChannel.cpp', '../windows/JackWinServerLaunch.cpp', '../windows/JackMMCSS.cpp', ] if bld.env['IS_MACOSX']: clientlib.cnum = bld.env['JACK_API_VERSION'] clientlib.vnum = bld.env['JACK_VERSION'] elif not bld.env['IS_WINDOWS']: clientlib.vnum = bld.env['JACK_API_VERSION'] if bld.env['IS_LINUX']: clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_FREEBSD']: clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') clientlib.env.append_value('LINKFLAGS', '-single_module') if bld.env['IS_SUN']: clientlib.env.append_value('LINKFLAGS', '-lnsl -lsocket') if bld.variant: # if there is variant defined, we expect it to be the 32bit client lib one # we don't want to build other stuff in this variant return serverlib = bld(features=['c', 'cxx', 'cxxshlib', 'cshlib']) if bld.env['IS_MACOSX']: serverlib.framework = ['CoreAudio', 'CoreFoundation', 'Accelerate'] serverlib.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE'] serverlib.includes = includes serverlib.name = 'serverlib' serverlib.target = 'jackserver'+libsuffix serverlib.install_path = '${LIBDIR}' serverlib.use = uselib if bld.env['IS_WINDOWS']: serverlib.env['cxxshlib_PATTERN'] = 'lib%s.dll' serverlib.env['cxxstlib_PATTERN'] = 'lib%s.a' serverlib.env['implib_PATTERN'] = 'lib%s.dll.a' if staticbuild: serverlib.env['SHLIB_MARKER'] = '' serverlib.env.append_value('LINKFLAGS', ['-static-libstdc++', '--disable-auto-import']) serverlib.env.append_value('LINKFLAGS', ['-Wl,--output-def,lib%s.def' % serverlib.target]) bld.install_files(serverlib.install_path, [os.path.join(buildbindir, 'lib%s.def' % serverlib.target)]) serverlib.source = [] + common_libsources serverlib.source += [ 'JackAudioDriver.cpp', 'JackTimedDriver.cpp', 'JackMidiDriver.cpp', 'JackDriver.cpp', 'JackEngine.cpp', 'JackExternalClient.cpp', 'JackFreewheelDriver.cpp', 'JackInternalClient.cpp', 'JackInternalSessionLoader.cpp', 'JackServer.cpp', 'JackThreadedDriver.cpp', 'JackRestartThreadedDriver.cpp', 'JackWaitThreadedDriver.cpp', 'JackWaitCallbackDriver.cpp', 'JackServerAPI.cpp', 'JackDriverLoader.cpp', 'JackServerGlobals.cpp', 'JackControlAPI.cpp', 'JackNetTool.cpp', 'JackNetInterface.cpp', 'JackArgParser.cpp', 'JackRequestDecoder.cpp', 'JackMidiAsyncQueue.cpp', 'JackMidiAsyncWaitQueue.cpp', 'JackMidiBufferReadQueue.cpp', 'JackMidiBufferWriteQueue.cpp', 'JackMidiRawInputWriteQueue.cpp', 'JackMidiRawOutputWriteQueue.cpp', 'JackMidiReadQueue.cpp', 'JackMidiReceiveQueue.cpp', 'JackMidiSendQueue.cpp', 'JackMidiUtil.cpp', 'JackMidiWriteQueue.cpp', 'JackMetadata.cpp', ] if bld.env['IS_LINUX']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', '../posix/JackSocketNotifyChannel.cpp', '../posix/JackSocketServerNotifyChannel.cpp', '../posix/JackNetUnixSocket.cpp', ] if bld.env['IS_FREEBSD']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', '../posix/JackSocketNotifyChannel.cpp', '../posix/JackSocketServerNotifyChannel.cpp', '../posix/JackNetUnixSocket.cpp', ] if bld.env['IS_SUN']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', '../posix/JackSocketNotifyChannel.cpp', '../posix/JackSocketServerNotifyChannel.cpp', '../posix/JackNetUnixSocket.cpp', ] if bld.env['IS_MACOSX']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', '../posix/JackSocketNotifyChannel.cpp', '../posix/JackSocketServerNotifyChannel.cpp', '../posix/JackNetUnixSocket.cpp', ] if bld.env['IS_WINDOWS']: serverlib.source += [ '../windows/JackMMCSS.cpp', '../windows/JackWinNamedPipe.cpp', '../windows/JackWinNamedPipeServerChannel.cpp', '../windows/JackWinNamedPipeServerNotifyChannel.cpp', '../windows/JackWinNamedPipeNotifyChannel.cpp', '../windows/JackNetWinSocket.cpp', ] if bld.env['IS_MACOSX']: serverlib.cnum = bld.env['JACK_API_VERSION'] serverlib.vnum = bld.env['JACK_VERSION'] elif not bld.env['IS_WINDOWS']: serverlib.vnum = bld.env['JACK_API_VERSION'] if bld.env['IS_LINUX']: serverlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: serverlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') serverlib.env.append_value('LINKFLAGS', '-single_module') if bld.env['IS_SUN']: serverlib.env.append_value('LINKFLAGS', '-lnsl -lsocket') if bld.env['BUILD_NETLIB']: netlib = bld(features=['c', 'cxx', 'cxxshlib', 'cshlib']) if bld.env['IS_MACOSX']: netlib.framework = ['CoreAudio'] netlib.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE'] netlib.includes = includes netlib.name = 'netlib' netlib.target = 'jacknet'+libsuffix netlib.install_path = '${LIBDIR}' netlib.use = ['SAMPLERATE', 'CELT', 'OPUS', 'PTHREAD'] if bld.env['IS_WINDOWS']: netlib.use += ['WS2_32', 'WINMM'] netlib.env['cxxshlib_PATTERN'] = 'lib%s.dll' netlib.env['cxxstlib_PATTERN'] = 'lib%s.a' netlib.env['implib_PATTERN'] = 'lib%s.dll.a' if staticbuild: netlib.env['SHLIB_MARKER'] = '' netlib.env.append_value('LINKFLAGS', ['-static-libstdc++', '--disable-auto-import']) netlib.env.append_value('LINKFLAGS', ['-Wl,--output-def,lib%s.def' % netlib.target]) bld.install_files(netlib.install_path, [os.path.join(buildbindir, 'lib%s.def' % netlib.target)]) elif not bld.env['IS_MACOSX']: netlib.use += ['RT'] netlib.source = [ 'JackNetAPI.cpp', 'JackNetInterface.cpp', 'JackNetTool.cpp', 'JackException.cpp', 'JackAudioAdapterInterface.cpp', 'JackLibSampleRateResampler.cpp', 'JackResampler.cpp', 'JackGlobals.cpp', 'ringbuffer.c'] if bld.env['IS_LINUX']: netlib.source += [ '../posix/JackNetUnixSocket.cpp', '../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../linux/JackLinuxTime.c', ] netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_FREEBSD']: netlib.source += [ '../posix/JackNetUnixSocket.cpp', '../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../linux/JackLinuxTime.c', ] netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_SUN']: netlib.source += [ '../posix/JackNetUnixSocket.cpp', '../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../solaris/JackSolarisTime.c', ] netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: netlib.source += [ '../posix/JackNetUnixSocket.cpp', '../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../macosx/JackMachThread.mm', '../macosx/JackMachTime.c', ] netlib.env.append_value('LINKFLAGS', '-single_module') if bld.env['IS_WINDOWS']: netlib.source += [ '../windows/JackNetWinSocket.cpp', '../windows/JackWinThread.cpp', '../windows/JackMMCSS.cpp', '../windows/JackWinTime.c', ] if bld.env['IS_MACOSX']: netlib.cnum = bld.env['JACK_API_VERSION'] netlib.vnum = bld.env['JACK_VERSION'] elif not bld.env['IS_WINDOWS']: netlib.vnum = bld.env['JACK_API_VERSION'] create_jack_process_obj(bld, 'netmanager', 'JackNetManager.cpp', serverlib) create_jack_process_obj(bld, 'profiler', 'JackProfiler.cpp', serverlib) net_adapter_sources = [ 'JackResampler.cpp', 'JackLibSampleRateResampler.cpp', 'JackAudioAdapter.cpp', 'JackAudioAdapterInterface.cpp', 'JackNetAdapter.cpp', ] if bld.env['BUILD_ADAPTER']: process = create_jack_process_obj(bld, 'netadapter', net_adapter_sources, serverlib) process.use += ['SAMPLERATE'] audio_adapter_sources = [ 'JackResampler.cpp', 'JackLibSampleRateResampler.cpp', 'JackAudioAdapter.cpp', 'JackAudioAdapterInterface.cpp', 'JackAudioAdapterFactory.cpp', ] if bld.env['BUILD_ADAPTER'] and bld.env['IS_MACOSX']: audio_adapter_sources += ['../macosx/coreaudio/JackCoreAudioAdapter.mm'] process = create_jack_process_obj( bld, 'audioadapter', audio_adapter_sources, serverlib, framework=[ 'CoreAudio', 'AudioUnit', 'AudioToolbox', 'CoreServices', ] ) process.use += ['SAMPLERATE'] if bld.env['BUILD_ADAPTER'] and bld.env['IS_LINUX'] and bld.env['BUILD_DRIVER_ALSA']: audio_adapter_sources += ['../linux/alsa/JackAlsaAdapter.cpp'] process = create_jack_process_obj(bld, 'audioadapter', audio_adapter_sources, serverlib) process.use += ['ALSA', 'SAMPLERATE'] if bld.env['BUILD_ADAPTER'] and (bld.env['IS_SUN'] or bld.env['IS_FREEBSD']): audio_adapter_sources += ['../solaris/oss/JackOSSAdapter.cpp', 'memops.c'] process = create_jack_process_obj(bld, 'audioadapter', audio_adapter_sources, serverlib) process.use += 'SAMPLERATE' if bld.env['BUILD_ADAPTER'] and bld.env['IS_WINDOWS']: audio_adapter_sources += [ '../windows/portaudio/JackPortAudioAdapter.cpp', '../windows/portaudio/JackPortAudioDevices.cpp', ] process = create_jack_process_obj(bld, 'audioadapter', audio_adapter_sources, serverlib) process.use += ['SAMPLERATE', 'PORTAUDIO'] bld.install_files('${PREFIX}/include/jack', bld.path.ant_glob('jack/*.h')) # process jack.pc.in -> jack.pc bld( features='subst_pc', source='../jack.pc.in', target='jack.pc', install_path='${PKGCONFDIR}', INCLUDEDIR=os.path.normpath(bld.env['PREFIX'] + '/include'), CLIENTLIB=clientlib.target, SERVERLIB=serverlib.target, ) jack2-1.9.22/compat/000077500000000000000000000000001436671425200140745ustar00rootroot00000000000000jack2-1.9.22/compat/README.md000066400000000000000000000026341436671425200153600ustar00rootroot00000000000000# Operating System Compatibility Modules for WAF This directory contains waf modules that aid compatibility across different operating systems. Here a module is a pluggable and reusable piece of code for the waf build system along with necessary replacements. To create a new compatibility module simply create a new subdirectory containing a `wscript` file and any necessary replacement files. The `wscript` must define the `options`, `configure` and `build` functions. To use the modules you need to call `recurse` in your `options`, `configure` and `build` commands. For example ```python def options(opt): # Do stuff... opt.recurse('compat') # Do other stuff... def configure(conf): # Do stuff... conf.recurse('compat') # Do other stuff... def build(bld): # Do stuff... bld.recurse('compat') # Do other stuff... ``` assuming this directory is called `compat`. After doing this you need to take any necessary actions described in the modules you want to use. The code in this directory is intended to be generic and reusable. When writing new modules, please keep this in mind. Whenever necessary it should be possible to make this directory a git submodule and all the subdirectories other submodules, to aid reuse. If you would like to use these modules in another project, please file an issue so that we can join forces and maintain the compatabilitiy modules in a separate repository. jack2-1.9.22/compat/alloca/000077500000000000000000000000001436671425200153275ustar00rootroot00000000000000jack2-1.9.22/compat/alloca/README.md000066400000000000000000000005031436671425200166040ustar00rootroot00000000000000# Compatibility Module for alloca This module contains a replacement header for alloca. To use this module simply make sure to `#include ` in the file where alloca should be used. If the header is missing the path to the replacement header is added to the global include paths, so nothing more needs to be done. jack2-1.9.22/compat/alloca/alloca.h000066400000000000000000000020661436671425200167370ustar00rootroot00000000000000/* * alloca.h * * Replacement header for systems lacking the alloca.h header required * to use the alloca function. * * Copyright (C) 2018 Karl Linden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * */ #ifndef ALLOCA_H # define ALLOCA_H # if _MSC_VER # include # define alloca _alloca # endif /* _MSC_VER */ #endif /* !ALLOCA_H */ jack2-1.9.22/compat/alloca/wscript000066400000000000000000000017551436671425200167550ustar00rootroot00000000000000#!/usr/bin/python3 # encoding: utf-8 # # Copyright (C) 2018 Karl Linden # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # from waflib import Errors def options(opt): pass def configure(conf): try: conf.check(header_name='alloca.h') except Errors.ConfigurationError: conf.env.append_unique('INCLUDES', conf.path.abspath()) def build(bld): pass jack2-1.9.22/compat/wscript000066400000000000000000000032351436671425200155150ustar00rootroot00000000000000#!/usr/bin/python3 # encoding: utf-8 # # Copyright (C) 2018 Karl Linden # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # import os def get_subdirs(ctx): """ Get the compatibility module subirectories. The compat modules are found dynamically so that this script does not have to be modified if more modules are added. :param ctx: the waf context :type ctx: waflib.Context.Context :returns: list of str -- the subdirectories """ subdirs = [] for entry in ctx.path.listdir(): path = os.path.join(ctx.path.abspath(), entry) if os.path.isdir(path) and not entry.startswith('.'): subdirs.append(entry) return subdirs def recurse_into_subdirs(ctx): """ Recurse into compatibility module subdirectories. :param ctx: the waf context :type ctx: waflib.Context.Context """ for x in get_subdirs(ctx): ctx.recurse(x) def options(opt): recurse_into_subdirs(opt) def configure(conf): recurse_into_subdirs(conf) def build(bld): recurse_into_subdirs(bld) jack2-1.9.22/dbus/000077500000000000000000000000001436671425200135465ustar00rootroot00000000000000jack2-1.9.22/dbus/audio_reserve.c000066400000000000000000000070001436671425200165430ustar00rootroot00000000000000/* Copyright (C) 2009 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include "reserve.h" #include "audio_reserve.h" #include "jack/control.h" #define DEVICE_MAX 2 typedef struct reserved_audio_device { char device_name[64]; rd_device * reserved_device; } reserved_audio_device; static DBusConnection* gConnection = NULL; static reserved_audio_device gReservedDevice[DEVICE_MAX]; static int gReserveCount = 0; SERVER_EXPORT int audio_reservation_init() { DBusError error; dbus_error_init(&error); if (!(gConnection = dbus_bus_get(DBUS_BUS_SESSION, &error))) { jack_error("Failed to connect to session bus for device reservation: %s\n", error.message); jack_info("To bypass device reservation via session bus, set JACK_NO_AUDIO_RESERVATION=1 prior to starting jackd.\n"); return -1; } jack_info("audio_reservation_init"); return 0; } SERVER_EXPORT int audio_reservation_finish() { if (gConnection) { dbus_connection_unref(gConnection); gConnection = NULL; jack_info("audio_reservation_finish"); } return 0; } SERVER_EXPORT bool audio_acquire(const char * device_name) { DBusError error; int ret; // Open DBus connection first time if (gReserveCount == 0) { if (audio_reservation_init() != 0) { return false; } } assert(gReserveCount < DEVICE_MAX); dbus_error_init(&error); if ((ret= rd_acquire( &gReservedDevice[gReserveCount].reserved_device, gConnection, device_name, "Jack audio server", INT32_MAX, NULL, &error)) < 0) { jack_error("Failed to acquire device name : %s error : %s", device_name, (error.message ? error.message : strerror(-ret))); dbus_error_free(&error); return false; } strcpy(gReservedDevice[gReserveCount].device_name, device_name); gReserveCount++; jack_info("Acquire audio card %s", device_name); return true; } SERVER_EXPORT void audio_release(const char * device_name) { int i; // Look for corresponding reserved device for (i = 0; i < DEVICE_MAX; i++) { if (strcmp(gReservedDevice[i].device_name, device_name) == 0) break; } if (i < DEVICE_MAX) { jack_info("Released audio card %s", device_name); rd_release(gReservedDevice[i].reserved_device); } else { jack_error("Audio card %s not found!!", device_name); } // Close DBus connection last time gReserveCount--; if (gReserveCount == 0) audio_reservation_finish(); } SERVER_EXPORT void audio_reserve_loop() { if (gConnection != NULL) { dbus_connection_read_write_dispatch (gConnection, 200); } } jack2-1.9.22/dbus/audio_reserve.h000066400000000000000000000022021436671425200165470ustar00rootroot00000000000000/* Copyright (C) 2009 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __audio_reserve__ #define __audio_reserve__ #include "JackCompilerDeps.h" #include #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT int audio_reservation_init(); SERVER_EXPORT int audio_reservation_finish(); SERVER_EXPORT bool audio_acquire(const char * device_name); SERVER_EXPORT void audio_release(const char * device_name); SERVER_EXPORT void audio_reserve_loop(); #ifdef __cplusplus } /* extern "C" */ #endif #endif jack2-1.9.22/dbus/controller.c000066400000000000000000000536521436671425200161100ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008,2010,2011 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include #include "controller.h" #include "controller_internal.h" #include "xml.h" #include "reserve.h" #include "uptime.h" struct jack_dbus_interface_descriptor * g_jackcontroller_interfaces[] = { &g_jack_controller_iface_introspectable, &g_jack_controller_iface_control, &g_jack_controller_iface_configure, &g_jack_controller_iface_patchbay, &g_jack_controller_iface_session_manager, &g_jack_controller_iface_transport, NULL }; static jackctl_driver_t * jack_controller_find_driver( jackctl_server_t *server, const char *driver_name) { const JSList * node_ptr; node_ptr = jackctl_server_get_drivers_list(server); while (node_ptr) { if (strcmp(jackctl_driver_get_name((jackctl_driver_t *)node_ptr->data), driver_name) == 0) { return node_ptr->data; } node_ptr = jack_slist_next(node_ptr); } return NULL; } static bool jack_controller_check_slave_driver(struct jack_controller * controller_ptr, const char * name) { struct list_head * node_ptr; struct jack_controller_slave_driver * driver_ptr; list_for_each(node_ptr, &controller_ptr->slave_drivers) { driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); if (strcmp(name, driver_ptr->name) == 0) { return true; } } return false; } static bool jack_controller_load_slave_drivers(struct jack_controller * controller_ptr) { struct list_head * node_ptr; struct jack_controller_slave_driver * driver_ptr; list_for_each(node_ptr, &controller_ptr->slave_drivers) { driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); assert(driver_ptr->handle != NULL); assert(!driver_ptr->loaded); if (!jackctl_server_add_slave(controller_ptr->server, driver_ptr->handle)) { jack_error("Driver \"%s\" cannot be loaded", driver_ptr->name); return false; } driver_ptr->loaded = true; } return true; } static void jack_controller_unload_slave_drivers(struct jack_controller * controller_ptr) { struct list_head * node_ptr; struct jack_controller_slave_driver * driver_ptr; list_for_each(node_ptr, &controller_ptr->slave_drivers) { driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); if (driver_ptr->loaded) { jackctl_server_remove_slave(controller_ptr->server, driver_ptr->handle); driver_ptr->loaded = false; } } } static void jack_controller_remove_slave_drivers(struct jack_controller * controller_ptr) { struct jack_controller_slave_driver * driver_ptr; while (!list_empty(&controller_ptr->slave_drivers)) { driver_ptr = list_entry(controller_ptr->slave_drivers.next, struct jack_controller_slave_driver, siblings); assert(!driver_ptr->loaded); list_del(&driver_ptr->siblings); free(driver_ptr->name); free(driver_ptr); } controller_ptr->slave_drivers_vparam_value.str[0] = 0; } static jackctl_internal_t * jack_controller_find_internal( jackctl_server_t *server, const char *internal_name) { const JSList * node_ptr; node_ptr = jackctl_server_get_internals_list(server); while (node_ptr) { if (strcmp(jackctl_internal_get_name((jackctl_internal_t *)node_ptr->data), internal_name) == 0) { return node_ptr->data; } node_ptr = jack_slist_next(node_ptr); } return NULL; } bool jack_controller_select_driver( struct jack_controller * controller_ptr, const char * driver_name) { if (!jack_params_set_driver(controller_ptr->params, driver_name)) { return false; } jack_info("driver \"%s\" selected", driver_name); return true; } static int jack_controller_xrun(void * arg) { ((struct jack_controller *)arg)->xruns++; return 0; } bool jack_controller_start_server( struct jack_controller * controller_ptr, void *dbus_call_context_ptr) { int ret; jack_info("Starting jack server..."); assert(!controller_ptr->started); /* should be ensured by caller */ controller_ptr->xruns = 0; if (!jackctl_server_open( controller_ptr->server, jack_params_get_driver(controller_ptr->params))) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to open server"); goto fail; } jack_controller_load_slave_drivers(controller_ptr); if (!jackctl_server_start( controller_ptr->server)) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to start server"); goto fail_close_server; } controller_ptr->client = jack_client_open( "dbusapi", JackNoStartServer, NULL); if (controller_ptr->client == NULL) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "failed to create dbusapi jack client"); goto fail_stop_server; } ret = jack_set_xrun_callback(controller_ptr->client, jack_controller_xrun, controller_ptr); if (ret != 0) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "failed to set xrun callback. error is %d", ret); goto fail_close_client; } if (!jack_controller_patchbay_init(controller_ptr)) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to initialize patchbay district"); goto fail_close_client; } ret = jack_activate(controller_ptr->client); if (ret != 0) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "failed to activate dbusapi jack client. error is %d", ret); goto fail_patchbay_uninit; } controller_ptr->started = true; return TRUE; fail_patchbay_uninit: jack_controller_patchbay_uninit(controller_ptr); fail_close_client: ret = jack_client_close(controller_ptr->client); if (ret != 0) { jack_error("jack_client_close() failed with error %d", ret); } controller_ptr->client = NULL; fail_stop_server: if (!jackctl_server_stop(controller_ptr->server)) { jack_error("failed to stop jack server"); } fail_close_server: jack_controller_unload_slave_drivers(controller_ptr); if (!jackctl_server_close(controller_ptr->server)) { jack_error("failed to close jack server"); } fail: return FALSE; } bool jack_controller_stop_server( struct jack_controller * controller_ptr, void *dbus_call_context_ptr) { int ret; pthread_mutex_lock(&controller_ptr->lock); if (!list_empty(&controller_ptr->session_pending_commands)) { pthread_mutex_unlock(&controller_ptr->lock); jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Refusing to stop JACK server because of pending session commands"); return false; } pthread_mutex_unlock(&controller_ptr->lock); jack_info("Stopping jack server..."); assert(controller_ptr->started); /* should be ensured by caller */ ret = jack_deactivate(controller_ptr->client); if (ret != 0) { jack_error("failed to deactivate dbusapi jack client. error is %d", ret); } jack_controller_patchbay_uninit(controller_ptr); ret = jack_client_close(controller_ptr->client); if (ret != 0) { jack_error("jack_client_close() failed with error %d", ret); } controller_ptr->client = NULL; if (!jackctl_server_stop(controller_ptr->server)) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to stop server"); return FALSE; } jack_controller_unload_slave_drivers(controller_ptr); if (!jackctl_server_close(controller_ptr->server)) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to close server"); return FALSE; } controller_ptr->started = false; return TRUE; } bool jack_controller_switch_master( struct jack_controller * controller_ptr, void *dbus_call_context_ptr) { assert(controller_ptr->started); /* should be ensured by caller */ if (!jackctl_server_switch_master( controller_ptr->server, jack_params_get_driver(controller_ptr->params))) { jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to switch master"); controller_ptr->started = false; return FALSE; } return TRUE; } #define DEVICE_MAX 2 typedef struct reserved_audio_device { char device_name[64]; rd_device * reserved_device; } reserved_audio_device; int g_device_count = 0; static reserved_audio_device g_reserved_device[DEVICE_MAX]; static bool on_device_acquire(const char * device_name) { int ret; DBusError error; dbus_error_init(&error); ret = rd_acquire( &g_reserved_device[g_device_count].reserved_device, g_connection, device_name, "Jack audio server", INT32_MAX, NULL, &error); if (ret < 0) { jack_error("Failed to acquire device name : %s error : %s", device_name, (error.message ? error.message : strerror(-ret))); dbus_error_free(&error); return false; } strcpy(g_reserved_device[g_device_count].device_name, device_name); g_device_count++; jack_info("Acquired audio card %s", device_name); return true; } static void on_device_release(const char * device_name) { int i; // Look for corresponding reserved device for (i = 0; i < DEVICE_MAX; i++) { if (strcmp(g_reserved_device[i].device_name, device_name) == 0) break; } if (i < DEVICE_MAX) { jack_info("Released audio card %s", device_name); rd_release(g_reserved_device[i].reserved_device); } else { jack_error("Audio card %s not found!!", device_name); } g_device_count--; } #define controller_ptr ((struct jack_controller *)obj) static bool slave_drivers_parameter_is_set(void * obj) { return controller_ptr->slave_drivers_set; } static bool slave_drivers_parameter_reset(void * obj) { if (controller_ptr->started) { jack_error("Cannot modify slave-drivers when server is started"); return false; } jack_controller_remove_slave_drivers(controller_ptr); controller_ptr->slave_drivers_set = false; return true; } static union jackctl_parameter_value slave_drivers_parameter_get_value(void * obj) { return controller_ptr->slave_drivers_vparam_value; } static bool slave_drivers_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) { char * buffer; char * save; const char * token; struct list_head old_list; struct list_head new_list; union jackctl_parameter_value old_value; union jackctl_parameter_value new_value; bool old_set; if (controller_ptr->started) { jack_error("Cannot modify slave-drivers when server is started"); return false; } old_set = controller_ptr->slave_drivers_set; old_value = controller_ptr->slave_drivers_vparam_value; controller_ptr->slave_drivers_vparam_value.str[0] = 0; old_list = controller_ptr->slave_drivers; INIT_LIST_HEAD(&controller_ptr->slave_drivers); buffer = strdup(value_ptr->str); if (buffer == NULL) { jack_error("strdup() failed."); return false; } token = strtok_r(buffer, ",", &save); while (token) { //jack_info("slave driver '%s'", token); if (!jack_controller_add_slave_driver(controller_ptr, token)) { jack_controller_remove_slave_drivers(controller_ptr); controller_ptr->slave_drivers = old_list; controller_ptr->slave_drivers_vparam_value = old_value; controller_ptr->slave_drivers_set = old_set; free(buffer); return false; } token = strtok_r(NULL, ",", &save); } new_value = controller_ptr->slave_drivers_vparam_value; new_list = controller_ptr->slave_drivers; controller_ptr->slave_drivers = old_list; jack_controller_remove_slave_drivers(controller_ptr); controller_ptr->slave_drivers_vparam_value = new_value; controller_ptr->slave_drivers = new_list; controller_ptr->slave_drivers_set = true; free(buffer); return true; } static union jackctl_parameter_value slave_drivers_parameter_get_default_value(void * obj) { union jackctl_parameter_value value; value.str[0] = 0; return value; } #undef controller_ptr void * jack_controller_create( DBusConnection *connection) { int error; struct jack_controller *controller_ptr; const char * address[PARAM_ADDRESS_SIZE]; DBusObjectPathVTable vtable = { jack_dbus_message_handler_unregister, jack_dbus_message_handler, NULL }; controller_ptr = malloc(sizeof(struct jack_controller)); if (!controller_ptr) { jack_error("Ran out of memory trying to allocate struct jack_controller"); goto fail; } error = pthread_mutex_init(&controller_ptr->lock, NULL); if (error != 0) { jack_error("Failed to initialize mutex. error %d", error); goto fail_free; } INIT_LIST_HEAD(&controller_ptr->session_pending_commands); controller_ptr->server = jackctl_server_create2(on_device_acquire, on_device_release, NULL); if (controller_ptr->server == NULL) { jack_error("Failed to create server object"); goto fail_uninit_mutex; } controller_ptr->params = jack_params_create(controller_ptr->server); if (controller_ptr->params == NULL) { jack_error("Failed to initialize parameter tree"); goto fail_destroy_server; } controller_ptr->client = NULL; controller_ptr->started = false; controller_ptr->pending_save = 0; INIT_LIST_HEAD(&controller_ptr->slave_drivers); controller_ptr->slave_drivers_set = false; controller_ptr->slave_drivers_vparam_value.str[0] = 0; controller_ptr->slave_drivers_vparam.obj = controller_ptr; controller_ptr->slave_drivers_vparam.vtable.is_set = slave_drivers_parameter_is_set; controller_ptr->slave_drivers_vparam.vtable.reset = slave_drivers_parameter_reset; controller_ptr->slave_drivers_vparam.vtable.get_value = slave_drivers_parameter_get_value; controller_ptr->slave_drivers_vparam.vtable.set_value = slave_drivers_parameter_set_value; controller_ptr->slave_drivers_vparam.vtable.get_default_value = slave_drivers_parameter_get_default_value; controller_ptr->slave_drivers_vparam.type = JackParamString; controller_ptr->slave_drivers_vparam.name = "slave-drivers"; controller_ptr->slave_drivers_vparam.short_decr = "Slave drivers to use"; controller_ptr->slave_drivers_vparam.long_descr = "A comma separated list of slave drivers"; controller_ptr->slave_drivers_vparam.constraint_flags = 0; address[0] = PTNODE_ENGINE; address[1] = NULL; jack_params_add_parameter(controller_ptr->params, address, true, &controller_ptr->slave_drivers_vparam); controller_ptr->dbus_descriptor.context = controller_ptr; controller_ptr->dbus_descriptor.interfaces = g_jackcontroller_interfaces; if (!dbus_connection_register_object_path( connection, JACK_CONTROLLER_OBJECT_PATH, &vtable, &controller_ptr->dbus_descriptor)) { jack_error("Ran out of memory trying to register D-Bus object path"); goto fail_destroy_params; } jack_controller_settings_load(controller_ptr); return controller_ptr; fail_destroy_params: jack_params_destroy(controller_ptr->params); fail_destroy_server: jackctl_server_destroy(controller_ptr->server); fail_uninit_mutex: pthread_mutex_destroy(&controller_ptr->lock); fail_free: free(controller_ptr); fail: return NULL; } bool jack_controller_add_slave_driver( struct jack_controller * controller_ptr, const char * driver_name) { jackctl_driver_t * driver; struct jack_controller_slave_driver * driver_ptr; size_t len_old; size_t len_new; len_old = strlen(controller_ptr->slave_drivers_vparam_value.str); len_new = strlen(driver_name); if (len_old + len_new + 2 > sizeof(controller_ptr->slave_drivers_vparam_value.str)) { jack_error("No more space for slave drivers."); return false; } driver = jack_controller_find_driver(controller_ptr->server, driver_name); if (driver == NULL) { jack_error("Unknown driver \"%s\"", driver_name); return false; } if (jack_controller_check_slave_driver(controller_ptr, driver_name)) { jack_info("Driver \"%s\" is already slave", driver_name); return true; } driver_ptr = malloc(sizeof(struct jack_controller_slave_driver)); if (driver_ptr == NULL) { jack_error("malloc() failed to allocate jack_controller_slave_driver struct"); return false; } driver_ptr->name = strdup(driver_name); if (driver_ptr->name == NULL) { jack_error("strdup() failed for slave driver name \"%s\"", driver_name); free(driver_ptr); return false; } driver_ptr->handle = driver; driver_ptr->loaded = false; jack_info("driver \"%s\" set as slave", driver_name); list_add_tail(&driver_ptr->siblings, &controller_ptr->slave_drivers); if (len_old != 0) { controller_ptr->slave_drivers_vparam_value.str[len_old++] = ','; } memcpy(controller_ptr->slave_drivers_vparam_value.str + len_old, driver_name, len_new + 1); controller_ptr->slave_drivers_set = true; return true; } bool jack_controller_remove_slave_driver( struct jack_controller * controller_ptr, const char * driver_name) { struct list_head * node_ptr; struct jack_controller_slave_driver * driver_ptr; list_for_each(node_ptr, &controller_ptr->slave_drivers) { driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); if (strcmp(driver_ptr->name, driver_name) == 0) { list_del(&driver_ptr->siblings); free(driver_ptr->name); free(driver_ptr); /* update the slave-drivers param value */ controller_ptr->slave_drivers_vparam_value.str[0] = 0; list_for_each(node_ptr, &controller_ptr->slave_drivers) { driver_ptr = list_entry(node_ptr, struct jack_controller_slave_driver, siblings); if (controller_ptr->slave_drivers_vparam_value.str[0] != 0) { strcat(controller_ptr->slave_drivers_vparam_value.str, ","); } strcat(controller_ptr->slave_drivers_vparam_value.str, driver_ptr->name); } jack_info("driver \"%s\" is not slave anymore", driver_name); return true; } } return false; } bool jack_controller_load_internal( struct jack_controller *controller_ptr, const char * internal_name) { jackctl_internal_t *internal; internal = jack_controller_find_internal(controller_ptr->server, internal_name); if (internal == NULL) { return false; } jack_info("internal \"%s\" selected", internal_name); return jackctl_server_load_internal(controller_ptr->server, internal); } bool jack_controller_unload_internal( struct jack_controller *controller_ptr, const char * internal_name) { jackctl_internal_t *internal; internal = jack_controller_find_internal(controller_ptr->server, internal_name); if (internal == NULL) { return false; } jack_info("internal \"%s\" selected", internal_name); return jackctl_server_unload_internal(controller_ptr->server, internal); } #define controller_ptr ((struct jack_controller *)context) void jack_controller_destroy( void * context) { if (controller_ptr->started) { while (!jack_controller_stop_server(controller_ptr, NULL)) { jack_info("jack server failed to stop, retrying in 3 seconds..."); usleep(3000000); } } jack_controller_remove_slave_drivers(controller_ptr); jack_params_destroy(controller_ptr->params); jackctl_server_destroy(controller_ptr->server); pthread_mutex_destroy(&controller_ptr->lock); free(controller_ptr); } void jack_controller_run( void * context) { long ut; if (controller_ptr->pending_save == 0) { return; } if ((ut = uptime()) < 0) { jack_error(UPTIME_FUNCTION_NAME "() failed with %d", errno); } else if (ut < controller_ptr->pending_save + 2) /* delay save by two seconds */ { return; } controller_ptr->pending_save = 0; jack_controller_settings_save_auto(controller_ptr); } #undef controller_ptr void jack_controller_pending_save( struct jack_controller * controller_ptr) { long ut; if ((ut = uptime()) < 0) { jack_error(UPTIME_FUNCTION_NAME "() failed with %d.", errno); controller_ptr->pending_save = 0; jack_controller_settings_save_auto(controller_ptr); return; } controller_ptr->pending_save = ut; } jack2-1.9.22/dbus/controller.h000066400000000000000000000021521436671425200161020ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef CONTROLLER_H__2CC80B1E_8D5D_45E3_A9D8_9086DDF68BB5__INCLUDED #define CONTROLLER_H__2CC80B1E_8D5D_45E3_A9D8_9086DDF68BB5__INCLUDED void * jack_controller_create( DBusConnection *connection); void jack_controller_run( void *controller_ptr); void jack_controller_destroy( void *controller_ptr); #endif /* #ifndef CONTROLLER_H__2CC80B1E_8D5D_45E3_A9D8_9086DDF68BB5__INCLUDED */ jack2-1.9.22/dbus/controller_iface_configure.c000066400000000000000000000734261436671425200213010ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008,2011 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include "jackdbus.h" #include "controller_internal.h" #include "xml.h" unsigned char jack_controller_dbus_types[JACK_PARAM_MAX] = { [JackParamInt] = DBUS_TYPE_INT32, [JackParamUInt] = DBUS_TYPE_UINT32, [JackParamChar] = DBUS_TYPE_BYTE, [JackParamString] = DBUS_TYPE_STRING, [JackParamBool] = DBUS_TYPE_BOOLEAN, }; const char *jack_controller_dbus_type_signatures[JACK_PARAM_MAX] = { [JackParamInt] = DBUS_TYPE_INT32_AS_STRING, [JackParamUInt] = DBUS_TYPE_UINT32_AS_STRING, [JackParamChar] = DBUS_TYPE_BYTE_AS_STRING, [JackParamString] = DBUS_TYPE_STRING_AS_STRING, [JackParamBool] = DBUS_TYPE_BOOLEAN_AS_STRING, }; #define PARAM_TYPE_JACK_TO_DBUS(_) jack_controller_dbus_types[_] #define PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(_) jack_controller_dbus_type_signatures[_] static bool jack_controller_jack_to_dbus_variant( jackctl_param_type_t type, const union jackctl_parameter_value *value_ptr, message_arg_t *dbusv_ptr) { switch (type) { case JackParamInt: dbusv_ptr->int32 = (dbus_int32_t)value_ptr->i; return true; case JackParamUInt: dbusv_ptr->uint32 = (dbus_uint32_t)value_ptr->ui; return true; case JackParamChar: dbusv_ptr->byte = value_ptr->c; return true; case JackParamString: dbusv_ptr->string = value_ptr->str; return true; case JackParamBool: dbusv_ptr->boolean = (dbus_bool_t)value_ptr->b; return true; } jack_error("Unknown JACK parameter type %i", (int)type); assert(0); return false; } static bool jack_controller_dbus_to_jack_variant( int type, const message_arg_t *dbusv_ptr, union jackctl_parameter_value *value_ptr) { size_t len; switch (type) { case DBUS_TYPE_INT32: value_ptr->i = dbusv_ptr->int32; return true; case DBUS_TYPE_UINT32: value_ptr->ui = dbusv_ptr->uint32; return true; case DBUS_TYPE_BYTE: value_ptr->c = dbusv_ptr->byte; return true; case DBUS_TYPE_STRING: len = strlen(dbusv_ptr->string); if (len > JACK_PARAM_STRING_MAX) { jack_error("Parameter string value is too long (%u)", (unsigned int)len); return false; } memcpy(value_ptr->str, dbusv_ptr->string, len + 1); return true; case DBUS_TYPE_BOOLEAN: value_ptr->b = dbusv_ptr->boolean; return true; } jack_error("Unknown D-Bus parameter type %i", (int)type); return false; } /* * Construct a return message for a Get[Driver|Engine]ParameterValue method call. * * The operation can only fail due to lack of memory, in which case * there's no sense in trying to construct an error return. Instead, * call->reply will be set to NULL and handled in send_method_return(). */ static void jack_dbus_construct_method_return_parameter( struct jack_dbus_method_call * call, dbus_bool_t is_set, int type, const char *signature, message_arg_t default_value, message_arg_t value) { DBusMessageIter iter; /* Create a new method return message. */ call->reply = dbus_message_new_method_return (call->message); if (!call->reply) { goto fail; } dbus_message_iter_init_append (call->reply, &iter); /* Append the is_set argument. */ if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, (const void *) &is_set)) { goto fail_unref; } /* Append the 'default' and 'value' arguments. */ if (!jack_dbus_message_append_variant(&iter, type, signature, &default_value)) { goto fail_unref; } if (!jack_dbus_message_append_variant(&iter, type, signature, &value)) { goto fail_unref; } return; fail_unref: dbus_message_unref (call->reply); call->reply = NULL; fail: jack_error ("Ran out of memory trying to construct method return"); } static bool jack_controller_dbus_get_parameter_address_ex( struct jack_dbus_method_call * call, DBusMessageIter * iter_ptr, const char ** address_array) { const char * signature; DBusMessageIter array_iter; int type; int index; if (!dbus_message_iter_init(call->message, iter_ptr)) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method '%s'. No input arguments found.", call->method_name); return false; } signature = dbus_message_iter_get_signature(iter_ptr); if (signature == NULL) { jack_error("dbus_message_iter_get_signature() failed"); return false; } if (strcmp(signature, "as") != 0) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method '%s'. Input arguments signature '%s', must begin with 'as'.", call->method_name, signature); return false; } dbus_message_iter_recurse(iter_ptr, &array_iter); index = 0; while ((type = dbus_message_iter_get_arg_type(&array_iter)) != DBUS_TYPE_INVALID) { if (index == PARAM_ADDRESS_SIZE) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method '%s'. Parameter address array must contain not more than %u elements.", (unsigned int)PARAM_ADDRESS_SIZE, call->method_name); return false; } ; if (type != DBUS_TYPE_STRING) { jack_dbus_error( call, JACK_DBUS_ERROR_FATAL, "Internal error when parsing parameter address of method '%s'. Address array element type '%c' is not string type.", call->method_name, type); return false; } dbus_message_iter_get_basic(&array_iter, address_array + index); //jack_info("address component: '%s'", address_array[index]); dbus_message_iter_next(&array_iter); index++; } while (index < PARAM_ADDRESS_SIZE) { address_array[index] = NULL; index++; } return true; } static bool jack_controller_dbus_get_parameter_address( struct jack_dbus_method_call * call, const char ** address_array) { DBusMessageIter iter; bool ret; ret = jack_controller_dbus_get_parameter_address_ex(call, &iter, address_array); if (ret && dbus_message_iter_has_next(&iter)) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method '%s'. Input arguments signature must be 'as'.", call->method_name); return false; } return ret; } #define controller_ptr ((struct jack_controller *)call->context) static bool append_node_name(void * context, const char * name) { return dbus_message_iter_append_basic(context, DBUS_TYPE_STRING, &name); } static void jack_controller_dbus_read_container( struct jack_dbus_method_call * call) { const char * address[PARAM_ADDRESS_SIZE]; dbus_bool_t leaf; DBusMessageIter iter; DBusMessageIter array_iter; //jack_info("jack_controller_dbus_read_container() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has * constructed an error for us. */ return; } //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); /* Create a new method return message. */ call->reply = dbus_message_new_method_return(call->message); if (!call->reply) { goto oom; } dbus_message_iter_init_append(call->reply, &iter); if (!jack_params_check_address(controller_ptr->params, address, false)) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", address[0], address[1], address[2], call->method_name); return; } leaf = jack_params_is_leaf_container(controller_ptr->params, address); if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &leaf)) { goto oom_unref; } if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) { goto oom_unref; } if (!jack_params_iterate_container(controller_ptr->params, address, append_node_name, &array_iter)) { goto oom_close_unref; } dbus_message_iter_close_container(&iter, &array_iter); return; oom_close_unref: dbus_message_iter_close_container(&iter, &array_iter); oom_unref: dbus_message_unref(call->reply); call->reply = NULL; oom: jack_error ("Ran out of memory trying to construct method return"); } static bool append_parameter(void * context, const struct jack_parameter * param_ptr) { DBusMessageIter struct_iter; unsigned char type; /* Open the struct. */ if (!dbus_message_iter_open_container(context, DBUS_TYPE_STRUCT, NULL, &struct_iter)) { goto fail; } /* Append parameter type. */ type = PARAM_TYPE_JACK_TO_DBUS(param_ptr->type); if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &type)) { goto fail_close; } /* Append parameter name. */ if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¶m_ptr->name)) { goto fail_close; } /* Append parameter short description. */ if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¶m_ptr->short_decr)) { goto fail_close; } /* Append parameter long description. */ if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¶m_ptr->long_descr)) { goto fail_close; } /* Close the struct. */ if (!dbus_message_iter_close_container(context, &struct_iter)) { goto fail; } return true; fail_close: dbus_message_iter_close_container(context, &struct_iter); fail: return false; } static void jack_controller_dbus_get_parameters_info( struct jack_dbus_method_call * call) { const char * address[PARAM_ADDRESS_SIZE]; DBusMessageIter iter, array_iter; //jack_info("jack_controller_dbus_get_parameters_info() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has * constructed an error for us. */ return; } //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); if (!jack_params_check_address(controller_ptr->params, address, true)) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", address[0], address[1], address[2], call->method_name); return; } call->reply = dbus_message_new_method_return (call->message); if (!call->reply) { goto fail; } dbus_message_iter_init_append (call->reply, &iter); /* Open the array. */ if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ysss)", &array_iter)) { goto fail_unref; } if (!jack_params_iterate_params(controller_ptr->params, address, append_parameter, &array_iter)) { goto fail_close_unref; } /* Close the array. */ if (!dbus_message_iter_close_container (&iter, &array_iter)) { goto fail_unref; } return; fail_close_unref: dbus_message_iter_close_container (&iter, &array_iter); fail_unref: dbus_message_unref (call->reply); call->reply = NULL; fail: jack_error ("Ran out of memory trying to construct method return"); } static void jack_controller_dbus_get_parameter_info( struct jack_dbus_method_call * call) { const char * address[PARAM_ADDRESS_SIZE]; const struct jack_parameter * param_ptr; DBusMessageIter iter; //jack_info("jack_controller_dbus_get_parameter_info() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has * constructed an error for us. */ return; } //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); param_ptr = jack_params_get_parameter(controller_ptr->params, address); if (param_ptr == NULL) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", address[0], address[1], address[2], call->method_name); return; } call->reply = dbus_message_new_method_return(call->message); if (!call->reply) { goto fail; } dbus_message_iter_init_append(call->reply, &iter); if (!append_parameter(&iter, param_ptr)) { goto fail_unref; } return; fail_unref: dbus_message_unref(call->reply); call->reply = NULL; fail: jack_error("Ran out of memory trying to construct method return"); } static void jack_controller_dbus_get_parameter_constraint( struct jack_dbus_method_call * call) { const char * address[PARAM_ADDRESS_SIZE]; const struct jack_parameter * param_ptr; uint32_t index; DBusMessageIter iter, array_iter, struct_iter; const char * descr; message_arg_t value; //jack_info("jack_controller_dbus_get_parameter_constraint() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has * constructed an error for us. */ return; } //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); param_ptr = jack_params_get_parameter(controller_ptr->params, address); if (param_ptr == NULL) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", address[0], address[1], address[2], call->method_name); return; } call->reply = dbus_message_new_method_return(call->message); if (!call->reply) { goto fail; } dbus_message_iter_init_append(call->reply, &iter); if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0) { value.boolean = param_ptr->constraint_range; } else { value.boolean = false; } if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value)) { goto fail_unref; } if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0) { value.boolean = (param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_STRICT) != 0; } if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value)) { goto fail_unref; } if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0) { value.boolean = (param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_FAKE_VALUE) != 0; } if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value)) { goto fail_unref; } /* Open the array. */ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(vs)", &array_iter)) { goto fail_unref; } if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) == 0) { goto close; } if (param_ptr->constraint_range) { /* Open the struct. */ if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) { goto fail_close_unref; } jack_controller_jack_to_dbus_variant(param_ptr->type, ¶m_ptr->constraint.range.min, &value); if (!jack_dbus_message_append_variant( &struct_iter, PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), &value)) { goto fail_close2_unref; } descr = "min"; if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &descr)) { goto fail_close2_unref; } /* Close the struct. */ if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) { goto fail_close_unref; } /* Open the struct. */ if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) { goto fail_close_unref; } jack_controller_jack_to_dbus_variant(param_ptr->type, ¶m_ptr->constraint.range.max, &value); if (!jack_dbus_message_append_variant( &struct_iter, PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), &value)) { goto fail_close2_unref; } descr = "max"; if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &descr)) { goto fail_close2_unref; } /* Close the struct. */ if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) { goto fail_close_unref; } } else { /* Append enum values to the array. */ for (index = 0 ; index < param_ptr->constraint.enumeration.count ; index++) { descr = param_ptr->constraint.enumeration.possible_values_array[index].short_desc; jack_controller_jack_to_dbus_variant(param_ptr->type, ¶m_ptr->constraint.enumeration.possible_values_array[index].value, &value); /* Open the struct. */ if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) { goto fail_close_unref; } if (!jack_dbus_message_append_variant( &struct_iter, PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), &value)) { goto fail_close2_unref; } if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &descr)) { goto fail_close2_unref; } /* Close the struct. */ if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) { goto fail_close_unref; } } } close: /* Close the array. */ if (!dbus_message_iter_close_container(&iter, &array_iter)) { goto fail_unref; } return; fail_close2_unref: dbus_message_iter_close_container(&array_iter, &struct_iter); fail_close_unref: dbus_message_iter_close_container(&iter, &array_iter); fail_unref: dbus_message_unref(call->reply); call->reply = NULL; fail: jack_error ("Ran out of memory trying to construct method return"); } static void jack_controller_dbus_get_parameter_value( struct jack_dbus_method_call * call) { const char * address[PARAM_ADDRESS_SIZE]; const struct jack_parameter * param_ptr; union jackctl_parameter_value jackctl_value; union jackctl_parameter_value jackctl_default_value; message_arg_t value; message_arg_t default_value; //jack_info("jack_controller_dbus_get_parameter_value() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has * constructed an error for us. */ return; } //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); param_ptr = jack_params_get_parameter(controller_ptr->params, address); if (param_ptr == NULL) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", address[0], address[1], address[2], call->method_name); return; } jackctl_default_value = param_ptr->vtable.get_default_value(param_ptr->obj); jackctl_value = param_ptr->vtable.get_value(param_ptr->obj); jack_controller_jack_to_dbus_variant(param_ptr->type, &jackctl_value, &value); jack_controller_jack_to_dbus_variant(param_ptr->type, &jackctl_default_value, &default_value); /* Construct the reply. */ jack_dbus_construct_method_return_parameter( call, (dbus_bool_t)(param_ptr->vtable.is_set(param_ptr->obj) ? TRUE : FALSE), PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), PARAM_TYPE_JACK_TO_DBUS_SIGNATURE(param_ptr->type), default_value, value); } static void jack_controller_dbus_set_parameter_value( struct jack_dbus_method_call * call) { const char * address[PARAM_ADDRESS_SIZE]; const struct jack_parameter * param_ptr; DBusMessageIter iter; DBusMessageIter variant_iter; message_arg_t arg; int arg_type; union jackctl_parameter_value value; //jack_info("jack_controller_dbus_set_parameter_value() called"); if (!jack_controller_dbus_get_parameter_address_ex(call, &iter, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has * constructed an error for us. */ return; } //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); param_ptr = jack_params_get_parameter(controller_ptr->params, address); if (param_ptr == NULL) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", address[0], address[1], address[2], call->method_name); return; } dbus_message_iter_next(&iter); if (dbus_message_iter_has_next(&iter)) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method '%s'. Too many arguments.", call->method_name); return; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method '%s'. Value to set must be variant.", call->method_name); return; } dbus_message_iter_recurse (&iter, &variant_iter); dbus_message_iter_get_basic(&variant_iter, &arg); arg_type = dbus_message_iter_get_arg_type(&variant_iter); //jack_info("argument of type '%c'", arg_type); if (PARAM_TYPE_JACK_TO_DBUS(param_ptr->type) != arg_type) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Parameter value type mismatch: was expecting '%c', got '%c'", (char)PARAM_TYPE_JACK_TO_DBUS(param_ptr->type), (char)arg_type); return; } if (!jack_controller_dbus_to_jack_variant( arg_type, &arg, &value)) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Cannot convert parameter value"); return; } param_ptr->vtable.set_value(param_ptr->obj, &value); jack_controller_pending_save(controller_ptr); jack_dbus_construct_method_return_empty(call); } static void jack_controller_dbus_reset_parameter_value( struct jack_dbus_method_call * call) { const char * address[PARAM_ADDRESS_SIZE]; const struct jack_parameter * param_ptr; //jack_info("jack_controller_dbus_reset_parameter_value() called"); if (!jack_controller_dbus_get_parameter_address(call, address)) { /* The method call had invalid arguments meaning that * jack_controller_dbus_get_parameter_address() has * constructed an error for us. */ return; } //jack_info("address is '%s':'%s':'%s'", address[0], address[1], address[2]); param_ptr = jack_params_get_parameter(controller_ptr->params, address); if (param_ptr == NULL) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid container address '%s':'%s':'%s' supplied to method '%s'.", address[0], address[1], address[2], call->method_name); return; } param_ptr->vtable.reset(param_ptr->obj); jack_controller_pending_save(controller_ptr); jack_dbus_construct_method_return_empty(call); } #undef controller_ptr JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(ReadContainer, "Get names of child parameters or containers") JACK_DBUS_METHOD_ARGUMENT_IN("parent", "as", "Address of parent container") JACK_DBUS_METHOD_ARGUMENT_OUT("leaf", "b", "Whether children are parameters (true) or containers (false)") JACK_DBUS_METHOD_ARGUMENT_OUT("children", "as", "Array of child names") JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(GetParametersInfo, "Retrieve info about parameters") JACK_DBUS_METHOD_ARGUMENT_IN("parent", "as", "Address of parameters parent") JACK_DBUS_METHOD_ARGUMENT_OUT("parameter_info_array", "a(ysss)", "Array of parameter info structs. Each info struct contains: type char, name, short and long description") JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(GetParameterInfo, "Retrieve info about parameter") JACK_DBUS_METHOD_ARGUMENT_IN("parameter", "as", "Address of parameter") JACK_DBUS_METHOD_ARGUMENT_OUT("parameter_info", "(ysss)", "Parameter info struct that contains: type char, name, short and long description") JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(GetParameterConstraint, "Get constraint of parameter") JACK_DBUS_METHOD_ARGUMENT_IN("parameter", "as", "Address of parameter") JACK_DBUS_METHOD_ARGUMENT_OUT("is_range", "b", "Whether constrinat is a range. If so, values parameter will contain two values, min and max") JACK_DBUS_METHOD_ARGUMENT_OUT("is_strict", "b", "Whether enum constraint is strict. I.e. value not listed in values array will not work") JACK_DBUS_METHOD_ARGUMENT_OUT("is_fake_value", "b", "Whether enum values are fake. I.e. have no user meaningful meaning") JACK_DBUS_METHOD_ARGUMENT_OUT("values", "a(vs)", "Values. If there is no constraint, this array will be empty. For range constraint there will be two values, min and max. For enum constraint there will be 2 or more values.") JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(GetParameterValue, "Get value of parameter") JACK_DBUS_METHOD_ARGUMENT_IN("parameter", "as", "Address of parameter") JACK_DBUS_METHOD_ARGUMENT_OUT("is_set", "b", "Whether parameter is set or its default value is used") JACK_DBUS_METHOD_ARGUMENT_OUT("default", "v", "Default value of parameter") JACK_DBUS_METHOD_ARGUMENT_OUT("value", "v", "Actual value of parameter") JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(SetParameterValue, "Set value of parameter") JACK_DBUS_METHOD_ARGUMENT_IN("parameter", "as", "Address of parameter") JACK_DBUS_METHOD_ARGUMENT_IN("value", "v", "New value for parameter") JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(ResetParameterValue, "Reset parameter to default value") JACK_DBUS_METHOD_ARGUMENT_IN("parameter", "as", "Address of parameter") JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHODS_BEGIN JACK_DBUS_METHOD_DESCRIBE(ReadContainer, jack_controller_dbus_read_container) JACK_DBUS_METHOD_DESCRIBE(GetParametersInfo, jack_controller_dbus_get_parameters_info) JACK_DBUS_METHOD_DESCRIBE(GetParameterInfo, jack_controller_dbus_get_parameter_info) JACK_DBUS_METHOD_DESCRIBE(GetParameterConstraint, jack_controller_dbus_get_parameter_constraint) JACK_DBUS_METHOD_DESCRIBE(GetParameterValue, jack_controller_dbus_get_parameter_value) JACK_DBUS_METHOD_DESCRIBE(SetParameterValue, jack_controller_dbus_set_parameter_value) JACK_DBUS_METHOD_DESCRIBE(ResetParameterValue, jack_controller_dbus_reset_parameter_value) JACK_DBUS_METHODS_END /* * Parameter addresses: * * "engine" * "engine", "driver" * "engine", "realtime" * "engine", ...more engine parameters * * "driver", "device" * "driver", ...more driver parameters * * "drivers", "alsa", "device" * "drivers", "alsa", ...more alsa driver parameters * * "drivers", ...more drivers * * "internals", "netmanager", "multicast_ip" * "internals", "netmanager", ...more netmanager parameters * * "internals", ...more internals * */ JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_configure, "org.jackaudio.Configure") JACK_DBUS_IFACE_EXPOSE_METHODS JACK_DBUS_IFACE_END jack2-1.9.22/dbus/controller_iface_control.c000066400000000000000000000313711436671425200207710ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008,2010 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include "jackdbus.h" #include "controller_internal.h" #include "xml.h" #define JACK_DBUS_IFACE_NAME "org.jackaudio.JackControl" void jack_controller_control_send_signal_server_started() { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "ServerStarted", DBUS_TYPE_INVALID); } void jack_controller_control_send_signal_server_stopped() { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "ServerStopped", DBUS_TYPE_INVALID); } #define controller_ptr ((struct jack_controller *)call->context) /* * Check if the supplied method name exists in org.jackaudio.JackControl, * if it does execute it and return true. Otherwise return false. */ static bool jack_control_run_method( struct jack_dbus_method_call * call, const struct jack_dbus_interface_method_descriptor * methods) { int ret; int type; message_arg_t arg; /* use empty reply if not overridden in the code that follows */ type = DBUS_TYPE_INVALID; if (strcmp (call->method_name, "Exit") == 0) { g_exit_command = TRUE; } else if (strcmp (call->method_name, "IsStarted") == 0) { type = DBUS_TYPE_BOOLEAN; arg.boolean = (dbus_bool_t) (controller_ptr->started ? TRUE : FALSE); } else if (strcmp (call->method_name, "StartServer") == 0) { if (controller_ptr->started) { jack_info("Ignoring JACK server start request because server is already started."); } else { if (!jack_controller_start_server(controller_ptr, call)) { /* the reply is set by the failed function */ assert(call->reply != NULL); return true; } jack_controller_control_send_signal_server_started(); } } else if (strcmp (call->method_name, "StopServer") == 0) { if (!controller_ptr->started) { jack_info("Ignoring JACK server stop request because server is already stopped."); } else { if (!jack_controller_stop_server(controller_ptr, call)) { /* the reply is set by the failed function */ assert(call->reply != NULL); return true; } jack_controller_control_send_signal_server_stopped(); } } else if (strcmp (call->method_name, "SwitchMaster") == 0) { if (!controller_ptr->started) { goto not_started; } else { if (!jack_controller_switch_master(controller_ptr, call)) { /* the reply is set by the failed function */ assert(call->reply != NULL); return true; } } } else if (strcmp (call->method_name, "GetLoad") == 0) { if (!controller_ptr->started) { goto not_started; } type = DBUS_TYPE_DOUBLE; arg.doubl = jack_cpu_load(controller_ptr->client); } else if (strcmp (call->method_name, "GetXruns") == 0) { type = DBUS_TYPE_UINT32; arg.uint32 = controller_ptr->xruns; } else if (strcmp (call->method_name, "GetSampleRate") == 0) { if (!controller_ptr->started) { goto not_started; } type = DBUS_TYPE_UINT32; arg.uint32 = jack_get_sample_rate(controller_ptr->client); } else if (strcmp (call->method_name, "GetLatency") == 0) { if (!controller_ptr->started) { goto not_started; } type = DBUS_TYPE_DOUBLE; arg.doubl = ((float)jack_get_buffer_size(controller_ptr->client) / (float)jack_get_sample_rate(controller_ptr->client)) * 1000.0f; } else if (strcmp (call->method_name, "GetBufferSize") == 0) { if (!controller_ptr->started) { goto not_started; } type = DBUS_TYPE_UINT32; arg.uint32 = jack_get_buffer_size(controller_ptr->client); } else if (strcmp (call->method_name, "SetBufferSize") == 0) { dbus_uint32_t buffer_size; if (!controller_ptr->started) { goto not_started; } if (!jack_dbus_get_method_args(call, DBUS_TYPE_UINT32, &buffer_size, DBUS_TYPE_INVALID)) { /* jack_dbus_get_method_args() has set reply for us */ goto exit; } ret = jack_set_buffer_size(controller_ptr->client, buffer_size); if (ret != 0) { jack_dbus_error( call, JACK_DBUS_ERROR_GENERIC, "jack_set_buffer_size(%u) failed with error %d", (unsigned int)buffer_size, ret); goto exit; } } else if (strcmp (call->method_name, "IsRealtime") == 0) { type = DBUS_TYPE_BOOLEAN; arg.boolean = jack_is_realtime(controller_ptr->client) ? TRUE : FALSE; } else if (strcmp (call->method_name, "ResetXruns") == 0) { controller_ptr->xruns = 0; } else if (strcmp (call->method_name, "LoadInternal") == 0) { const char *internal_name; if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &internal_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * get_method_args() has constructed an error for us. */ goto exit; } if (!jack_controller_load_internal(controller_ptr, internal_name)) { jack_dbus_error( call, JACK_DBUS_ERROR_GENERIC, "jack_controller_load_internal failed for internal (%s)", internal_name); } } else if (strcmp (call->method_name, "AddSlaveDriver") == 0) { const char *driver_name; if (controller_ptr->started) { goto fail_started; } if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &driver_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * get_method_args() has constructed an error for us. */ goto exit; } if (!jack_controller_add_slave_driver(controller_ptr, driver_name)) { jack_dbus_error( call, JACK_DBUS_ERROR_GENERIC, "jack_controller_add_slave_driver failed for driver (%s)", driver_name); } else { jack_controller_pending_save(controller_ptr); } } else if (strcmp (call->method_name, "RemoveSlaveDriver") == 0) { const char *driver_name; if (controller_ptr->started) { goto fail_started; } if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &driver_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * get_method_args() has constructed an error for us. */ goto exit; } if (!jack_controller_remove_slave_driver(controller_ptr, driver_name)) { jack_dbus_error( call, JACK_DBUS_ERROR_GENERIC, "jack_controller_remove_slave_driver failed for driver (%s)", driver_name); } else { jack_controller_pending_save(controller_ptr); } } else if (strcmp (call->method_name, "UnloadInternal") == 0) { const char *internal_name; if (!jack_dbus_get_method_args(call, DBUS_TYPE_STRING, &internal_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * get_method_args() has constructed an error for us. */ goto exit; } if (!jack_controller_unload_internal(controller_ptr, internal_name)) { jack_dbus_error( call, JACK_DBUS_ERROR_GENERIC, "jack_controller_unload_internal failed for internal (%s)", internal_name); } } else { return false; } jack_dbus_construct_method_return_single(call, type, arg); goto exit; not_started: jack_dbus_only_error( call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute method '%s' with stopped JACK server", call->method_name); goto exit; fail_started: jack_dbus_only_error( call, JACK_DBUS_ERROR_SERVER_RUNNING, "Can't execute method '%s' with started JACK server", call->method_name); goto exit; exit: return true; } #undef controller_ptr JACK_DBUS_METHOD_ARGUMENTS_BEGIN(IsStarted) JACK_DBUS_METHOD_ARGUMENT("started", "b", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(StartServer) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(StopServer) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(SwitchMaster) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetLoad) JACK_DBUS_METHOD_ARGUMENT("load", "d", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetXruns) JACK_DBUS_METHOD_ARGUMENT("xruns_count", "u", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetSampleRate) JACK_DBUS_METHOD_ARGUMENT("sample_rate", "u", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetLatency) JACK_DBUS_METHOD_ARGUMENT("latency_ms", "d", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetBufferSize) JACK_DBUS_METHOD_ARGUMENT("buffer_size_frames", "u", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(SetBufferSize) JACK_DBUS_METHOD_ARGUMENT("buffer_size_frames", "u", false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(IsRealtime) JACK_DBUS_METHOD_ARGUMENT("realtime", "b", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ResetXruns) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(LoadInternal) JACK_DBUS_METHOD_ARGUMENT("internal", "s", false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(UnloadInternal) JACK_DBUS_METHOD_ARGUMENT("internal", "s", false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(AddSlaveDriver) JACK_DBUS_METHOD_ARGUMENT("driver_name", "s", false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(RemoveSlaveDriver) JACK_DBUS_METHOD_ARGUMENT("driver_name", "s", false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHODS_BEGIN JACK_DBUS_METHOD_DESCRIBE(IsStarted, NULL) JACK_DBUS_METHOD_DESCRIBE(StartServer, NULL) JACK_DBUS_METHOD_DESCRIBE(StopServer, NULL) JACK_DBUS_METHOD_DESCRIBE(SwitchMaster, NULL) JACK_DBUS_METHOD_DESCRIBE(GetLoad, NULL) JACK_DBUS_METHOD_DESCRIBE(GetXruns, NULL) JACK_DBUS_METHOD_DESCRIBE(GetSampleRate, NULL) JACK_DBUS_METHOD_DESCRIBE(GetLatency, NULL) JACK_DBUS_METHOD_DESCRIBE(GetBufferSize, NULL) JACK_DBUS_METHOD_DESCRIBE(SetBufferSize, NULL) JACK_DBUS_METHOD_DESCRIBE(IsRealtime, NULL) JACK_DBUS_METHOD_DESCRIBE(ResetXruns, NULL) JACK_DBUS_METHOD_DESCRIBE(LoadInternal, NULL) JACK_DBUS_METHOD_DESCRIBE(UnloadInternal, NULL) JACK_DBUS_METHOD_DESCRIBE(AddSlaveDriver, NULL) JACK_DBUS_METHOD_DESCRIBE(RemoveSlaveDriver, NULL) JACK_DBUS_METHODS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(ServerStarted) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(ServerStopped) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNALS_BEGIN JACK_DBUS_SIGNAL_DESCRIBE(ServerStarted) JACK_DBUS_SIGNAL_DESCRIBE(ServerStopped) JACK_DBUS_SIGNALS_END JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_control, JACK_DBUS_IFACE_NAME) JACK_DBUS_IFACE_HANDLER(jack_control_run_method) JACK_DBUS_IFACE_EXPOSE_METHODS JACK_DBUS_IFACE_EXPOSE_SIGNALS JACK_DBUS_IFACE_END jack2-1.9.22/dbus/controller_iface_introspectable.c000066400000000000000000000110601436671425200223200ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007-2008 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include "jackdbus.h" static char g_xml_data[102400]; static void jack_controller_dbus_introspect( struct jack_dbus_method_call * call) { jack_dbus_construct_method_return_single( call, DBUS_TYPE_STRING, (message_arg_t)(const char *)g_xml_data); } JACK_DBUS_METHOD_ARGUMENTS_BEGIN(Introspect) JACK_DBUS_METHOD_ARGUMENT("xml_data", "s", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHODS_BEGIN JACK_DBUS_METHOD_DESCRIBE(Introspect, jack_controller_dbus_introspect) JACK_DBUS_METHODS_END JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_introspectable, "org.freedesktop.DBus.Introspectable") JACK_DBUS_IFACE_EXPOSE_METHODS JACK_DBUS_IFACE_END static char * g_buffer_ptr; static void write_line_format(const char * format, ...) { va_list ap; va_start(ap, format); g_buffer_ptr += vsprintf(g_buffer_ptr, format, ap); va_end(ap); } static void write_line(const char * line) { write_line_format("%s\n", line); } void jack_controller_introspect_init() __attribute__((constructor)); void jack_controller_introspect_init() { struct jack_dbus_interface_descriptor ** interface_ptr_ptr; const struct jack_dbus_interface_method_descriptor * method_ptr; const struct jack_dbus_interface_method_argument_descriptor * method_argument_ptr; const struct jack_dbus_interface_signal_descriptor * signal_ptr; const struct jack_dbus_interface_signal_argument_descriptor * signal_argument_ptr; g_buffer_ptr = g_xml_data; write_line(""); write_line(""); interface_ptr_ptr = g_jackcontroller_interfaces; while (*interface_ptr_ptr != NULL) { write_line_format(" \n", (*interface_ptr_ptr)->name); if ((*interface_ptr_ptr)->methods != NULL) { method_ptr = (*interface_ptr_ptr)->methods; while (method_ptr->name != NULL) { write_line_format(" \n", method_ptr->name); method_argument_ptr = method_ptr->arguments; while (method_argument_ptr->name != NULL) { write_line_format( " \n", method_argument_ptr->name, method_argument_ptr->type, method_argument_ptr->direction_out ? "out" : "in"); method_argument_ptr++; } write_line(" "); method_ptr++; } } if ((*interface_ptr_ptr)->signals != NULL) { signal_ptr = (*interface_ptr_ptr)->signals; while (signal_ptr->name != NULL) { write_line_format(" \n", signal_ptr->name); signal_argument_ptr = signal_ptr->arguments; while (signal_argument_ptr->name != NULL) { write_line_format( " \n", signal_argument_ptr->name, signal_argument_ptr->type); signal_argument_ptr++; } write_line(" "); signal_ptr++; } } write_line(" "); interface_ptr_ptr++; } write_line(""); *g_buffer_ptr = 0; } jack2-1.9.22/dbus/controller_iface_patchbay.c000066400000000000000000001647041436671425200211130ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #define _GNU_SOURCE /* PTHREAD_MUTEX_RECURSIVE */ #include #include #include #include #include #include #include #include "jackdbus.h" #include "controller_internal.h" #include "list.h" #define JACK_DBUS_IFACE_NAME "org.jackaudio.JackPatchbay" /* FIXME: these need to be retrieved from common headers */ #define JACK_CLIENT_NAME_SIZE 64 #define JACK_PORT_NAME_SIZE 256 struct jack_graph { uint64_t version; struct list_head clients; struct list_head ports; struct list_head connections; }; struct jack_graph_client { uint64_t id; char * name; int pid; struct list_head siblings; struct list_head ports; }; struct jack_graph_port { uint64_t id; char * name; uint32_t flags; uint32_t type; struct list_head siblings_graph; struct list_head siblings_client; struct jack_graph_client * client; }; struct jack_graph_connection { uint64_t id; struct jack_graph_port * port1; struct jack_graph_port * port2; struct list_head siblings; }; struct jack_controller_patchbay { pthread_mutex_t lock; struct jack_graph graph; uint64_t next_client_id; uint64_t next_port_id; uint64_t next_connection_id; }; void jack_controller_patchbay_send_signal_graph_changed( dbus_uint64_t new_graph_version) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "GraphChanged", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_INVALID); } void jack_controller_patchbay_send_signal_client_appeared( dbus_uint64_t new_graph_version, dbus_uint64_t client_id, const char * client_name) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "ClientAppeared", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID); } void jack_controller_patchbay_send_signal_client_disappeared( dbus_uint64_t new_graph_version, dbus_uint64_t client_id, const char * client_name) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "ClientDisappeared", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID); } void jack_controller_patchbay_send_signal_port_appeared( dbus_uint64_t new_graph_version, dbus_uint64_t client_id, const char * client_name, dbus_uint64_t port_id, const char * port_name, dbus_uint32_t port_flags, dbus_uint32_t port_type) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "PortAppeared", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_UINT64, &port_id, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_UINT32, &port_flags, DBUS_TYPE_UINT32, &port_type, DBUS_TYPE_INVALID); } void jack_controller_patchbay_send_signal_port_disappeared( dbus_uint64_t new_graph_version, dbus_uint64_t client_id, const char * client_name, dbus_uint64_t port_id, const char * port_name) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "PortDisappeared", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_UINT64, &port_id, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID); } void jack_controller_patchbay_send_signal_ports_connected( dbus_uint64_t new_graph_version, dbus_uint64_t client1_id, const char * client1_name, dbus_uint64_t port1_id, const char * port1_name, dbus_uint64_t client2_id, const char * client2_name, dbus_uint64_t port2_id, const char * port2_name, dbus_uint64_t connection_id) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "PortsConnected", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client1_id, DBUS_TYPE_STRING, &client1_name, DBUS_TYPE_UINT64, &port1_id, DBUS_TYPE_STRING, &port1_name, DBUS_TYPE_UINT64, &client2_id, DBUS_TYPE_STRING, &client2_name, DBUS_TYPE_UINT64, &port2_id, DBUS_TYPE_STRING, &port2_name, DBUS_TYPE_UINT64, &connection_id, DBUS_TYPE_INVALID); } void jack_controller_patchbay_send_signal_ports_disconnected( dbus_uint64_t new_graph_version, dbus_uint64_t client1_id, const char * client1_name, dbus_uint64_t port1_id, const char * port1_name, dbus_uint64_t client2_id, const char * client2_name, dbus_uint64_t port2_id, const char * port2_name, dbus_uint64_t connection_id) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "PortsDisconnected", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client1_id, DBUS_TYPE_STRING, &client1_name, DBUS_TYPE_UINT64, &port1_id, DBUS_TYPE_STRING, &port1_name, DBUS_TYPE_UINT64, &client2_id, DBUS_TYPE_STRING, &client2_name, DBUS_TYPE_UINT64, &port2_id, DBUS_TYPE_STRING, &port2_name, DBUS_TYPE_UINT64, &connection_id, DBUS_TYPE_INVALID); } void jack_controller_patchbay_send_signal_port_renamed( dbus_uint64_t new_graph_version, dbus_uint64_t client_id, const char * client_name, dbus_uint64_t port_id, const char * port_old_name, const char * port_new_name) { jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "PortRenamed", DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_UINT64, &port_id, DBUS_TYPE_STRING, &port_old_name, DBUS_TYPE_STRING, &port_new_name, DBUS_TYPE_INVALID); } static struct jack_graph_client * jack_controller_patchbay_find_client( struct jack_controller_patchbay *patchbay_ptr, const char *client_name, /* not '\0' terminated */ size_t client_name_len) /* without terminating '\0' */ { struct list_head *node_ptr; struct jack_graph_client *client_ptr; list_for_each(node_ptr, &patchbay_ptr->graph.clients) { client_ptr = list_entry(node_ptr, struct jack_graph_client, siblings); if (strlen(client_ptr->name) == client_name_len && strncmp(client_ptr->name, client_name, client_name_len) == 0) { return client_ptr; } } return NULL; } static struct jack_graph_client * jack_controller_patchbay_find_client_by_id( struct jack_controller_patchbay *patchbay_ptr, uint64_t id) { struct list_head *node_ptr; struct jack_graph_client *client_ptr; list_for_each(node_ptr, &patchbay_ptr->graph.clients) { client_ptr = list_entry(node_ptr, struct jack_graph_client, siblings); if (client_ptr->id == id) { return client_ptr; } } return NULL; } static struct jack_graph_client * jack_controller_patchbay_create_client( struct jack_controller_patchbay *patchbay_ptr, const char *client_name, /* not '\0' terminated */ size_t client_name_len) /* without terminating '\0' */ { struct jack_graph_client * client_ptr; client_ptr = malloc(sizeof(struct jack_graph_client)); if (client_ptr == NULL) { jack_error("Memory allocation of jack_graph_client structure failed."); goto fail; } client_ptr->name = malloc(client_name_len + 1); if (client_ptr->name == NULL) { jack_error("malloc() failed to allocate memory for client name."); goto fail_free_client; } memcpy(client_ptr->name, client_name, client_name_len); client_ptr->name[client_name_len] = 0; client_ptr->pid = jack_get_client_pid(client_ptr->name); jack_info("New client '%s' with PID %d", client_ptr->name, client_ptr->pid); client_ptr->id = patchbay_ptr->next_client_id++; INIT_LIST_HEAD(&client_ptr->ports); pthread_mutex_lock(&patchbay_ptr->lock); list_add_tail(&client_ptr->siblings, &patchbay_ptr->graph.clients); patchbay_ptr->graph.version++; jack_controller_patchbay_send_signal_client_appeared(patchbay_ptr->graph.version, client_ptr->id, client_ptr->name); jack_controller_patchbay_send_signal_graph_changed(patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); return client_ptr; fail_free_client: free(client_ptr); fail: return NULL; } static void jack_controller_patchbay_destroy_client( struct jack_controller_patchbay *patchbay_ptr, struct jack_graph_client *client_ptr) { jack_info("Client '%s' with PID %d is out", client_ptr->name, client_ptr->pid); pthread_mutex_lock(&patchbay_ptr->lock); list_del(&client_ptr->siblings); patchbay_ptr->graph.version++; jack_controller_patchbay_send_signal_client_disappeared(patchbay_ptr->graph.version, client_ptr->id, client_ptr->name); jack_controller_patchbay_send_signal_graph_changed(patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); free(client_ptr->name); free(client_ptr); } static void jack_controller_patchbay_destroy_client_by_name( struct jack_controller_patchbay *patchbay_ptr, const char *client_name) /* '\0' terminated */ { struct jack_graph_client *client_ptr; client_ptr = jack_controller_patchbay_find_client(patchbay_ptr, client_name, strlen(client_name)); if (client_ptr == NULL) { jack_error("Cannot destroy unknown client '%s'", client_name); return; } jack_controller_patchbay_destroy_client(patchbay_ptr, client_ptr); } static void jack_controller_patchbay_new_port( struct jack_controller_patchbay *patchbay_ptr, const char *port_full_name, uint32_t port_flags, uint32_t port_type) { struct jack_graph_client *client_ptr; struct jack_graph_port *port_ptr; const char *port_short_name; size_t client_name_len; //jack_info("new port: %s", port_full_name); port_short_name = strchr(port_full_name, ':'); if (port_short_name == NULL) { jack_error("port name '%s' does not contain ':' separator char", port_full_name); return; } port_short_name++; /* skip ':' separator char */ client_name_len = port_short_name - port_full_name - 1; /* without terminating '\0' */ client_ptr = jack_controller_patchbay_find_client(patchbay_ptr, port_full_name, client_name_len); if (client_ptr == NULL) { client_ptr = jack_controller_patchbay_create_client(patchbay_ptr, port_full_name, client_name_len); if (client_ptr == NULL) { jack_error("Creation of new jack_graph client failed."); return; } } port_ptr = malloc(sizeof(struct jack_graph_port)); if (port_ptr == NULL) { jack_error("Memory allocation of jack_graph_port structure failed."); return; } port_ptr->name = strdup(port_short_name); if (port_ptr->name == NULL) { jack_error("strdup() call for port name '%s' failed.", port_short_name); free(port_ptr); return; } port_ptr->id = patchbay_ptr->next_port_id++; port_ptr->flags = port_flags; port_ptr->type = port_type; port_ptr->client = client_ptr; pthread_mutex_lock(&patchbay_ptr->lock); list_add_tail(&port_ptr->siblings_client, &client_ptr->ports); list_add_tail(&port_ptr->siblings_graph, &patchbay_ptr->graph.ports); patchbay_ptr->graph.version++; jack_controller_patchbay_send_signal_port_appeared( patchbay_ptr->graph.version, client_ptr->id, client_ptr->name, port_ptr->id, port_ptr->name, port_ptr->flags, port_ptr->type); jack_controller_patchbay_send_signal_graph_changed(patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); } static void jack_controller_patchbay_remove_port( struct jack_controller_patchbay *patchbay_ptr, struct jack_graph_port *port_ptr) { //jack_info("remove port: %s", port_ptr->name); pthread_mutex_lock(&patchbay_ptr->lock); list_del(&port_ptr->siblings_client); list_del(&port_ptr->siblings_graph); patchbay_ptr->graph.version++; jack_controller_patchbay_send_signal_port_disappeared(patchbay_ptr->graph.version, port_ptr->client->id, port_ptr->client->name, port_ptr->id, port_ptr->name); jack_controller_patchbay_send_signal_graph_changed(patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); free(port_ptr->name); free(port_ptr); } static struct jack_graph_port * jack_controller_patchbay_find_port_by_id( struct jack_controller_patchbay *patchbay_ptr, uint64_t port_id) { struct list_head *node_ptr; struct jack_graph_port *port_ptr; list_for_each(node_ptr, &patchbay_ptr->graph.ports) { port_ptr = list_entry(node_ptr, struct jack_graph_port, siblings_graph); if (port_ptr->id == port_id) { return port_ptr; } } return NULL; } static struct jack_graph_port * jack_controller_patchbay_find_client_port_by_name( struct jack_controller_patchbay *patchbay_ptr, struct jack_graph_client *client_ptr, const char *port_name) { struct list_head *node_ptr; struct jack_graph_port *port_ptr; list_for_each(node_ptr, &client_ptr->ports) { port_ptr = list_entry(node_ptr, struct jack_graph_port, siblings_client); if (strcmp(port_ptr->name, port_name) == 0) { return port_ptr; } } return NULL; } static struct jack_graph_port * jack_controller_patchbay_find_port_by_full_name( struct jack_controller_patchbay *patchbay_ptr, const char *port_full_name) { const char *port_short_name; size_t client_name_len; struct jack_graph_client *client_ptr; //jack_info("name: %s", port_full_name); port_short_name = strchr(port_full_name, ':'); if (port_short_name == NULL) { jack_error("port name '%s' does not contain ':' separator char", port_full_name); return NULL; } port_short_name++; /* skip ':' separator char */ client_name_len = port_short_name - port_full_name - 1; /* without terminating '\0' */ client_ptr = jack_controller_patchbay_find_client(patchbay_ptr, port_full_name, client_name_len); if (client_ptr == NULL) { jack_error("cannot find client of port '%s'", port_full_name); return NULL; } return jack_controller_patchbay_find_client_port_by_name(patchbay_ptr, client_ptr, port_short_name); } static struct jack_graph_port * jack_controller_patchbay_find_port_by_names( struct jack_controller_patchbay *patchbay_ptr, const char *client_name, const char *port_name) { struct jack_graph_client *client_ptr; client_ptr = jack_controller_patchbay_find_client(patchbay_ptr, client_name, strlen(client_name)); if (client_ptr == NULL) { jack_error("cannot find client '%s'", client_name); return NULL; } return jack_controller_patchbay_find_client_port_by_name(patchbay_ptr, client_ptr, port_name); } static struct jack_graph_connection * jack_controller_patchbay_create_connection( struct jack_controller_patchbay *patchbay_ptr, struct jack_graph_port *port1_ptr, struct jack_graph_port *port2_ptr) { struct jack_graph_connection * connection_ptr; connection_ptr = malloc(sizeof(struct jack_graph_connection)); if (connection_ptr == NULL) { jack_error("Memory allocation of jack_graph_connection structure failed."); return NULL; } connection_ptr->id = patchbay_ptr->next_connection_id++; connection_ptr->port1 = port1_ptr; connection_ptr->port2 = port2_ptr; pthread_mutex_lock(&patchbay_ptr->lock); list_add_tail(&connection_ptr->siblings, &patchbay_ptr->graph.connections); patchbay_ptr->graph.version++; jack_controller_patchbay_send_signal_ports_connected( patchbay_ptr->graph.version, port1_ptr->client->id, port1_ptr->client->name, port1_ptr->id, port1_ptr->name, port2_ptr->client->id, port2_ptr->client->name, port2_ptr->id, port2_ptr->name, connection_ptr->id); jack_controller_patchbay_send_signal_graph_changed(patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); return connection_ptr; } static void jack_controller_patchbay_destroy_connection( struct jack_controller_patchbay *patchbay_ptr, struct jack_graph_connection *connection_ptr) { pthread_mutex_lock(&patchbay_ptr->lock); list_del(&connection_ptr->siblings); patchbay_ptr->graph.version++; jack_controller_patchbay_send_signal_ports_disconnected( patchbay_ptr->graph.version, connection_ptr->port1->client->id, connection_ptr->port1->client->name, connection_ptr->port1->id, connection_ptr->port1->name, connection_ptr->port2->client->id, connection_ptr->port2->client->name, connection_ptr->port2->id, connection_ptr->port2->name, connection_ptr->id); jack_controller_patchbay_send_signal_graph_changed(patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); free(connection_ptr); } static struct jack_graph_connection * jack_controller_patchbay_find_connection( struct jack_controller_patchbay *patchbay_ptr, struct jack_graph_port *port1_ptr, struct jack_graph_port *port2_ptr) { struct list_head *node_ptr; struct jack_graph_connection *connection_ptr; list_for_each(node_ptr, &patchbay_ptr->graph.connections) { connection_ptr = list_entry(node_ptr, struct jack_graph_connection, siblings); if ((connection_ptr->port1 == port1_ptr && connection_ptr->port2 == port2_ptr) || (connection_ptr->port1 == port2_ptr && connection_ptr->port2 == port1_ptr)) { return connection_ptr; } } return NULL; } static struct jack_graph_connection * jack_controller_patchbay_find_connection_by_id( struct jack_controller_patchbay *patchbay_ptr, uint64_t connection_id) { struct list_head *node_ptr; struct jack_graph_connection *connection_ptr; list_for_each(node_ptr, &patchbay_ptr->graph.connections) { connection_ptr = list_entry(node_ptr, struct jack_graph_connection, siblings); if (connection_ptr->id == connection_id) { return connection_ptr; } } return NULL; } static bool jack_controller_patchbay_connect( struct jack_dbus_method_call *dbus_call_ptr, struct jack_controller *controller_ptr, struct jack_graph_port *port1_ptr, struct jack_graph_port *port2_ptr) { int ret; char port1_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char port2_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; sprintf(port1_name, "%s:%s", port1_ptr->client->name, port1_ptr->name); sprintf(port2_name, "%s:%s", port2_ptr->client->name, port2_ptr->name); ret = jack_connect(controller_ptr->client, port1_name, port2_name); if (ret != 0) { jack_dbus_error(dbus_call_ptr, JACK_DBUS_ERROR_GENERIC, "jack_connect() failed with %d", ret); return false; } return true; } static bool jack_controller_patchbay_disconnect( struct jack_dbus_method_call *dbus_call_ptr, struct jack_controller *controller_ptr, struct jack_graph_port *port1_ptr, struct jack_graph_port *port2_ptr) { int ret; char port1_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char port2_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; sprintf(port1_name, "%s:%s", port1_ptr->client->name, port1_ptr->name); sprintf(port2_name, "%s:%s", port2_ptr->client->name, port2_ptr->name); ret = jack_disconnect(controller_ptr->client, port1_name, port2_name); if (ret != 0) { jack_dbus_error(dbus_call_ptr, JACK_DBUS_ERROR_GENERIC, "jack_disconnect() failed with %d", ret); return false; } return true; } #define controller_ptr ((struct jack_controller *)call->context) #define patchbay_ptr ((struct jack_controller_patchbay *)controller_ptr->patchbay_context) static void jack_controller_dbus_get_all_ports( struct jack_dbus_method_call * call) { struct list_head * client_node_ptr; struct list_head * port_node_ptr; struct jack_graph_client * client_ptr; struct jack_graph_port * port_ptr; DBusMessageIter iter, sub_iter; char fullname[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; char *fullname_var = fullname; if (!controller_ptr->started) { jack_dbus_error( call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute this method with stopped JACK server"); return; } call->reply = dbus_message_new_method_return (call->message); if (!call->reply) { goto fail; } dbus_message_iter_init_append (call->reply, &iter); if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub_iter)) { goto fail_unref; } pthread_mutex_lock(&patchbay_ptr->lock); list_for_each(client_node_ptr, &patchbay_ptr->graph.clients) { client_ptr = list_entry(client_node_ptr, struct jack_graph_client, siblings); list_for_each(port_node_ptr, &client_ptr->ports) { port_ptr = list_entry(port_node_ptr, struct jack_graph_port, siblings_client); jack_info("%s:%s", client_ptr->name, port_ptr->name); sprintf(fullname, "%s:%s", client_ptr->name, port_ptr->name); if (!dbus_message_iter_append_basic (&sub_iter, DBUS_TYPE_STRING, &fullname_var)) { pthread_mutex_unlock(&patchbay_ptr->lock); dbus_message_iter_close_container (&iter, &sub_iter); goto fail_unref; } } } pthread_mutex_unlock(&patchbay_ptr->lock); if (!dbus_message_iter_close_container (&iter, &sub_iter)) { goto fail_unref; } return; fail_unref: dbus_message_unref (call->reply); call->reply = NULL; fail: jack_error ("Ran out of memory trying to construct method return"); } static void jack_controller_dbus_get_graph( struct jack_dbus_method_call * call) { struct list_head * client_node_ptr; struct list_head * port_node_ptr; struct list_head * connection_node_ptr; struct jack_graph_client * client_ptr; struct jack_graph_port * port_ptr; struct jack_graph_connection * connection_ptr; DBusMessageIter iter; DBusMessageIter clients_array_iter; DBusMessageIter client_struct_iter; DBusMessageIter ports_array_iter; DBusMessageIter port_struct_iter; dbus_uint64_t version; DBusMessageIter connections_array_iter; DBusMessageIter connection_struct_iter; if (!controller_ptr->started) { jack_dbus_error( call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute this method with stopped JACK server"); return; } if (!jack_dbus_get_method_args(call, DBUS_TYPE_UINT64, &version, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * jack_dbus_get_method_args() has constructed an error for us. */ goto exit; } //jack_info("Getting graph, know version is %" PRIu32, version); call->reply = dbus_message_new_method_return(call->message); if (!call->reply) { jack_error("Ran out of memory trying to construct method return"); goto exit; } dbus_message_iter_init_append (call->reply, &iter); pthread_mutex_lock(&patchbay_ptr->lock); if (version > patchbay_ptr->graph.version) { jack_dbus_error( call, JACK_DBUS_ERROR_INVALID_ARGS, "known graph version %" PRIu64 " is newer than actual version %" PRIu64, version, patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); goto exit; } if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &patchbay_ptr->graph.version)) { goto nomem_unlock; } if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(tsa(tsuu))", &clients_array_iter)) { goto nomem_unlock; } if (version < patchbay_ptr->graph.version) { list_for_each(client_node_ptr, &patchbay_ptr->graph.clients) { client_ptr = list_entry(client_node_ptr, struct jack_graph_client, siblings); if (!dbus_message_iter_open_container (&clients_array_iter, DBUS_TYPE_STRUCT, NULL, &client_struct_iter)) { goto nomem_close_clients_array; } if (!dbus_message_iter_append_basic(&client_struct_iter, DBUS_TYPE_UINT64, &client_ptr->id)) { goto nomem_close_client_struct; } if (!dbus_message_iter_append_basic(&client_struct_iter, DBUS_TYPE_STRING, &client_ptr->name)) { goto nomem_close_client_struct; } if (!dbus_message_iter_open_container(&client_struct_iter, DBUS_TYPE_ARRAY, "(tsuu)", &ports_array_iter)) { goto nomem_close_client_struct; } list_for_each(port_node_ptr, &client_ptr->ports) { port_ptr = list_entry(port_node_ptr, struct jack_graph_port, siblings_client); if (!dbus_message_iter_open_container(&ports_array_iter, DBUS_TYPE_STRUCT, NULL, &port_struct_iter)) { goto nomem_close_ports_array; } if (!dbus_message_iter_append_basic(&port_struct_iter, DBUS_TYPE_UINT64, &port_ptr->id)) { goto nomem_close_port_struct; } if (!dbus_message_iter_append_basic(&port_struct_iter, DBUS_TYPE_STRING, &port_ptr->name)) { goto nomem_close_port_struct; } if (!dbus_message_iter_append_basic(&port_struct_iter, DBUS_TYPE_UINT32, &port_ptr->flags)) { goto nomem_close_port_struct; } if (!dbus_message_iter_append_basic(&port_struct_iter, DBUS_TYPE_UINT32, &port_ptr->type)) { goto nomem_close_port_struct; } if (!dbus_message_iter_close_container(&ports_array_iter, &port_struct_iter)) { goto nomem_close_ports_array; } } if (!dbus_message_iter_close_container(&client_struct_iter, &ports_array_iter)) { goto nomem_close_client_struct; } if (!dbus_message_iter_close_container(&clients_array_iter, &client_struct_iter)) { goto nomem_close_clients_array; } } } if (!dbus_message_iter_close_container(&iter, &clients_array_iter)) { goto nomem_unlock; } if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(tstststst)", &connections_array_iter)) { goto nomem_unlock; } if (version < patchbay_ptr->graph.version) { list_for_each(connection_node_ptr, &patchbay_ptr->graph.connections) { connection_ptr = list_entry(connection_node_ptr, struct jack_graph_connection, siblings); if (!dbus_message_iter_open_container(&connections_array_iter, DBUS_TYPE_STRUCT, NULL, &connection_struct_iter)) { goto nomem_close_connections_array; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_UINT64, &connection_ptr->port1->client->id)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_STRING, &connection_ptr->port1->client->name)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_UINT64, &connection_ptr->port1->id)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_STRING, &connection_ptr->port1->name)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_UINT64, &connection_ptr->port2->client->id)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_STRING, &connection_ptr->port2->client->name)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_UINT64, &connection_ptr->port2->id)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_STRING, &connection_ptr->port2->name)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_append_basic(&connection_struct_iter, DBUS_TYPE_UINT64, &connection_ptr->id)) { goto nomem_close_connection_struct; } if (!dbus_message_iter_close_container(&connections_array_iter, &connection_struct_iter)) { goto nomem_close_connections_array; } } } if (!dbus_message_iter_close_container(&iter, &connections_array_iter)) { goto nomem_unlock; } pthread_mutex_unlock(&patchbay_ptr->lock); return; nomem_close_connection_struct: dbus_message_iter_close_container(&connections_array_iter, &connection_struct_iter); nomem_close_connections_array: dbus_message_iter_close_container(&iter, &connections_array_iter); goto nomem_unlock; nomem_close_port_struct: dbus_message_iter_close_container(&ports_array_iter, &port_struct_iter); nomem_close_ports_array: dbus_message_iter_close_container(&client_struct_iter, &ports_array_iter); nomem_close_client_struct: dbus_message_iter_close_container(&clients_array_iter, &client_struct_iter); nomem_close_clients_array: dbus_message_iter_close_container(&iter, &clients_array_iter); nomem_unlock: pthread_mutex_unlock(&patchbay_ptr->lock); //nomem: dbus_message_unref(call->reply); call->reply = NULL; jack_error("Ran out of memory trying to construct method return"); exit: return; } static void jack_controller_dbus_connect_ports_by_name( struct jack_dbus_method_call * call) { const char * client1_name; const char * port1_name; const char * client2_name; const char * port2_name; struct jack_graph_port *port1_ptr; struct jack_graph_port *port2_ptr; /* jack_info("jack_controller_dbus_connect_ports_by_name() called."); */ if (!controller_ptr->started) { jack_dbus_error( call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute this method with stopped JACK server"); return; } if (!jack_dbus_get_method_args( call, DBUS_TYPE_STRING, &client1_name, DBUS_TYPE_STRING, &port1_name, DBUS_TYPE_STRING, &client2_name, DBUS_TYPE_STRING, &port2_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * jack_dbus_get_method_args() has constructed an error for us. */ return; } /* jack_info("connecting %s:%s and %s:%s", client1_name, port1_name, client2_name, port2_name); */ pthread_mutex_lock(&patchbay_ptr->lock); port1_ptr = jack_controller_patchbay_find_port_by_names(patchbay_ptr, client1_name, port1_name); if (port1_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port '%s':'%s'", client1_name, port1_name); goto unlock; } port2_ptr = jack_controller_patchbay_find_port_by_names(patchbay_ptr, client2_name, port2_name); if (port2_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port '%s':'%s'", client2_name, port2_name); goto unlock; } if (!jack_controller_patchbay_connect( call, controller_ptr, port1_ptr, port2_ptr)) { /* jack_controller_patchbay_connect() constructed error reply */ goto unlock; } jack_dbus_construct_method_return_empty(call); unlock: pthread_mutex_unlock(&patchbay_ptr->lock); } static void jack_controller_dbus_connect_ports_by_id( struct jack_dbus_method_call * call) { dbus_uint64_t port1_id; dbus_uint64_t port2_id; struct jack_graph_port *port1_ptr; struct jack_graph_port *port2_ptr; /* jack_info("jack_controller_dbus_connect_ports_by_id() called."); */ if (!controller_ptr->started) { jack_dbus_error( call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute this method with stopped JACK server"); return; } if (!jack_dbus_get_method_args( call, DBUS_TYPE_UINT64, &port1_id, DBUS_TYPE_UINT64, &port2_id, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * jack_dbus_get_method_args() has constructed an error for us. */ return; } pthread_mutex_lock(&patchbay_ptr->lock); port1_ptr = jack_controller_patchbay_find_port_by_id(patchbay_ptr, port1_id); if (port1_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port %" PRIu64, port1_id); goto unlock; } port2_ptr = jack_controller_patchbay_find_port_by_id(patchbay_ptr, port2_id); if (port2_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port %" PRIu64, port2_id); goto unlock; } if (!jack_controller_patchbay_connect( call, controller_ptr, port1_ptr, port2_ptr)) { /* jack_controller_patchbay_connect() constructed error reply */ goto unlock; } jack_dbus_construct_method_return_empty(call); unlock: pthread_mutex_unlock(&patchbay_ptr->lock); } static void jack_controller_dbus_disconnect_ports_by_name( struct jack_dbus_method_call * call) { const char * client1_name; const char * port1_name; const char * client2_name; const char * port2_name; struct jack_graph_port *port1_ptr; struct jack_graph_port *port2_ptr; /* jack_info("jack_controller_dbus_disconnect_ports_by_name() called."); */ if (!controller_ptr->started) { jack_dbus_error( call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute this method with stopped JACK server"); return; } if (!jack_dbus_get_method_args( call, DBUS_TYPE_STRING, &client1_name, DBUS_TYPE_STRING, &port1_name, DBUS_TYPE_STRING, &client2_name, DBUS_TYPE_STRING, &port2_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * jack_dbus_get_method_args() has constructed an error for us. */ return; } /* jack_info("disconnecting %s:%s and %s:%s", client1_name, port1_name, client2_name, port2_name); */ pthread_mutex_lock(&patchbay_ptr->lock); port1_ptr = jack_controller_patchbay_find_port_by_names(patchbay_ptr, client1_name, port1_name); if (port1_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port '%s':'%s'", client1_name, port1_name); goto unlock; } port2_ptr = jack_controller_patchbay_find_port_by_names(patchbay_ptr, client2_name, port2_name); if (port2_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port '%s':'%s'", client2_name, port2_name); goto unlock; } if (!jack_controller_patchbay_disconnect( call, controller_ptr, port1_ptr, port2_ptr)) { /* jack_controller_patchbay_connect() constructed error reply */ goto unlock; } jack_dbus_construct_method_return_empty(call); unlock: pthread_mutex_unlock(&patchbay_ptr->lock); } static void jack_controller_dbus_disconnect_ports_by_id( struct jack_dbus_method_call * call) { dbus_uint64_t port1_id; dbus_uint64_t port2_id; struct jack_graph_port *port1_ptr; struct jack_graph_port *port2_ptr; /* jack_info("jack_controller_dbus_disconnect_ports_by_id() called."); */ if (!controller_ptr->started) { jack_dbus_error( call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute this method with stopped JACK server"); return; } if (!jack_dbus_get_method_args( call, DBUS_TYPE_UINT64, &port1_id, DBUS_TYPE_UINT64, &port2_id, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * jack_dbus_get_method_args() has constructed an error for us. */ return; } pthread_mutex_lock(&patchbay_ptr->lock); port1_ptr = jack_controller_patchbay_find_port_by_id(patchbay_ptr, port1_id); if (port1_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port %" PRIu64, port1_id); goto unlock; } port2_ptr = jack_controller_patchbay_find_port_by_id(patchbay_ptr, port2_id); if (port2_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find port %" PRIu64, port2_id); goto unlock; } if (!jack_controller_patchbay_disconnect( call, controller_ptr, port1_ptr, port2_ptr)) { /* jack_controller_patchbay_connect() constructed error reply */ goto unlock; } jack_dbus_construct_method_return_empty(call); unlock: pthread_mutex_unlock(&patchbay_ptr->lock); } static void jack_controller_dbus_disconnect_ports_by_connection_id( struct jack_dbus_method_call * call) { dbus_uint64_t connection_id; struct jack_graph_connection *connection_ptr; /* jack_info("jack_controller_dbus_disconnect_ports_by_id() called."); */ if (!jack_dbus_get_method_args( call, DBUS_TYPE_UINT64, &connection_id, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * jack_dbus_get_method_args() has constructed an error for us. */ return; } pthread_mutex_lock(&patchbay_ptr->lock); connection_ptr = jack_controller_patchbay_find_connection_by_id(patchbay_ptr, connection_id); if (connection_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find connection %" PRIu64, connection_id); goto unlock; } if (!jack_controller_patchbay_disconnect( call, controller_ptr, connection_ptr->port1, connection_ptr->port2)) { /* jack_controller_patchbay_connect() constructed error reply */ goto unlock; } jack_dbus_construct_method_return_empty(call); unlock: pthread_mutex_unlock(&patchbay_ptr->lock); } static void jack_controller_dbus_get_client_pid( struct jack_dbus_method_call * call) { dbus_uint64_t client_id; struct jack_graph_client *client_ptr; message_arg_t arg; /* jack_info("jack_controller_dbus_get_client_pid() called."); */ if (!jack_dbus_get_method_args( call, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that * jack_dbus_get_method_args() has constructed an error for us. */ return; } pthread_mutex_lock(&patchbay_ptr->lock); client_ptr = jack_controller_patchbay_find_client_by_id(patchbay_ptr, client_id); if (client_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "cannot find client %" PRIu64, client_id); goto unlock; } arg.int64 = client_ptr->pid; jack_dbus_construct_method_return_single(call, DBUS_TYPE_INT64, arg); unlock: pthread_mutex_unlock(&patchbay_ptr->lock); } #undef controller_ptr #define controller_ptr ((struct jack_controller *)context) static int jack_controller_graph_order_callback( void *context) { const char **ports; int i; jack_port_t *port_ptr; if (patchbay_ptr->graph.version > 1) { /* we use this only for initial catchup */ return 0; } ports = jack_get_ports(controller_ptr->client, NULL, NULL, 0); if (ports) { for (i = 0; ports[i]; ++i) { jack_info("graph reorder: new port '%s'", ports[i]); port_ptr = jack_port_by_name(controller_ptr->client, ports[i]);; jack_controller_patchbay_new_port(patchbay_ptr, ports[i], jack_port_flags(port_ptr), jack_port_type_id(port_ptr)); } free(ports); } if (patchbay_ptr->graph.version == 1) { /* we have empty initial graph, increment graph version, so we don't do jack_get_ports() again, on next next graph change */ patchbay_ptr->graph.version++; } return 0; } void jack_controller_client_registration_callback( const char *client_name, int created, void *context) { if (created) { jack_log("client '%s' created", client_name); jack_controller_patchbay_create_client(patchbay_ptr, client_name, strlen(client_name)); } else { jack_log("client '%s' destroyed", client_name); jack_controller_patchbay_destroy_client_by_name(patchbay_ptr, client_name); } } void jack_controller_port_registration_callback( jack_port_id_t port_id, int created, void *context) { jack_port_t *port_ptr; struct jack_graph_port *graph_port_ptr; const char *port_name; port_ptr = jack_port_by_id(controller_ptr->client, port_id); port_name = jack_port_name(port_ptr); if (created) { jack_log("port '%s' created", port_name); jack_controller_patchbay_new_port(patchbay_ptr, port_name, jack_port_flags(port_ptr), jack_port_type_id(port_ptr)); } else { jack_log("port '%s' destroyed", port_name); graph_port_ptr = jack_controller_patchbay_find_port_by_full_name(patchbay_ptr, port_name); if (graph_port_ptr == NULL) { jack_error("Failed to find port '%s' to destroy", port_name); return; } jack_controller_patchbay_remove_port(patchbay_ptr, graph_port_ptr); } } void jack_controller_port_connect_callback( jack_port_id_t port1_id, jack_port_id_t port2_id, int connect, void *context) { jack_port_t *port1; jack_port_t *port2; const char *port1_name; const char *port2_name; struct jack_graph_port *port1_ptr; struct jack_graph_port *port2_ptr; struct jack_graph_connection *connection_ptr; port1 = jack_port_by_id(controller_ptr->client, port1_id); port2 = jack_port_by_id(controller_ptr->client, port2_id); port1_name = jack_port_name(port1); port2_name = jack_port_name(port2); port1_ptr = jack_controller_patchbay_find_port_by_full_name(patchbay_ptr, port1_name); if (port1_ptr == NULL) { jack_error("Failed to find port '%s' to [dis]connect", port1_name); return; } port2_ptr = jack_controller_patchbay_find_port_by_full_name(patchbay_ptr, port2_name); if (port2_ptr == NULL) { jack_error("Failed to find port '%s' to [dis]connect", port2_name); return; } if (connect) { jack_info("Connecting '%s' to '%s'", port1_name, port2_name); connection_ptr = jack_controller_patchbay_find_connection(patchbay_ptr, port1_ptr, port2_ptr); if (connection_ptr != NULL) { jack_error("'%s' and '%s' are already connected", port1_name, port2_name); return; } jack_controller_patchbay_create_connection(patchbay_ptr, port1_ptr, port2_ptr); } else { jack_info("Disconnecting '%s' from '%s'", port1_name, port2_name); connection_ptr = jack_controller_patchbay_find_connection(patchbay_ptr, port1_ptr, port2_ptr); if (connection_ptr == NULL) { jack_error("Cannot find connection being removed"); return; } jack_controller_patchbay_destroy_connection(patchbay_ptr, connection_ptr); } } void jack_controller_port_rename_callback(jack_port_id_t port, const char * old_name, const char * new_name, void * context) { struct jack_graph_port * port_ptr; const char * port_new_short_name; const char * port_old_short_name; char * name_buffer; jack_info("port renamed: '%s' -> '%s'", old_name, new_name); port_new_short_name = strchr(new_name, ':'); if (port_new_short_name == NULL) { jack_error("renamed port new name '%s' does not contain ':' separator char", new_name); return; } port_new_short_name++; /* skip ':' separator char */ port_old_short_name = strchr(old_name, ':'); if (port_old_short_name == NULL) { jack_error("renamed port old name '%s' does not contain ':' separator char", old_name); return; } port_old_short_name++; /* skip ':' separator char */ port_ptr = jack_controller_patchbay_find_port_by_full_name(patchbay_ptr, old_name); if (port_ptr == NULL) { jack_error("renamed port '%s' not found", old_name); return; } name_buffer = strdup(port_new_short_name); if (name_buffer == NULL) { jack_error("strdup() call for port name '%s' failed.", port_new_short_name); return; } free(port_ptr->name); port_ptr->name = name_buffer; pthread_mutex_lock(&patchbay_ptr->lock); patchbay_ptr->graph.version++; jack_controller_patchbay_send_signal_port_renamed( patchbay_ptr->graph.version, port_ptr->client->id, port_ptr->client->name, port_ptr->id, port_old_short_name, port_ptr->name); jack_controller_patchbay_send_signal_graph_changed(patchbay_ptr->graph.version); pthread_mutex_unlock(&patchbay_ptr->lock); } #undef controller_ptr void jack_controller_patchbay_uninit( struct jack_controller * controller_ptr) { struct jack_graph_client *client_ptr; struct jack_graph_port *port_ptr; /* jack_info("jack_controller_patchbay_uninit() called"); */ while (!list_empty(&patchbay_ptr->graph.ports)) { port_ptr = list_entry(patchbay_ptr->graph.ports.next, struct jack_graph_port, siblings_graph); jack_controller_patchbay_remove_port(patchbay_ptr, port_ptr); } while (!list_empty(&patchbay_ptr->graph.clients)) { client_ptr = list_entry(patchbay_ptr->graph.clients.next, struct jack_graph_client, siblings); jack_controller_patchbay_destroy_client(patchbay_ptr, client_ptr); } pthread_mutex_destroy(&patchbay_ptr->lock); } #undef patchbay_ptr bool jack_controller_patchbay_init( struct jack_controller * controller_ptr) { int ret; struct jack_controller_patchbay * patchbay_ptr; pthread_mutexattr_t attr; /* jack_info("jack_controller_patchbay_init() called"); */ patchbay_ptr = malloc(sizeof(struct jack_controller_patchbay)); if (patchbay_ptr == NULL) { jack_error("Memory allocation of jack_controller_patchbay structure failed."); goto fail; } ret = pthread_mutexattr_init(&attr); if (ret != 0) { goto fail; } ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); if (ret != 0) { goto fail; } pthread_mutex_init(&patchbay_ptr->lock, &attr); INIT_LIST_HEAD(&patchbay_ptr->graph.clients); INIT_LIST_HEAD(&patchbay_ptr->graph.ports); INIT_LIST_HEAD(&patchbay_ptr->graph.connections); patchbay_ptr->graph.version = 1; patchbay_ptr->next_client_id = 1; patchbay_ptr->next_port_id = 1; patchbay_ptr->next_connection_id = 1; controller_ptr->patchbay_context = patchbay_ptr; ret = jack_set_graph_order_callback(controller_ptr->client, jack_controller_graph_order_callback, controller_ptr); if (ret != 0) { jack_error("jack_set_graph_order_callback() failed with error %d", ret); goto fail_uninit_mutex; } ret = jack_set_client_registration_callback(controller_ptr->client, jack_controller_client_registration_callback, controller_ptr); if (ret != 0) { jack_error("jack_set_client_registration_callback() failed with error %d", ret); goto fail_uninit_mutex; } ret = jack_set_port_registration_callback(controller_ptr->client, jack_controller_port_registration_callback, controller_ptr); if (ret != 0) { jack_error("jack_set_port_registration_callback() failed with error %d", ret); goto fail_uninit_mutex; } ret = jack_set_port_connect_callback(controller_ptr->client, jack_controller_port_connect_callback, controller_ptr); if (ret != 0) { jack_error("jack_set_port_connect_callback() failed with error %d", ret); goto fail_uninit_mutex; } ret = jack_set_port_rename_callback(controller_ptr->client, jack_controller_port_rename_callback, controller_ptr); if (ret != 0) { jack_error("jack_set_port_rename_callback() failed with error %d", ret); goto fail_uninit_mutex; } return true; fail_uninit_mutex: pthread_mutex_destroy(&patchbay_ptr->lock); fail: return false; } JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetAllPorts) JACK_DBUS_METHOD_ARGUMENT("ports_list", "as", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetGraph) JACK_DBUS_METHOD_ARGUMENT("known_graph_version", DBUS_TYPE_UINT64_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("current_graph_version", DBUS_TYPE_UINT64_AS_STRING, true) JACK_DBUS_METHOD_ARGUMENT("clients_and_ports", "a(tsa(tsuu))", true) JACK_DBUS_METHOD_ARGUMENT("connections", "a(tstststst)", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ConnectPortsByName) JACK_DBUS_METHOD_ARGUMENT("client1_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("port1_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("client2_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("port2_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ConnectPortsByID) JACK_DBUS_METHOD_ARGUMENT("port1_id", DBUS_TYPE_UINT64_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("port2_id", DBUS_TYPE_UINT64_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(DisconnectPortsByName) JACK_DBUS_METHOD_ARGUMENT("client1_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("port1_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("client2_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("port2_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(DisconnectPortsByID) JACK_DBUS_METHOD_ARGUMENT("port1_id", DBUS_TYPE_UINT64_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("port2_id", DBUS_TYPE_UINT64_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(DisconnectPortsByConnectionID) JACK_DBUS_METHOD_ARGUMENT("connection_id", DBUS_TYPE_UINT64_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetClientPID) JACK_DBUS_METHOD_ARGUMENT("client_id", DBUS_TYPE_UINT64_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("process_id", DBUS_TYPE_INT64_AS_STRING, true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHODS_BEGIN JACK_DBUS_METHOD_DESCRIBE(GetAllPorts, jack_controller_dbus_get_all_ports) JACK_DBUS_METHOD_DESCRIBE(GetGraph, jack_controller_dbus_get_graph) JACK_DBUS_METHOD_DESCRIBE(ConnectPortsByName, jack_controller_dbus_connect_ports_by_name) JACK_DBUS_METHOD_DESCRIBE(ConnectPortsByID, jack_controller_dbus_connect_ports_by_id) JACK_DBUS_METHOD_DESCRIBE(DisconnectPortsByName, jack_controller_dbus_disconnect_ports_by_name) JACK_DBUS_METHOD_DESCRIBE(DisconnectPortsByID, jack_controller_dbus_disconnect_ports_by_id) JACK_DBUS_METHOD_DESCRIBE(DisconnectPortsByConnectionID, jack_controller_dbus_disconnect_ports_by_connection_id) JACK_DBUS_METHOD_DESCRIBE(GetClientPID, jack_controller_dbus_get_client_pid) JACK_DBUS_METHODS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(GraphChanged) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(ClientAppeared) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(ClientDisappeared) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(PortAppeared) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_flags", DBUS_TYPE_UINT32_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_type", DBUS_TYPE_UINT32_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(PortDisappeared) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(PortsConnected) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client1_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client1_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port1_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port1_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client2_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client2_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port2_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port2_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("connection_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(PortsDisconnected) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client1_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client1_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port1_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port1_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client2_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client2_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port2_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port2_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("connection_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(PortRenamed) JACK_DBUS_SIGNAL_ARGUMENT("new_graph_version", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_id", DBUS_TYPE_UINT64_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_old_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("port_new_name", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_SIGNALS_BEGIN JACK_DBUS_SIGNAL_DESCRIBE(GraphChanged) JACK_DBUS_SIGNAL_DESCRIBE(ClientAppeared) JACK_DBUS_SIGNAL_DESCRIBE(ClientDisappeared) JACK_DBUS_SIGNAL_DESCRIBE(PortAppeared) JACK_DBUS_SIGNAL_DESCRIBE(PortDisappeared) JACK_DBUS_SIGNAL_DESCRIBE(PortsConnected) JACK_DBUS_SIGNAL_DESCRIBE(PortsDisconnected) JACK_DBUS_SIGNAL_DESCRIBE(PortRenamed) JACK_DBUS_SIGNALS_END JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_patchbay, JACK_DBUS_IFACE_NAME) JACK_DBUS_IFACE_EXPOSE_METHODS JACK_DBUS_IFACE_EXPOSE_SIGNALS JACK_DBUS_IFACE_END jack2-1.9.22/dbus/controller_iface_session_manager.c000066400000000000000000000421511436671425200224640ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2011 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include "jackdbus.h" #include "controller_internal.h" #include "jack/session.h" #include "jack/control.h" #define JACK_DBUS_IFACE_NAME "org.jackaudio.SessionManager" static void jack_controller_control_send_signal_session_state_changed( jack_session_event_type_t type, const char * target) { dbus_uint32_t u32; u32 = type; if (target == NULL) { target = ""; } jack_dbus_send_signal( JACK_CONTROLLER_OBJECT_PATH, JACK_DBUS_IFACE_NAME, "StateChanged", DBUS_TYPE_UINT32, &u32, DBUS_TYPE_STRING, &target, DBUS_TYPE_INVALID); } static bool start_detached_thread(void * (* start_routine)(void *), void * arg) { int ret; static pthread_attr_t attr; pthread_t tid; ret = pthread_attr_init(&attr); if (ret != 0) { jack_error("pthread_attr_init() failed with %d", ret); goto exit; } ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ret != 0) { jack_error("pthread_attr_setdetachstate() failed with %d", ret); goto destroy_attr; } ret = pthread_create(&tid, &attr, start_routine, arg); if (ret != 0) { jack_error("pthread_create() failed with %d", ret); goto destroy_attr; } jack_log("Detached thread %d created", (int)tid); destroy_attr: pthread_attr_destroy(&attr); exit: return ret == 0; } static void send_session_notify_reply(struct jack_session_pending_command * pending_cmd_ptr, jack_session_command_t * commands) { struct jack_dbus_method_call call; const jack_session_command_t * cmd_ptr; DBusMessageIter top_iter, array_iter, struct_iter; dbus_uint32_t u32; /* jack_dbus_error() wants call struct */ call.message = pending_cmd_ptr->message; call.connection = pending_cmd_ptr->connection; if (commands == NULL) { jack_dbus_error(&call, JACK_DBUS_ERROR_GENERIC, "jack_session_notify() failed"); goto send_reply; } jack_info("Session notify complete, commands follow:"); call.reply = dbus_message_new_method_return(pending_cmd_ptr->message); if (call.reply == NULL) { goto oom; } dbus_message_iter_init_append(call.reply, &top_iter); if (!dbus_message_iter_open_container(&top_iter, DBUS_TYPE_ARRAY, "(sssu)", &array_iter)) { goto unref; } for (cmd_ptr = commands; cmd_ptr->uuid != NULL; cmd_ptr++) { if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) { goto close_array; } if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->uuid)) { goto close_struct; } if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->client_name)) { goto close_struct; } if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->command)) { goto close_struct; } u32 = cmd_ptr->flags; if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &u32)) { goto close_struct; } jack_info("uuid='%s', client='%s', command='%s', flags=0x%"PRIX32, cmd_ptr->uuid, cmd_ptr->client_name, cmd_ptr->command, u32); if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) { goto close_array; } } jack_info("End of session commands."); if (!dbus_message_iter_close_container(&top_iter, &array_iter)) { goto unref; } goto send_reply; close_struct: dbus_message_iter_close_container(&array_iter, &struct_iter); close_array: dbus_message_iter_close_container(&top_iter, &array_iter); unref: dbus_message_unref(call.reply); goto oom; send_reply: if (call.reply != NULL) { if (!dbus_connection_send(pending_cmd_ptr->connection, call.reply, NULL)) { jack_error("Ran out of memory trying to queue method return"); } dbus_connection_flush(pending_cmd_ptr->connection); dbus_message_unref(call.reply); } else { oom: jack_error("Ran out of memory trying to construct method return"); } } #define controller_ptr ((struct jack_controller *)context) void * jack_controller_process_session_command_thread(void * context) { struct jack_session_pending_command * pending_cmd_ptr; jack_session_command_t * commands; jack_log("jack_controller_process_session_command_thread enter"); pthread_mutex_lock(&controller_ptr->lock); loop: /* get next command */ assert(!list_empty(&controller_ptr->session_pending_commands)); pending_cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings); pthread_mutex_unlock(&controller_ptr->lock); jack_info("Session notify initiated. target='%s', type=%d, path='%s'", pending_cmd_ptr->target, (int)pending_cmd_ptr->type, pending_cmd_ptr->path); jack_controller_control_send_signal_session_state_changed(pending_cmd_ptr->type, pending_cmd_ptr->target); commands = jack_session_notify(controller_ptr->client, pending_cmd_ptr->target, pending_cmd_ptr->type, pending_cmd_ptr->path); send_session_notify_reply(pending_cmd_ptr, commands); if (commands != NULL) { jack_session_commands_free(commands); } pthread_mutex_lock(&controller_ptr->lock); /* keep state consistent by sending signal after to lock */ /* otherwise the main thread may receive not-to-be-queued request and fail */ jack_controller_control_send_signal_session_state_changed(0, NULL); /* remove the head of the list (queue) */ assert(!list_empty(&controller_ptr->session_pending_commands)); assert(pending_cmd_ptr == list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings)); list_del(&pending_cmd_ptr->siblings); /* command cleanup */ dbus_message_unref(pending_cmd_ptr->message); dbus_connection_ref(pending_cmd_ptr->connection); free(pending_cmd_ptr); /* If there are more commands, process them. Otherwise - exit the thread */ if (!list_empty(&controller_ptr->session_pending_commands)) { goto loop; } pthread_mutex_unlock(&controller_ptr->lock); jack_log("jack_controller_process_session_command_thread exit"); return NULL; } #undef controller_ptr #define controller_ptr ((struct jack_controller *)call->context) static void jack_controller_dbus_session_notify( struct jack_dbus_method_call * call) { dbus_bool_t queue; const char * target; dbus_uint32_t u32; const char * path; jack_session_event_type_t type; struct jack_session_pending_command * cmd_ptr; if (!controller_ptr->started) { jack_dbus_only_error(call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute method '%s' with stopped JACK server", call->method_name); return; } if (!jack_dbus_get_method_args( call, DBUS_TYPE_BOOLEAN, &queue, DBUS_TYPE_STRING, &target, DBUS_TYPE_UINT32, &u32, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ return; } if (*target == 0) { target = NULL; } type = (jack_session_event_type_t)u32; if (type != JackSessionSave && type != JackSessionSaveAndQuit && type != JackSessionSaveTemplate) { jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid session event type %" PRIu32, u32); return; } pthread_mutex_lock(&controller_ptr->lock); if (list_empty(&controller_ptr->session_pending_commands)) { if (!start_detached_thread(jack_controller_process_session_command_thread, controller_ptr)) { jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Cannot start thread to process the command"); goto unlock; } jack_log("Session notify thread started"); } else if (!queue) { jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Busy"); goto unlock; } cmd_ptr = malloc(sizeof(struct jack_session_pending_command)); if (cmd_ptr == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "malloc() failed for jack_session_pending_command struct"); goto unlock; } cmd_ptr->message = dbus_message_ref(call->message); call->message = NULL; /* mark that reply will be sent asynchronously */ cmd_ptr->connection = dbus_connection_ref(call->connection); /* it is safe to use the retrieved pointers because we already made an additional message reference */ cmd_ptr->type = type; cmd_ptr->target = target; cmd_ptr->path = path; list_add_tail(&cmd_ptr->siblings, &controller_ptr->session_pending_commands); jack_log("Session notify scheduled. target='%s', type=%"PRIu32", path='%s'", target, u32, path); unlock: pthread_mutex_unlock(&controller_ptr->lock); } static void jack_controller_dbus_get_uuid_for_client_name( struct jack_dbus_method_call * call) { const char * client_name; char * client_uuid; if (!jack_dbus_get_method_args( call, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ return; } client_uuid = jack_get_uuid_for_client_name(controller_ptr->client, client_name); if (client_uuid == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_uuid_for_client_name(\"%s\") failed", client_name); return; } jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_uuid); free(client_uuid); } static void jack_controller_dbus_get_client_name_by_uuid( struct jack_dbus_method_call * call) { const char * client_uuid; char * client_name; if (!jack_dbus_get_method_args( call, DBUS_TYPE_STRING, &client_uuid, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ return; } client_name = jack_get_client_name_by_uuid(controller_ptr->client, client_uuid); if (client_name == NULL) { jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_client_name_by_uuid(\"%s\") failed", client_uuid); return; } jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_name); free(client_name); } static void jack_controller_dbus_reserve_client_name( struct jack_dbus_method_call * call) { int ret; const char * client_name; const char * client_uuid; if (!jack_dbus_get_method_args( call, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_STRING, &client_uuid, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ return; } ret = jack_reserve_client_name(controller_ptr->client, client_name, client_uuid); if (ret < 0) { jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_reserve_client_name(name=\"%s\", uuid=\"%s\") failed (%d)", client_name, client_uuid, ret); return; } jack_dbus_construct_method_return_empty(call); } static void jack_controller_dbus_has_session_callback( struct jack_dbus_method_call * call) { int ret; const char * client_name; message_arg_t retval; if (!jack_dbus_get_method_args( call, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID)) { /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ return; } ret = jack_client_has_session_callback(controller_ptr->client, client_name); if (ret < 0) { jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_client_has_session_callback(\"%s\") failed (%d)", client_name, ret); return; } retval.boolean = ret; jack_dbus_construct_method_return_single(call, DBUS_TYPE_BOOLEAN, retval); } static void jack_controller_dbus_get_session_state( struct jack_dbus_method_call * call) { DBusMessageIter iter; struct jack_session_pending_command * cmd_ptr; const char * target; dbus_uint32_t type; bool append_failed; call->reply = dbus_message_new_method_return(call->message); if (call->reply == NULL) { goto oom; } dbus_message_iter_init_append(call->reply, &iter); pthread_mutex_lock(&controller_ptr->lock); if (list_empty(&controller_ptr->session_pending_commands)) { type = 0; target = ""; } else { cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings); type = (dbus_uint32_t)cmd_ptr->type; target = cmd_ptr->target; } append_failed = !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &type) || !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &target); pthread_mutex_unlock(&controller_ptr->lock); if (!append_failed) { return; } dbus_message_unref(call->reply); call->reply = NULL; oom: jack_error("Ran out of memory trying to construct method return"); } #undef controller_ptr JACK_DBUS_METHOD_ARGUMENTS_BEGIN(Notify) JACK_DBUS_METHOD_ARGUMENT("queue", DBUS_TYPE_BOOLEAN_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("path", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("result", "a(sssu)", true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetUuidForClientName) JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetClientNameByUuid) JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ReserveClientName) JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(HasSessionCallback) JACK_DBUS_METHOD_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING, false) JACK_DBUS_METHOD_ARGUMENT("has_session_callback", DBUS_TYPE_BOOLEAN_AS_STRING, true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetState) JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, true) JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, true) JACK_DBUS_METHOD_ARGUMENTS_END JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(StateChanged) JACK_DBUS_SIGNAL_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING) JACK_DBUS_SIGNAL_ARGUMENTS_END JACK_DBUS_METHODS_BEGIN JACK_DBUS_METHOD_DESCRIBE(Notify, jack_controller_dbus_session_notify) JACK_DBUS_METHOD_DESCRIBE(GetUuidForClientName, jack_controller_dbus_get_uuid_for_client_name) JACK_DBUS_METHOD_DESCRIBE(GetClientNameByUuid, jack_controller_dbus_get_client_name_by_uuid) JACK_DBUS_METHOD_DESCRIBE(ReserveClientName, jack_controller_dbus_reserve_client_name) JACK_DBUS_METHOD_DESCRIBE(HasSessionCallback, jack_controller_dbus_has_session_callback) JACK_DBUS_METHOD_DESCRIBE(GetState, jack_controller_dbus_get_session_state) JACK_DBUS_METHODS_END JACK_DBUS_SIGNALS_BEGIN JACK_DBUS_SIGNAL_DESCRIBE(StateChanged) JACK_DBUS_SIGNALS_END JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_session_manager, JACK_DBUS_IFACE_NAME) JACK_DBUS_IFACE_EXPOSE_METHODS JACK_DBUS_IFACE_EXPOSE_SIGNALS JACK_DBUS_IFACE_END jack2-1.9.22/dbus/controller_iface_transport.c000066400000000000000000000022111436671425200213340ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2008 Nedko Arnaudov Copyright (C) 2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include "jackdbus.h" JACK_DBUS_METHODS_BEGIN JACK_DBUS_METHODS_END JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_transport, "org.jackaudio.JackTransport") JACK_DBUS_IFACE_EXPOSE_METHODS JACK_DBUS_IFACE_END jack2-1.9.22/dbus/controller_internal.h000066400000000000000000000130721436671425200200010ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008,2011 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef CONTROLLER_INTERNAL_H__04D54D51_3D79_49A2_A1DA_F8587E9E7F42__INCLUDED #define CONTROLLER_INTERNAL_H__04D54D51_3D79_49A2_A1DA_F8587E9E7F42__INCLUDED #include #include "jslist.h" #include "jack/control.h" #include "jack/jack.h" #include "jack/session.h" #include "jackdbus.h" #include "list.h" #include "params.h" struct jack_controller_slave_driver { struct list_head siblings; char * name; jackctl_driver_t * handle; bool loaded; }; struct jack_session_pending_command { struct list_head siblings; DBusConnection * connection; DBusMessage * message; jack_session_event_type_t type; const char * target; const char * path; }; struct jack_controller { jackctl_server_t *server; jack_params_handle params; void *patchbay_context; bool started; jack_client_t *client; unsigned int xruns; struct list_head slave_drivers; bool slave_drivers_set; struct jack_parameter slave_drivers_vparam; union jackctl_parameter_value slave_drivers_vparam_value; struct jack_dbus_object_descriptor dbus_descriptor; pthread_mutex_t lock; struct list_head session_pending_commands; long pending_save; /* uptime seconds */ }; #define DEFAULT_DRIVER "dummy" #define JACK_CONF_HEADER_TEXT \ "JACK settings, as persisted by D-Bus object.\n" \ "You probably don't want to edit this because\n" \ "it will be overwritten next time jackdbus saves.\n" void jack_controller_pending_save( struct jack_controller *controller_ptr); bool jack_controller_start_server( struct jack_controller *controller_ptr, void *dbus_call_context_ptr); bool jack_controller_stop_server( struct jack_controller *controller_ptr, void *dbus_call_context_ptr); bool jack_controller_switch_master( struct jack_controller *controller_ptr, void *dbus_call_context_ptr); bool jack_controller_add_slave_driver( struct jack_controller *controller_ptr, const char * driver_name); bool jack_controller_remove_slave_driver( struct jack_controller *controller_ptr, const char * driver_name); bool jack_controller_select_driver( struct jack_controller *controller_ptr, const char * driver_name); bool jack_controller_load_internal( struct jack_controller *controller_ptr, const char * internal_name); bool jack_controller_unload_internal( struct jack_controller *controller_ptr, const char * internal_name); void jack_controller_deserialize_parameter_value( struct jack_controller *controller_ptr, const char * const * address, const char * value); void jack_controller_serialize_parameter_value( const struct jack_parameter * param_ptr, char * value_buffer); bool jack_controller_patchbay_init( struct jack_controller *controller_ptr); void jack_controller_patchbay_uninit( struct jack_controller *controller_ptr); void * jack_controller_patchbay_client_appeared_callback( void * server_context, uint64_t client_id, const char *client_name); void jack_controller_patchbay_client_disappeared_callback( void *server_context, uint64_t client_id, void *client_context); void * jack_controller_patchbay_port_appeared_callback( void *server_context, uint64_t client_id, void *client_context, uint64_t port_id, const char *port_name, uint32_t port_flags, uint32_t port_type); void jack_controller_patchbay_port_disappeared_callback( void *server_context, uint64_t client_id, void *client_context, uint64_t port_id, void *port_context); void * jack_controller_patchbay_ports_connected_callback( void *server_context, uint64_t client1_id, void *client1_context, uint64_t port1_id, void *port1_context, uint64_t client2_id, void *client2_context, uint64_t port2_id, void *port2_context, uint64_t connection_id); void jack_controller_patchbay_ports_disconnected_callback( void *server_context, uint64_t client1_id, void *client1_context, uint64_t port1_id, void *port1_context, uint64_t client2_id, void *client2_context, uint64_t port2_id, void *port2_context, uint64_t connection_id, void *connection_context); extern struct jack_dbus_interface_descriptor g_jack_controller_iface_introspectable; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_control; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_configure; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_patchbay; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_session_manager; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_transport; #endif /* #ifndef CONTROLLER_INTERNAL_H__04D54D51_3D79_49A2_A1DA_F8587E9E7F42__INCLUDED */ jack2-1.9.22/dbus/jack_control000077500000000000000000000365011436671425200161510ustar00rootroot00000000000000#!/usr/bin/python3 from __future__ import print_function import dbus import sys name_base = 'org.jackaudio' control_interface_name = name_base + '.JackControl' configure_interface_name = name_base + '.Configure' service_name = name_base + '.service' control_iface = None configure_iface = None def bool_convert(str_value): if str_value == "0" or str_value.lower() in ["false", "off", "no", "(null)"]: return False return bool(str_value) def dbus_type_to_python_type(dbus_value): t = type(dbus_value) if t == dbus.Boolean: return bool(dbus_value) if t == dbus.Int32 or t == dbus.UInt32: return int(dbus_value) return dbus_value def python_type_to_jackdbus_type(value, type_char): type_char = str(type_char) if type_char == "b": return bool_convert(value) elif type_char == "y": return dbus.Byte(ord(value)) elif type_char == "i": return dbus.Int32(value) elif type_char == "u": return dbus.UInt32(value) return value def dbus_typesig_to_type_string(type_char): type_char = str(type_char) if type_char == 'i': return "sint" if type_char == 'u': return "uint" if type_char == 'y': return "char" if type_char == 's': return "str" if type_char == 'b': return "bool" print('err: unknown dbus typesig "%s"' % type_char) return None # throw exception here? def get_parameters(iface, path): params = iface.GetParametersInfo(path) # print params for param in params: typestr = dbus_typesig_to_type_string(param[0]) name = param[1] # print name descr = param[2] # print descr isset, default, value = iface.GetParameterValue(path + [name]) # print typestr if bool(isset): isset = "set" else: isset = "notset" value = dbus_type_to_python_type(value) default = dbus_type_to_python_type(default) print("%20s: %s (%s:%s:%s:%s)" % (name, descr, typestr, isset, default, value)) def print_help(): help_output = ( "Usage: jack_control [command] [command] ...\n" "Commands:\n" " shell - execute commands from stdin until End Of File (Ctrl+D in terminal)\n" " exit - exit jack dbus service (stops jack server if currently running)\n" " help - print this help text\n" " status - check whether jack server is started, " "return value is 0 if running and 1 otherwise\n" " start - start jack server if not currently started\n" " stop - stop jack server if currently started\n" " sm - switch master to currently selected driver\n" " dl - get list of available drivers\n" " dg - get currently selected driver\n" " ds - select driver\n" " dp - get parameters of currently selected driver\n" " dpd - get long description for driver parameter\n" " dps - set driver parameter\n" " dpr - reset driver parameter to its default value\n" " asd - add slave driver\n" " rsd - remove slave driver\n" " il - get list of available internals\n" " ip - get parameters of given internal\n" " ipd - get long description for internal parameter\n" " ips - set internal parameter\n" " ipr - reset internal parameter to its default value\n" " iload - load internal\n" " iunload - unload internal\n" " ep - get engine parameters\n" " epd - get long description for engine parameter\n" " eps - set engine parameter\n" " epr - reset engine parameter to its default value\n" ) print(help_output) def maybe_print_param_constraint(iface, param): is_range, is_strict, is_fake, values = iface.GetParameterConstraint(param) if is_range: print() print(("allowed range: %s to %s (inclusive)" % (values[0][0], values[1][0]))) elif len(values): print() if is_strict: print("allowed values:") else: print("suggested values:") max_len = 0 for value in values: if len(str(value[0])) > max_len: max_len = len(str(value[0])) for value in values: print(("%*s'%s' - %s" % (1 + max_len - len(str(value[0])), "", str(value[0]), str(value[1])))) def parse_argv(argv): global control_iface, configure_iface # check arguments index = 0 while index < len(argv): arg = argv[index] index += 1 try: if arg == 'exit': print("--- exit") control_iface.Exit() elif arg == 'status': print("--- status") if control_iface.IsStarted(): return (0, "started") else: return (1, "stopped") elif arg == 'start': print("--- start") control_iface.StartServer() elif arg == 'stop': print("--- stop") control_iface.StopServer() elif arg == 'sm': print("--- switch master driver") control_iface.SwitchMaster() elif arg == 'ism': if control_iface.IsManuallyActivated(): print("Manually activated") else: print("Automatically activated") elif arg == 'dl': print("--- drivers list") is_range, is_strict, is_fake_values, values = configure_iface.GetParameterConstraint( ['engine', 'driver'] ) for value in values: print(value[1]) elif arg == 'dg': print("--- get selected driver") isset, default, value = configure_iface.GetParameterValue(['engine', 'driver']) print(value) elif arg == 'ds': if index >= len(argv): return (1, "driver select command requires driver name argument") arg = argv[index] index += 1 print("--- driver select \"%s\"" % arg) configure_iface.SetParameterValue(['engine', 'driver'], dbus.String(arg)) elif arg == 'dp': print("--- get driver parameters (type:isset:default:value)") get_parameters(configure_iface, ['driver']) elif arg == 'dpd': if index >= len(argv): return (1, "get driver parameter long description command requires parameter name argument") param = argv[index] index += 1 print("--- get driver parameter description (%s)" % param) type_char, name, short_descr, long_descr = configure_iface.GetParameterInfo(['driver', param]) print(long_descr) maybe_print_param_constraint(configure_iface, ['driver', param]) elif arg == 'dps': if index + 1 >= len(argv): return (1, "driver parameter set command requires parameter name and value arguments") param = argv[index] index += 1 value = argv[index] index += 1 print("--- driver param set \"%s\" -> \"%s\"" % (param, value)) type_char, name, short_descr, long_descr = configure_iface.GetParameterInfo(['driver', param]) configure_iface.SetParameterValue(['driver', param], python_type_to_jackdbus_type(value, type_char)) elif arg == 'dpr': if index >= len(argv): return (1, "driver parameter reset command requires parameter name argument") param = argv[index] index += 1 print("--- driver param reset \"%s\"" % param) configure_iface.ResetParameterValue(['driver', param]) elif arg == 'ep': print("--- get engine parameters (type:isset:default:value)") get_parameters(configure_iface, ['engine']) elif arg == 'epd': if index >= len(argv): return (1, "get engine parameter long description command requires parameter name argument") param_name = argv[index] index += 1 print("--- get engine parameter description (%s)" % param_name) type_char, name, short_descr, long_descr = configure_iface.GetParameterInfo(['engine', param_name]) print(long_descr) maybe_print_param_constraint(configure_iface, ['engine', param_name]) elif arg == 'eps': if index + 1 >= len(argv): return (1, "engine parameter set command requires parameter name and value arguments") param = argv[index] index += 1 value = argv[index] index += 1 print("--- engine param set \"%s\" -> \"%s\"" % (param, value)) type_char, name, short_descr, long_descr = configure_iface.GetParameterInfo(['engine', param]) configure_iface.SetParameterValue(['engine', param], python_type_to_jackdbus_type(value, type_char)) elif arg == 'epr': if index >= len(argv): return (1, "engine parameter reset command requires parameter name") param = argv[index] index += 1 print("--- engine param reset \"%s\"" % param) type_char, name, short_descr, long_descr = configure_iface.GetParameterInfo(['engine', param]) configure_iface.ResetParameterValue(['engine', param]) elif arg == 'il': print("--- internals list") is_leaf, internals = configure_iface.ReadContainer(['internals']) for internal in internals: print(internal) elif arg == 'ip': print("--- get internal parameters (type:isset:default:value)") if index >= len(argv): return (1, "internal parameters command requires internal name argument") internal_name = argv[index] index += 1 get_parameters(configure_iface, ['internals', internal_name]) elif arg == 'ipd': if index + 1 >= len(argv): return ( 1, "get internal parameter long description command requires internal and parameter name arguments" ) name = argv[index] index += 1 param = argv[index] index += 1 print("--- get internal parameter description (%s)" % param) type_char, name, short_descr, long_descr = configure_iface.GetParameterInfo(['internals', name, param]) print(long_descr) elif arg == 'ips': if index + 2 >= len(argv): return (1, "internal parameter set command requires internal, parameter name and value arguments") internal_name = argv[index] index += 1 param = argv[index] index += 1 value = argv[index] index += 1 print("--- internal param set \"%s\" -> \"%s\"" % (param, value)) type_char, name, short_descr, long_descr = configure_iface.GetParameterInfo( ['internals', internal_name, param] ) configure_iface.SetParameterValue( ['internals', internal_name, param], python_type_to_jackdbus_type(value, type_char), ) elif arg == 'ipr': if index + 1 >= len(argv): return (1, "reset internal parameter command requires internal and parameter name arguments") internal_name = argv[index] index += 1 param = argv[index] index += 1 print("--- internal param reset \"%s\"" % param) configure_iface.ResetParameterValue(['internals', internal_name, param]) elif arg == 'iload': print("--- load internal") if index >= len(argv): return (1, "load internal command requires internal name argument") name = argv[index] index += 1 control_iface.LoadInternal(name) elif arg == 'iunload': print("--- unload internal") if index >= len(argv): return (1, "unload internal command requires internal name argument") name = argv[index] index += 1 control_iface.UnloadInternal(name) elif arg == 'asd': print("--- add slave driver") if index >= len(argv): return (1, "add slave driver command requires driver name argument") name = argv[index] index += 1 control_iface.AddSlaveDriver(name) elif arg == 'rsd': print("--- remove slave driver") if index >= len(argv): return (1, "remove slave driver command requires driver name argument") name = argv[index] index += 1 control_iface.RemoveSlaveDriver(name) elif arg == 'help': print_help() else: return (0, "Unknown command '%s'" % arg) except dbus.DBusException as e: return (1, "DBus exception: %s" % str(e)) return (0, "") def shell(): from click.parser import split_arg_string from sys import stdin while True: try: cmd = stdin.readline() if (cmd == ''): break s, t = parse_argv(split_arg_string(cmd)) print("%i: %s" % (s, t), flush=True) except Exception: break def main(): global control_iface, configure_iface if len(sys.argv) == 1 or sys.argv[1] in ["-h", "--help"]: print_help() return 0 bus = dbus.SessionBus() controller = bus.get_object(service_name, "/org/jackaudio/Controller") control_iface = dbus.Interface(controller, control_interface_name) configure_iface = dbus.Interface(controller, configure_interface_name) # check arguments if sys.argv[1] == "shell": shell() else: s, t = parse_argv(sys.argv[1:]) if (t): print(t) return s if __name__ == '__main__': sys.exit(main()) jack2-1.9.22/dbus/jackdbus.c000066400000000000000000000572641436671425200155160ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008,2010 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari Copyright (C) 2008 Marc-Olivier Barre This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "jackdbus.h" #include "controller.h" #include "jack/jack.h" #include "jack/jslist.h" #include "jack/control.h" #include "sigsegv.h" static char * g_log_filename; static ino_t g_log_file_ino; FILE *g_logfile; char *g_jackdbus_config_dir; size_t g_jackdbus_config_dir_len; /* without terminating '\0' char */ char *g_jackdbus_log_dir; size_t g_jackdbus_log_dir_len; /* without terminating '\0' char */ int g_exit_command; DBusConnection *g_connection; void jack_dbus_send_signal( const char *sender_object_path, const char *iface, const char *signal_name, int first_arg_type, ...) { DBusMessage *message_ptr; va_list ap; va_start(ap, first_arg_type); message_ptr = dbus_message_new_signal(sender_object_path, iface, signal_name); if (message_ptr == NULL) { jack_error("dbus_message_new_signal() failed."); goto exit; } if (!dbus_message_append_args_valist(message_ptr, first_arg_type, ap)) { jack_error("dbus_message_append_args_valist() failed."); goto unref; } /* Add message to outgoing message queue */ if (!dbus_connection_send(g_connection, message_ptr, NULL)) { jack_error("dbus_connection_send() failed."); goto unref; } unref: dbus_message_unref(message_ptr); exit: va_end(ap); } /* * Send a method return. * * If call->reply is NULL (i.e. a message construct method failed * due to lack of memory) attempt to send a void method return. */ static void jack_dbus_send_method_return( struct jack_dbus_method_call * call) { if (call->message == NULL) { /* async call */ return; } if (call->reply) { retry_send: if (!dbus_connection_send (call->connection, call->reply, NULL)) { jack_error ("Ran out of memory trying to queue method return"); } dbus_connection_flush (call->connection); dbus_message_unref (call->reply); call->reply = NULL; } else { jack_error ("send_method_return() called with a NULL message," " trying to construct a void return..."); if ((call->reply = dbus_message_new_method_return (call->message))) { goto retry_send; } else { jack_error ("Failed to construct method return!"); } } } #define object_ptr ((struct jack_dbus_object_descriptor *)data) /* * The D-Bus message handler for object path /org/jackaudio/Controller. */ DBusHandlerResult jack_dbus_message_handler( DBusConnection *connection, DBusMessage *message, void *data) { struct jack_dbus_method_call call; const char *interface_name; struct jack_dbus_interface_descriptor ** interface_ptr_ptr; /* Check if the message is a method call. If not, ignore it. */ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL) { goto handled; } /* Get the invoked method's name and make sure it's non-NULL. */ if (!(call.method_name = dbus_message_get_member (message))) { jack_dbus_error( &call, JACK_DBUS_ERROR_UNKNOWN_METHOD, "Received method call with empty method name"); goto send_return; } /* Initialize our data. */ call.context = object_ptr->context; call.connection = connection; call.message = message; call.reply = NULL; /* Check if there's an interface specified for this method call. */ interface_name = dbus_message_get_interface (message); if (interface_name != NULL) { /* Check if we can match the interface and method. * The interface handler functions only return false if the * method name was unknown, otherwise they run the specified * method and return TRUE. */ interface_ptr_ptr = object_ptr->interfaces; while (*interface_ptr_ptr != NULL) { if (strcmp(interface_name, (*interface_ptr_ptr)->name) == 0) { if (!(*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods)) { break; } goto send_return; } interface_ptr_ptr++; } } else { /* No interface was specified so we have to try them all. This is * dictated by the D-Bus specification which states that method calls * omitting the interface must never be rejected. */ interface_ptr_ptr = object_ptr->interfaces; while (*interface_ptr_ptr != NULL) { if ((*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods)) { goto send_return; } interface_ptr_ptr++; } } jack_dbus_error( &call, JACK_DBUS_ERROR_UNKNOWN_METHOD, "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist", call.method_name, dbus_message_get_signature(message), interface_name); send_return: jack_dbus_send_method_return(&call); handled: return DBUS_HANDLER_RESULT_HANDLED; } void jack_dbus_message_handler_unregister( DBusConnection *connection, void *data) { jack_info ("Message handler was unregistered"); } #undef object_ptr /* * Check if the supplied method name exists in org.jackaudio.JackConfigure, * if it does execute it and return TRUE. Otherwise return FALSE. */ bool jack_dbus_run_method( struct jack_dbus_method_call *call, const struct jack_dbus_interface_method_descriptor * methods) { const struct jack_dbus_interface_method_descriptor * method_ptr; method_ptr = methods; while (method_ptr->name != NULL) { if (strcmp(call->method_name, method_ptr->name) == 0) { method_ptr->handler(call); return TRUE; } method_ptr++; } return FALSE; } /* * Read arguments from a method call. * If the operation fails construct an error and return false, * otherwise return true. */ bool jack_dbus_get_method_args( struct jack_dbus_method_call *call, int type, ...) { va_list args; DBusError error; bool retval = true; va_start (args, type); dbus_error_init (&error); if (!dbus_message_get_args_valist (call->message, &error, type, args)) { jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\"", call->method_name); retval = false; } dbus_error_free (&error); va_end (args); return retval; } /* * Read a string and a variant argument from a method call. * If the operation fails construct an error and return false, * otherwise return true. */ bool jack_dbus_get_method_args_string_and_variant( struct jack_dbus_method_call *call, const char **arg1, message_arg_t *arg2, int *type_ptr) { DBusMessageIter iter, sub_iter; /* First we want a string... */ if (dbus_message_iter_init (call->message, &iter) && dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic (&iter, arg1); dbus_message_iter_next (&iter); /* ...and then a variant. */ if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT) { dbus_message_iter_recurse (&iter, &sub_iter); dbus_message_iter_get_basic (&sub_iter, arg2); *type_ptr = dbus_message_iter_get_arg_type (&sub_iter); /* Got what we wanted. */ return true; } } jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\"", call->method_name); return false; } /* * Read two strings and a variant argument from a method call. * If the operation fails construct an error and return false, * otherwise return true. */ bool jack_dbus_get_method_args_two_strings_and_variant( struct jack_dbus_method_call *call, const char **arg1, const char **arg2, message_arg_t *arg3, int *type_ptr) { DBusMessageIter iter, sub_iter; /* First we want a string... */ if (dbus_message_iter_init (call->message, &iter) && dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic (&iter, arg1); dbus_message_iter_next (&iter); /* ...and then a second string. */ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) { return false; } /* Got what we wanted. */ dbus_message_iter_get_basic (&iter, arg2); dbus_message_iter_next (&iter); /* ...and then a variant. */ if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT) { dbus_message_iter_recurse (&iter, &sub_iter); dbus_message_iter_get_basic (&sub_iter, arg3); *type_ptr = dbus_message_iter_get_arg_type (&sub_iter); /* Got what we wanted. */ return true; } } jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\"", call->method_name); return false; } /* * Append a variant type to a D-Bus message. * Return false if something fails, true otherwise. */ bool jack_dbus_message_append_variant( DBusMessageIter *iter, int type, const char *signature, message_arg_t *arg) { DBusMessageIter sub_iter; /* Open a variant container. */ if (!dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, signature, &sub_iter)) { goto fail; } /* Append the supplied value. */ if (!dbus_message_iter_append_basic (&sub_iter, type, (const void *) arg)) { dbus_message_iter_close_container (iter, &sub_iter); goto fail; } /* Close the container. */ if (!dbus_message_iter_close_container (iter, &sub_iter)) { goto fail; } return true; fail: return false; } /* * Construct an empty method return message. * * The operation can only fail due to lack of memory, in which case * there's no sense in trying to construct an error return. Instead, * call->reply will be set to NULL and handled in send_method_return(). */ void jack_dbus_construct_method_return_empty( struct jack_dbus_method_call * call) { call->reply = dbus_message_new_method_return (call->message); if (call->reply == NULL) { jack_error ("Ran out of memory trying to construct method return"); } } /* * Construct a method return which holds a single argument or, if * the type parameter is DBUS_TYPE_INVALID, no arguments at all * (a void message). * * The operation can only fail due to lack of memory, in which case * there's no sense in trying to construct an error return. Instead, * call->reply will be set to NULL and handled in send_method_return(). */ void jack_dbus_construct_method_return_single( struct jack_dbus_method_call *call, int type, message_arg_t arg) { DBusMessageIter iter; call->reply = dbus_message_new_method_return (call->message); if (call->reply == NULL) { goto fail_no_mem; } /* Void method return requested by caller. */ if (type == DBUS_TYPE_INVALID) { return; } /* Prevent crash on NULL input string. */ else if (type == DBUS_TYPE_STRING && arg.string == NULL) { arg.string = ""; } dbus_message_iter_init_append (call->reply, &iter); if (!dbus_message_iter_append_basic (&iter, type, (const void *) &arg)) { dbus_message_unref (call->reply); call->reply = NULL; goto fail_no_mem; } return; fail_no_mem: jack_error ("Ran out of memory trying to construct method return"); } /* * Construct a method return which holds an array of strings. * * The operation can only fail due to lack of memory, in which case * there's no sense in trying to construct an error return. Instead, * call->reply will be set to NULL and handled in send_method_return(). */ void jack_dbus_construct_method_return_array_of_strings( struct jack_dbus_method_call *call, unsigned int num_members, const char **array) { DBusMessageIter iter, sub_iter; unsigned int i; call->reply = dbus_message_new_method_return (call->message); if (!call->reply) { goto fail; } dbus_message_iter_init_append (call->reply, &iter); if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub_iter)) { goto fail_unref; } for (i = 0; i < num_members; ++i) { if (!dbus_message_iter_append_basic (&sub_iter, DBUS_TYPE_STRING, (const void *) &array[i])) { dbus_message_iter_close_container (&iter, &sub_iter); goto fail_unref; } } if (!dbus_message_iter_close_container (&iter, &sub_iter)) { goto fail_unref; } return; fail_unref: dbus_message_unref (call->reply); call->reply = NULL; fail: jack_error ("Ran out of memory trying to construct method return"); } static bool jack_dbus_log_open(void) { struct stat st; int ret; int retry; if (g_logfile != NULL) { ret = stat(g_log_filename, &st); if (ret != 0 || g_log_file_ino != st.st_ino) { fclose(g_logfile); } else { return true; } } for (retry = 0; retry < 10; retry++) { g_logfile = fopen(g_log_filename, "a"); if (g_logfile == NULL) { fprintf(stderr, "Cannot open jackdbus log file \"%s\": %d (%s)\n", g_log_filename, errno, strerror(errno)); return false; } ret = stat(g_log_filename, &st); if (ret == 0) { g_log_file_ino = st.st_ino; return true; } fclose(g_logfile); g_logfile = NULL; } fprintf(stderr, "Cannot stat just opened jackdbus log file \"%s\": %d (%s). %d retries\n", g_log_filename, errno, strerror(errno), retry); return false; } void jack_dbus_info_callback(const char *msg) { time_t timestamp; char timestamp_str[26]; time(×tamp); ctime_r(×tamp, timestamp_str); timestamp_str[24] = 0; if (jack_dbus_log_open()) { fprintf(g_logfile, "%s: %s\n", timestamp_str, msg); fflush(g_logfile); } } #define ANSI_BOLD_ON "\033[1m" #define ANSI_BOLD_OFF "\033[22m" #define ANSI_COLOR_RED "\033[31m" #define ANSI_RESET "\033[0m" void jack_dbus_error_callback(const char *msg) { time_t timestamp; char timestamp_str[26]; time(×tamp); ctime_r(×tamp, timestamp_str); timestamp_str[24] = 0; if (jack_dbus_log_open()) { fprintf(g_logfile, "%s: " ANSI_BOLD_ON ANSI_COLOR_RED "ERROR: %s" ANSI_RESET "\n", timestamp_str, msg); fflush(g_logfile); } } bool ensure_dir_exist(const char *dirname, int mode) { struct stat st; if (stat(dirname, &st) != 0) { if (errno == ENOENT) { printf("Directory \"%s\" does not exist. Creating...\n", dirname); if (mkdir(dirname, mode) != 0) { fprintf(stderr, "Failed to create \"%s\" directory: %d (%s)\n", dirname, errno, strerror(errno)); return false; } } else { fprintf(stderr, "Failed to stat \"%s\": %d (%s)\n", dirname, errno, strerror(errno)); return false; } } else { if (!S_ISDIR(st.st_mode)) { fprintf(stderr, "\"%s\" exists but is not directory.\n", dirname); return false; } } return true; } char * pathname_cat(const char *pathname_a, const char *pathname_b) { char *pathname; int pathname_a_len, pathname_b_len, pathname_len; pathname_a_len = strlen(pathname_a); pathname_b_len = strlen(pathname_b); pathname = malloc(pathname_a_len + pathname_b_len + 1); if (pathname == NULL) { fprintf(stderr, "Out of memory\n"); return NULL; } memcpy(pathname, pathname_a, pathname_a_len); memcpy(pathname + pathname_a_len, pathname_b, pathname_b_len); pathname_len = pathname_a_len + pathname_b_len; pathname[pathname_len] = 0; return pathname; } bool paths_init() { const char *home_dir, *xdg_config_home, *xdg_log_home; home_dir = getenv("HOME"); if (home_dir == NULL) { fprintf(stderr, "Environment variable HOME not set\n"); goto fail; } xdg_config_home = getenv("XDG_CONFIG_HOME"); if (xdg_config_home == NULL) { if (!(xdg_config_home = pathname_cat(home_dir, DEFAULT_XDG_CONFIG))) goto fail; } if (!(xdg_log_home = pathname_cat(home_dir, DEFAULT_XDG_LOG))) goto fail; if (!(g_jackdbus_config_dir = pathname_cat(xdg_config_home, JACKDBUS_DIR))) goto fail; if (!(g_jackdbus_log_dir = pathname_cat(xdg_log_home, JACKDBUS_DIR))) goto fail; if (!ensure_dir_exist(xdg_config_home, 0700)) { goto fail; } if (!ensure_dir_exist(xdg_log_home, 0700)) { goto fail; } if (!ensure_dir_exist(g_jackdbus_config_dir, 0700)) { free(g_jackdbus_config_dir); goto fail; } g_jackdbus_config_dir_len = strlen(g_jackdbus_config_dir); if (!ensure_dir_exist(g_jackdbus_log_dir, 0700)) { free(g_jackdbus_log_dir); goto fail; } g_jackdbus_log_dir_len = strlen(g_jackdbus_log_dir); return true; fail: return false; } void paths_uninit() { free(g_jackdbus_config_dir); free(g_jackdbus_log_dir); } static bool log_init(void) { size_t log_len; log_len = strlen(JACKDBUS_LOG); g_log_filename = malloc(g_jackdbus_log_dir_len + log_len + 1); if (g_log_filename == NULL) { fprintf(stderr, "Out of memory\n"); return false; } memcpy(g_log_filename, g_jackdbus_log_dir, g_jackdbus_log_dir_len); memcpy(g_log_filename + g_jackdbus_log_dir_len, JACKDBUS_LOG, log_len); g_log_filename[g_jackdbus_log_dir_len + log_len] = 0; if (!jack_dbus_log_open()) { return false; } return true; } static void log_uninit(void) { if (g_logfile != NULL) { fclose(g_logfile); } free(g_log_filename); } void jack_dbus_error( void *dbus_call_context_ptr, const char *error_name, const char *format, ...) { va_list ap; char buffer[300]; va_start(ap, format); vsnprintf(buffer, sizeof(buffer), format, ap); jack_error_callback(buffer); if (dbus_call_context_ptr != NULL) { if (((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply != NULL) { dbus_message_unref(((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply); ((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = NULL; } ((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = dbus_message_new_error( ((struct jack_dbus_method_call *)dbus_call_context_ptr)->message, error_name, buffer); } va_end(ap); } void jack_dbus_only_error( void *dbus_call_context_ptr, const char *error_name, const char *format, ...) { va_list ap; char buffer[300]; va_start(ap, format); vsnprintf(buffer, sizeof(buffer), format, ap); if (((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply != NULL) { dbus_message_unref(((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply); ((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = NULL; } ((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = dbus_message_new_error( ((struct jack_dbus_method_call *)dbus_call_context_ptr)->message, error_name, buffer); va_end(ap); } int main (int argc, char **argv) { DBusError error; int ret; void *controller_ptr; struct stat st; char timestamp_str[26]; st.st_mtime = 0; stat(argv[0], &st); ctime_r(&st.st_mtime, timestamp_str); timestamp_str[24] = 0; if (!jack_controller_settings_init()) { ret = 1; goto fail; } if (argc != 2 || strcmp(argv[1], "auto") != 0) { ret = 0; fprintf( stderr, "jackdbus should be auto-executed by D-Bus message bus daemon.\n" "If you want to run it manually anyway, specify \"auto\" as only parameter\n"); goto fail_uninit_xml; } if (!paths_init()) { ret = 1; goto fail_uninit_xml; } if (!log_init()) { ret = 1; goto fail_uninit_paths; } #if !defined(DISABLE_SIGNAL_MAGIC) jackctl_setup_signals(0); #endif jack_set_error_function(jack_dbus_error_callback); jack_set_info_function(jack_dbus_info_callback); /* setup our SIGSEGV magic that prints nice stack in our logfile */ setup_sigsegv(); jack_info("------------------"); jack_info("Controller activated. Version %s (%s) built on %s", jack_get_version_string(), JACK_VERSION, timestamp_str); if (!dbus_threads_init_default()) { jack_error("dbus_threads_init_default() failed"); ret = 1; goto fail_uninit_log; } dbus_error_init (&error); g_connection = dbus_bus_get (DBUS_BUS_SESSION, &error); if (dbus_error_is_set (&error)) { jack_error("Cannot connect to D-Bus session bus: %s", error.message); ret = 1; goto fail_uninit_log; } ret = dbus_bus_request_name( g_connection, "org.jackaudio.service", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); if (ret == -1) { jack_error("Cannot request service name: %s", error.message); dbus_error_free(&error); ret = 1; goto fail_unref_connection; } else if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS) { jack_error("Requested D-Bus service name already exists"); ret = 1; goto fail_unref_connection; } controller_ptr = jack_controller_create(g_connection); if (controller_ptr == NULL) { ret = 1; goto fail_unref_connection; } jack_info("Listening for D-Bus messages"); g_exit_command = FALSE; while (!g_exit_command && dbus_connection_read_write_dispatch (g_connection, 200)) { jack_controller_run(controller_ptr); } jack_controller_destroy(controller_ptr); jack_info("Controller deactivated."); ret = 0; fail_unref_connection: dbus_connection_unref(g_connection); fail_uninit_log: log_uninit(); fail_uninit_paths: paths_uninit(); fail_uninit_xml: jack_controller_settings_uninit(); fail: return ret; } jack2-1.9.22/dbus/jackdbus.h000066400000000000000000000301121436671425200155020ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008 Nedko Arnaudov Copyright (C) 2007-2008 Juuso Alasuutari Copyright (C) 2008 Marc-Olivier Barre This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef DBUS_H__3DB2458F_44B2_43EA_882A_9F888DF71A88__INCLUDED #define DBUS_H__3DB2458F_44B2_43EA_882A_9F888DF71A88__INCLUDED #include #define JACK_DBUS_DEBUG //#define DISABLE_SIGNAL_MAGIC #define DEFAULT_XDG_CONFIG "/.config" #define DEFAULT_XDG_LOG "/.log" #define JACKDBUS_DIR "/jack" #define JACKDBUS_LOG "/jackdbus.log" #define JACKDBUS_CONF "/conf.xml" extern char *g_jackdbus_config_dir; extern size_t g_jackdbus_config_dir_len; /* without terminating '\0' char */ extern int g_exit_command; bool jack_controller_settings_init(); void jack_controller_settings_uninit(); #define JACK_DBUS_ERROR_UNKNOWN_METHOD "org.jackaudio.Error.UnknownMethod" #define JACK_DBUS_ERROR_SERVER_NOT_RUNNING "org.jackaudio.Error.ServerNotRunning" #define JACK_DBUS_ERROR_SERVER_RUNNING "org.jackaudio.Error.ServerRunning" #define JACK_DBUS_ERROR_UNKNOWN_DRIVER "org.jackaudio.Error.UnknownDriver" #define JACK_DBUS_ERROR_UNKNOWN_INTERNAL "org.jackaudio.Error.UnknownInternal" #define JACK_DBUS_ERROR_UNKNOWN_PARAMETER "org.jackaudio.Error.UnknownParameter" #define JACK_DBUS_ERROR_INVALID_ARGS "org.jackaudio.Error.InvalidArgs" #define JACK_DBUS_ERROR_GENERIC "org.jackaudio.Error.Generic" #define JACK_DBUS_ERROR_FATAL "org.jackaudio.Error.Fatal" struct jack_dbus_method_call { void *context; DBusConnection *connection; const char *method_name; DBusMessage *message; DBusMessage *reply; }; struct jack_dbus_interface_method_argument_descriptor { const char * name; const char * type; bool direction_out; /* true - out, false - in */ }; struct jack_dbus_interface_method_descriptor { const char * name; const struct jack_dbus_interface_method_argument_descriptor * arguments; void (* handler)(struct jack_dbus_method_call * call); }; struct jack_dbus_interface_signal_argument_descriptor { const char * name; const char * type; }; struct jack_dbus_interface_signal_descriptor { const char * name; const struct jack_dbus_interface_signal_argument_descriptor * arguments; }; struct jack_dbus_interface_descriptor { const char * name; bool (* handler)( struct jack_dbus_method_call * call, const struct jack_dbus_interface_method_descriptor * methods); const struct jack_dbus_interface_method_descriptor * methods; const struct jack_dbus_interface_signal_descriptor * signals; }; struct jack_dbus_object_descriptor { struct jack_dbus_interface_descriptor ** interfaces; void * context; }; typedef union { unsigned char byte; dbus_bool_t boolean; dbus_int16_t int16; dbus_uint16_t uint16; dbus_int32_t int32; dbus_uint32_t uint32; dbus_int64_t int64; dbus_uint64_t uint64; double doubl; const char *string; } message_arg_t; #define JACK_DBUS_METHOD_ARGUMENTS_BEGIN(method_name) \ static const \ struct jack_dbus_interface_method_argument_descriptor method_name ## _arguments[] = \ { #define JACK_DBUS_METHOD_ARGUMENTS_BEGIN_EX(method_name, descr) \ static const \ struct jack_dbus_interface_method_argument_descriptor method_name ## _arguments[] = \ { #define JACK_DBUS_METHOD_ARGUMENT(argument_name, argument_type, argument_direction_out) \ { \ .name = argument_name, \ .type = argument_type, \ .direction_out = argument_direction_out \ }, #define JACK_DBUS_METHOD_ARGUMENT_IN(argument_name, argument_type, descr) \ { \ .name = argument_name, \ .type = argument_type, \ .direction_out = false \ }, #define JACK_DBUS_METHOD_ARGUMENT_OUT(argument_name, argument_type, descr) \ { \ .name = argument_name, \ .type = argument_type, \ .direction_out = true \ }, #define JACK_DBUS_METHOD_ARGUMENT(argument_name, argument_type, argument_direction_out) \ { \ .name = argument_name, \ .type = argument_type, \ .direction_out = argument_direction_out \ }, #define JACK_DBUS_METHOD_ARGUMENTS_END \ JACK_DBUS_METHOD_ARGUMENT(NULL, NULL, false) \ }; #define JACK_DBUS_METHODS_BEGIN \ static const \ struct jack_dbus_interface_method_descriptor methods_dtor[] = \ { #define JACK_DBUS_METHOD_DESCRIBE(method_name, handler_name) \ { \ .name = # method_name, \ .arguments = method_name ## _arguments, \ .handler = handler_name \ }, #define JACK_DBUS_METHODS_END \ { \ .name = NULL, \ .arguments = NULL, \ .handler = NULL \ } \ }; #define JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(signal_name) \ static const \ struct jack_dbus_interface_signal_argument_descriptor signal_name ## _arguments[] = \ { #define JACK_DBUS_SIGNAL_ARGUMENT(argument_name, argument_type) \ { \ .name = argument_name, \ .type = argument_type \ }, #define JACK_DBUS_SIGNAL_ARGUMENTS_END \ JACK_DBUS_SIGNAL_ARGUMENT(NULL, NULL) \ }; #define JACK_DBUS_SIGNALS_BEGIN \ static const \ struct jack_dbus_interface_signal_descriptor signals_dtor[] = \ { #define JACK_DBUS_SIGNAL_DESCRIBE(signal_name) \ { \ .name = # signal_name, \ .arguments = signal_name ## _arguments \ }, #define JACK_DBUS_SIGNALS_END \ { \ .name = NULL, \ .arguments = NULL, \ } \ }; #define JACK_DBUS_IFACE_BEGIN(iface_var, iface_name) \ struct jack_dbus_interface_descriptor iface_var = \ { \ .name = iface_name, \ .handler = jack_dbus_run_method, #define JACK_DBUS_IFACE_HANDLER(handler_func) \ .handler = handler_func, #define JACK_DBUS_IFACE_EXPOSE_METHODS \ .methods = methods_dtor, #define JACK_DBUS_IFACE_EXPOSE_SIGNALS \ .signals = signals_dtor, #define JACK_DBUS_IFACE_END \ }; DBusHandlerResult jack_dbus_message_handler( DBusConnection *connection, DBusMessage *message, void *data); void jack_dbus_message_handler_unregister( DBusConnection *connection, void *data); bool jack_dbus_run_method( struct jack_dbus_method_call * call, const struct jack_dbus_interface_method_descriptor * methods); void jack_dbus_error( void *dbus_call_context_ptr, const char *error_name, const char *format, ...); void jack_dbus_only_error( void *dbus_call_context_ptr, const char *error_name, const char *format, ...); bool jack_dbus_get_method_args( struct jack_dbus_method_call *call, int type, ...); bool jack_dbus_get_method_args_string_and_variant( struct jack_dbus_method_call *call, const char **arg1, message_arg_t *arg2, int *type_ptr); bool jack_dbus_get_method_args_two_strings_and_variant( struct jack_dbus_method_call *call, const char **arg1, const char **arg2, message_arg_t *arg3, int *type_ptr); bool jack_dbus_message_append_variant( DBusMessageIter *iter, int type, const char *signature, message_arg_t *arg); void jack_dbus_construct_method_return_empty( struct jack_dbus_method_call * call); void jack_dbus_construct_method_return_single( struct jack_dbus_method_call *call, int type, message_arg_t arg); void jack_dbus_construct_method_return_array_of_strings( struct jack_dbus_method_call *call, unsigned int num_members, const char **array); void jack_dbus_send_signal( const char *sender_object_path, const char *iface, const char *signal_name, int first_arg_type, ...); #define JACK_CONTROLLER_OBJECT_PATH "/org/jackaudio/Controller" extern struct jack_dbus_interface_descriptor * g_jackcontroller_interfaces[]; extern DBusConnection * g_connection; #endif /* #ifndef DBUS_H__3DB2458F_44B2_43EA_882A_9F888DF71A88__INCLUDED */ jack2-1.9.22/dbus/list.h000066400000000000000000000671601436671425200147040ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 2 -*- */ /***************************************************************************** * * Linux kernel header adapted for user-mode * The 2.6.17-rt1 version was used. * * Original copyright holders of this code are unknown, they were not * mentioned in the original file. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * *****************************************************************************/ #ifndef _LINUX_LIST_H #define _LINUX_LIST_H #include #if !defined(offsetof) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif /** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define prefetch(x) (x = x) /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add_rcu(struct list_head * new, struct list_head * prev, struct list_head * next) { new->next = next; new->prev = prev; // smp_wmb(); next->prev = new; prev->next = new; } /** * list_add_rcu - add a new entry to rcu-protected list * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as list_add_rcu() * or list_del_rcu(), running on this same list. * However, it is perfectly legal to run concurrently with * the _rcu list-traversal primitives, such as * list_for_each_entry_rcu(). */ static inline void list_add_rcu(struct list_head *new, struct list_head *head) { __list_add_rcu(new, head, head->next); } /** * list_add_tail_rcu - add a new entry to rcu-protected list * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as list_add_tail_rcu() * or list_del_rcu(), running on this same list. * However, it is perfectly legal to run concurrently with * the _rcu list-traversal primitives, such as * list_for_each_entry_rcu(). */ static inline void list_add_tail_rcu(struct list_head *new, struct list_head *head) { __list_add_rcu(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is * in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } /** * list_del_rcu - deletes entry from list without re-initialization * @entry: the element to delete from the list. * * Note: list_empty on entry does not return true after this, * the entry is in an undefined state. It is useful for RCU based * lockfree traversal. * * In particular, it means that we can not poison the forward * pointers that may still be used for walking the list. * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as list_del_rcu() * or list_add_rcu(), running on this same list. * However, it is perfectly legal to run concurrently with * the _rcu list-traversal primitives, such as * list_for_each_entry_rcu(). * * Note that the caller is not permitted to immediately free * the newly deleted entry. Instead, either synchronize_rcu() * or call_rcu() must be used to defer freeing until an RCU * grace period has elapsed. */ static inline void list_del_rcu(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->prev = LIST_POISON2; } /* * list_replace_rcu - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * The old entry will be replaced with the new entry atomically. */ static inline void list_replace_rcu(struct list_head *old, struct list_head *new) { new->next = old->next; new->prev = old->prev; // smp_wmb(); new->next->prev = new; new->prev->next = new; old->prev = LIST_POISON2; } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add(list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add_tail(list, head); } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(const struct list_head *head) { return head->next == head; } /** * list_empty_careful - tests whether a list is * empty _and_ checks that no other CPU might be * in the process of still modifying either member * * NOTE: using list_empty_careful() without synchronization * can only be safe if the only activity that can happen * to the list entry is list_del_init(). Eg. it cannot be used * if another CPU could re-list_add() it. * * @head: the list to test. */ static inline int list_empty_careful(const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->prev); } static inline void __list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } /** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head); INIT_LIST_HEAD(list); } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; prefetch(pos->next), pos != (head); \ pos = pos->next) /** * __list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. * * This variant differs from list_for_each() in that it's the * simplest possible list iteration code, no prefetching is done. * Use this for code that knows the list to be very short (empty * or 1 entry) most of the time. */ #define __list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member); \ prefetch(pos->member.prev), &pos->member != (head); \ pos = list_entry(pos->member.prev, typeof(*pos), member)) /** * list_prepare_entry - prepare a pos entry for use as a start point in * list_for_each_entry_continue * @pos: the type * to use as a start point * @head: the head of the list * @member: the name of the list_struct within the struct. */ #define list_prepare_entry(pos, head, member) \ ((pos) ? : list_entry(head, typeof(*pos), member)) /** * list_for_each_entry_continue - iterate over list of given type * continuing after existing point * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member); \ prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_from - iterate over list of given type * continuing from existing point * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_from(pos, head, member) \ for (; prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_continue - iterate over list of given type * continuing after existing point safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe_continue(pos, n, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_from - iterate over list of given type * from existing point safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe_from(pos, n, head, member) \ for (n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against * removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member), \ n = list_entry(pos->member.prev, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.prev, typeof(*n), member)) /** * list_for_each_rcu - iterate over an rcu-protected list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as list_add_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_rcu(pos, head) \ for (pos = (head)->next; \ prefetch(rcu_dereference(pos)->next), pos != (head); \ pos = pos->next) #define __list_for_each_rcu(pos, head) \ for (pos = (head)->next; \ rcu_dereference(pos) != (head); \ pos = pos->next) /** * list_for_each_safe_rcu - iterate over an rcu-protected list safe * against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as list_add_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_safe_rcu(pos, n, head) \ for (pos = (head)->next; \ n = rcu_dereference(pos)->next, pos != (head); \ pos = n) /** * list_for_each_entry_rcu - iterate over rcu list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as list_add_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_entry_rcu(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ prefetch(rcu_dereference(pos)->member.next), \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_continue_rcu - iterate over an rcu-protected list * continuing after existing point. * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as list_add_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_continue_rcu(pos, head) \ for ((pos) = (pos)->next; \ prefetch(rcu_dereference((pos))->next), (pos) != (head); \ (pos) = (pos)->next) /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is * too wasteful. * You lose the ability to access the tail in O(1). */ struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; #define HLIST_HEAD_INIT { .first = NULL } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) static inline void INIT_HLIST_NODE(struct hlist_node *h) { h->next = NULL; h->pprev = NULL; } static inline int hlist_unhashed(const struct hlist_node *h) { return !h->pprev; } static inline int hlist_empty(const struct hlist_head *h) { return !h->first; } static inline void __hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; if (next) next->pprev = pprev; } static inline void hlist_del(struct hlist_node *n) { __hlist_del(n); n->next = LIST_POISON1; n->pprev = LIST_POISON2; } /** * hlist_del_rcu - deletes entry from hash list without re-initialization * @n: the element to delete from the hash list. * * Note: list_unhashed() on entry does not return true after this, * the entry is in an undefined state. It is useful for RCU based * lockfree traversal. * * In particular, it means that we can not poison the forward * pointers that may still be used for walking the hash list. * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as hlist_add_head_rcu() * or hlist_del_rcu(), running on this same list. * However, it is perfectly legal to run concurrently with * the _rcu list-traversal primitives, such as * hlist_for_each_entry(). */ static inline void hlist_del_rcu(struct hlist_node *n) { __hlist_del(n); n->pprev = LIST_POISON2; } static inline void hlist_del_init(struct hlist_node *n) { if (!hlist_unhashed(n)) { __hlist_del(n); INIT_HLIST_NODE(n); } } /* * hlist_replace_rcu - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * The old entry will be replaced with the new entry atomically. */ static inline void hlist_replace_rcu(struct hlist_node *old, struct hlist_node *new) { struct hlist_node *next = old->next; new->next = next; new->pprev = old->pprev; // smp_wmb(); if (next) new->next->pprev = &new->next; *new->pprev = new; old->pprev = LIST_POISON2; } static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; } /** * hlist_add_head_rcu - adds the specified element to the specified hlist, * while permitting racing traversals. * @n: the element to add to the hash list. * @h: the list to add to. * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as hlist_add_head_rcu() * or hlist_del_rcu(), running on this same list. * However, it is perfectly legal to run concurrently with * the _rcu list-traversal primitives, such as * hlist_for_each_entry_rcu(), used to prevent memory-consistency * problems on Alpha CPUs. Regardless of the type of CPU, the * list-traversal primitive must be guarded by rcu_read_lock(). */ static inline void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; n->pprev = &h->first; // smp_wmb(); if (first) first->pprev = &n->next; h->first = n; } /* next must be != NULL */ static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; *(n->pprev) = n; } static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next) { next->next = n->next; n->next = next; next->pprev = &n->next; if(next->next) next->next->pprev = &next->next; } /** * hlist_add_before_rcu - adds the specified element to the specified hlist * before the specified node while permitting racing traversals. * @n: the new element to add to the hash list. * @next: the existing element to add the new element before. * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as hlist_add_head_rcu() * or hlist_del_rcu(), running on this same list. * However, it is perfectly legal to run concurrently with * the _rcu list-traversal primitives, such as * hlist_for_each_entry_rcu(), used to prevent memory-consistency * problems on Alpha CPUs. */ static inline void hlist_add_before_rcu(struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; // smp_wmb(); next->pprev = &n->next; *(n->pprev) = n; } /** * hlist_add_after_rcu - adds the specified element to the specified hlist * after the specified node while permitting racing traversals. * @prev: the existing element to add the new element after. * @n: the new element to add to the hash list. * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as hlist_add_head_rcu() * or hlist_del_rcu(), running on this same list. * However, it is perfectly legal to run concurrently with * the _rcu list-traversal primitives, such as * hlist_for_each_entry_rcu(), used to prevent memory-consistency * problems on Alpha CPUs. */ static inline void hlist_add_after_rcu(struct hlist_node *prev, struct hlist_node *n) { n->next = prev->next; n->pprev = &prev->next; // smp_wmb(); prev->next = n; if (n->next) n->next->pprev = &n->next; } #define hlist_entry(ptr, type, member) container_of(ptr,type,member) #define hlist_for_each(pos, head) \ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ pos = pos->next) #define hlist_for_each_safe(pos, n, head) \ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ pos = n) /** * hlist_for_each_entry - iterate over list of given type * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry(tpos, pos, head, member) \ for (pos = (head)->first; \ pos && ({ prefetch(pos->next); 1;}) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) /** * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue(tpos, pos, member) \ for (pos = (pos)->next; \ pos && ({ prefetch(pos->next); 1;}) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) /** * hlist_for_each_entry_from - iterate over a hlist continuing from existing point * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_from(tpos, pos, member) \ for (; pos && ({ prefetch(pos->next); 1;}) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) /** * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ for (pos = (head)->first; \ pos && ({ n = pos->next; 1; }) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = n) /** * hlist_for_each_entry_rcu - iterate over rcu list of given type * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @head: the head for your list. * @member: the name of the hlist_node within the struct. * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as hlist_add_head_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ #define hlist_for_each_entry_rcu(tpos, pos, head, member) \ for (pos = (head)->first; \ rcu_dereference(pos) && ({ prefetch(pos->next); 1;}) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) #endif jack2-1.9.22/dbus/org.jackaudio.service.in000066400000000000000000000001071436671425200202530ustar00rootroot00000000000000[D-BUS Service] Name=org.jackaudio.service Exec=@BINDIR@/jackdbus auto jack2-1.9.22/dbus/params.c000066400000000000000000000470031436671425200152010ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2011 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Parameter addresses: * * "engine" * "engine", "driver" * "engine", "realtime" * "engine", ...more engine parameters * * "driver", "device" * "driver", ...more driver parameters * * "drivers", "alsa", "device" * "drivers", "alsa", ...more alsa driver parameters * * "drivers", ...more drivers * * "internals", "netmanager", "multicast_ip" * "internals", "netmanager", ...more netmanager parameters * * "internals", ...more internals * */ #include #include #include #include #include "params.h" #include "controller_internal.h" #define PTNODE_ENGINE "engine" #define PTNODE_DRIVER "driver" #define PTNODE_DRIVERS "drivers" #define PTNODE_INTERNALS "internals" struct jack_parameter_container { struct list_head siblings; char * name; struct jack_parameter_container * symlink; bool leaf; struct list_head children; void * obj; }; struct jack_params { jackctl_server_t * server; struct jack_parameter_container root; struct list_head * drivers_ptr; uint32_t drivers_count; struct jack_parameter_container * driver_ptr; bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */ }; static bool controlapi_parameter_is_set(void * obj) { return jackctl_parameter_is_set((jackctl_parameter_t *)obj); } static bool controlapi_parameter_reset(void * obj) { return jackctl_parameter_reset((jackctl_parameter_t *)obj); } union jackctl_parameter_value controlapi_parameter_get_value(void * obj) { return jackctl_parameter_get_value((jackctl_parameter_t *)obj); } bool controlapi_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) { return jackctl_parameter_set_value((jackctl_parameter_t *)obj, value_ptr); } union jackctl_parameter_value controlapi_parameter_get_default_value(void * obj) { return jackctl_parameter_get_default_value((jackctl_parameter_t *)obj); } static struct jack_parameter_container * create_container(struct list_head * parent_list_ptr, const char * name) { struct jack_parameter_container * container_ptr; container_ptr = malloc(sizeof(struct jack_parameter_container)); if (container_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_parameter_container"); goto fail; } container_ptr->name = strdup(name); if (container_ptr->name == NULL) { jack_error("Ran out of memory trying to strdup parameter container name"); goto free; } container_ptr->leaf = false; container_ptr->symlink = NULL; container_ptr->obj = NULL; INIT_LIST_HEAD(&container_ptr->children); list_add_tail(&container_ptr->siblings, parent_list_ptr); return container_ptr; free: free(container_ptr); fail: return NULL; } static bool add_controlapi_param(struct list_head * parent_list_ptr, jackctl_parameter_t * param) { struct jack_parameter * param_ptr; uint32_t i; param_ptr = malloc(sizeof(struct jack_parameter)); if (param_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_parameter"); goto fail; } param_ptr->ext = false; param_ptr->obj = param; param_ptr->vtable.is_set = controlapi_parameter_is_set; param_ptr->vtable.reset = controlapi_parameter_reset; param_ptr->vtable.get_value = controlapi_parameter_get_value; param_ptr->vtable.set_value = controlapi_parameter_set_value; param_ptr->vtable.get_default_value = controlapi_parameter_get_default_value; param_ptr->type = jackctl_parameter_get_type(param); param_ptr->name = jackctl_parameter_get_name(param); param_ptr->short_decr = jackctl_parameter_get_short_description(param); param_ptr->long_descr = jackctl_parameter_get_long_description(param); if (jackctl_parameter_has_range_constraint(param)) { param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; param_ptr->constraint_range = true; jackctl_parameter_get_range_constraint(param, ¶m_ptr->constraint.range.min, ¶m_ptr->constraint.range.max); } else if (jackctl_parameter_has_enum_constraint(param)) { param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; param_ptr->constraint_range = false; param_ptr->constraint.enumeration.count = jackctl_parameter_get_enum_constraints_count(param); param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * param_ptr->constraint.enumeration.count); if (param_ptr->constraint.enumeration.possible_values_array == NULL) { goto free; } for (i = 0; i < param_ptr->constraint.enumeration.count; i++) { param_ptr->constraint.enumeration.possible_values_array[i].value = jackctl_parameter_get_enum_constraint_value(param, i); param_ptr->constraint.enumeration.possible_values_array[i].short_desc = jackctl_parameter_get_enum_constraint_description(param, i); } } else { param_ptr->constraint_flags = 0; goto add; } if (jackctl_parameter_constraint_is_strict(param)) { param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_STRICT; } if (jackctl_parameter_constraint_is_fake_value(param)) { param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_FAKE_VALUE; } add: list_add_tail(¶m_ptr->siblings, parent_list_ptr); return true; free: free(param_ptr); fail: return false; } static void free_params(struct list_head * parent_list_ptr) { struct jack_parameter * param_ptr; while (!list_empty(parent_list_ptr)) { param_ptr = list_entry(parent_list_ptr->next, struct jack_parameter, siblings); list_del(¶m_ptr->siblings); if (param_ptr->ext) { continue; } if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0 && !param_ptr->constraint_range && param_ptr->constraint.enumeration.possible_values_array != NULL) { free(param_ptr->constraint.enumeration.possible_values_array); } free(param_ptr); } } static void free_containers(struct list_head * parent_list_ptr) { struct jack_parameter_container * container_ptr; while (!list_empty(parent_list_ptr)) { container_ptr = list_entry(parent_list_ptr->next, struct jack_parameter_container, siblings); list_del(&container_ptr->siblings); if (container_ptr->leaf) { free_params(&container_ptr->children); } else { free_containers(&container_ptr->children); } free(container_ptr->name); free(container_ptr); } } static struct jack_parameter_container * find_container(struct jack_parameter_container * parent_ptr, const char * const * address, int max_depth) { struct list_head * node_ptr; struct jack_parameter_container * container_ptr; if (max_depth == 0 || *address == NULL) { return parent_ptr; } if (parent_ptr->leaf) { return NULL; } if (max_depth > 0) { max_depth--; } list_for_each(node_ptr, &parent_ptr->children) { container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); if (strcmp(container_ptr->name, *address) == 0) { if (container_ptr->symlink != NULL) { container_ptr = container_ptr->symlink; } return find_container(container_ptr, address + 1, max_depth); } } return NULL; } static bool init_leaf(struct list_head * parent_list_ptr, const char * name, const JSList * params_list, void * obj) { struct jack_parameter_container * container_ptr; container_ptr = create_container(parent_list_ptr, name); if (container_ptr == NULL) { return false; } container_ptr->leaf = true; container_ptr->obj = obj; while (params_list) { if (!add_controlapi_param(&container_ptr->children, params_list->data)) { return false; } params_list = jack_slist_next(params_list); } return true; } static bool init_engine(struct jack_params * params_ptr) { return init_leaf(¶ms_ptr->root.children, PTNODE_ENGINE, jackctl_server_get_parameters(params_ptr->server), NULL); } static bool init_drivers(struct jack_params * params_ptr) { const JSList * list; struct jack_parameter_container * container_ptr; container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVERS); if (container_ptr == NULL) { return false; } params_ptr->drivers_ptr = &container_ptr->children; params_ptr->drivers_count = 0; list = jackctl_server_get_drivers_list(params_ptr->server); while (list) { if (!init_leaf(&container_ptr->children, jackctl_driver_get_name(list->data), jackctl_driver_get_parameters(list->data), list->data)) { return false; } params_ptr->drivers_count++; list = jack_slist_next(list); } return true; } static bool init_internals(struct jack_params * params_ptr) { const JSList * list; struct jack_parameter_container * container_ptr; container_ptr = create_container(¶ms_ptr->root.children, PTNODE_INTERNALS); if (container_ptr == NULL) { return false; } list = jackctl_server_get_internals_list(params_ptr->server); while (list) { if (!init_leaf(&container_ptr->children, jackctl_internal_get_name(list->data), jackctl_internal_get_parameters(list->data), NULL)) { return false; } list = jack_slist_next(list); } return true; } static bool init_driver(struct jack_params * params_ptr) { struct jack_parameter_container * container_ptr; container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVER); if (container_ptr == NULL) { return false; } params_ptr->driver_ptr = container_ptr; return true; } #define params_ptr ((struct jack_params *)obj) static bool engine_driver_parameter_is_set(void * obj) { return params_ptr->driver_set; } static bool engine_driver_parameter_reset(void * obj) { if (!jack_params_set_driver(obj, DEFAULT_DRIVER)) { return false; } params_ptr->driver_set = false; return true; } union jackctl_parameter_value engine_driver_parameter_get_value(void * obj) { union jackctl_parameter_value value; strcpy(value.str, params_ptr->driver_ptr->symlink->name); return value; } bool engine_driver_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) { return jack_params_set_driver(obj, value_ptr->str); } union jackctl_parameter_value engine_driver_parameter_get_default_value(void * obj) { union jackctl_parameter_value value; strcpy(value.str, DEFAULT_DRIVER); return value; } #undef params_ptr static bool add_engine_driver_enum_constraint(void * context, const char * name) { strcpy((*((struct jack_parameter_enum **)context))->value.str, name); (*((struct jack_parameter_enum **)context))->short_desc = name; (*((struct jack_parameter_enum **)context))++; return true; } static bool init_engine_driver_parameter(struct jack_params * params_ptr) { struct jack_parameter * param_ptr; const char * address[PARAM_ADDRESS_SIZE] = {PTNODE_ENGINE, NULL}; struct jack_parameter_container * engine_ptr; struct jack_parameter_enum * possible_value; engine_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (engine_ptr == NULL) { return false; } param_ptr = malloc(sizeof(struct jack_parameter)); if (param_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_parameter"); goto fail; } param_ptr->ext = false; param_ptr->obj = params_ptr; param_ptr->vtable.is_set = engine_driver_parameter_is_set; param_ptr->vtable.reset = engine_driver_parameter_reset; param_ptr->vtable.get_value = engine_driver_parameter_get_value; param_ptr->vtable.set_value = engine_driver_parameter_set_value; param_ptr->vtable.get_default_value = engine_driver_parameter_get_default_value; param_ptr->type = JackParamString; param_ptr->name = "driver"; param_ptr->short_decr = "Driver to use"; param_ptr->long_descr = ""; param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID | JACK_CONSTRAINT_FLAG_STRICT | JACK_CONSTRAINT_FLAG_FAKE_VALUE; param_ptr->constraint_range = false; param_ptr->constraint.enumeration.count = params_ptr->drivers_count; param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * params_ptr->drivers_count); if (param_ptr->constraint.enumeration.possible_values_array == NULL) { goto free; } address[0] = PTNODE_DRIVERS; possible_value = param_ptr->constraint.enumeration.possible_values_array; jack_params_iterate_container((jack_params_handle)params_ptr, address, add_engine_driver_enum_constraint, &possible_value); list_add(¶m_ptr->siblings, &engine_ptr->children); return true; free: free(param_ptr); fail: return false; } jack_params_handle jack_params_create(jackctl_server_t * server) { struct jack_params * params_ptr; params_ptr = malloc(sizeof(struct jack_params)); if (params_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_params"); return NULL; } params_ptr->server = server; INIT_LIST_HEAD(¶ms_ptr->root.children); params_ptr->root.leaf = false; params_ptr->root.name = NULL; if (!init_engine(params_ptr) || !init_drivers(params_ptr) || !init_driver(params_ptr) || !init_engine_driver_parameter(params_ptr) || !jack_params_set_driver((jack_params_handle)params_ptr, DEFAULT_DRIVER) || !init_internals(params_ptr)) { jack_params_destroy((jack_params_handle)params_ptr); return NULL; } params_ptr->driver_set = false; assert(strcmp(params_ptr->driver_ptr->symlink->name, DEFAULT_DRIVER) == 0); return (jack_params_handle)params_ptr; } #define params_ptr ((struct jack_params *)params) void jack_params_destroy(jack_params_handle params) { free_containers(¶ms_ptr->root.children); free(params); } bool jack_params_set_driver(jack_params_handle params, const char * name) { struct list_head * node_ptr; struct jack_parameter_container * container_ptr; list_for_each(node_ptr, params_ptr->drivers_ptr) { container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); if (strcmp(container_ptr->name, name) == 0) { params_ptr->driver_ptr->symlink = container_ptr; params_ptr->driver_set = true; return true; } } return false; } jackctl_driver_t * jack_params_get_driver(jack_params_handle params) { return params_ptr->driver_ptr->symlink->obj; } bool jack_params_check_address(jack_params_handle params, const char * const * address, bool want_leaf) { struct jack_parameter_container * container_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL) { return false; } if (want_leaf && !container_ptr->leaf) { return false; } return true; } bool jack_params_is_leaf_container(jack_params_handle params, const char * const * address) { struct jack_parameter_container * container_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL) { assert(false); return false; } return container_ptr->leaf; } bool jack_params_iterate_container( jack_params_handle params, const char * const * address, bool (* callback)(void * context, const char * name), void * context) { struct jack_parameter_container * container_ptr; struct list_head * node_ptr; const char * name; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL) { assert(false); return true; } list_for_each(node_ptr, &container_ptr->children) { if (container_ptr->leaf) { name = list_entry(node_ptr, struct jack_parameter, siblings)->name; } else { name = list_entry(node_ptr, struct jack_parameter_container, siblings)->name; } if (!callback(context, name)) { return false; } } return true; } bool jack_params_iterate_params( jack_params_handle params, const char * const * address, bool (* callback)(void * context, const struct jack_parameter * param_ptr), void * context) { struct jack_parameter_container * container_ptr; struct list_head * node_ptr; struct jack_parameter * param_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL || !container_ptr->leaf) { assert(false); return true; } list_for_each(node_ptr, &container_ptr->children) { param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); if (!callback(context, param_ptr)) { return false; } } return true; } const struct jack_parameter * jack_params_get_parameter(jack_params_handle params, const char * const * address) { int depth; struct jack_parameter_container * container_ptr; struct list_head * node_ptr; struct jack_parameter * param_ptr; for (depth = 0; depth < PARAM_ADDRESS_SIZE; depth++) { if (address[depth] == NULL) { break; } } depth--; container_ptr = find_container(¶ms_ptr->root, address, depth); if (container_ptr == NULL || !container_ptr->leaf) { return NULL; } list_for_each(node_ptr, &container_ptr->children) { param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); if (strcmp(param_ptr->name, address[depth]) == 0) { return param_ptr; } } return NULL; } void jack_params_add_parameter(jack_params_handle params, const char * const * address, bool end, struct jack_parameter * param_ptr) { struct jack_parameter_container * container_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL || !container_ptr->leaf) { assert(false); return; } param_ptr->ext = true; if (end) { list_add_tail(¶m_ptr->siblings, &container_ptr->children); } else { list_add(¶m_ptr->siblings, &container_ptr->children); } return; } #undef params_ptr jack2-1.9.22/dbus/params.h000066400000000000000000000076751436671425200152210ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2011 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED #define PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED #include "jack/control.h" #include "list.h" #define PARAM_ADDRESS_SIZE 3 #define PTNODE_ENGINE "engine" #define PTNODE_DRIVER "driver" #define PTNODE_DRIVERS "drivers" #define PTNODE_INTERNALS "internals" struct jack_parameter_vtable { bool (* is_set)(void * obj); bool (* reset)(void * obj); union jackctl_parameter_value (* get_value)(void * obj); bool (* set_value)(void * obj, const union jackctl_parameter_value * value_ptr); union jackctl_parameter_value (* get_default_value)(void * obj); }; #define JACK_CONSTRAINT_FLAG_VALID ((uint32_t)1) /**< if not set, there is no constraint */ #define JACK_CONSTRAINT_FLAG_STRICT ((uint32_t)2) /**< if set, constraint is strict, i.e. supplying non-matching value will not work */ #define JACK_CONSTRAINT_FLAG_FAKE_VALUE ((uint32_t)4) /**< if set, values have no user meaningful meaning */ struct jack_parameter_enum { union jackctl_parameter_value value; const char * short_desc; }; struct jack_parameter { void * obj; struct jack_parameter_vtable vtable; struct list_head siblings; bool ext; jackctl_param_type_t type; const char * name; const char * short_decr; const char * long_descr; uint32_t constraint_flags; /**< JACK_CONSTRAINT_FLAG_XXX */ bool constraint_range; /**< if true, constraint is a range (min-max), otherwise it is an enumeration */ union { struct { union jackctl_parameter_value min; union jackctl_parameter_value max; } range; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is set */ struct { uint32_t count; struct jack_parameter_enum * possible_values_array; } enumeration; /**< valid when JACK_CONSTRAINT_FLAG_RANGE flag is not set */ } constraint; }; typedef struct _jack_params { int unused; } * jack_params_handle; jack_params_handle jack_params_create(jackctl_server_t * server); void jack_params_destroy(jack_params_handle params); bool jack_params_set_driver(jack_params_handle params, const char * name); jackctl_driver_t * jack_params_get_driver(jack_params_handle params); bool jack_params_check_address(jack_params_handle params, const char * const * address, bool want_leaf); bool jack_params_is_leaf_container(jack_params_handle params, const char * const * address); bool jack_params_iterate_container( jack_params_handle params, const char * const * address, bool (* callback)(void * context, const char * name), void * context); bool jack_params_iterate_params( jack_params_handle params, const char * const * address, bool (* callback)(void * context, const struct jack_parameter * param_ptr), void * context); const struct jack_parameter * jack_params_get_parameter(jack_params_handle params, const char * const * address); void jack_params_add_parameter(jack_params_handle params, const char * const * address, bool end, struct jack_parameter * param_ptr); #endif /* #ifndef PARAMS_H__A23EDE06_C1C9_4489_B253_FD1B26B66929__INCLUDED */ jack2-1.9.22/dbus/reserve.c000066400000000000000000000357101436671425200153730ustar00rootroot00000000000000/*** Copyright 2009 Lennart Poettering 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. ***/ #include #include #include #include #include #include #include #include "reserve.h" #include "jack/control.h" #define RESERVE_ERROR_NO_MEMORY "org.freedesktop.ReserveDevice1.Error.NoMemory" #define RESERVE_ERROR_PROTOCOL_VIOLATION "org.freedesktop.ReserveDevice1.Error.Protocol" #define RESERVE_ERROR_RELEASE_DENIED "org.freedesktop.ReserveDevice1.Error.ReleaseDenied" struct rd_device { int ref; char *device_name; char *application_name; char *application_device_name; char *service_name; char *object_path; int32_t priority; DBusConnection *connection; int owning:1; int registered:1; int filtering:1; int gave_up:1; rd_request_cb_t request_cb; void *userdata; }; #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1." #define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/" static const char introspection[] = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "" " \n" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static dbus_bool_t add_variant( DBusMessage *m, int type, const void *data) { DBusMessageIter iter, sub; char t[2]; t[0] = (char) type; t[1] = 0; dbus_message_iter_init_append(m, &iter); if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub)) return FALSE; if (!dbus_message_iter_append_basic(&sub, type, data)) return FALSE; if (!dbus_message_iter_close_container(&iter, &sub)) return FALSE; return TRUE; } static DBusHandlerResult object_handler( DBusConnection *c, DBusMessage *m, void *userdata) { rd_device *d; DBusError error; DBusMessage *reply = NULL; dbus_error_init(&error); d = (rd_device*)userdata; assert(d->ref >= 1); if (dbus_message_is_method_call( m, "org.freedesktop.ReserveDevice1", "RequestRelease")) { int32_t priority; dbus_bool_t ret; if (!dbus_message_get_args( m, &error, DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) goto invalid; ret = FALSE; if (priority > d->priority && d->request_cb) { d->ref++; if (d->request_cb(d, 0) > 0) { ret = TRUE; d->gave_up = 1; } rd_release(d); } if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!dbus_message_append_args( reply, DBUS_TYPE_BOOLEAN, &ret, DBUS_TYPE_INVALID)) goto oom; if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call( m, "org.freedesktop.DBus.Properties", "Get")) { const char *interface, *property; if (!dbus_message_get_args( m, &error, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) goto invalid; if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) { const char *empty = ""; if (strcmp(property, "ApplicationName") == 0 && d->application_name) { if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!add_variant( reply, DBUS_TYPE_STRING, d->application_name ? (const char**) &d->application_name : &empty)) goto oom; } else if (strcmp(property, "ApplicationDeviceName") == 0) { if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!add_variant( reply, DBUS_TYPE_STRING, d->application_device_name ? (const char**) &d->application_device_name : &empty)) goto oom; } else if (strcmp(property, "Priority") == 0) { if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!add_variant( reply, DBUS_TYPE_INT32, &d->priority)) goto oom; } else { if (!(reply = dbus_message_new_error_printf( m, DBUS_ERROR_UNKNOWN_METHOD, "Unknown property %s", property))) goto oom; } if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } } else if (dbus_message_is_method_call( m, "org.freedesktop.DBus.Introspectable", "Introspect")) { const char *i = introspection; if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!dbus_message_append_args( reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) goto oom; if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; invalid: if (reply) dbus_message_unref(reply); if (!(reply = dbus_message_new_error( m, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) goto oom; if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); dbus_error_free(&error); return DBUS_HANDLER_RESULT_HANDLED; oom: if (reply) dbus_message_unref(reply); dbus_error_free(&error); return DBUS_HANDLER_RESULT_NEED_MEMORY; } static DBusHandlerResult filter_handler( DBusConnection *c, DBusMessage *m, void *userdata) { DBusMessage *reply; rd_device *d; DBusError error; dbus_error_init(&error); d = (rd_device*)userdata; if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) { const char *name; if (!dbus_message_get_args( m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) goto invalid; if (strcmp(name, d->service_name) == 0 && d->owning) { d->owning = 0; if (!d->gave_up) { d->ref++; if (d->request_cb) d->request_cb(d, 1); d->gave_up = 1; rd_release(d); } return DBUS_HANDLER_RESULT_HANDLED; } } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; invalid: if (!(reply = dbus_message_new_error( m, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) goto oom; if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); dbus_error_free(&error); return DBUS_HANDLER_RESULT_HANDLED; oom: if (reply) dbus_message_unref(reply); dbus_error_free(&error); return DBUS_HANDLER_RESULT_NEED_MEMORY; } static DBusObjectPathVTable vtable; int rd_acquire( rd_device **_d, DBusConnection *connection, const char *device_name, const char *application_name, int32_t priority, rd_request_cb_t request_cb, DBusError *error) { rd_device *d = NULL; int r, k; DBusError _error; DBusMessage *m = NULL, *reply = NULL; dbus_bool_t good; vtable.message_function = object_handler; if (!error) { error = &_error; dbus_error_init(error); } if (!_d) { assert(0); r = -EINVAL; goto fail; } if (!connection) { assert(0); r = -EINVAL; goto fail; } if (!device_name) { assert(0); r = -EINVAL; goto fail; } if (!request_cb && priority != INT32_MAX) { assert(0); r = -EINVAL; goto fail; } if (!(d = (rd_device *)calloc(sizeof(rd_device), 1))) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot allocate memory for rd_device struct"); r = -ENOMEM; goto fail; } d->ref = 1; if (!(d->device_name = strdup(device_name))) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot duplicate device name string"); r = -ENOMEM; goto fail; } if (!(d->application_name = strdup(application_name))) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot duplicate application name string"); r = -ENOMEM; goto fail; } d->priority = priority; d->connection = dbus_connection_ref(connection); d->request_cb = request_cb; if (!(d->service_name = (char*)malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot allocate memory for service name string"); r = -ENOMEM; goto fail; } sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name); if (!(d->object_path = (char*)malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot allocate memory for object path string"); r = -ENOMEM; goto fail; } sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name); if ((k = dbus_bus_request_name( d->connection, d->service_name, DBUS_NAME_FLAG_DO_NOT_QUEUE| (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0), error)) < 0) { jack_error("dbus_bus_request_name() failed. (1)"); r = -EIO; goto fail; } switch (k) { case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: goto success; case DBUS_REQUEST_NAME_REPLY_EXISTS: break; case DBUS_REQUEST_NAME_REPLY_IN_QUEUE : /* DBUS_NAME_FLAG_DO_NOT_QUEUE was specified */ default: /* unknown reply returned */ jack_error("request name reply with unexpected value %d.", k); assert(0); r = -EIO; goto fail; } if (priority <= INT32_MIN) { r = -EBUSY; dbus_set_error(error, RESERVE_ERROR_RELEASE_DENIED, "Device reservation request with priority %"PRIi32" denied for \"%s\"", priority, device_name); goto fail; } if (!(m = dbus_message_new_method_call( d->service_name, d->object_path, "org.freedesktop.ReserveDevice1", "RequestRelease"))) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot allocate memory for RequestRelease method call"); r = -ENOMEM; goto fail; } if (!dbus_message_append_args( m, DBUS_TYPE_INT32, &d->priority, DBUS_TYPE_INVALID)) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot append args for RequestRelease method call"); r = -ENOMEM; goto fail; } if (!(reply = dbus_connection_send_with_reply_and_block( d->connection, m, 5000, /* 5s */ error))) { if (dbus_error_has_name(error, DBUS_ERROR_TIMED_OUT) || dbus_error_has_name(error, DBUS_ERROR_UNKNOWN_METHOD) || dbus_error_has_name(error, DBUS_ERROR_NO_REPLY)) { /* This must be treated as denied. */ jack_info("Device reservation request with priority %"PRIi32" denied for \"%s\": %s (%s)", priority, device_name, error->name, error->message); r = -EBUSY; goto fail; } jack_error("dbus_connection_send_with_reply_and_block(RequestRelease) failed."); r = -EIO; goto fail; } if (!dbus_message_get_args( reply, error, DBUS_TYPE_BOOLEAN, &good, DBUS_TYPE_INVALID)) { jack_error("RequestRelease() reply is invalid."); r = -EIO; goto fail; } if (!good) { dbus_set_error(error, RESERVE_ERROR_RELEASE_DENIED, "Device reservation request with priority %"PRIi32" denied for \"%s\" via RequestRelease()", priority, device_name); r = -EBUSY; goto fail; } if ((k = dbus_bus_request_name( d->connection, d->service_name, DBUS_NAME_FLAG_DO_NOT_QUEUE| (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)| DBUS_NAME_FLAG_REPLACE_EXISTING, error)) < 0) { jack_error("dbus_bus_request_name() failed. (2)"); r = -EIO; goto fail; } if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { /* this is racy, another contender may have acquired the device */ dbus_set_error(error, RESERVE_ERROR_PROTOCOL_VIOLATION, "request name reply is not DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER but %d.", k); r = -EIO; goto fail; } success: d->owning = 1; if (!(dbus_connection_try_register_object_path( d->connection, d->object_path, &vtable, d, error))) { jack_error("cannot register object path \"%s\": %s", d->object_path, error->message); r = -ENOMEM; goto fail; } d->registered = 1; if (!dbus_connection_add_filter( d->connection, filter_handler, d, NULL)) { dbus_set_error(error, RESERVE_ERROR_NO_MEMORY, "Cannot add filter"); r = -ENOMEM; goto fail; } d->filtering = 1; *_d = d; return 0; fail: if (m) dbus_message_unref(m); if (reply) dbus_message_unref(reply); if (&_error == error) dbus_error_free(&_error); if (d) rd_release(d); return r; } void rd_release( rd_device *d) { if (!d) return; assert(d->ref > 0); if (--d->ref) return; if (d->filtering) dbus_connection_remove_filter( d->connection, filter_handler, d); if (d->registered) dbus_connection_unregister_object_path( d->connection, d->object_path); if (d->owning) { DBusError error; dbus_error_init(&error); dbus_bus_release_name( d->connection, d->service_name, &error); dbus_error_free(&error); } free(d->device_name); free(d->application_name); free(d->application_device_name); free(d->service_name); free(d->object_path); if (d->connection) dbus_connection_unref(d->connection); free(d); } int rd_set_application_device_name(rd_device *d, const char *n) { char *t; if (!d) return -EINVAL; assert(d->ref > 0); if (!(t = strdup(n))) return -ENOMEM; free(d->application_device_name); d->application_device_name = t; return 0; } void rd_set_userdata(rd_device *d, void *userdata) { if (!d) return; assert(d->ref > 0); d->userdata = userdata; } void* rd_get_userdata(rd_device *d) { if (!d) return NULL; assert(d->ref > 0); return d->userdata; } jack2-1.9.22/dbus/reserve.h000066400000000000000000000065621436671425200154030ustar00rootroot00000000000000#ifndef fooreservehfoo #define fooreservehfoo /*** Copyright 2009 Lennart Poettering 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. ***/ #include #include # ifndef INT32_MIN # define INT32_MIN (-2147483647-1) # endif # ifndef INT32_MAX # define INT32_MAX (2147483647) # endif typedef struct rd_device rd_device; /* Prototype for a function that is called whenever someone else wants * your application to release the device it has locked. A return * value <= 0 denies the request, a positive return value agrees to * it. Before returning your application should close the device in * question completely to make sure the new application may access * it. */ typedef int (*rd_request_cb_t)( rd_device *d, int forced); /* Non-zero if an application forcibly took the lock away without asking. If this is the case then the return value of this call is ignored. */ #ifdef __cplusplus extern "C" { #endif /* Try to lock the device. Returns 0 on success, a negative errno * style return value on error. The DBus error might be set as well if * the error was caused D-Bus. */ int rd_acquire( rd_device **d, /* On success a pointer to the newly allocated rd_device object will be filled in here */ DBusConnection *connection, const char *device_name, /* The device to lock, e.g. "Audio0" */ const char *application_name, /* A human readable name of the application, e.g. "PulseAudio Sound Server" */ int32_t priority, /* The priority for this application. If unsure use 0 */ rd_request_cb_t request_cb, /* Will be called whenever someone requests that this device shall be released. May be NULL if priority is INT32_MAX */ DBusError *error); /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */ /* Unlock (if needed) and destroy an rd_device object again */ void rd_release(rd_device *d); /* Set the application device name for an rd_device object. Returns 0 * on success, a negative errno style return value on error. */ int rd_set_application_device_name(rd_device *d, const char *name); /* Attach a userdata pointer to an rd_device */ void rd_set_userdata(rd_device *d, void *userdata); /* Query the userdata pointer from an rd_device. Returns NULL if no * userdata was set. */ void* rd_get_userdata(rd_device *d); #ifdef __cplusplus } /* extern "C" */ #endif #endif jack2-1.9.22/dbus/sigsegv.c000066400000000000000000000126571436671425200153740ustar00rootroot00000000000000/** * This source file is used to print out a stack-trace when your program * segfaults. It is relatively reliable and spot-on accurate. * * This code is in the public domain. Use it as you see fit, some credit * would be appreciated, but is not a prerequisite for usage. Feedback * on it's use would encourage further development and maintenance. * * Author: Jaco Kroon * * Copyright (C) 2005 - 2008 Jaco Kroon */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif //#define NO_CPP_DEMANGLE #define SIGSEGV_NO_AUTO_INIT #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #ifdef HAVE_EXECINFO_H # include #endif #include #ifndef NO_CPP_DEMANGLE char * __cxa_demangle(const char * __mangled_name, char * __output_buffer, size_t * __length, int * __status); #endif #include "jack/control.h" #if defined(REG_RIP) # define SIGSEGV_STACK_IA64 # define REGFORMAT "%016lx" #elif defined(REG_EIP) # define SIGSEGV_STACK_X86 # define REGFORMAT "%08x" #else # define SIGSEGV_STACK_GENERIC # define REGFORMAT "%x" #endif #ifdef __APPLE__ // TODO : does not compile yet on OSX static void signal_segv(int signum, siginfo_t* info, void*ptr) {} #else #include static void signal_segv(int signum, siginfo_t* info, void*ptr) { static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"}; const char *si_code_str; ucontext_t *ucontext = (ucontext_t*)ptr; #if (defined(HAVE_UCONTEXT) && defined(HAVE_NGREG)) || defined(HAVE_EXECINFO_H) size_t i; #endif #if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64) int f = 0; Dl_info dlinfo; void **bp = 0; void *ip = 0; #else void *bt[20]; char **strings; size_t sz; #endif if (signum == SIGSEGV) { jack_error("Segmentation Fault!"); } else if (signum == SIGABRT) { jack_error("Abort!"); } else if (signum == SIGILL) { jack_error("Illegal instruction!"); } else if (signum == SIGFPE) { jack_error("Floating point exception!"); } else { jack_error("Unknown bad signal caught!"); } if (info->si_code >= 0 && info->si_code < 3) si_code_str = si_codes[info->si_code]; else si_code_str = "unknown"; jack_error("info.si_signo = %d", signum); jack_error("info.si_errno = %d", info->si_errno); jack_error("info.si_code = %d (%s)", info->si_code, si_code_str); jack_error("info.si_addr = %p", info->si_addr); #if defined(HAVE_UCONTEXT) && defined(HAVE_NGREG) for(i = 0; i < NGREG; i++) jack_error("reg[%02d] = 0x" REGFORMAT, i, #if defined(HAVE_UCONTEXT_GP_REGS) ucontext->uc_mcontext.gp_regs[i] #elif defined(HAVE_UCONTEXT_UC_REGS) ucontext->uc_mcontext.uc_regs[i] #elif defined(HAVE_UCONTEXT_MC_GREGS) ucontext->uc_mcontext.mc_gregs[i] #elif defined(HAVE_UCONTEXT_GREGS) ucontext->uc_mcontext.gregs[i] #endif ); #endif /* defined(HAVE_UCONTEXT) && defined(HAVE_NGREG) */ #if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64) # if defined(SIGSEGV_STACK_IA64) ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP]; bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP]; # elif defined(SIGSEGV_STACK_X86) ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP]; bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP]; # endif jack_error("Stack trace:"); while(bp && ip) { if(!dladdr(ip, &dlinfo)) break; const char *symname = dlinfo.dli_sname; #ifndef NO_CPP_DEMANGLE int status; char *tmp = __cxa_demangle(symname, NULL, 0, &status); if(status == 0 && tmp) symname = tmp; #endif jack_error("% 2d: %p <%s+%u> (%s)", ++f, ip, symname, (unsigned)(ip - dlinfo.dli_saddr), dlinfo.dli_fname); #ifndef NO_CPP_DEMANGLE if(tmp) free(tmp); #endif if(dlinfo.dli_sname && !strcmp(dlinfo.dli_sname, "main")) break; ip = bp[1]; bp = (void**)bp[0]; } #else # ifdef HAVE_EXECINFO_H jack_error("Stack trace (non-dedicated):"); sz = backtrace(bt, 20); strings = backtrace_symbols(bt, sz); for(i = 0; i < sz; ++i) jack_error("%s", strings[i]); # else jack_error("Stack trace not available"); # endif #endif jack_error("End of stack trace"); exit (-1); } #endif int setup_sigsegv() { struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_sigaction = signal_segv; #ifdef SA_SIGINFO action.sa_flags = SA_SIGINFO; #endif if(sigaction(SIGSEGV, &action, NULL) < 0) { jack_error("sigaction failed. errno is %d (%s)", errno, strerror(errno)); return 0; } if(sigaction(SIGILL, &action, NULL) < 0) { jack_error("sigaction failed. errno is %d (%s)", errno, strerror(errno)); return 0; } if(sigaction(SIGABRT, &action, NULL) < 0) { jack_error("sigaction failed. errno is %d (%s)", errno, strerror(errno)); return 0; } if(sigaction(SIGFPE, &action, NULL) < 0) { jack_error("sigaction failed. errno is %d (%s)", errno, strerror(errno)); return 0; } return 1; } #ifndef SIGSEGV_NO_AUTO_INIT static void __attribute((constructor)) init(void) { setup_sigsegv(); } #endif jack2-1.9.22/dbus/sigsegv.h000066400000000000000000000001571436671425200153710ustar00rootroot00000000000000#ifndef __SIGSEGV_H__ #define __SIGSEGV_H__ #ifdef __cplusplus extern "C" #endif int setup_sigsegv(); #endif jack2-1.9.22/dbus/wscript000066400000000000000000000065111436671425200151670ustar00rootroot00000000000000#! /usr/bin/python3 # encoding: utf-8 import os.path from waflib import Logs, Options def options(opt): opt.add_option( '--enable-pkg-config-dbus-service-dir', action='store_true', default=False, help='force D-Bus service install dir to be one returned by pkg-config', ) def configure(conf): conf.env['BUILD_JACKDBUS'] = False conf.define('JACK_VERSION', conf.env['JACK_VERSION']) if not conf.check_cfg(package='dbus-1 >= 1.0.0', args='--cflags --libs', mandatory=False): print( Logs.colors.RED + 'ERROR !! jackdbus will not be built because libdbus-dev is missing' + Logs.colors.NORMAL ) return dbus_dir = conf.check_cfg(package='dbus-1', args='--variable=session_bus_services_dir') if not dbus_dir: print( Logs.colors.RED + 'ERROR !! jackdbus will not be built because service dir is unknown' + Logs.colors.NORMAL ) return dbus_dir = dbus_dir.strip() conf.env['DBUS_SERVICES_DIR_REAL'] = dbus_dir if Options.options.enable_pkg_config_dbus_service_dir: conf.env['DBUS_SERVICES_DIR'] = dbus_dir else: conf.env['DBUS_SERVICES_DIR'] = os.path.normpath(conf.env['PREFIX'] + '/share/dbus-1/services') if not conf.check_cfg(package='expat', args='--cflags --libs', mandatory=False): print(Logs.colors.RED + 'ERROR !! jackdbus will not be built because of expat is missing' + Logs.colors.NORMAL) return conf.env['BUILD_JACKDBUS'] = True def build(bld): obj = bld(features=['c', 'cprogram'], idx=17) if bld.env['IS_LINUX']: sysdeps_dbus_include = ['../linux', '../posix'] if bld.env['IS_FREEBSD']: sysdeps_dbus_include = ['../freebsd', '../posix'] if bld.env['IS_MACOSX']: sysdeps_dbus_include = ['../macosx', '../posix'] obj.includes = sysdeps_dbus_include + ['.', '../', '../common', '../common/jack'] obj.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE'] obj.source = [ 'jackdbus.c', 'controller.c', 'params.c', 'controller_iface_configure.c', 'controller_iface_control.c', 'controller_iface_introspectable.c', 'controller_iface_patchbay.c', 'controller_iface_session_manager.c', 'controller_iface_transport.c', 'xml.c', 'xml_expat.c', 'xml_write_raw.c', 'sigsegv.c', 'reserve.c', ] obj.use = ['serverlib'] if bld.env['IS_LINUX']: obj.source += [ '../linux/uptime.c', ] obj.use += ['PTHREAD', 'DL', 'RT', 'DBUS-1', 'EXPAT', 'STDC++'] if bld.env['IS_FREEBSD']: obj.source += [ '../linux/uptime.c', ] obj.use += ['PTHREAD', 'EXECINFO', 'LIBSYSINFO', 'DBUS-1', 'EXPAT'] if bld.env['IS_MACOSX']: obj.source += [ '../macosx/uptime.c', ] obj.use += ['PTHREAD', 'DL', 'DBUS-1', 'EXPAT'] obj.target = 'jackdbus' # process org.jackaudio.service.in -> org.jackaudio.service obj = bld( features='subst', source='org.jackaudio.service.in', target='org.jackaudio.service', install_path='${DBUS_SERVICES_DIR}/', BINDIR=bld.env['PREFIX'] + '/bin') if not bld.env['IS_WINDOWS']: bld.install_files('${PREFIX}/bin', 'jack_control', chmod=0o755) jack2-1.9.22/dbus/xml.c000066400000000000000000000074751436671425200145270ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008,2011 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include #include "controller_internal.h" void jack_controller_deserialize_parameter_value( struct jack_controller *controller_ptr, const char * const * address, const char * option_value) { const struct jack_parameter * param_ptr; union jackctl_parameter_value value; size_t size; param_ptr = jack_params_get_parameter(controller_ptr->params, address); if (param_ptr == NULL) { jack_error("Unknown parameter"); goto ignore; } jack_info("setting parameter '%s':'%s':'%s' to value \"%s\"", address[0], address[1], address[2], option_value); switch (param_ptr->type) { case JackParamInt: value.i = atoi(option_value); break; case JackParamUInt: value.ui = strtoul(option_value, NULL, 10); break; case JackParamChar: if (option_value[0] == 0 || option_value[1] != 0) { jack_error("invalid char option value \"%s\"", option_value); goto ignore; } value.c = *option_value; break; case JackParamString: size = strlen(option_value); if (size >= sizeof(value.str)) { jack_error("string option value \"%s\" is too long, max is %zu chars (including terminating zero)", option_value, sizeof(value.str)); goto ignore; } strcpy(value.str, option_value); break; case JackParamBool: if (strcmp(option_value, "true") == 0) { value.b = true; } else if (strcmp(option_value, "false") == 0) { value.b = false; } else { jack_error("ignoring unknown bool value \"%s\"", option_value); goto ignore; } break; default: jack_error("Unknown type %d", (int)param_ptr->type); goto ignore; } if (param_ptr->vtable.set_value(param_ptr->obj, &value)) { return; } jack_error("Parameter set failed"); ignore: jack_error("Ignoring restore attempt of parameter '%s':'%s':'%s'", address[0], address[1], address[2]); } void jack_controller_serialize_parameter_value( const struct jack_parameter * param_ptr, char * value_buffer) { union jackctl_parameter_value value; value = param_ptr->vtable.get_value(param_ptr->obj); switch (param_ptr->type) { case JackParamInt: sprintf(value_buffer, "%d", (int)value.i); return; case JackParamUInt: sprintf(value_buffer, "%u", (unsigned int)value.ui); return; case JackParamChar: sprintf(value_buffer, "%c", (char)value.c); return; case JackParamString: strcpy(value_buffer, value.str); return; case JackParamBool: strcpy(value_buffer, value.b ? "true" : "false"); return; } jack_error("parameter of unknown type %d", (int)param_ptr->type); assert(false); *value_buffer = 0; } jack2-1.9.22/dbus/xml.h000066400000000000000000000023361436671425200145230ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef XML_H__4F102BD2_3354_41C9_B842_DC00E1557A0F__INCLUDED #define XML_H__4F102BD2_3354_41C9_B842_DC00E1557A0F__INCLUDED bool jack_controller_settings_save( struct jack_controller * controller_ptr, void *dbus_call_context_ptr); void jack_controller_settings_load( struct jack_controller * controller_ptr); void jack_controller_settings_save_auto( struct jack_controller * controller_ptr); #endif /* #ifndef XML_H__4F102BD2_3354_41C9_B842_DC00E1557A0F__INCLUDED */ jack2-1.9.22/dbus/xml_expat.c000066400000000000000000000203411436671425200157130ustar00rootroot00000000000000/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2007,2008,2011 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "controller_internal.h" #include "jackdbus.h" bool jack_controller_settings_init() { return true; } void jack_controller_settings_uninit() { } struct parse_context { struct jack_controller *controller_ptr; XML_Bool error; bool option_value_capture; char option[JACK_PARAM_STRING_MAX+1]; int option_used; const char * address[PARAM_ADDRESS_SIZE]; int address_index; char * container; char *name; }; #define context_ptr ((struct parse_context *)data) void jack_controller_settings_callback_chrdata(void *data, const XML_Char *s, int len) { if (context_ptr->error) { return; } if (context_ptr->option_value_capture) { if (context_ptr->option_used + len >= JACK_PARAM_STRING_MAX) { jack_error("xml parse max char data length reached"); context_ptr->error = XML_TRUE; return; } memcpy(context_ptr->option + context_ptr->option_used, s, len); context_ptr->option_used += len; } } void jack_controller_settings_callback_elstart(void *data, const char *el, const char **attr) { if (context_ptr->error) { return; } if (context_ptr->address_index >= PARAM_ADDRESS_SIZE) { assert(context_ptr->address_index == PARAM_ADDRESS_SIZE); jack_error("xml param address max depth reached"); context_ptr->error = XML_TRUE; return; } //jack_info("<%s>", el); if (strcmp(el, "jack") == 0) { return; } if (strcmp(el, PTNODE_ENGINE) == 0) { context_ptr->address[context_ptr->address_index++] = PTNODE_ENGINE; return; } if (strcmp(el, PTNODE_DRIVERS) == 0) { context_ptr->address[context_ptr->address_index++] = PTNODE_DRIVERS; return; } if (strcmp(el, PTNODE_INTERNALS) == 0) { context_ptr->address[context_ptr->address_index++] = PTNODE_INTERNALS; return; } if (strcmp(el, "driver") == 0 || strcmp(el, "internal") == 0) { if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) { jack_error("<%s> XML element must contain exactly one attribute, named \"name\"", el); context_ptr->error = XML_TRUE; return; } context_ptr->container = strdup(attr[1]); if (context_ptr->container == NULL) { jack_error("strdup() failed"); context_ptr->error = XML_TRUE; return; } context_ptr->address[context_ptr->address_index++] = context_ptr->container; return; } if (strcmp(el, "option") == 0) { if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0) { jack_error("