pax_global_header00006660000000000000000000000064131075475000014514gustar00rootroot0000000000000052 comment=278aa9320aea7166d75fe55fbd6c91d8f5e0606f jack_capture-0.9.73/000077500000000000000000000000001310754750000142475ustar00rootroot00000000000000jack_capture-0.9.73/COPYING000066400000000000000000000465101310754750000153100ustar00rootroot00000000000000This software is distributed under GNU Public License (version 2 or later) except jack_capture_gui2 which is distributed under the BSD License. ----------------------------------------------------------- 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. ----------------------------------------------------------- Copyright (c) 2008, Herman Meyer 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 Herman Meyer's organisation 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 REGENTS 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 REGENTS 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. ----------------------------------------------------------- jack_capture-0.9.73/Makefile000066400000000000000000000043001310754750000157040ustar00rootroot00000000000000PREFIX ?= /usr/local bindir = $(PREFIX)/bin VERSION=0.9.73 CC=gcc CPP=g++ # Needed on OSX CFLAGS += -I/opt/local/include OPTIMIZE=-O3 #OPTIMIZE=-O0 -g COMPILEFLAGS=$(CFLAGS) $(OPTIMIZE) -DVERSION=\"$(VERSION)\" -Wall -Wextra -Wno-unused LINKFLAGS=$(LDFLAGS) -ljack -lsndfile -lm -lpthread OS := $(shell uname) ifneq ($(OS),Darwin) LINKFLAGS += -lrt endif targets = jack_capture all: check_dependencies jack_capture install: $(targets) install -d $(DESTDIR)$(bindir) install -m755 $(targets) $(DESTDIR)$(bindir) uninstall: rm -f $(DESTDIR)$(bindir)/jack_capture -rmdir $(DESTDIR)$(bindir) check_dependencies: @echo @echo "Checking dependencies: " which bash which tr which sed which install which pkg-config which $(CC) which $(CPP) $(CC) $(CFLAGS) -E testsndfile.c >/dev/null @echo "All seems good " @echo dist: clean rm -fr /tmp/jack_capture-$(VERSION) rm -fr jack_capture-$(VERSION) mkdir /tmp/jack_capture-$(VERSION) cp -a * /tmp/jack_capture-$(VERSION)/ mv /tmp/jack_capture-$(VERSION) . tar cvf jack_capture-$(VERSION).tar jack_capture-$(VERSION) gzip jack_capture-$(VERSION).tar marcel_upload jack_capture-$(VERSION).tar.gz ls -la jack_capture-$(VERSION) rm -fr jack_capture-$(VERSION) jack_capture: setformat.c jack_capture.c vringbuffer.c upwaker.c osc.c Makefile das_config.h config_flags $(CC) $(COMPILEFLAGS) jack_capture.c vringbuffer.c upwaker.c osc.c -o jack_capture $(LINKFLAGS) `cat config_flags` jack_capture_gui2: jack_capture_gui2.cpp $(CPP) $(CPPFLAGS) $(OPTIMIZE) jack_capture_gui2.cpp $(LDFLAGS) `pkg-config --libs --cflags gtk+-2.0` -o jack_capture_gui2 config_flags: Makefile das_config.h cat das_config.h |grep COMPILEFLAGS|sed s/\\/\\/COMPILEFLAGS//|tr '\n' ' ' >config_flags das_config.h: gen_das_config_h.sh bash gen_das_config_h.sh >das_config.h @echo @echo "jack_capture was configured with the following options:" @echo "------------------------------" @cat das_config.h @echo "------------------------------" @echo setformat.c: gen_setformat_c.sh bash gen_setformat_c.sh >setformat.c clean: rm -f *~ jack_capture jack_capture_gui2 config_flags *.wav *.flac *.ogg *.mp3 *.au *.aiff *.wavex temp.c* setformat.c* das_config.h* a.out *.gz *.orig "#*" jack_capture-0.9.73/README000066400000000000000000001026141310754750000151330ustar00rootroot00000000000000 jack_capture V0.9.73 Released 2017-05-19 Kjetil S. Matheussen ABOUT ***** jack_capture is a program for recording soundfiles with jack. The default operation of the program is executed by writing "jack_capture" in the terminal without any extra command line options: $ jack_capture ...which will record what you hear in your loudspeakers into a stereo wav file. Other features: * Autogenerated filenames are unique and humanly readable. * The 4GB size barrier for wav files is handled by continuing writing to new files when reaching 4GB. * Supports all soundfile formats supported by sndfile. (wav, aiff, ogg, flac, wavex, au, etc.) (option: -f ) * Supports mp3 by using liblame. (option: -mp3) * Option for writing raw 16 bit data to stdout. (option: -ws) * Built-in console meter, plus option for automatically starting and stopping the Meterbridge jack meter program. Port connections to Meterbridge are done automatically, and on the fly, by jack_capture. * jack_capture can connect to any input or output jack port. When "connecting" to a jack input port (i.e. a writable port), jack_capture constantly monitors which jack ports which are connected to that input port, and makes sure jack_capture is always connected to the same ports. In other words, jack_capture will reconnect its ports automatically during recording to match the connections of the ports. This is for instance convenient when recording the playback ports since jack_capture can be started first, and then other programs can start and stop at any moment while all sound still should be recorded. * No limit on the number of jack ports jack_capture can connect to. (I.e. the --port argument can be specified more than once, plus that it accepts wildcard arguments. For instance, jack_capture --port "*" will connect to all current jack ports, both input and output ports, except jack_capture's own ports.) * Buffers are automatically increased during runtime to prevent underruns and to avoid wasting memory by preallocating too much. * The disk thread is automatically reniced to a higher priority when using more than half of the buffer. * Significantly better recording performance than Ardour. (probably because jack_capture writes all channels into only one file and that it is not creating peak files). (tested on athlonXP) * No problem writing at least 256 channels of 32 bit wav at once to a local hard drive. (tested on icore7) COMMON USAGE ************ $ jack_capture ADVANCED USAGE ************** jack_capture [--bitdepth n] [--channels n] [--port port] [filename] [ -b n] [ -c n] [ -p port] "bitdepth" is by default FLOAT. It can be set to either 8, 16, 24 or 32. (for relevant formats) "channels" is by default 2. "port" is by default set to the two first physical outputs. The "port" argument can be specified more than once. "filename" is by default autogenerated to "jack_capture_." Beware that you might risk overwriting an old file by using this option. By not using this option, you will not overwrite an old file. See also the option --filename-prefix Additional arguments: [--recording-time n] or [-d n] -> Recording is stopped after "n" seconds. This options also starts jack_capture in --no-stdin mode. To stop recording before the timeout, one can press Ctrl-C.) [--filename-prefix s]/[-fp n] -> Sets first part of the autogenerated filename. (default is "jack_capture_") [--leading-zeros n] or [-z n] -> "n" is the number of zeros to in the autogenerated filename. (-z 2 -> jack_capture_001.wav, and so on.) (default is 1) [--format format] or [-f format] -> Selects fileformat provided by libsndfile. See http://www.mega-nerd.com/libsndfile/api.html#open (Default is wav for 1 or 2 channels, and wavex for more than 2.) [--print-formats] or [-pf] -> Prints all sound formats provided to sndfile to screen and then exits. [--version] or [-v] -> Prints out version. [--silent] or [-s] -> Suppresses some common messages printed to the terminal. [--absolutely-silent] or [-as] -> Suppresses all messages printed to the terminal. Warning: libraries used by jack_capture may still print messages. [--verbose] or [-V] -> Prints some extra information to the terminal. [--mp3] or [-mp3] -> Writes to an mp3 file using liblame (LAME). (the --format option has no effect using this option) [--mp3-quality n] or [-mp3q n] -> Selects mp3 quality provided by liblame. n=0 is best, n=9 is worst. Default n is 2. (0 uses the most amount of CPU, 9 uses the least) [--mp3-bitrate n] or [-mp3b n] -> Selects mp3 bitrate (in kbit/s). Default is set by liblame. (currently 128) [--write-to-stdout] or [-ws] -> Writes 16 bit little endian to stdout. (the --format option, the --mp3 option, and some others have no effect using this option) [--disable-meter] or [-dm] -> Disable console meter. [--hide-buffer-usage] or [-hbu] -> Disable buffer usage updates in the console. [--disable-console] or [-dc] -> Disable console updates. Same as "-dm -hbu". [--no-stdin] or [-ns] -> Don't read the console. (i.e pressing return won't stop recording.) [--daemon] -> Same as writing "--no-stdin --absolutely-silent". [--linear-meter] or [-lm] -> Use linear scale for the console meter (default is dB scale) [--dB-meter-reference or [-dBr] -> Specify reference level for dB meter. (default=0) [--meterbridge] or [-mb] -> Start up meterbridge to monitor recorded sound. [--meterbridge-type] or [-mt] -> Specify type. vu (default), ppm, dpm, jf or sco. [--meterbridge-reference]/[-mr] -> Specify reference level for meterbidge. [--jack-transport]/[-jt] -> Start program, but do not start recording until jack transport has started rolling When jack transport stops, the recording is also stopped, and the program ends. [--jack-transport-multi]/[-jtm] -> Similar to --jack-transport, but do not end program when jack transport stops. Instead, record to a new file when jack_transport starts rolling again. (not implemented yet) [--jack-freewheeling]/[-jf] -> Start program, but do not start recording until jack enters freewheeling mode When jack leaves freewheeling, the recording is also stopped, and the program ends. [--manual-connections]/[-mc] -> jack_capture will not connect any ports for you. [--bufsize s] or [-B s] -> Initial/minimum buffer size in seconds. Default is 8 seconds for mp3 files, and 4 seconds for all other formats. [--maxbufsize] or [-MB] -> Maximum buffer size in seconds jack_capture will allocate. Default is 40. (Buffer is automatically increased during recording when needed. But it will never go beyond this size.) [--filename] or [-fn] -> Specify filename. Beware that you might risk overwriting an old file by using this option. To only set prefix, use --filename-prefix / -fp instead. (It's usually easier to set last argument instead of using this option) [--osc] or [-O] -> Specify OSC port number to listen on. see --help-osc [--timestamp] or [-S] -> create a FILENAME.tme file for each recording, storing the system-time corresponding to the first audio sample. [--rotatefile N] or [-Rf N] -> force rotate files every N audio-frames. [--hook-open c] or [-Ho c] -> command to execute on successful file-open. (see below) [--hook-close c] or [-Hc c] -> command to execute when closing the session. (see below) [--hook-rotate c] or [-Hr c] -> command to execute on file-name-rotation. (see below) [--hook-timing c] or [-Ht c] -> callback when first audio frame is received. (see below) [--timemachine] or [-tm] -> jack_capture operates in "timemachine" mode. [--timemachine-prebuffer s] -> Specify (in seconds) how long time to prebuffer in timemachine mode. [ -tmpb s] -> ------------------------ "" ---------------------------------------- All hook options take a full-path to an executable as argument. The commands are executed in a fire-and-forget style upon internal events. All output of the hooks is discarded. Paramaters passed to the hook-scripts: open: CMD close: CMD rotate: CMD timing: CMD Examples: To record a stereo file of what you hear: $jack_capture To record a stereo file of what you hear in the flac format: $jack_capture -f flac To record a stereo file of what you hear in the ogg format: $jack_capture -f ogg To record a stereo file of what you hear in the mp3 format: $jack_capture -mp3 To record a stereo file of what you hear in the wav format: $jack_capture --port system:playback_1 --port system:playback_2 **************************************************************************** **** NOTE! The above example does _exactly_ the same as the default!!! **** **************************************************************************** Same result as above, but using a different syntax: $jack_capture --channels 2 --port system:playback* To record the output from jamin: $jack_capture --port jamin:out* sound_from_jamin.wav To record all sound coming in to jamin: $jack_capture --port jamin:in* sound_to_jamin.wav To record all sound coming in and out of jamin: $jack_capture --port jamin* sound_to_and_from_jamin.wav To record a stereo file from the soundcard: $jack_capture -c 2 -p system:capture* ACKNOWLEDGMENT ************** Originally based on the jackrec program in the jack distribution made by Paul Davis and Jack O'Quin. Not much left of that code now. http://www.jackaudio.org Peak indicator code for the console meter taken from meterbridge by Steve Harris. http://plugin.org.uk/meterbridge/ Code for writing to stdout was made by looking at http://mir.dnsalias.com/oss/jackstdout/start made by Robin Gareus. Code contributions from: Florian Faber, Robin Gareus, Adrian Knoth, Filipe Lopes, Svend-Erik Kjær Madsen (made jack_capture_gui), Hermann Meyer (made jack_capture_gui2), Dan Muresan, Orcan Ogetbil, and Alessio Treglia. Thanks to the following for helping to fix bugs, report bugs, give suggestions, etc.: Fons Adriaensen, Gabriel J.L. Beckers, Julien Claassen, Dominique Fober, Peder Hedlund, Cesare Marilungo, Jaromír Mike¨, Dragan Noveski, Andras Simon, and Anders Vinjar. CHANGES ******* 0.9.72 -> 0.9.73 * Update version to 0.9.73. (Version number was set to 0.9.71 in 0.9.72.) * Make it clearer that you risk overwriting an old file by specifying filename * OSX compilation fix (Julius Smith) 0.9.71 -> 0.9.72 * Print warning in case user overwrites existing file. * Use execvp, not execv. Also print strerror() if execvp failed. * Support OSX (Luciano Iammarino) * Some tsan fixes * replace static .wav suffix with correct soundfile_format (Sebastian Reimers) * Silence unused parameter warnings from gcc 4.8 * Warn about unable to set SCHED_OTHER process using setpriority(), not a SCHED_FIFO process * Compilation patches from debian 0.7.70 -> 0.9.71 * Dont limit mp3 files to stereo only * Flush mp3 files before closing 0.9.69 -> 0.9.70 * notify user about jack x-runs that occurred during recording (Robin Gareus) * Include timemachine documentation in README file 0.9.67 -> 0.6.69 * Timemachine mode. Make jack_capture work like Steve Harries timemachine recorder Enable with the -tm / --timemachine option. Set prebuffer time with the -tmpb / --timemachine-prebuffer option. (With help from Robin Gareus) * OSC methods to control time-machine mode. (Robin Gareus) * Check if the call to sem_wait failed. Do not stop recording if so. * Fix reset-colour. (Robin Gareus) * Use jack_free to free port name array, not free. * Set mono/24bit the default format for SDS files. 0.9.66 -> 0.9.67 * osc_help must be available, even without HAVE_LIBLO * fix for signed / unsigned integer comparison 0.9.65 -> 0.9.66 * Respect both DESTDIR and PREFIX. (Robin Gareus) 0.9.64 -> 0.9.65 * Fix for jack_capture_gui console printing. 0.9.63 -> 0.9.64 * Add necessary -lrt linker flag * Change prefix to PREFIX from makefile, and remove DESTDIR * Fix the jack_capture_gui script 0.9.62 -> 0.9.63 * Fix. Swapped --no-stdin and --daemon options 0.9.61 -> 0.9.62 * CHANGE IN BEHAVIOR: The --daemon option now also suppresses all console message. * Support for using a config file. A file in the distribution called 'config' can be copied into ~/.jack_capture/config and modifed as needed. * Allow to remote-control jack-capture via OSC. (Robin Gareus) (see --help-osc) * Added exec-hooks: jack_capture can launch an external app in fire&forget mode (from the disk-thread). (Robin Gareus) * When it opens a new file for writing * When it closes a file, recording finished * On file-name-rotation * When first audio frame is received. * Write a timestamp file with sample-accurate system time of the start of recording. (--timestamp) (Robin Gareus) * Forced file-name rotatation every N audio-frames. (--forcerotate) (Robin Gareus) * A new option to make jack_capture absolutely silent. (--absolutely-silent) (Robin Gareus) * Option for recording only while freewheeling. (--jack-freewheel) (Robin Gareus) * Total recorded time shown in console, as minutes and seconds. * Backwards compatible JACK latency API. (Robin Gareus) * Don't close jack client too early. * New option: --no-stdin. Works the same way as the old --daemon option. 0.9.60 -> 0.9.61 * Fix writing to stdout. * Don't use helper thread when writing to stdout. 0.9.59 -> 0.9.60 * Applied Filipe Lopes' freewheel patch. Do not reconnect ports when freewheeling. 0.9.58 -> 0.9.59 * Terminal printing looks okay now. (Don't look at the code!) 0.9.57 -> 0.9.58 * Created git repository for jack_capture at github: https://github.com/kmatheussen/jack_capture * Removed jack_capture_gui2 from target list. The source is still included with jack_capture, and can be compiled up by writing "make jack_capture_gui2", but I'm not willing to maintain it. Please use at your own risk. If anyone wants to distribute it and maintain it as a program separate from jack_capture, please do so. * Fix bug which in theory could cause malformed sound data to be sent to stdout. * Fix bug that caused longer durations than 2^31 frames to fail (when using the --duration switch). (Caused by a function returning int instead of int64_t.) Thanks to Dan Muresan for pointing out the bug. * Added the "-daemon" switch patch from Dan Muresan. If the "-daemon" switch is enabled, jack_capture will not read from stdin. * Added the "-manual-connections" switch. jack_capture will not connect any ports for you when this is enabled. 0.9.56 -> 0.9.57 * Applied gcc 4.5 linking order fix from Alessio Treglia. * Changed march=native to mtune=native 0.9.55 -> 0.9.56 * Fix for possible error when trying to connect to a non-existing port. 0.9.54 -> 0.9.55 * Don't hardcode meterbridge path during compilation. Thanks to Adrian Knoth for fix. * Avoid killing random process at exit in case meterbridge couldn't be started. 0.9.53 -> 0.9.54 * Removed jack_capture_gui from the target list since it uses Xdialog, which again belongs to a package requiring gtk1. (This should make things simpler for package managers) * Made meterbridge optional. 0.9.52 -> 0.53 * Added the option --filename-prefix / -fp for setting the prefix for autogenerated filenames. The default is "jack_capture_". * Made jack_capture_gui2 use the -fp option to avoid overwriting files from older sessions. * Changed jack_capture_gui2 to use "-f ogg" instead of piping through oggenc. Did not do the same change for mp3 files, since liblame is not always available. * Added code to jack_capture_gui2 to create uniqe filenames when creating mp3 files. 0.9.51_beta -> 0.9.52 * Smaller fixes * Crashfix for exit handling. (introduced in 0.51_beta) 0.9.50 -> 0.51_beta * Smaller reorganizing of buffer handling, disk handling and exit handling. 0.9.49 -> 0.50 * Only print warning if unable to set nice value less than 0. (-10 is okay now) * Added JackPortIsTerminal as jack port flag. * Various cleanups. 0.9.48 -> 0.9.49 * Let the jack process thread trigger the buffer check thread instead of using usleep. * Added the -jt option to let jack transport trigger when to start and stop recording. 0.9.47 -> 0.9.48 * Fixed possible bug when finishing the recording if user has specified a duration. * When allocating buffer, do not zero it out, only touch all pages belonging to it to make sure all memory is mapped into physical memory before it is used by the realtime process. This avoids hogging the memory bus unnecessarily. The first 8 blocks are still zeroed out during initialization though, for the CPU cache. (Might create a less shocking startup.) * Push back the '\n' character after reading it from stdin. For some unknown reason this seems to fix the occational problem of mixed up characters in the console when exiting. (if only ncurses worked in non-fullscreen mode...) 0.9.46 -> 0.9.47 * Only call sem_post if waiting. In extremely extreme situations, this avoids the sem value to overflow. Don't know what happens then. 0.95_beta -> 0.9.46: * Smaller cleanups. * Reduced chance of cache misses in the buffering scheme. 0.9.44 -> 0.9.45_beta: * Replaced the two lockless fifo/lifo queues with three lockless ringbuffers. (CPUs without CAS2 instruction are now supported.) * Only increase buffer by two blocks at the time between soundcard interrupts. This should decrease the chance of jack_capture hogging the memory bus for too long. (Which in theory can lower the chance of xruns in memory intensive realtime processes.) * Changed buffering incrementing scheme. If, at any time, the amount of free buffer is smaller than the initial buffer size, increase the buffer a little bit. Only do this if the buffer is less than maxbuffer. Buffer is never decreased. * Replaced all posix pthread_cond variables with posix semaphores. Far simpler code and much easier to understand. 0.9.43 -> 0.9.44: * Increased default buffer time from 4 seconds to 8 seconds for mp3 files. (Saw more than 1 second of the buffer was used one time in the start of the recording on a very fast machine. Buffer was increased automatically as supposed to, but the default init buffer size is increased anyway just in case.) 0.9.42 -> 0.9.43: * Fixed correct autogenerated file suffix for mp3 files. 0.9.41 -> 0.9.42: * Updated --advanced-help option and README file 0.9.40 -> 0.9.41: * Added missing link libraries to the makefile. Caused by Fedora not supporting indirect linking anymore. Patch from Orcan Ogetbil. (https://fedoraproject.org/wiki/UnderstandingDSOLinkChange) * Applied patch from Orcan Ogetbil to fix 64 bit PowerPC compilation. 0.9.39 -> 0.9.40: * Made "-f mp3" do the same as "-mp3". ("-mp3" still works) 0.9.38 -> 0.9.39: * Reduced default buffering time from 10 to 4 seconds. (Buffering time can probably safely be set below 1 second with the new buffer monitor thread) * Also increase buffer when more than 1/4 of the buffer is used and the unused buffer time is less than 5 seconds. (This in addition to always increase buffer when more than half of it is used) * Fixed console update and keyboard handling. 0.9.37 -> 0.9.38: * Added the --mp3-bitrate option. * Renamed --lame-quality to --mp3-quality. * Use a new dedicated high-priority SCHED_OTHER thread to monitor buffer status. Buffer status was previously only checked in the disk thread, but since the disk thread might stall for longer periods, it is not an ideal place to do this. * Replaced the --help output with a much simpler text, making it clearer that extra options are normally not needed. Also added the --advanced-options option, which displays the contents of the old --help option. * Fix --help a bit 0.9.36 -> 0.9.37: * Print warning in case nice value in /etc/security/limits.conf is set to -10 instead of -20. * Added support for mp3 files. Requires liblame. Not very well tested yet. Note that jack_capture also compiles without liblame installed. (should be practical for package distributors who can not make jack_capture depend on liblame). There is no runtime linking to liblame yet though, so mp3 files are not supported unless libmp3lame.so + lame/lame.h is installed when jack_capture is compiled. * Check whether sndfile supports ogg during compilation. (Avoid having to edit the Makefile when using an older version of sndfile) * Better screen clean-up at shutdown. (Not working very well yet, maybe ncurses should be used instead of manually sending escape sequences?) 0.9.35 -> 0.9.36: * Add support for OGG (requires sndlib>=1.0.18) * Check if file format is supported by sndlib before creating file * Added auto-support for WVE, MPC2K and RF64. (untested) 0.9.34 -> 0.9.35: * Reset terminal colors when exiting. * Check dependencies for various programs in the Makefile * Tried to make it even more clear (if that's possible) that 'jack_capture --port system:playback_1 --port system:playback_2' does exactly the same as the default. 0.9.33 -> 0.9.34: * Added untested patch from Orcan Ogetbil to make jack_capture compile on a ppc64 platform. In case jack_capture is ran on a ppc64 platform, a warning is printed during runtime. 0.9.32 -> 0.9.33: *Clearing up licenses *A fix for open() from Florian Faber. *A couple of gui fixes from Orcan Ogetbil. 0.9.31 -> 0.9.32: *Changed default format for from wav to wavex for files with more than two channels. Thanks to Fons Adriaensen for the suggestion. *Added fix for 64 bit mode. Thanks to Andras Simon for helping to find the bug and Dominique Fober for fixing the bug. *Added the "-verbose" option for easier debugging in case program crashes or freezes. *Check if sndfile.h exists before generating setformat.c. 0.9.30 -> 0.9.31: *Cleaned up exit handling and connection thread startup. When recording is stopped, the "Please wait, writing to disk" message is visible, and the console is not removed until everything is written to disk. 0.9.29 -> 0.9.30: *Added Hermann Meyer's jack_capture_gui2 program. jack_capture_gui2 is a nice graphical frontend for for jack_capture with lots of options. Many thanks to Herman for the contribution. 0.9.28 -> 0.9.29: *Don't exit in case port is not found. *Print runtime warning and error messages on top of the console to avoid printing the console meter yet another time. (it's much prettier also) 0.9.27 -> 0.9.28: *Fixed a bug that could cause (and especially after the switch from calloc to my_calloc apparantely) segfault when specifying --port more than once. Thanks to Peder Hedlund for spotting the bug. *Print error instead of segfaulting when a specified jack port does not exist. *Removed -g option and changed -O0 to -O2. (Oops, don't know how long that's been there) 0.9.26 -> 0.9.27: *Make sure the stop semaphore is initialized before it might be called. 0.9.25-beta -> 0.9.26: *Changed the --recording-time / -d option to record exactly the correct number of frames. (The format for the option is still in seconds though). This fixes the problem where the wall clock and the soundcard clock drifts apart. *Always increase the buffer size with 2 seconds when more than than half the buffer is used, unless maximum buffer size is reached. *Added the --maxbufsize / -MB option which sets maximum buffer size. Default value is 40 seconds. *Decreased the default buffer size from 20 to 10 seconds. 0.9.24-beta -> 0.9.25-beta: *Do not use this release! *Cleaned up lots of stuff and added support for blocksize change again. *Lots of internal changes: Don't use this release for important recordings! 0.9.23 -> 0.9.24-beta: *Do not use this release! *Changed internal data representation from lockless ringbuffer to lockless lifo and fifo stacks. Unmodified lifo/fifo code taken from midishare. (Copyright Grame 1999-2005) *The code is much prettier now because of the lifo/fifo thing, but I haven't looked too much at it yet, so this version might very well contain serious bugs which could destroy your recording. 0.9.22 -> 0.9.23: *Minor spellings *Check for out of memory 0.9.22 -> 0.9.22: *Clean up source a bit *Stop connection thread before closing jack client. *Made --help a tiny bit cleaner 0.9.20 -> 0.9.21: *Removed shut down code from the SIGINT signal handler. 0.9.19 -> 0.9.20: *Fixed segfault in case jack shuts down. Thanks to Julien Claassen for reporting the bug. 0.9.18 -> 0.9.19: *Do not accept filename starting with "-" when filename is last argument. *Added the "--filename"/"-fn" option to set filename not as the last argument. Can also contain "-" as first character. *Fixed colors a bit and removed a superfluous linebreak. *Fixed segfault for missing values in commandline. *Manually clear allocated memory instead of using calloc. *Removed buffer-info line when recording to stdout. 0.9.17 -> 0.9.18: *Removed printing of "continue recording" when writing the last overruns. *Niceify the disk writing thread to -20 when more than half the buffer is used. *Continously show buffer usage, total number of overruns and whether the disk thread has high priority, in the console. Turn off by using the -hdu option. *Added option -dc to disable console update. (same as "-dm -hdu") *Fancier colors. *Removed "hepp". *Made dB meter the default and replaced the -dB option with the -lm option. *Made the console meter wider to fit the info line at the bottom. 0.9.16 -> 0.9.17: *Made sure the process thread won't continue sending data when jack_capture is told to quit. This lead to race conditions when recording too many channels (256 channels on my machine). *Added the jack_capture_gui script, based on code by Svend-Erik Kjær Madsen. 0.9.15 -> 0.9.16: *Replaced sh by bash to make it work in ubuntu. 0.9.14 -> 0.9.15: *Fixed exact port name match and gen_setformat. Thanks to Gabriel J.L. Beckers for reporting the bugs. 0.9.13 -> 0.9.14: *Increased the time the console meter is displayed in red from 50ms to 2000ms. (when the recorded jack value is equal to or higher than 1.0.) *Added the -dBr argument to specify reference level when using the console dB meter. *Added the -mr argument to specify reference level when using the meterbridge meter. 0.9.12 -> 0.9.13: *Added peak indicators to the console meter. Code to do so taken from meterbridge by Steve Harris. *Updated --help and README with the new options. 0.9.11 -> 0.9.12: *Added option "-mt" to change meterbridge type. Current valid options are vu (default), ppm, dpm, jf or sco. It's not necesarry to specify "-mb" if using "-mt". *Added option "-dB" to get a dB meter for the console meter. 0.9.10 -> 0.9.11: *Added examples how to record ogg and mp3 files using the -ws option. *Decreased default buffersize from 60 to 20 seconds. (I even had trouble provocing underruns using a minimal 0.05 seconds long buffer, so 60 seconds was obviously overkill) *Fixed message and error printing to stderr when vu meter is running. 0.9.9 -> 0.9.10: *Fixed segfault in case the --channels / -c argument is higher than the number of ports which is possible to connect to. *Added a terminal vu meter by default. It'll display red in case the recorded jack value is equal to or higher than 1.0. Use "-dv" to disable. *Added the --meterbridge / -mb option, which automatically starts Steve Harris' meterbridge (http://plugin.org.uk/meterbridge/) and constantly connects them to the same ports as jack_capture is connected to. 0.9.8 -> 0.9.9: *Added the --write-to-stdout / -ws option which writes 16 bit little endian sound to stdout. Code made by looking at jack-stdout.c by Robin Gareus. 0.9.7 -> 0.9.8: *Added the --print-formats / -pf option. *Replaced jack_client_new() with jack_client_open() 0.9.6 -> 0.9.7: *Don't segfault if the opening of a subsequent wav soundfile fails. (ie. if the 4GB limitation is reached on the old file and jack_capture continues writing on a new file.) 0.9.5 -> 0.9.6: *Fixed so that 2 channels are the default again, as supposed to. (bug was introduced in 0.9.4) 0.9.4 -> 0.9.5: *Fixed a bug that in some situations can cause segfault when jack buffer size is changed while recording. 0.9.3 -> 0.9.4: *Fixed bug that caused max 2 channels to be recorded. 0.9.2 -> 0.9.3: *Fixed horrible deadlock. Bug found by Ken Restivo. 0.9.1 -> 0.9.2: *Fix for a potensional deadlock. *Added the --silent/-s argument. *Some smaller fixes. 0.9.0 -> 0.9.1: *If recording to wav (the default) and the the 4GB limitation is reached, automatically close the file and continue writing to a new file with an autogenerated name. *Added the --version/-v argument. *Changed default number of zeros in the autogenerated filename to 1. 0.3.9 -> 0.9.0: *Better error output. *Autogenerate code to check if various formats are supported by sndlibfile. *Bumped version up to 0.9. jack_capture should reach a 1.0 state quite soon. *Changed the name of --recording-time to --duration to match -d. 0.3.8 -> 0.3.9: *Changed the -rt option name to -d, to be compatible with jackrec. *Do not stop recording in case of disk errors. *Got rid of deprecated libsndfile functions. *Added the --format/-f option. ("jack_capture -f flac", nice :-) ) (All sndfile formats are supported: http://www.mega-nerd.com/libsndfile/api.html#open) 0.3.7 -> 0.3.8: *Added the --recording-time option to stop recording after a certain number of seconds. *Quitting with CTRL-C/SIGINT writes remaining buffer to disk before ending program. 0.3.6 -> 0.3.7: *Fixed potentional buffer underrun error. 0.3.5 -> 0.3.6: *Fixed potentional ringbuffer size allocation miscalculation. *Better way to set leading zeros in filename. Thanks to Melanie. *Better underrun handling. Thanks to Dmitry Baikov. 0.3.4 -> 0.3.5: *Added support for jack buffer size change. *Removed some unnecessary code and comments *Beautified code a bit. 0.3.3 -> 0.3.4: *Fixed a bug in the reconnection code. *Beautified code a lot. 0.3.2 -> 0.3.3: *Changed bufsize argument to accept seconds instead of frames. Default buffer size is 60 seconds. *Improved documentation and help option. *Beautified source a bit. *Fixed bug in ringbuffer size allocation. 0.3.1 -> 0.3.2: *Fixed so that more than one instance of jack_capture can run at once. 0.3.0 -> 0.3.1: *Added the --port argument, which can be specified many times and accepts both input and output port names (including regexp expressions). This makes jack_capture to completely replace jackrec. 0.2.6 -> 0.3.0: *Rewrote buffer handling. Silence is now inserted when underruns occure. Previously, the file became shorter than the recording in case of underrun. It can still happen though, but much more seldom, and a warning about that will be printed to the terminal. *Removed last rests of jackrec code. Well, almost, at least. *Niceified code a lot. 0.2.5 -> 0.2.6: *Reduced CPU usage a lot because of better disk handling. (25% -> 1%) *Make sure the rest of the recorded file is not garbage in case of an overrun. *More efficient way of handling overruns. 0.2.4 -> 0.2.5: *Fixed really stupid compilation error. Thanks to Dragan Noveski for spotting it. 0.2.3 -> 0.2.4: *Give message to stderr during recording (not only after) if any overruns occur. *Do not delete file after recording if any overruns have occured. (stupid jackreq code #$!@$) *Increased default buffer size from 0.5M to 2M. 0.2.2 -> 0.2.3: *Added -z argument that choose number of leading zeros. *Various changes. 0.2.1 -> 0.2.2: *Only connect/disconnect ports if it's necessary. This puts less stress on the jack system and possibly avoids never ending connect/reconnecting loops. *Replaced the timemachine-way of automatically setting filename by a much simpler one. Now the autogenerated filename is just jack_capture_.wav: jack_capture_1.wav, jack_capture_2.wav, jack_capture_3.wav and so on. *If jack is shut down during recording, stop the recording properly. *Various smaller changes. 0.2.0 -> 0.2.1: *Removed optional -f argument (that didn't work anyway) for setting the filename. Usage for program is now: "jack_capture [ -b bitdepth ] [-c channels] [ -B bufsize ] [filename]" Thanks to Cesare Marilungo for pointing out the bug. *Sound filename is also printed to the screen. 0.1.1 -> 0.2.0: *Automatically disconnect and connect ports while program is running. Previously, the connections where only set up when the program started. *Added make install. *Various smaller changes. jack_capture-0.9.73/SanitizerSuppr.txt000066400000000000000000000011201310754750000200040ustar00rootroot00000000000000leak:OS_getmem leak:talloc_realloc__ leak:PR_init_plugin_types # allow reading and writing floats from various threads simultaneously: race:safe_float_write race:safe_volatile_float_write race:safe_volatile_float_read race:safe_float_read race:safe_double_write race:safe_double_read race:safe_pointer_read race:safe_int_write race:safe_int_read race:Compressor_wrapper::get_graph_value race:Compressor_wrapper::set_parameter race:QCoreApplication::postEvent race:RT_call_instead_of_process # Calling 3rd party code: race:call_ladspa_get_descriptor_func #interceptor_via_lib:fglrx_dri.so jack_capture-0.9.73/atomic.h000066400000000000000000000274001310754750000156770ustar00rootroot00000000000000 #ifndef RADIUM_COMMON_ATOMIC_H #define RADIUM_COMMON_ATOMIC_H #include #include #define ATOMIC_NAME(name) \ name##_atomic #define DEFINE_ATOMIC(type, name) \ type ATOMIC_NAME(name) #define ATOMIC_SET(name, val) \ __atomic_store_n (&(ATOMIC_NAME(name)), (val), __ATOMIC_SEQ_CST) #define ATOMIC_SET_RELAXED(name, val) \ __atomic_store_n (&(ATOMIC_NAME(name)), (val), __ATOMIC_RELAXED) #define ATOMIC_GET(name) \ __atomic_load_n (&(ATOMIC_NAME(name)), __ATOMIC_SEQ_CST) /* #define ATOMIC_GET2(name) \ __atomic_load_n (&(name), __ATOMIC_SEQ_CST) */ #define ATOMIC_GET_ARRAY(name,pos) \ __atomic_load_n (&(ATOMIC_NAME(name)[pos]), __ATOMIC_SEQ_CST) #define ATOMIC_GET_RELAXED(name) \ __atomic_load_n (&(ATOMIC_NAME(name)), __ATOMIC_RELAXED) #define ATOMIC_SET_ARRAY(name, pos, val) \ __atomic_store_n (&(ATOMIC_NAME(name)[pos]), (val), __ATOMIC_SEQ_CST) /* __atomic_compare_exchange_n(type *ptr, type *expected, type desired, bool weak, int success_memorder, int failure_memorder ); works like this: if (ptr==expected) { ptr = desired; return true; } else { expected = ptr; return false } */ /* atomic_compare_and_set_bool(bool *variable, bool old_value, bool new_value ); works like this: if (variable==old_value) { variable = new_value; return true; } else { return false; } */ static inline bool atomic_compare_and_set_bool(bool *variable, bool old_value, bool new_value){ return __atomic_compare_exchange_n (variable, &old_value, new_value, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); } static inline bool atomic_compare_and_set_int(int *variable, int old_value, int new_value){ return __atomic_compare_exchange_n (variable, &old_value, new_value, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); } static inline bool atomic_compare_and_set_uint32(uint32_t *variable, uint32_t old_value, uint32_t new_value){ return __atomic_compare_exchange_n (variable, &old_value, new_value, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); } static inline bool atomic_compare_and_set_pointer(void **variable, void *old_value, void *new_value){ return __atomic_compare_exchange_n (variable, &old_value, new_value, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); } #define ATOMIC_COMPARE_AND_SET_BOOL(name, old_value, new_value) \ atomic_compare_and_set_bool(&ATOMIC_NAME(name), old_value, new_value) #define ATOMIC_COMPARE_AND_SET_INT(name, old_value, new_value) \ atomic_compare_and_set_int(&ATOMIC_NAME(name), old_value, new_value) #define ATOMIC_COMPARE_AND_SET_UINT32(name, old_value, new_value) \ atomic_compare_and_set_uint32(&ATOMIC_NAME(name), old_value, new_value) #define ATOMIC_COMPARE_AND_SET_POINTER(name, old_value, new_value) \ atomic_compare_and_set_pointer(&ATOMIC_NAME(name), old_value, new_value) #define ATOMIC_COMPARE_AND_SET_POINTER_ARRAY(name, pos, old_value, new_value) \ atomic_compare_and_set_pointer(&ATOMIC_NAME(name)[pos], old_value, new_value) #define ATOMIC_SET_RETURN_OLD(name, val) \ __atomic_exchange_n (&ATOMIC_NAME(name), val, __ATOMIC_SEQ_CST) #define ATOMIC_ADD_RETURN_OLD(name, how_much) \ __atomic_fetch_add (&ATOMIC_NAME(name), how_much, __ATOMIC_SEQ_CST) #define ATOMIC_ADD(name, how_much) ATOMIC_ADD_RETURN_OLD(name, how_much) /* #define ATOMIC_ADD_RETURN_OLD2(name, how_much) \ __atomic_fetch_add (&(name), how_much, __ATOMIC_SEQ_CST) #define ATOMIC_ADD2(name, how_much) ATOMIC_ADD_RETURN_OLD2(name, how_much) */ // doesn't work with bool! #define ATOMIC_ADD_RETURN_NEW(name, how_much) \ (__atomic_fetch_add (&ATOMIC_NAME(name), how_much, __ATOMIC_SEQ_CST) + how_much) #define DEFINE_SPINLOCK_NOINIT(name) \ DEFINE_ATOMIC(bool, name) #define INIT_SPINLOCK(name) \ ATOMIC_SET(name, false) #define DEFINE_SPINLOCK(name) \ DEFINE_SPINLOCK_NOINIT(name) = false #define SPINLOCK_OBTAIN(name) \ while(atomic_compare_and_set_bool(&ATOMIC_NAME(name), false, true)==false) #define SPINLOCK_RELEASE(name) \ ATOMIC_SET(name, false) #define SPINLOCK_IS_OBTAINED(name) \ ATOMIC_GET(spinlock)==true /************** float ******************/ // These functions are suppressed from tsan static inline void safe_float_write(float *pos, float value){ *pos = value; } static inline void safe_volatile_float_write(volatile float *pos, float value){ *pos = value; } static inline float safe_volatile_float_read(volatile const float *pos){ return *pos; } static inline float safe_float_read(const float *pos){ return *pos; } static inline void safe_int_write(int *pos, int value){ *pos = value; } static inline int safe_int_read(const int *pos){ return *pos; } //#define ATOMIC_RELAXED_WRITE(var, value) __atomic_store_n (&var,value, __ATOMIC_RELAXED) // careful. Probably never any point using. //#define ATOMIC_RELAXED_READ(var) __atomic_load_n (&var, __ATOMIC_RELAXED) // careful. Probably never any point using. #define ATOMIC_WRITE(var, value) __atomic_store_n (&var,value, __ATOMIC_SEQ_CST) #define ATOMIC_INC(var, how_much) __atomic_fetch_add (&var, how_much, __ATOMIC_SEQ_CST) #define ATOMIC_READ(var) __atomic_load_n (&var, __ATOMIC_SEQ_CST) /************** pointers ******************/ #if 0 static inline void *safe_pointer_read(void **p){ return __atomic_load_n(p, __ATOMIC_RELAXED); } #endif static inline void *atomic_pointer_read(void **p){ return __atomic_load_n(p, __ATOMIC_SEQ_CST); } static inline void atomic_pointer_write(void **p, void *v){ __atomic_store_n(p, v, __ATOMIC_SEQ_CST); } /************** doubles ******************/ // Is this really working? (Think I spent a long time investigating it, and found out that it was. Double is just 8 bytes, so it shouldn't be different to int64_t) /* double dasdouble; void set_das_double(void){ double new_value_variable = 5.0; __atomic_store(&dasdouble, &new_value_variable, __ATOMIC_SEQ_CST); } => 64 bit ====== set_das_double: .LFB0: .cfi_startproc movabsq $4617315517961601024, %rax movq %rax, dasdouble(%rip) mfence ret .cfi_endproc 32 bit ====== set_das_double: .LFB0: .cfi_startproc subl $12, %esp .cfi_def_cfa_offset 16 xorl %eax, %eax movl $1075052544, %edx movl %eax, (%esp) movl %edx, 4(%esp) movq (%esp), %xmm0 movq %xmm0, dasdouble mfence addl $12, %esp .cfi_def_cfa_offset 4 ret .cfi_endproc ---------------------------------------------------------- double get_das_double(void){ double result; __atomic_load (&dasdouble, &result, __ATOMIC_SEQ_CST); return result; } => 64 bit ====== get_das_double: .LFB1: .cfi_startproc movq dasdouble(%rip), %rax movq %rax, -8(%rsp) movsd -8(%rsp), %xmm0 ret .cfi_endproc 32 bit ====== get_das_double: .LFB1: .cfi_startproc subl $20, %esp .cfi_def_cfa_offset 24 movq dasdouble, %xmm0 movsd %xmm0, (%esp) fldl (%esp) addl $20, %esp .cfi_def_cfa_offset 4 ret .cfi_endproc */ typedef double atomic_double_t; #define ATOMIC_DOUBLE_GET(name) ({ \ double result; \ __atomic_load (&ATOMIC_NAME(name), &result, __ATOMIC_SEQ_CST); \ result; \ }) #define ATOMIC_DOUBLE_SET(name,new_value) ({ \ double new_value_variable = new_value; \ __atomic_store (&ATOMIC_NAME(name), &new_value_variable, __ATOMIC_SEQ_CST); \ }) /* // redhat gcc 5.3.1: "warning: parameter ‘atomic_double’ set but not used [-Wunused-but-set-parameter]" static inline double atomic_double_read(const atomic_double_t *atomic_double){ double result; __atomic_load(atomic_double, &result, __ATOMIC_SEQ_CST); return result; } // redhat gcc 5.3.1: "warning: parameter ‘atomic_double’ set but not used [-Wunused-but-set-parameter]" static inline void atomic_double_write(atomic_double_t *atomic_double, double new_value){ __atomic_store(atomic_double, &new_value, __ATOMIC_SEQ_CST); } */ #ifdef __cplusplus namespace radium{ // Can be used if one thread set a set of variables, while another thread read the set of variables // The writing thread will not block, while the reading thread might block. // Note: I'm not 100% sure the code is correct, but it probably protects more than if it had not been used. // Class should not be used if it is extremely important that it works correctly. // class SetSeveralAtomicVariables{ DEFINE_ATOMIC(int, generation); DEFINE_ATOMIC(bool, is_writing); public: SetSeveralAtomicVariables(){ ATOMIC_SET(generation, 0); ATOMIC_SET(is_writing, false); } void write_start(void){ ATOMIC_ADD(generation, 1); ATOMIC_SET(is_writing, true); ATOMIC_ADD(generation, 1); } void write_end(void){ ATOMIC_ADD(generation, 1); ATOMIC_SET(is_writing, false); ATOMIC_ADD(generation, 1); } int read_start(void){ while(ATOMIC_GET(is_writing)==true); return ATOMIC_GET(generation); } bool read_end(int read_start_generation){ while(ATOMIC_GET(is_writing)==true); if (ATOMIC_GET(generation) == read_start_generation) return true; else return false; } }; // Class to store a pointer. // The main thread can set, replace and free the pointer at any time. // A realtime thread can access the pointer at any time by using the ScopedUsage class. // class AtomicPointerStorage{ friend class RT_AtomicPointerStorage_ScopedUsage; private: DEFINE_ATOMIC(void *, _pointer) = NULL; DEFINE_ATOMIC(void *, _old_pointer_to_be_freed) = NULL; void (*_free_pointer_function)(void *); void maybe_free_something(void *a, void *b){ if (_free_pointer_function != NULL){ if (a!=NULL) _free_pointer_function(a); if (b!=NULL) _free_pointer_function(b); } } public: AtomicPointerStorage(void (*free_pointer_function)(void *)) : _free_pointer_function(free_pointer_function) { } ~AtomicPointerStorage(){ maybe_free_something(ATOMIC_GET(_pointer), ATOMIC_GET(_old_pointer_to_be_freed)); } // May be called at any time. 'free_pointer_function' may be called 0, 1, or 2 times. (usually 1 time) void set_new_pointer(void *new_pointer){ void *old_pointer_to_be_freed = ATOMIC_SET_RETURN_OLD(_old_pointer_to_be_freed, NULL); void *old = ATOMIC_SET_RETURN_OLD(_pointer, new_pointer); //printf("Has set. new: %p, old: %p, curr: %p\n", new_pointer, old, ATOMIC_GET(_pointer)); maybe_free_something(old, old_pointer_to_be_freed); } }; // Create an instance of this class to access pointer from a realtime thread. // I don't think it works to create more than one instance of this at the same time. class RT_AtomicPointerStorage_ScopedUsage{ AtomicPointerStorage *_storage; void *_pointer; public: void *get_pointer(void){ return _pointer; } RT_AtomicPointerStorage_ScopedUsage(AtomicPointerStorage *storage) :_storage(storage) { _pointer = ATOMIC_SET_RETURN_OLD(storage->_pointer, NULL); } ~RT_AtomicPointerStorage_ScopedUsage(){ if (ATOMIC_COMPARE_AND_SET_POINTER(_storage->_pointer, NULL, _pointer)) { return; } else { #if !defined(RELEASE) void *old_pointer = ATOMIC_GET(_storage->_old_pointer_to_be_freed); if (old_pointer != NULL && old_pointer!=_pointer) abort(); #endif ATOMIC_SET(_storage->_old_pointer_to_be_freed, _pointer); } } }; } #endif #endif jack_capture-0.9.73/config000066400000000000000000000017541310754750000154460ustar00rootroot00000000000000# Config file for jack_capture. # Only long-form options work (i.e. those starting with --). # Options given here can in many cases be overriden with command line argument to jack_capture. # Copy the file here: ~/.jack_capture/config # A line can be one of: # 1. = # 2. # 3. # (a comment) # Variables and switches: # # recording-time = n # filename-prefix = s # leading-zeros = n # format = format # print-formats # version # silent # quiet # verbose # mp3 # mp3-quality = n # mp3-bitrate = n # write-to-stdout # disable-meter # hide-buffer-usage # disable-console # no-stdin # daemon # linear-meter # dB-meter-reference = n # meterbridge # meterbridge-type type # meterbridge-reference = level # jack-transport # jack-transport-multi # jack-frewheeling # manual-connections # bufsize = s # maxbufsize = s # filename = filename # osc = port # timestamp # rotatefile frames # hook-open command # hook-close command # hook-rotate command # hook-timing command jack_capture-0.9.73/gen_das_config_h.sh000066400000000000000000000033761310754750000200500ustar00rootroot00000000000000#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. echo "#include " >temp$$.c echo "main(){return SF_FORMAT_OGG;}" >>temp$$.c echo >>temp$$.c if gcc temp$$.c 2>/dev/null; then echo "#define HAVE_OGG 1" else echo "#define HAVE_OGG 0" fi echo "#include " >temp$$.c echo "main(){return 0;}" >>temp$$.c echo >>temp$$.c if gcc temp$$.c -lmp3lame 2>/dev/null; then echo "#define HAVE_LAME 1" echo "//COMPILEFLAGS -lmp3lame" else echo "#define HAVE_LAME 0" fi echo "#include " >temp$$.c echo "main(){return 0;}" >>temp$$.c echo >>temp$$.c if pkg-config --cflags --libs liblo >/dev/null 2>/dev/null && gcc temp$$.c `pkg-config --cflags --libs liblo` 2>/dev/null ; then echo "#define HAVE_LIBLO 1" echo "//COMPILEFLAGS " `pkg-config --cflags --libs liblo` else echo "#define HAVE_LIBLO 0" fi echo "#include " >temp$$.c echo "main(){return (int)jack_port_get_latency_range;}" >>temp$$.c echo >>temp$$.c if gcc temp$$.c -ljack 2>/dev/null ; then echo "#define NEW_JACK_LATENCY_API 1" else echo "#define NEW_JACK_LATENCY_API 0" fi rm temp$$.c jack_capture-0.9.73/gen_setformat_c.sh000066400000000000000000000030661310754750000177470ustar00rootroot00000000000000#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. ai=" WAV AIFF AU RAW PAF SVX NIST VOC IRCAM W64 MAT4 MAT5 PVF XI HTK SDS AVR WAVEX SD2 FLAC CAF WVE OGG MPC2K RF64 MP3 MP2 SPEEX WMA AAC VQF RA ALAC AIFC " echo "static int setformat_base(char *soundfile_format){" echo " return(" for a in $ai;do echo "#include " >temp.c echo "main(){return SF_FORMAT_"$a";}" >>temp.c echo >>temp.c if gcc temp.c 2>/dev/null; then echo " (!strcasecmp(\""$a"\",soundfile_format)) ? SF_FORMAT_"$a":" fi done echo " -1);" echo "}" echo echo "int getformat(char *soundfile_format){" echo " return setformat_base(soundfile_format);" echo "}" echo echo "void print_all_formats(void){" #echo " printf(\"Supported formats: \\n\");" echo for a in $ai;do echo " if(setformat_base(\""$a"\")!=-1)" echo " printf(\""`echo $a|tr '[:upper:]' '[:lower:]'`" \");" done echo ' printf("\n");' echo "}" jack_capture-0.9.73/jack_capture.c000066400000000000000000002365351310754750000170640ustar00rootroot00000000000000 /* Kjetil Matheussen, 2005-2013. 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 "das_config.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #else #include #endif #include #include #include #ifndef __APPLE__ #include #endif #include #include #include #include #include #include #include #include #if HAVE_LAME #include #endif #if HAVE_LIBLO #include /* defined in osc.c */ int init_osc(int osc_port); void shutdown_osc(void); #endif #include "atomic.h" #include "vringbuffer.h" #define JC_MAX(a,b) (((a)>(b))?(a):(b)) #define JC_MIN(a,b) (((a)<(b))?(a):(b)) #define ALIGN_UP(value,alignment) (((uintptr_t)value + alignment - 1) & -alignment) #define ALIGN_UP_DOUBLE(p) ALIGN_UP(p,sizeof(double)) // Using double because double should always be very large. #define OPTARGS_CHECK_GET(wrong,right) lokke==argc-1?(fprintf(stderr,"Must supply argument for '%s'\n",argv[lokke]),exit(-4),wrong):right #define OPTARGS_BEGIN(das_usage) {int lokke;const char *usage=das_usage;for(lokke=0;lokke0){ int ch; cportnames=realloc(cportnames,(num_cportnames+add_ch)*sizeof(char*)); for(ch=0;ch=num_cportnames) return NULL; else{ jack_port_t *port = jack_port_by_name(client,cportnames[ch]); const char **ret; if(port==NULL){ print_message("Error, port with name \"%s\" not found.\n",cportnames[ch]); return NULL; } if(jack_port_flags(port) & JackPortIsInput){ ret = jack_port_get_all_connections(client,port); *using_calloc = false; }else{ ret = my_calloc(2,sizeof(char*)); ret[0] = cportnames[ch]; *using_calloc = true; } return ret; } } ///////////////////////////////////////////////////////////////////// //////////////////////// console meter ////////////////////////////// ///////////////////////////////////////////////////////////////////// // Note that the name "vu" is used instead of "console meter". // I know (now) it's not a vu at all. :-) // Function iec_scale picked from meterbridge by Steve Harris. static int iec_scale(float db) { float def = 0.0f; /* Meter deflection %age */ if (db < -70.0f) { def = 0.0f; } else if (db < -60.0f) { def = (db + 70.0f) * 0.25f; } else if (db < -50.0f) { def = (db + 60.0f) * 0.5f + 5.0f; } else if (db < -40.0f) { def = (db + 50.0f) * 0.75f + 7.5; } else if (db < -30.0f) { def = (db + 40.0f) * 1.5f + 15.0f; } else if (db < -20.0f) { def = (db + 30.0f) * 2.0f + 30.0f; } else if (db < 0.0f) { def = (db + 20.0f) * 2.5f + 50.0f; } else { def = 100.0f; } return (int)(def * 2.0f); } static void msleep(int n){ usleep(n*1000); } static void print_ln(void){ putchar('\n'); //msleep(3); } static void print_console_top(void){ if(use_vu){ int lokke=0; char c='"'; // Set cyan color printf("%c[36m",0x1b); //printf("****"); printf(" |"); for(lokke=0;lokke vu_peaks[ch]) { vu_peaks[ch] = pos; vu_peakvals[ch] = val; vu_times[ch] = 0; } else if (vu_times[ch]++ > 40) { vu_peaks[ch] = pos; vu_peakvals[ch] = val; } if(ch>9){ vol[0] = '0'+ch/10; vol[1] = '0'+ch-(10*(ch/10)); }else{ vol[0] = '0'; vol[1] = '0'+ch; } if (timemachine_mode==true && timemachine_recording==false) { for(i=0;i0.0f;i++) vol[4+i] = vu_not_recording[i]; vol[4+pos]='\0'; if(vu_peakvals[ch]>=1.0f) printf("%c[31m",0x1b); // Peaking, show red color printf("%s", vol); for(;i0.0f) vol[4+i] = '*'; else if(i<=pos && val>0.0f) vol[4+i] = '-'; else vol[4+i] = ' '; if(vu_peakvals[ch]>=1.0f){ vol[4+vu_len]='!'; printf("%c[31m",0x1b); //red color puts(vol); printf("%c[36m",0x1b); // back to cyan }else{ vol[4+vu_len]='|'; puts(vol); } } } } if(show_bufferusage){ int num_bufleft = vringbuffer_writing_size(vringbuffer); int num_buffers = (vringbuffer_reading_size(vringbuffer)+ vringbuffer_writing_size(vringbuffer)); float buflen = buffers_to_seconds(num_buffers); float bufleft = buffers_to_seconds(num_bufleft); int recorded_seconds = (int)frames_to_seconds(ATOMIC_GET(num_frames_recorded)); if(timemachine_mode==true) recorded_seconds = (int)frames_to_seconds(num_frames_written_to_disk); int recorded_minutes = recorded_seconds/60; char buffer_string[1000]; { sprintf(buffer_string,"%.2fs./%.2fs",bufleft,buflen); int len_buffer=strlen(buffer_string); int i; for(i=len_buffer;i<14;i++) buffer_string[i]=' '; buffer_string[i]='\0'; } printf("%c[32m" "Buffer: %s" " Time: %d.%s%dm. %s" "DHP: [%c] " "Overruns: %d " "Xruns: %d" "%c[0m", //fmaxf(0.0f,buflen-bufleft),buflen, 0x1b, // green color buffer_string, recorded_minutes, recorded_seconds%60<10?"0":"", recorded_seconds%60, recorded_minutes<10?" ":"", disk_thread_has_high_priority?'x':' ', total_overruns, total_xruns, 0x1b // reset color ); print_ln(); }else{ printf("%c[0m",0x1b); // reset colors fprintf(stderr,"%c[0m",0x1b); // reset colors } fflush(stdout); fflush(stderr); } ///////////////////////////////////////////////////////////////////// //////////////////////// Helper thread ////////////////////////////// ///////////////////////////////////////////////////////////////////// #define MESSAGE_PREFIX ">>> " static char message_string[5000]={0}; static volatile int helper_thread_running=0; static int init_meterbridge_ports(); static bool is_helper_running=true; static void *helper_thread_func(void *arg){ (void)arg; helper_thread_running=1; if(use_vu||show_bufferusage) print_console_top(); if(use_vu) init_vu(); if(show_bufferusage) init_show_bufferusage(); do{ bool move_cursor_to_top_doit=true; if(message_string[0]!=0){ if(use_vu || show_bufferusage){ move_cursor_to_top(); if(!use_vu){ print_ln(); } printf("%c[%dA",0x1b,1); // move up yet another line. msleep(5); printf("%c[31m",0x1b); // set red color { // clear line int lokke; for(lokke=0;lokke= size) { char *tmpbuff; /* Reallocate buffer now that we know how much space is needed. */ size = nchars+1; tmpbuff = (char*)realloc(*buffer, size); if (tmpbuff == NULL) { /* we need to free it*/ free(*buffer); return -1; } *buffer=tmpbuff; /* Try again. */ va_start(ap, fmt); nchars = vsnprintf(*buffer, size, fmt, ap); va_end(ap); } if (nchars < 0) return nchars; return size; } #endif #endif #define ARGS_ADD_ARGV(FMT,ARG) \ argv=(char**) realloc((void*)argv, (argc+2)*sizeof(char*)); \ asprintf(&argv[argc++], FMT, ARG); argv[argc] = 0; #define PREPARE_ARGV(CMD,ARGC,ARGV) \ {\ char *bntmp = strdup(CMD); \ ARGC=0; \ ARGV=(char**) calloc(2,sizeof(char*)); \ ARGV[ARGC++] = strdup(basename(bntmp));\ free(bntmp); \ } static void wait_child(int sig){ (void)sig; wait(NULL); } static void call_hook(const char *cmd, int argc, char **argv){ /* invoke external command */ if (verbose==true) { fprintf(stderr, "EXE: %s ", cmd); for (argc=0;argv[argc];++argc) printf("'%s' ", argv[argc]); printf("\n"); } pid_t pid=fork(); if (pid==0) { /* child process */ if(1){ /* redirect all output of child process*/ /* one day this if(1) could become a global option */ int fd; if((fd = open("/dev/null", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR))==-1){ perror("open"); }else{ dup2(fd,STDOUT_FILENO); dup2(fd,STDERR_FILENO); close(fd); } } execvp (cmd, (char *const *) argv); print_message("EXE: error; exec returned.\n"); //pause(); exit(127); } /* parent/main process */ if (pid < 0 ) { print_message("EXE: error; can not fork child process\n"); } signal(SIGCHLD,wait_child); /* clean up */ for (argc=0;argv[argc];++argc) { free(argv[argc]); } free (argv); } static void hook_file_opened(char *fn){ char **argv; int argc; const char *cmd = hook_cmd_opened; if (!cmd) return; PREPARE_ARGV(cmd, argc, argv); ARGS_ADD_ARGV("%s", fn); call_hook(cmd, argc, argv); } static void hook_rec_timimg(char *fn, struct timespec start, jack_nframes_t latency){ char **argv; int argc; const char *cmd = hook_cmd_timing; if (!cmd) return; PREPARE_ARGV(cmd, argc, argv); ARGS_ADD_ARGV("%s", fn); ARGS_ADD_ARGV("%ld", start.tv_sec); ARGS_ADD_ARGV("%ld", start.tv_nsec); ARGS_ADD_ARGV("%d", latency); call_hook(cmd, argc, argv); } static void hook_file_closed(char *fn, int xruns, int io_errors){ char **argv; int argc; const char *cmd = hook_cmd_closed; if (!cmd) return; PREPARE_ARGV(cmd, argc, argv); ARGS_ADD_ARGV("%s", fn); ARGS_ADD_ARGV("%d", xruns); ARGS_ADD_ARGV("%d", io_errors); call_hook(cmd, argc, argv); } static void hook_file_rotated(char *oldfn, char *newfn, int num, int xruns, int io_errors){ char **argv; int argc; const char *cmd = hook_cmd_rotate; if (!cmd) return; PREPARE_ARGV(cmd, argc, argv); ARGS_ADD_ARGV("%s", oldfn); ARGS_ADD_ARGV("%d", xruns); ARGS_ADD_ARGV("%d", io_errors); ARGS_ADD_ARGV("%s", newfn); ARGS_ADD_ARGV("%d", num); call_hook(cmd, argc, argv); } ///////////////////////////////////////////////////////////////////// //////////////////////// DISK /////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // These four variables are used in case we break the 4GB barriere for standard wav files. static int num_files=1; static int64_t disksize=0; static bool is_using_wav=true; static int bytes_per_frame; static SNDFILE *soundfile=NULL; static int64_t overruns=0; #if HAVE_LIBLO bool queued_file_rotate=false; #ifdef __APPLE__ void osc_stop() { semaphore_signal(stop_sem); } #else void osc_stop() { sem_post(&stop_sem); } #endif void osc_tm_start() { timemachine_recording=true; } void osc_tm_stop() { program_ended_with_return=true; osc_stop(); } #endif static struct timespec rtime; static DEFINE_ATOMIC(int, g_store_sync) = 0; static int ssync_offset = 0; static jack_nframes_t j_latency = 0; #if HAVE_LAME static FILE *mp3file = NULL; static unsigned char *mp3buf; static int mp3bufsize; static int open_mp3file(void){ buffer_interleaved = false; mp3bufsize = buffer_size_in_bytes * 10 * num_channels; if(mp3bufsize<4096*4) // lame_encode_flush requires at least 7200 bytes mp3bufsize=4096*4; mp3buf = malloc(mp3bufsize); lame = lame_init(); if(lame==NULL){ print_message("lame_init failed.\n"); return 0; } lame_set_num_channels(lame, num_channels); lame_set_in_samplerate(lame,(int)jack_samplerate); lame_set_out_samplerate(lame,(int)jack_samplerate); lame_set_quality(lame,das_lame_quality); if(das_lame_bitrate!=-1){ lame_set_brate(lame, das_lame_bitrate); lame_set_VBR_min_bitrate_kbps(lame, lame_get_brate(lame)); } { int ret = lame_init_params(lame); if(ret<0){ print_message("Illegal parameters for lame. (%d)\n",ret); return 0; } } if(lame_get_num_channels(lame)!=num_channels){ print_message("Error. lame does not support %d channel mp3 files.\n",num_channels); return 0; } mp3file = fopen(filename,"w"); if(mp3file == NULL){ print_message("Can not open file \"%s\" for output (%s)\n", filename, strerror(errno)); return 0; } hook_file_opened(filename); return 1; } #endif #include "setformat.c" static int open_soundfile(void){ int subformat; SF_INFO sf_info; memset(&sf_info,0,sizeof(sf_info)); if(write_to_stdout==true) { hook_file_opened("file:///stdout"); return 1; } if(filename==NULL){ if (rotateframe>0){ // always use .NUM. with rotation filename=my_calloc(1,strlen(base_filename)+500); sprintf(filename,"%s.%0*d.%s",base_filename,leading_zeros+1,0,soundfile_format); }else{ filename=strdup(base_filename); } } #if HAVE_LAME if(write_to_mp3==true) return open_mp3file(); #endif ///////////////////////// // Code below for sndfile ///////////////////////// sf_info.samplerate = jack_samplerate; sf_info.channels = num_channels; { int format=getformat(soundfile_format); if(format==-1 && num_channels>2){ fprintf(stderr,"Warning, the format \"%s\" is not supported. Using %s instead.\n",soundfile_format_multi,soundfile_format); sf_info.format=MORE_THAN_TWO_CHANNELS_FORMAT; }else if(format==-1){ fprintf(stderr,"Warning, the format \"%s\" is not supported. Using %s instead.\n",soundfile_format_one_or_two,soundfile_format); sf_info.format=ONE_OR_TWO_CHANNELS_FORMAT; }else sf_info.format=format; } is_using_wav = (sf_info.format==SF_FORMAT_WAV)?true:false; switch (bitdepth) { case 8: subformat = SF_FORMAT_PCM_U8; break; case 16: subformat = SF_FORMAT_PCM_16; break; case 24: subformat = SF_FORMAT_PCM_24; break; case 32: subformat = SF_FORMAT_PCM_32; break; default: if(!strcasecmp("flac",soundfile_format) || !strcasecmp("sds",soundfile_format)){ bitdepth=24; subformat=SF_FORMAT_PCM_24; #if HAVE_OGG }else if(!strcasecmp("ogg",soundfile_format)){ subformat = SF_FORMAT_VORBIS; #endif }else{ bitdepth=32; // sizeof(float)*8 would be incorrect in case sizeof(float)!=4 subformat = SF_FORMAT_FLOAT; } break; } bytes_per_frame=bitdepth/8; sf_info.format |= subformat; if(sf_format_check(&sf_info)==0){ fprintf (stderr, "\nFileformat not supported by libsndfile. Try other options.\n"); return 0; } if(write_to_stdout==true) soundfile=sf_open_fd(fileno(stdout),SFM_WRITE,&sf_info,false); // ??? this code is never reached. else soundfile=sf_open(filename,SFM_WRITE,&sf_info); // debugging lines below. //static int ai=0; //ai++; if(soundfile==NULL){ // || ai==10){ fprintf (stderr, "\nCan not open sndfile \"%s\" for output (%s)\n", filename,sf_strerror(NULL)); return 0; } hook_file_opened(filename); return 1; } #if HAVE_LAME static int mp3_write(void *das_data,size_t frames,bool do_flush); #endif static void close_soundfile(void){ if(write_to_stdout==false){ if(soundfile!=NULL) sf_close (soundfile); #if HAVE_LAME if(mp3file!=NULL){ mp3_write(NULL,0,true); // flush lame_close(lame); fclose(mp3file); } #endif } hook_file_closed(filename, total_overruns + total_xruns, disk_errors); if (overruns > 0) { print_message("jack_capture failed with a total of %d overruns.\n", total_overruns); print_message(" try a bigger buffer than -B %f\n",min_buffer_time); } if (disk_errors > 0) print_message("jack_capture failed with a total of %d disk errors.\n",disk_errors); if (total_xruns > 0) print_message("jack_capture encountered %d jack x-runs.\n", total_xruns); disk_errors = 0; total_overruns = 0; total_xruns = 0; } static int rotate_file(size_t frames, int reset_totals){ ATOMIC_SET(g_store_sync, 0); // Explanation: new file will already contain the CURRENT buffer! // but sync-timeframe will only be saved on the start of next jack cycle. // // -> save current audio ringbuffer-size -> subtract from next sync timestamp. ssync_offset = frames; sf_close(soundfile); char *filename_new; filename_new=my_calloc(1,strlen(base_filename)+500); sprintf(filename_new,"%s.%0*d.%s",base_filename,leading_zeros+1,num_files,soundfile_format); print_message("Closing %s, and continue writing to %s.\n",filename,filename_new); num_files++; hook_file_rotated(filename, filename_new, num_files, total_overruns + total_xruns, disk_errors); free(filename); filename=filename_new; disksize=0; if (reset_totals) { /* reset totals on file-rotate */ disk_errors = 0; total_overruns = 0; total_xruns = 0; if (overruns > 0) { print_message("jack_capture failed with a total of %d overruns.\n", total_overruns); print_message(" try a bigger buffer than -B %f\n",min_buffer_time); } if (disk_errors > 0) print_message("jack_capture failed with a total of %d disk errors.\n",disk_errors); if (total_xruns > 0) print_message("jack_capture encountered %d jack x-runs.\n", total_xruns); } if(!open_soundfile()) return 0; return 1; } // To test filelimit handler, uncomment two next lines. //#undef UINT32_MAX //#define UINT32_MAX 100000+(1024*1024) static int handle_filelimit(size_t frames){ int new_bytes=frames*bytes_per_frame*num_channels; if(is_using_wav && (disksize + ((int64_t)new_bytes) >= UINT32_MAX-(1024*1024))){ // (1024*1024) should be enough for the header. print_message("Warning. 4GB limit on wav file almost reached."); if (!rotate_file(frames, false)) return 0; } #if HAVE_LIBLO else if (queued_file_rotate) { queued_file_rotate=false; print_message("Note. file-name rotation request received."); if (!rotate_file(frames, true)) return 0; } #endif else if (rotateframe > 0 && disksize > (rotateframe*bytes_per_frame*num_channels) ) { if (!rotate_file(frames, false)) return 0; } disksize+=new_bytes; return 1; } // stdout_write made by looking at http://mir.dnsalias.com/oss/jackstdout/start // made by Robin Gareus. static int stdout_write(sample_t *buffer,size_t frames){ static char *tobuffer=NULL; static int bufferlen=0; int bytes_to_write=frames*num_channels*2; if(bufferlen>8)&0xff); } } { int fd = fileno(stdout); char *tobuffer_use = tobuffer; while(bytes_to_write > 0){ int written=write(fd,tobuffer_use,bytes_to_write); if(written==-1){ fprintf(stderr,"Error writing to stdout.\n"); break; } bytes_to_write -= written; tobuffer_use += written; } } return 1; } #if HAVE_LAME static int mp3_write(void *das_data,size_t frames,bool do_flush){ int size; if(do_flush){ size = lame_encode_flush(lame, mp3buf, mp3bufsize); //print_message("mp3 flush size: %d\n",size); }else{ sample_t *data1=(sample_t*)das_data; sample_t *data2=&data1[frames]; size = lame_encode_buffer_float(lame, data1,data2, frames, mp3buf, mp3bufsize); } if(size>0) fwrite(mp3buf,size,1,mp3file); return 1; } #endif static int disk_write(void *data,size_t frames){ num_frames_written_to_disk += frames; if(write_to_stdout==true) return stdout_write(data,frames); #if HAVE_LAME if(write_to_mp3==true) return mp3_write(data,frames,false); #endif if(soundfile==NULL) return 0; if(!handle_filelimit(frames)) return 0; if((size_t)sf_writef_float(soundfile,data,frames) != frames){ print_message("Error. Can not write sndfile (%s)\n", sf_strerror(soundfile) ); disk_errors++; return 0; } return 1; } static int disk_write_overruns(int num_overruns){ if(verbose==true) print_message( "jack_capture failed writing %d frame%s. Some parts of the recording will contain silence.\n" " Try a bigger buffer than -B %f\n%s", num_overruns,num_overruns==1 ? "" : "s", min_buffer_time, ATOMIC_GET(is_running) ? "Continue recording...\n" : "" ); overruns+=num_overruns; while(num_overruns>0){ int size=JC_MIN(block_size,num_overruns); if( ! disk_write(empty_buffer,size)) return 0; num_overruns-=size; } return 1; } static void disk_thread_control_priority(void){ int adjusted_writing_size = vringbuffer_writing_size(vringbuffer); if (timemachine_mode==true) adjusted_writing_size += seconds_to_blocks(timemachine_prebuffer*jack_samplerate); if(1 && disk_thread_has_high_priority==false && vringbuffer_reading_size(vringbuffer) >= adjusted_writing_size && use_jack_freewheel==false ) { if(set_high_priority()==true){ disk_thread_has_high_priority=true; print_message("Less than half the buffer used. Setting higher priority for the disk thread.\n"); }else{ static bool message_sent=false; if(message_sent==false) print_message("Error. Could not set higher priority for disk thread.\n"); message_sent=true; } } } static enum vringbuffer_receiver_callback_return_t disk_callback(vringbuffer_t *vrb,bool first_time,void *element){ static bool printed_receive_message=false; buffer_t *buffer=(buffer_t*)element; if (first_time==true) { return true; } if (timemachine_mode==true && timemachine_recording==false) { int num_buffers = vringbuffer_reading_size(vrb); if (buffers_to_seconds(num_buffers) > timemachine_prebuffer) return VRB_CALLBACK_USED_BUFFER; // i.e throw away the buffer. else return VRB_CALLBACK_DIDNT_USE_BUFFER; } if (timemachine_mode==true && printed_receive_message==false){ print_message("Recording. Press to stop.\n"); printed_receive_message=true; } if(use_jack_transport==true && printed_receive_message==false){ print_message("Received JackTranportRolling. Recording.\n"); printed_receive_message=true; } if(use_jack_freewheel==true && printed_receive_message==false){ print_message("Entered Jack Freewheeling. Recording.\n"); printed_receive_message=true; } disk_thread_control_priority(); if (ATOMIC_COMPARE_AND_SET_INT(g_store_sync, 1, 2)) { hook_rec_timimg(filename, rtime, j_latency); if (create_tme_file) { /* write .tme info-file */ /*subtract port latency.*/ int64_t lat_nsec = 1000000000 * j_latency / jack_samplerate; int64_t lat_sec = lat_nsec/1000000000; lat_nsec = lat_nsec%1000000000; if (rtime.tv_nsec >= lat_nsec) rtime.tv_nsec-=lat_nsec; else {rtime.tv_nsec+=(1000000000-lat_nsec); rtime.tv_sec--;} rtime.tv_sec-=lat_sec; /* subtract (buffer) offset after file-rotate */ int64_t sync_nsec = (ssync_offset%((int)jack_samplerate))*1000000000/jack_samplerate; int64_t sync_sec = ssync_offset/((int)jack_samplerate); if (rtime.tv_nsec > sync_nsec) rtime.tv_nsec-=sync_nsec; else {rtime.tv_nsec+=(1000000000-sync_nsec); rtime.tv_sec--;} rtime.tv_sec-=sync_sec; ssync_offset = 0; /* ok, write to file */ FILE *file = fopen(string_concat(filename, ".tme"),"w"); if(file) { fprintf(file, "%ld.%ld\n", rtime.tv_sec, rtime.tv_nsec); fprintf(file, "# port-latency: %d frames\n", j_latency); fprintf(file, "# sample-rate : %f samples/sec\n", jack_samplerate); char tme[64]; struct tm time; gmtime_r(&rtime.tv_sec, &time); strftime(tme, 63, "%F %T", &time); fprintf(file, "# system-time : %s.%ld UTC\n", tme, rtime.tv_nsec); fclose(file); } } } if( buffer->overruns > 0) disk_write_overruns(buffer->overruns); disk_write(buffer->data,buffer->pos); return VRB_CALLBACK_USED_BUFFER;} static void cleanup_disk(void){ // Adding silence at the end. Not much point. if(unreported_overruns>0) disk_write_overruns(unreported_overruns); close_soundfile(); if(verbose==true) fprintf(stderr,"disk thread finished\n"); } ///////////////////////////////////////////////////////////////////// //////////////////////// JACK PROCESS /////////////////////////////// ///////////////////////////////////////////////////////////////////// static void send_buffer_to_disk_thread(buffer_t *buffer){ buffer->overruns = unreported_overruns; vringbuffer_return_writing(vringbuffer,buffer); unreported_overruns = 0; } static void process_fill_buffer(sample_t *in[],buffer_t *buffer,int i,int end){ sample_t *data=buffer->data; int pos=buffer->pos*num_channels; int ch; if(buffer_interleaved == true){ for(;isafe_float_read(&vu_vals[ch])) safe_float_write(&vu_vals[ch], val); } } }else{ int start_i = i; for(ch=0;chmax_vu) max_vu=val; } safe_float_write(&vu_vals[ch], max_vu); } } //fprintf(stderr,"pos: %d %d\n",pos,num_channels); buffer->pos=pos/num_channels; } static bool process_new_current_buffer(int frames_left){ if (use_jack_freewheel==true) { while (vringbuffer_writing_size(vringbuffer)==0) msleep(2); } current_buffer=(buffer_t*)vringbuffer_get_writing(vringbuffer); if(current_buffer==NULL){ total_overruns++; unreported_overruns += frames_left; return false; } current_buffer->pos=0; return true; } static void process_fill_buffers(int jack_block_size){ sample_t *in[num_channels]; int i=0,ch; jack_port_t **ports = ATOMIC_GET(g_ports); for(ch=0;chpos ); process_fill_buffer(in,current_buffer,i,i+size); i+=size; if(current_buffer->pos == block_size){ send_buffer_to_disk_thread(current_buffer); if(process_new_current_buffer(jack_block_size-i)==false) return; } } } static bool jack_transport_started=false; static bool jack_freewheel_started=false; enum{ NOT_STARTED, HANDLING_LATENCY, RECORDING, RECORDING_FINISHED }; static int process_state=NOT_STARTED; static int process(jack_nframes_t nframes, void *arg){ (void)arg; jack_transport_state_t state=0; if(use_jack_transport==true){ state=jack_transport_query(client,NULL); if(state==JackTransportRolling){ jack_transport_started=true; } if(jack_transport_started==false) return 0; } if (use_jack_freewheel==true) { if (freewheel_mode > 0) jack_freewheel_started=true; if(jack_freewheel_started==false) return 0; } if(ATOMIC_GET(is_initialized)==false) return 0; if(ATOMIC_GET(is_running)==false) return 0; if(process_state==RECORDING_FINISHED) return 0; jack_port_t **ports = ATOMIC_GET(g_ports); if (ATOMIC_GET(g_store_sync)==0) { #ifndef NEW_JACK_LATENCY_API int ch; j_latency=0; for(ch=0;ch0) process_fill_buffers(num_frames); ATOMIC_ADD(num_frames_recorded, num_frames); if(ATOMIC_GET(num_frames_recorded)==num_frames_to_record){ send_buffer_to_disk_thread(current_buffer); #ifdef __APPLE__ semaphore_signal(stop_sem); #else sem_post(&stop_sem); #endif process_state=RECORDING_FINISHED; } }else{ process_fill_buffers(nframes); ATOMIC_ADD(num_frames_recorded, nframes); if( (use_jack_transport==true && state==JackTransportStopped) || (use_jack_freewheel==true && freewheel_mode==0) ){ send_buffer_to_disk_thread(current_buffer); #ifdef __APPLE__ semaphore_signal(stop_sem); #else sem_post(&stop_sem); #endif process_state=RECORDING_FINISHED; } } vringbuffer_trigger_autoincrease_callback(vringbuffer); return 0; } static int xrun(void *arg){ (void)arg; total_xruns++; return 0; } ///////////////////////////////////////////////////////////////////// /////////////////// METERBRIDGE ///////////////////////////////////// ///////////////////////////////////////////////////////////////////// static char* meterbridge_jackname; pid_t meterbridge_pid; static void start_meterbridge(int num_channels){ meterbridge_jackname=my_calloc(1,5000); sprintf(meterbridge_jackname,"%s_meterbridge",jack_get_client_name(client)); //meterbridge -t vu -n meterbri xmms-jack_12250_000:out_0 xmms-jack_12250_000:out_1 meterbridge_pid=fork(); if(meterbridge_pid==0){ char *argv[100+num_channels]; argv[0] = "meterbridge"; argv[1] = "-t"; argv[2] = meterbridge_type; argv[3] = "-n"; argv[4] = meterbridge_jackname; argv[5] = "-r"; argv[6] = meterbridge_reference; { int ch; for(ch=0;ch/dev/null 2>/dev/null" in a shell int fd; if((fd = open("/dev/null", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR))==-1){ perror("open"); }else{ dup2(fd,STDOUT_FILENO); dup2(fd,STDERR_FILENO); close(fd); } } execvp("meterbridge",argv); pause(); // Prevent anyone else using the meterbridge_pid. (parent kills meterbridge_pid at exit, whether it was successfully started or not) exit(0); } } static void wake_up_connection_thread(void); int connect_meterbridge=0; static int init_meterbridge_ports(){ if(ports_meterbridge!=NULL || use_meterbridge==false) return 0; { char portname[5000]; sprintf(portname,"%s:meter_1",meterbridge_jackname); jack_port_t *port1=jack_port_by_name(client,portname); if(port1==NULL){ return 0; } { jack_port_t **ports_meterbridge2 = (jack_port_t **) my_calloc (sizeof (jack_port_t *),num_channels); ports_meterbridge2[0]=port1; { int ch; for(ch=1;ch 0) msleep(2); } } #if NEW_JACK_LATENCY_API static void jack_latency_cb(jack_latency_callback_mode_t mode, void *arg) { (void)arg; int ch; jack_latency_range_t jlty; jack_nframes_t max_latency = 0; if (mode != JackCaptureLatency) return; jack_port_t **ports = ATOMIC_GET(g_ports); if (!ports) return; for(ch=0;ch max_latency) max_latency= jlty.max; } j_latency = max_latency; } #endif static void create_ports(void){ jack_port_t** ports = my_calloc (sizeof (jack_port_t *),num_channels); { int ch; for(ch=0;ch.\"\n" " Beware that you might risk overwriting an old file by using this option.\n" " By not using this option, you will not overwrite an old file.\n" " See also the option --filename-prefix\n" "\n" "\n" "Additional arguments:\n" "[--recording-time n] or [-d n] -> Recording is stopped after \"n\" seconds.\n" " This options also starts jack_capture in --no-stdin mode.\n" " To stop recording before the timeout, one can press Ctrl-C.)\n" "[--filename-prefix s]/[-fp n] -> Sets first part of the autogenerated filename.\n" " (default is \"jack_capture_\")\n" "[--leading-zeros n] or [-z n] -> \"n\" is the number of zeros to in the autogenerated filename.\n" " (-z 2 -> jack_capture_001.wav, and so on.) (default is 1)\n" "[--format format] or [-f format] -> Selects fileformat provided by libsndfile.\n" " See http://www.mega-nerd.com/libsndfile/api.html#open\n" " (Default is wav for 1 or 2 channels, and wavex for more than 2.)\n" "[--print-formats] or [-pf] -> Prints all sound formats provided to sndfile to screen and then\n" " exits.\n" "[--version] or [-v] -> Prints out version.\n" "[--silent] or [-s] -> Suppresses some common messages printed to the terminal.\n" "[--absolutely-silent] or [-as] -> Suppresses all messages printed to the terminal.\n" " Warning: libraries used by jack_capture may still print messages.\n" "[--verbose] or [-V] -> Prints some extra information to the terminal.\n" "[--mp3] or [-mp3] -> Writes to an mp3 file using liblame (LAME).\n" " (the --format option has no effect using this option)\n" "[--mp3-quality n] or [-mp3q n] -> Selects mp3 quality provided by liblame. n=0 is best, n=9 is worst.\n" " Default n is 2. (0 uses the most amount of CPU, 9 uses the least)\n" "[--mp3-bitrate n] or [-mp3b n] -> Selects mp3 bitrate (in kbit/s).\n" " Default is set by liblame. (currently 128)\n" "[--write-to-stdout] or [-ws] -> Writes 16 bit little endian to stdout. (the --format option, the\n" " --mp3 option, and some others have no effect using this option)\n" "[--disable-meter] or [-dm] -> Disable console meter.\n" "[--hide-buffer-usage] or [-hbu] -> Disable buffer usage updates in the console.\n" "[--disable-console] or [-dc] -> Disable console updates. Same as \"-dm -hbu\".\n" "[--no-stdin] or [-ns] -> Don't read the console. (i.e pressing return won't stop recording.)\n" "[--daemon] -> Same as writing \"--no-stdin --absolutely-silent\".\n" "[--linear-meter] or [-lm] -> Use linear scale for the console meter (default is dB scale)\n" "[--dB-meter-reference or [-dBr] -> Specify reference level for dB meter. (default=0)\n" "[--meterbridge] or [-mb] -> Start up meterbridge to monitor recorded sound.\n" "[--meterbridge-type] or [-mt] -> Specify type. vu (default), ppm, dpm, jf or sco.\n" "[--meterbridge-reference]/[-mr] -> Specify reference level for meterbidge.\n" "[--jack-transport]/[-jt] -> Start program, but do not start recording until jack transport has started rolling\n" " When jack transport stops, the recording is also stopped, and the program ends.\n" "[--jack-transport-multi]/[-jtm] -> Similar to --jack-transport, but do not end program when jack transport stops.\n" " Instead, record to a new file when jack_transport starts rolling again.\n" " (not implemented yet)\n" "[--jack-freewheeling]/[-jf] -> Start program, but do not start recording until jack enters freewheeling mode\n" " When jack leaves freewheeling, the recording is also stopped, and the program ends.\n" "[--manual-connections]/[-mc] -> jack_capture will not connect any ports for you. \n" "[--bufsize s] or [-B s] -> Initial/minimum buffer size in seconds. Default is 8 seconds\n" " for mp3 files, and 4 seconds for all other formats.\n" "[--maxbufsize] or [-MB] -> Maximum buffer size in seconds jack_capture will allocate.\n" " Default is 40. (Buffer is automatically increased during\n" " recording when needed. But it will never go beyond this size.)\n" "[--filename] or [-fn] -> Specify filename.\n" " Beware that you might risk overwriting an old file by using this option.\n" " To only set prefix, use --filename-prefix / -fp instead.\n" " (It's usually easier to set last argument instead of using this option)\n" "[--osc] or [-O] -> Specify OSC port number to listen on. see --help-osc\n" "[--timestamp] or [-S] -> create a FILENAME.tme file for each recording, storing\n" " the system-time corresponding to the first audio sample.\n" "[--rotatefile N] or [-Rf N] -> force rotate files every N audio-frames.\n" "[--hook-open c] or [-Ho c] -> command to execute on successful file-open. (see below)\n" "[--hook-close c] or [-Hc c] -> command to execute when closing the session. (see below)\n" "[--hook-rotate c] or [-Hr c] -> command to execute on file-name-rotation. (see below)\n" "[--hook-timing c] or [-Ht c] -> callback when first audio frame is received. (see below)\n" "[--timemachine] or [-tm] -> jack_capture operates in \"timemachine\" mode.\n" "[--timemachine-prebuffer s] -> Specify (in seconds) how long time to prebuffer in timemachine mode.\n" "[ -tmpb s] -> ------------------------ \"\" ----------------------------------------\n" "\n" " All hook options take a full-path to an executable as argument.\n" " The commands are executed in a fire-and-forget style upon internal events.\n" " All output of the hooks is discarded.\n" " Paramaters passed to the hook-scripts:\n" " open: CMD \n" " close: CMD \n" " rotate: CMD \n" " timing: CMD \n" "\n" "Examples:\n" "\n" "To record a stereo file of what you hear:\n" " $jack_capture\n" "\n" "To record a stereo file of what you hear in the flac format:\n" " $jack_capture -f flac\n" "\n" "To record a stereo file of what you hear in the ogg format:\n" " $jack_capture -f ogg\n" "\n" "To record a stereo file of what you hear in the mp3 format:\n" " $jack_capture -mp3\n" "\n" "To record a stereo file of what you hear in the wav format:\n" " $jack_capture --port system:playback_1 --port system:playback_2\n" "****************************************************************************\n" "**** NOTE! The above example does _exactly_ the same as the default!!! ****\n" "****************************************************************************\n" "\n" "Same result as above, but using a different syntax:\n" " $jack_capture --channels 2 --port system:playback*\n" "\n" "To record the output from jamin:\n" " $jack_capture --port jamin:out* sound_from_jamin.wav\n" "\n" "To record all sound coming in to jamin:\n" " $jack_capture --port jamin:in* sound_to_jamin.wav\n" "\n" "To record all sound coming in and out of jamin:\n" " $jack_capture --port jamin* sound_to_and_from_jamin.wav\n" "\n" "To record a stereo file from the soundcard:\n" " $jack_capture -c 2 -p system:capture*\n" "\n"; static const char *osc_help = "If called with -O , jack-capture can be remote-controlled.\n" "The following OSC (Open Sound Control) messages are understood:\n" "\n" " /jack_capture/stop (no arguments) -- stop recording and exit\n" " /jack_capture/rotate (no arguments) -- rotate file-name\n" " /jack_capture/tm/start (no arguments) -- in timemachine-mode: start recording.\n" " /jack_capture/tm/stop (no arguments) -- in timemachine-mode: stop recording.\n" "\n" "Example:\n" " jack_capture -O 7777\n" " oscsend localhost 7777 /jack_capture/stop\n" "The 'oscsend' utility comes with liblo, deb-pkg: liblo-utils.\n" "\n" "Caveat:\n" "When used with hook-commands (-Hr, -Hc, etc) the OSC port will be in use\n" "until the last of the hook-commands has terminated.\n" "Launching a new instance of jack_capture with the same OSC port while some\n" "hook-script of a previous instance is still running, will prevent jack_capture\n" "from listening on that UDP-port (non fatal - \"port is in use\" warning).\n" "jack_capture will work fine, but can not be remote-controlled.\n" "\n"; void init_arguments(int argc, char *argv[]){ OPTARGS_BEGIN("\n" "To record what you hear, just run\n" "\n" " jack_capture\n" "\n" "To list advanced options, run\n" "\n" " jack_capture --advanced-options (or --help2)\n" "\n" ) { OPTARG("--advanced-options","--help2") printf("%s",advanced_help);exit(0); OPTARG("--help-osc","--help3") printf("%s",osc_help);exit(0); OPTARG("--bitdepth","-b") bitdepth = OPTARG_GETINT(); OPTARG("--bufsize","-B") min_buffer_time = OPTARG_GETFLOAT(); min_buffer_time=JC_MAX(0.01,min_buffer_time); OPTARG("--maxbufsize","-MB") max_buffer_time = OPTARG_GETFLOAT(); OPTARG("--channels","-c") num_channels = OPTARG_GETINT(); OPTARG("--filename-prefix","-fp") filename_prefix = OPTARG_GETSTRING(); OPTARG("--leading-zeros","-z") leading_zeros = OPTARG_GETINT(); OPTARG("--recording-time","-d"){ recording_time = OPTARG_GETFLOAT(); start_jack(); num_frames_to_record = seconds_to_frames(recording_time); no_stdin = true; fixed_duration = true; } OPTARG("--port","-p") { start_jack() ; portnames_add(OPTARG_GETSTRING()); } OPTARG("--format","-f"){ soundfile_format=OPTARG_GETSTRING(); if(!strcmp("mp3",soundfile_format)){ write_to_mp3 = true; } soundfile_format_is_set=true; } OPTARG("--version","-v") puts(VERSION);exit(0); OPTARG("--silent","-s") silent=true; OPTARG("--absolutely-silent","-as") absolutely_silent=true; use_vu=false; silent=true; show_bufferusage=false; OPTARG("--verbose","-V") verbose=true; OPTARG("--print-formats","-pf") print_all_formats();exit(0); OPTARG("--mp3","-mp3") write_to_mp3 = true; OPTARG("--mp3-quality","-mp3q") das_lame_quality = OPTARG_GETINT(); write_to_mp3 = true; OPTARG("--mp3-bitrate","-mp3b") das_lame_bitrate = OPTARG_GETINT(); write_to_mp3 = true; OPTARG("--write-to-stdout","-ws") write_to_stdout=true;use_vu=false;show_bufferusage=false; OPTARG("--disable-meter","-dm") use_vu=false; OPTARG("--hide-buffer-usage","-hbu") show_bufferusage=false; OPTARG("--disable-console","-dc") use_vu=false;show_bufferusage=false; OPTARG("--no-stdin","-ns") no_stdin=true; OPTARG("--daemon","") no_stdin=true; absolutely_silent=true; use_vu=false; silent=true; show_bufferusage=false; OPTARG("--linear-meter","-lm") vu_dB=false; OPTARG("--dB-meter-reference","-dBr") vu_dB=true;vu_bias=powf(10.0f,OPTARG_GETFLOAT()*-0.05f);//from meterbridge OPTARG("--meterbridge","-mb") use_meterbridge=true; OPTARG("--meterbridge-type","-mt") use_meterbridge=true;meterbridge_type=OPTARG_GETSTRING(); OPTARG("--meterbridge-reference","-mr") use_meterbridge=true;meterbridge_reference=OPTARG_GETSTRING(); OPTARG("--jack-transport","-jt") use_jack_transport=true; OPTARG("--jack-freewheel","-jf") use_jack_freewheel=true; OPTARG("--manual-connections","-mc") use_manual_connections=true; OPTARG("--filename","-fn") base_filename=OPTARG_GETSTRING(); OPTARG("--osc","-O") { #if HAVE_LIBLO osc_port=atoi(OPTARG_GETSTRING()); #else fprintf(stderr,"osc not supported. liblo was not installed when compiling jack_capture\n"); exit(3); #endif } OPTARG("--hook-open","-Ho") hook_cmd_opened = OPTARG_GETSTRING(); OPTARG("--hook-close","-Hc") hook_cmd_closed = OPTARG_GETSTRING(); OPTARG("--hook-rotate","-Hr") hook_cmd_rotate = OPTARG_GETSTRING(); OPTARG("--hook-timing","-Ht") hook_cmd_timing = OPTARG_GETSTRING(); OPTARG("--timestamp","-S") create_tme_file=true; OPTARG("--rotatefile","-Rf") rotateframe = OPTARG_GETINT(); OPTARG("--timemachine","-tm") timemachine_mode = true; OPTARG("--timemachine-prebuffer","-tmpb") timemachine_prebuffer=OPTARG_GETFLOAT(); OPTARG_LAST() base_filename=OPTARG_GETSTRING(); }OPTARGS_END; if(use_jack_freewheel==true && use_jack_transport==true){ fprintf(stderr,"--jack-transport and --jack-freewheel are mutually exclusive options.\n"); exit(2); } if(write_to_mp3==true){ #if HAVE_LAME soundfile_format="mp3"; soundfile_format_is_set=true; if(min_buffer_time<=0.0f) min_buffer_time = DEFAULT_MIN_MP3_BUFFER_TIME; #else fprintf(stderr,"mp3 not supported. liblame was not installed when compiling jack_capture\n"); exit(2); #endif }else{ if(min_buffer_time<=0.0f) min_buffer_time = DEFAULT_MIN_BUFFER_TIME; } if(timemachine_mode==true) { min_buffer_time += timemachine_prebuffer; max_buffer_time += timemachine_prebuffer; } verbose_print("main() find default file format\n"); if(soundfile_format_is_set==false){ if(num_channels>2) soundfile_format=soundfile_format_multi; else soundfile_format=soundfile_format_one_or_two; } verbose_print("main() find filename\n"); // Find filename { if(base_filename==NULL){ int try=0; base_filename=my_calloc(1,5000); for(;;){ sprintf(base_filename,"%s%0*d.%s",filename_prefix,leading_zeros+1,++try,soundfile_format); if(access(base_filename,F_OK)) break; } } } } char *string_concat(char *s1,char *s2){ char *ret=malloc(strlen(s1)+strlen(s2)+4); sprintf(ret,"%s%s",s1,s2); return ret; } int string_charpos(char *s, char c){ int pos=0; while(s[pos]!=0){ if(s[pos]==c) return pos; pos++; } return -1; } char *substring(char *s,int start,int end){ char *ret = calloc(1,end-start+1); int read_pos = start; int write_pos = 0; while(read_pos=max_size-3){ fprintf(stderr,"Too many arguments in config file.\n"); exit(-2); } int split_pos = string_charpos(line,'='); if(split_pos!=-1){ char *name = strip_whitespace(substring(line,0,split_pos)); char *value = strip_whitespace(substring(line,split_pos+1,strlen(line))); if(strlen(name)>0 && strlen(value)>0){ argv[*argc] = string_concat("--",name); *argc = *argc + 1; if(value[0]=='~') value = string_concat(getenv("HOME"),&value[1]); argv[*argc] = value; *argc = *argc + 1; //printf("pos: %d -%s- -%s-\n",split_pos,name,value); } }else{ argv[*argc] = string_concat("--",line); *argc = *argc + 1; } } return argv; } void init_various(void){ verbose_print("main() init jack 1\n"); // Init jack 1 { if(use_manual_connections==false) start_connection_thread(); start_jack(); portnames_add_defaults(); } verbose_print("main() init buffers\n"); // Init buffers { buffers_init(); } if (access(base_filename, F_OK)==0){ print_message("\n"); print_message("!!! Warning, Overwriting existing file %s !!!\n", base_filename); print_message("\n"); } verbose_print("main() Open soundfile and setup disk callback.\n"); // Open soundfile and start disk thread { if(!open_soundfile()){ jack_client_close(client); exit(-2); } vringbuffer_set_receiver_callback(vringbuffer,disk_callback); } verbose_print("main() Init waiting.\n"); // Init waiting. { #ifdef __APPLE__ semaphore_create(mach_task_self(), &stop_sem, SYNC_POLICY_FIFO, 0); #else sem_init(&stop_sem,0,0); #endif signal(SIGINT,finish); signal(SIGTERM,finish); if(no_stdin==false) start_keypress_thread(); } verbose_print("main() Init jack 2.\n"); // Init jack 2 { jack_set_process_callback(client, process, NULL); jack_set_xrun_callback(client, xrun, NULL); jack_on_shutdown(client, jack_shutdown, NULL); #if NEW_JACK_LATENCY_API jack_set_latency_callback (client, jack_latency_cb, NULL); #endif if(use_manual_connections==false) jack_set_graph_order_callback(client,graphordercallback,NULL); jack_set_freewheel_callback(client,freewheelcallback,NULL); if (jack_activate(client)) { fprintf (stderr,"\nCan not activate client"); exit(-2); } create_ports(); if(use_manual_connections==false) connect_ports(ATOMIC_GET(g_ports)); } verbose_print("main() Everything initialized.\n"); // Everything initialized. // (The threads are waiting for this variable, not the other way around, so now it just needs to be set.) { ATOMIC_SET(is_initialized, true); wake_up_connection_thread(); // Usually (?) not necessarry, but just in case. } verbose_print("main() Start meterbridge.\n"); // Start meterbridge { if(use_meterbridge) start_meterbridge(num_channels); } verbose_print("main() Print some info.\n"); // Print some info { if(fixed_duration==true){ if(silent==false) print_message( "Recording to \"%s\". The recording is going\n" MESSAGE_PREFIX "to last %lf seconds Press to stop before that.\n", base_filename, recording_time); }else{ if(silent==false) { if (timemachine_mode==true) { print_message("Waiting to start recording of \"%s\"\n",base_filename); print_message("Press to stop recording and quit.\n"); }else print_message("Recording to \"%s\". Press or to stop.\n",base_filename); //fprintf(stderr,"Recording to \"%s\". Press or to stop.\n",base_filename); } } } verbose_print("main() Start helper thread.\n"); // Start the helper thread, which takes care of the console { setup_helper_thread(); } //noecho(); } void wait_until_recording_finished(void){ verbose_print("main() Wait.\n"); if(use_jack_transport==true) print_message("Waiting for JackTransportRolling.\n"); if(use_jack_freewheel==true) print_message("Waiting for Jack Freewheeling .\n"); #ifdef __APPLE__ kern_return_t ret; while((ret=semaphore_wait(stop_sem))!=KERN_SUCCESS) print_message("Warning: semaphore_wait failed: %d",ret); #else while(sem_wait(&stop_sem)==-1) print_message("Warning: sem_wait failed: %s",strerror(errno)); #endif turn_on_echo(); if(helper_thread_running==1){ // if(use_vu || show_bufferusage) // printf("%c[%dA",0x1b,1); // Pressing return moves the cursor. if(silent==false){ // messy... print_message("Please wait while writing all data to disk. (shouldn't take long)\n"); msleep(2); } //print_message("%c[%dAPlease wait while writing all data to disk. (shouldn't take long)\n",0x1b,1); // necessary. } } void stop_recording_and_cleanup(void){ verbose_print("main() Stop recording and clean up.\n"); ATOMIC_SET(is_running, false); if(use_manual_connections==false) stop_connection_thread(); vringbuffer_stop_callbacks(vringbuffer); // Called before cleanup_disk to make sure all data are sent to the callback. cleanup_disk(); if(use_meterbridge) kill(meterbridge_pid,SIGINT); stop_helper_thread(); #if HAVE_LIBLO shutdown_osc(); #endif if(jack_has_been_shut_down==false) jack_client_close(client); if(silent==false){ usleep(50); // wait for terminal fprintf(stderr,"%c[31m",0x1b); //red color fprintf(stderr,"Finished."); fprintf(stderr,"%c[0m",0x1b); // reset colors fprintf(stderr,"\n"); } } void append_argv(char **v1,const char **v2,int len1,int len2,int max_size){ int write_pos = len1; int read_pos = 0; if(len1+len2>=max_size){ fprintf(stderr,"Too many arguments.\n"); exit(-3); } while(write_pos, ",argv[i]); } printf("-- finished. \n"); } #endif int main (int argc, char *argv[]){ //get_free_mem(); //mainpid=getpid(); char **org_argv = argv; // remove exe name from argument list. argv = &argv[1]; argc = argc-1; // get arguments both from command line and config file (config file is read first, so that command line can override) int c_argc; char **c_argv = read_config(&c_argc,500); append_argv(c_argv,(const char**)argv,c_argc,argc,500); //print_argv(c_argv,c_argc+argc); init_arguments(c_argc+argc,c_argv); #if HAVE_LIBLO if (init_osc(osc_port)) { /* no OSC available */ osc_port=-1; } #endif init_various(); wait_until_recording_finished(); stop_recording_and_cleanup(); if (timemachine_mode==true && program_ended_with_return==true){ execvp (org_argv[0], (char *const *) org_argv); print_message("Error: exec returned: %s.\n", strerror(errno)); exit(127); } return 0; } jack_capture-0.9.73/jack_capture_gui000077500000000000000000000023441310754750000174770ustar00rootroot00000000000000#!/bin/bash #script for using a graphical dialog to jack_capture. Originally written by Svend-Erik Kjær Madsen #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. OPTIONS=$@ if [ ! `which zenity` ] ; then echo "zenity needed"; exit; fi while true do zenity --question --ok-label="Yes" --cancel-label="No" --text="PRESS YES TO START REC" case "$?" in 0) `dirname $0`/jack_capture --no-stdin $OPTIONS & some_pid=$! zenity --info --text=" RECORDING IN PROGRESS\n PRESS OK TO STOP REC " kill -s SIGINT $some_pid ;; 1) exit; ;; esac done jack_capture-0.9.73/jack_capture_gui2.cpp000066400000000000000000001656041310754750000203500ustar00rootroot00000000000000//----------------------------------------------------- // name : "jack_capture_settings" // version : "1.00" // author : "brummer" // license : "BSD" // copyright : "is a present to Kjetil S. Matheussen" // // Prototyp generated with Faust 0.9.9.4f (http://faust.grame.fr) //----------------------------------------------------- /* link with */ #include #include #include #include #include #include #include #include using namespace std; //inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); } inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); } // g++ -O2 `pkg-config --cflags --libs gtk+-2.0` jack_capture_settings.cpp -o giveaname #define max(x,y) (((x)>(y)) ? (x) : (y)) #define min(x,y) (((x)<(y)) ? (x) : (y)) // ------------------define the parameter reading, take code from jack_capture ----------------------------------- #define OPTARGS_CHECK_GET(wrong,right) lokke==argc-1?(fprintf(stderr,"Must supply argument for '%s'\n",argv[lokke]),exit(-2),wrong):right #define OPTARGS_BEGIN(das_usage) {int lokke;const char *usage=das_usage;for(lokke=1;lokke> n); } inline int int2pow2 (int x) { int r=0; while ((1< #include using namespace std; struct Meta : map { void declare (const char* key, const char* value) { (*this)[key]=value; } }; struct uiItem; typedef void (*uiCallback)(float val, void* data); /** * Graphic User Interface : abstract definition */ class UI { typedef list clist; typedef map zmap; private: static list fGuiList; zmap fZoneMap; bool fStopped; public: UI() : fStopped(false) { fGuiList.push_back(this); } virtual ~UI() { // suppression de this dans fGuiList } // -- registerZone(z,c) : zone management void registerZone(float* z, uiItem* c) { if (fZoneMap.find(z) == fZoneMap.end()) fZoneMap[z] = new clist(); fZoneMap[z]->push_back(c); } // -- saveState(filename) : save the value of every zone to a file void saveState(const char* filename) { ofstream f(filename); for (zmap::iterator i=fZoneMap.begin(); i!=fZoneMap.end(); i++) { f << *(i->first) << ' '; } f << endl; f.close(); } // show the comanline given to jack_capture as tooltip when the mouse move over the record button // error handler for the port argument, check if it is true static void showtip( GtkWidget *widget, string mand1, string mand2, string mandc) { extern const char* capturas ; extern GtkWidget* status; extern int st; int error = 0; if (mandc != "" ) { string buffer,port1, port2; char bufer[256]; FILE* readports; std::string a(mand1); std::string b(" --port "); std::string::size_type in = a.find(b); if ((in != -1) & (mand1 != " --port ")) { a.replace(in, 8, ""); a += "\n|"; } else a = "no"; std::string d(mand2); in = d.find(b); if ((in != -1) & (mand2 != " --port ")) { d.replace(in, 8, ""); d += "\n|"; } else d = "no"; readports = popen("jack_lsp", "r"); while (fgets (bufer, 256, readports) != NULL) { b = bufer; b += "|"; in = b.find(a); if ((in == 0) & (a.c_str() != "no")) { error += 1; } in = b.find(d); if ((in == 0) & (d.c_str() != "no")) { error += 1; } } pclose(readports); if (mandc == " -c 1" ) error += 1; } else error = 2; if (error == 2) { gtk_widget_set_sensitive(widget, TRUE); GtkTooltips *comandline = gtk_tooltips_new (); gtk_tooltips_set_tip (GTK_TOOLTIPS (comandline), widget, capturas, "the comandline to jack_capture."); st = gtk_statusbar_push(GTK_STATUSBAR(status), st, "Ready"); } else if (error == 0) { gtk_widget_set_sensitive(widget, FALSE); GtkTooltips *comandline = gtk_tooltips_new (); gtk_tooltips_set_tip (GTK_TOOLTIPS (comandline), widget, "ERROR\nis attainable the choose'n record Port ?", "the comandline to jack_capture."); st = gtk_statusbar_push(GTK_STATUSBAR(status), st, "ERROR"); } #if 0 if (system(" pidof jackd.bin >/dev/null") != 0) { gtk_widget_set_sensitive(widget, FALSE); GtkTooltips *comandline = gtk_tooltips_new (); gtk_tooltips_set_tip (GTK_TOOLTIPS (comandline), widget, "ERROR\njack server not running", "the comandline to jack_capture."); st = gtk_statusbar_push(GTK_STATUSBAR(status), st, "ERROR"); } #endif } // convert int to string void IntToString(int i, string & s) { s = ""; if (i == 0) { s = "0"; return; } if (i < 0) { s += '-'; i = -i; } int count = log10(i); while (count >= 0) { s += ('0' + i/pow(10.0, count)); i -= static_cast(i/pow(10.0,count)) * static_cast(pow(10.0,count)); count--; } } // -- readactuellState(filename) : read the value of every zone to a string void readactuellState(const char* fname, const char* path, const char* input, const char* output, const char* serverpath, const char* sett) { extern const char* capturas ; extern GtkWidget* recbutton; extern string com; extern int getset; extern int countfile; extern gchar* text; extern gchar* text1; int is; int checkit = 1; const char* oggit = " "; const char* lameit = " "; int sel = 0; string mand, mandi, mando, manda, mandc, mandm, mandme, mand1, mand2, ma, can, can1, com2; float nobitdepth = 0; if (strcmp(serverpath, " ") == 0) { com = "jack_capture"; } else { com = serverpath; } // first is the meterbridge zmap::iterator i=fZoneMap.begin(); is = *(i->first); i++; if (is == 1) { mandme = " -mt dpm"; // set meterbridge to sco } is = *(i->first); i++; if (is == 1) { if (strcmp(fname, " ") == 0) { mandi = " -fp ~/session"; } else { IntToString(countfile,mand); std::string a(fname); std::string b("."); std::string::size_type in = a.find(b); if (in != -1) a.erase(in); mandi = " "; mandi += a; mandi += mand; mandi += ".wav"; } nobitdepth = 0; } // read it is ogg file is = *(i->first); i++; if (is == 1) { if (strcmp(fname, " ") == 0) { mandi = " -f ogg -fp ~/session "; } else { mandi = " -f ogg -fn "; IntToString(countfile,mand); std::string a(fname); std::string b("."); std::string::size_type in = a.find(b); if (in != -1) a.erase(in); mandi += a; mandi += mand; mandi += ".ogg"; } // set float as default format for ogg set it to 0 to disable that nobitdepth = 1; oggit = "y"; } // read it is mp3 file is = *(i->first); i++; if (is == 1) { if (strcmp(fname, " ") == 0) { mandi = " -ws|lame -V2 -r - ~/session"; { char base_filename[5000]; for(;;){ sprintf(base_filename,"%s/session%d.mp3",getenv("HOME"),countfile); fprintf(stderr,"base: -%s-\n",base_filename); if(access(base_filename,F_OK)) break; countfile++; } } IntToString(countfile,mand); mandi += mand; mandi += ".mp3"; } else { mandi = " -ws|lame -V2 -r - "; IntToString(countfile,mand); std::string a(fname); std::string b("."); std::string::size_type in = a.find(b); if (in != -1) a.erase(in); mandi += a; mandi += mand; mandi += ".mp3"; } // set float as default format for mp3 nobitdepth = 1; lameit = "y"; } // read it is flac file is = *(i->first); i++; if (is == 1) { if (strcmp(fname, " ") == 0) { mandi = " -f flac -fp ~/session "; } else { mandi = " -f flac "; IntToString(countfile,mand); std::string a(fname); std::string b("."); std::string::size_type in = a.find(b); if (in != -1) a.erase(in); mandi += a; mandi += mand; mandi += ".flac"; } // set float as default format for flac also. nobitdepth = 1; } // read bitdepth it is float is = *(i->first); i++; if ((is == 1) & (nobitdepth == 0 )) { // nothing to do when float is selected } // read bitdepth it is 32 is = *(i->first); i++; if ((is == 1) & (nobitdepth == 0 )) { mand = " -b 32"; com += mand; } // read bitdepth it is 24 is = *(i->first); i++; if ((is == 1) & (nobitdepth == 0 )) { mand = " -b 24"; com += mand; } // read bitdepth it is 16 is = *(i->first); i++; if ((is == 1) & (nobitdepth == 0 )) { mand = " -b 16"; com += mand; } // read bitdepth it is 8 is = *(i->first); i++; if ((is == 1) & (nobitdepth == 0 )) { mand = " -b 8"; com += mand; } // read port it is output is = *(i->first); i++; if (is == 1) { mand = " --port "; mand += output; checkit = 0; } // read port it is input is = *(i->first); i++; if (is == 1) { mand = " --port "; mand += input; checkit = 0; } // read port it is playback is = *(i->first); i++; if (is == 1) { // port selected from combolist sel = 1; if (text != NULL) { mand = " --port "; std::string a(text); mand += a; mand1 = mand; } } // read enable db_meter is = *(i->first); i++; if (is == 0) { mandm += " --silent --disable-meter"; } else mandm += " -lm"; // read 2 channels is = *(i->first); i++; if (is == 1) { mandc = " -c 2"; com += mandc; // second port from combolist if (sel == 1) { if (text1 != NULL) { std::string b(text1); mand2 = " --port "; mand2 += b; mand += mand2; sel = 0; // fprintf(stderr, "%s\n", text1); } } } // read 1 channel is = *(i->first); i++; if (is == 1) { mandc = " -c 1"; com += mandc; // case input file is 1 channel set output file also to 1 channel for ogg and mp3 if (lameit == "y") { if (strcmp(fname, " ") == 0) { mandi = " -ws|lame -m m -r - ~/session"; { char base_filename[5000]; for(;;){ sprintf(base_filename,"%s/session%d.mp3",getenv("HOME"),countfile); fprintf(stderr,"base: -%s-\n",base_filename); if(access(base_filename,F_OK)) break; countfile++; } } IntToString(countfile,ma); mandi += ma; mandi += ".mp3"; } else { mandi = " -ws|lame -m m -r - "; IntToString(countfile,ma); std::string a(fname); std::string b("."); std::string::size_type in = a.find(b); if (in != -1) a.erase(in); mandi += a; mandi += ma; mandi += ".mp3"; } } //} } // read set channel auto is = *(i->first); i++; if (is == 1) { if (sel == 1) { // second port from combolist if (text1 != NULL) { std::string b(text1); mand2 = " --port "; mand2 += b; mand += mand2; sel = 0; } } // nothing to do for auto selected channel setting } // write all in string com and to const char capturas com += mandme; com += mandm; com += mand; com += mandi; capturas = com.c_str(); getset = 1; // actuell setings to showtip if (strcmp(sett, "yes") != 0) { if (checkit != 1) mandc = ""; showtip(recbutton, mand1, mand2, mandc); } } // -- recallState(filename) : load the value of every zone from a file void recallState(const char* filename) { ifstream f(filename); if (f.good()) { for (zmap::iterator i=fZoneMap.begin(); i!=fZoneMap.end(); i++) { f >> *(i->first); } } f.close(); } void updateAllZones(); void updateZone(float* z); static void updateAllGuis() { list::iterator g; for (g = fGuiList.begin(); g != fGuiList.end(); g++) { (*g)->updateAllZones(); } } // -- active widgets virtual void addComboBox(int i, const char* label, float* zone) {}; virtual void addButton(const char* label, float* zone) {}; virtual void addExitButton(const char* label, float* zone) {}; virtual void addRecToggleButton(const char* label, float* zone) {}; virtual void addCheckButton(const char* label, float* zone) {}; virtual void addRadioButton(GtkWidget* group, const char* label, float* zone) {}; virtual void addRadioButtonwithGroup(const char* label, float* zone) {}; void addCallback(float* zone, uiCallback foo, void* data); // -- widget's layouts virtual void addDrawBox(int i) {}; virtual void openHorizontalBox(const char* label) {}; virtual void openVerticalBox(const char* label) {}; virtual void openEventBox(int i, const char* label) {}; virtual void openExpanderBox(const char* label, float* zone) {}; virtual void closeBox() {}; virtual void show() {}; virtual void run() {}; void stop() { fStopped = true; } bool stopped() { return fStopped; } virtual void declare(float* zone, const char* key, const char* value) {} }; /** * User Interface Item: abstract definition */ class uiItem { protected : UI* fGUI; float* fZone; float fCache; uiItem (UI* ui, float* zone) : fGUI(ui), fZone(zone), fCache(-123456.654321) { ui->registerZone(zone, this); } public : virtual ~uiItem() {} void modifyZone(float v) { //-------------- this are the parameter used from argv[] --------------------------- extern const char* path; extern const char* serverpath; extern const char* fname; extern const char* sett; extern const char* input; extern const char* output; fCache = v; if (*fZone != v) { *fZone = v; fGUI->updateZone(fZone); // translate GUI state to comandline fGUI->readactuellState(fname, path, input, output, serverpath, sett); } } float cache() { return fCache; } virtual void reflectZone() = 0; }; /** * Callback Item */ struct uiCallbackItem : public uiItem { uiCallback fCallback; void* fData; uiCallbackItem(UI* ui, float* zone, uiCallback foo, void* data) : uiItem(ui, zone), fCallback(foo), fData(data) {} virtual void reflectZone() { float v = *fZone; fCache = v; fCallback(v, fData); } }; // en cours d'installation de call back. a finir!!!!! /** * Update all user items reflecting zone z */ inline void UI::updateZone(float* z) { float v = *z; clist* l = fZoneMap[z]; for (clist::iterator c = l->begin(); c != l->end(); c++) { if ((*c)->cache() != v) (*c)->reflectZone(); } } /** * Update all user items not up to date */ inline void UI::updateAllZones() { for (zmap::iterator m = fZoneMap.begin(); m != fZoneMap.end(); m++) { float* z = m->first; clist* l = m->second; float v = *z; for (clist::iterator c = l->begin(); c != l->end(); c++) { if ((*c)->cache() != v) (*c)->reflectZone(); } } } inline void UI::addCallback(float* zone, uiCallback foo, void* data) { new uiCallbackItem(this, zone, foo, data); }; /****************************************************************************** ******************************************************************************* GRAPHIC USER INTERFACE gtk interface ******************************************************************************* *******************************************************************************/ #include #define stackSize 256 // Insertion modes #define kSingleMode 0 #define kBoxMode 1 #define kTabMode 2 class GTKUI : public UI { private : static bool fInitialized; static list fGuiList; protected : GtkWidget* fWindow; int fTop; GtkWidget* fBox[stackSize]; int fMode[stackSize]; bool fStopped; GtkWidget* addWidget(const char* label, GtkWidget* w); virtual void pushBox(int mode, GtkWidget* w); public : static const gboolean expand = TRUE; static const gboolean fill = TRUE; static const gboolean homogene = FALSE; GTKUI(char * name, int* pargc, char*** pargv); // -- layout groups virtual void addDrawBox(int i); virtual void openHorizontalBox(const char* label = ""); virtual void openVerticalBox(const char* label = ""); virtual void openEventBox(int i, const char* label = ""); virtual void openExpanderBox(const char* label, float* zone); virtual void closeBox(); // -- active widgets virtual void addComboBox(int i, const char* label, float* zone); virtual void addButton(const char* label, float* zone); virtual void addExitButton(const char* label, float* zone); virtual void addRecToggleButton(const char* label, float* zone); virtual void addCheckButton(const char* label, float* zone); virtual void addRadioButton(GtkWidget* group, const char* label, float* zone); virtual void addRadioButtonwithGroup(const char* label, float* zone); virtual void show(); virtual void run(); }; /****************************************************************************** ******************************************************************************* GRAPHIC USER INTERFACE (v2) gtk implementation ******************************************************************************* *******************************************************************************/ // global static fields FILE* control_stream; //-------------- check if the save path exists and create first settings------------------- bool Exists() { const char* home; char dirname[256]; char rcfilename[256]; home = getenv ("HOME"); if (home == 0) home = "."; snprintf(dirname, 256, "%s/.%s", home, "jack_capture"); struct stat my_stat; if ( !stat(dirname, &my_stat) == 0) { system("mkdir $HOME/.jack_capture" ); snprintf(rcfilename, 256, "%s/.%src", home, "jack_capture/settings"); ofstream f(rcfilename); string com = "0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 1 0 "; f << com < UI::fGuiList; GtkWidget* status; GtkWidget* buttongroup; GtkWidget* recbutton; GtkWidget* combo; GtkWidget* combo1; GtkWidget* button1 ; //--------- global parameters ----------- UI* interface; float rec3 = 0; const char* input; const char* output; const char* capturas; gchar* text; gchar* text1; string in, out; float settip = 0; int getset = 0; int countfile = 0; int st; int intext = 0; string com; // this is the comandline string //-------------- this are the parameter used from argv[] --------------------------- const char* path = " " ; const char* serverpath = " " ; const char* appname = " " ; const char* sett = " " ; const char* fname = " " ; static gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ) { return FALSE; } //----------------- read active entry in combobox selected port ------------- static void restore_plug( GtkWidget *widget, gpointer data ) { text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo)); text1 = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo1)); interface->readactuellState(fname, path, input, output, serverpath, sett); } //----------------- reload entrys in combobox selected port ------------- static void delet_plug( GtkWidget *widget, gpointer data ) { gint v = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); gint v1 = gtk_combo_box_get_active(GTK_COMBO_BOX(combo1)); for (int i = 0; i < intext; i++) { gtk_combo_box_remove_text (GTK_COMBO_BOX(combo), 0 ); gtk_combo_box_remove_text (GTK_COMBO_BOX(combo1), 0 ); } intext = 0; char bufer[100]; char bufer1[100]; FILE* readports; readports = popen("jack_lsp -t", "r"); while (fgets (bufer, 100, readports) != NULL) { std::string e(bufer); std::string a = "\n"; std::string::size_type isn = e.find(a); if (isn != -1) e.erase(isn); a = "audio"; isn = e.find(a); if ((isn == -1) ) snprintf(bufer1, 100, e.c_str()); if ((isn != -1) ) { gtk_combo_box_append_text(GTK_COMBO_BOX(combo1), bufer1); gtk_combo_box_append_text(GTK_COMBO_BOX(combo), bufer1); intext++; } } pclose(readports); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), v); gtk_combo_box_set_active(GTK_COMBO_BOX(combo1), v1); } //---------------------- quit gui and close jack_capture in the case it is still runing-------------------------------- static void destroy_event( GtkWidget *widget, gpointer data ) { if (system(" ps xa | grep -v grep | grep \"jack_capture \" > /dev/null") == 0) { fputs("quit\n",control_stream); fflush(control_stream); //pclose(control_stream); sleep(2); system(" killall jack_capture 2> /dev/null"); } gtk_main_quit (); } // -- readState(filename) : read the comandline for jack_capture from the gui state and write to a file void readState(const char* path) { const char* home; char gfilename[256]; home = getenv ("HOME"); if (home == 0) home = "."; if (strcmp(path, " ") == 0) { snprintf(gfilename, 256, "%s%src", home, "/.jack_capture/ja_ca_set"); } else { snprintf(gfilename, 256, "%s%s", home, path); } //----------------------------- save the results to a file --------------------------------------- ofstream fa(gfilename); fa << com <readactuellState(fname, path, input, output, serverpath, sett); capturas = com.c_str(); GtkTooltips *comandline = gtk_tooltips_new (); gtk_tooltips_set_tip (GTK_TOOLTIPS (comandline), widget, capturas, "the comandline to jack_capture."); } else if (settip == 1) { GtkTooltips *comandline = gtk_tooltips_new (); gtk_tooltips_set_tip (GTK_TOOLTIPS (comandline), widget, "RECORDING IN PROGRESS", "jack_capture run"); settip = 0; } else if (settip == 2) { GtkTooltips *comandline = gtk_tooltips_new (); gtk_tooltips_set_tip (GTK_TOOLTIPS (comandline), widget, "ERROR\nin record?", "jack_capture run"); settip = 0; } } } //---------------- record run jack_capture with popen() capturas is the comandline read from the file bool capture(const char* capturas) { countfile = countfile + 1; // count the recorded files fprintf(stderr, "%s\n", capturas); // print out the comanline to the terminal control_stream = popen (capturas, "w"); settip = 1; getset = 1; st = gtk_statusbar_push(GTK_STATUSBAR(status), st, "RECORD"); // minimal error handling if (system("sleep 1\n ps xa | grep -v grep | grep \"jack_capture \" > /dev/null") != 0) { st = gtk_statusbar_push(GTK_STATUSBAR(status), st, "ERROR"); fprintf(stderr, "error\n"); GdkColor colorRed; GdkColor colorOwn; gdk_color_parse ("#edef1e", &colorOwn); gdk_color_parse ("#fafb83", &colorRed); gtk_widget_modify_bg (recbutton, GTK_STATE_PRELIGHT, &colorOwn); gtk_widget_modify_bg (recbutton, GTK_STATE_ACTIVE, &colorRed); settip = 2; } show_tip(recbutton); } //-------------- set the name for the channels to lounch given by argv[] appname ---------------- static void set_appname() { if (strcmp(appname, " ") != 0) { in = appname; in += ":in*"; input = in.c_str(); out = appname; out += ":out*"; output = out.c_str(); } else { input = "system:capture*"; output = "system:playback*"; } } //--------------------- save the gui settings as commandline to file --------------------------- static void save_event( GtkWidget *widget, gpointer data ) { char rcfilename[256]; const char* home; home = getenv ("HOME"); if (home == 0) home = "."; snprintf(rcfilename, 256, "%s/.%src", home, "jack_capture/settings"); interface->saveState(rcfilename); readState( path); if (strcmp(sett, "yes") != 0) { show_tip(recbutton); } if (strcmp(sett, "yes") == 0) { gtk_main_quit (); } } //---------------- funktions for the recordbutton -------------------------------------------- static void recordit( GtkWidget *widget, gpointer data ) { //-------- end record by press the recordbutton the second time if (rec3 == 1) { fprintf (stderr, "stop\n"); rec3 = 0; if (system(" ps xa | grep -v grep | grep \"jack_capture \" > /dev/null") == 0) { fputs("\n",control_stream); fflush(control_stream); //pclose(control_stream); } else { GdkColor colorRed; GdkColor colorOwn; gdk_color_parse ("#fba6a6", &colorOwn); gdk_color_parse ("#f48784", &colorRed); gtk_widget_modify_bg (recbutton, GTK_STATE_PRELIGHT, &colorOwn); gtk_widget_modify_bg (recbutton, GTK_STATE_ACTIVE, &colorRed); } st = gtk_statusbar_push(GTK_STATUSBAR(status), st, "Ready"); show_tip(recbutton); } //-------- start record by press the recordbutton the first time else if (rec3 == 0) { fprintf (stderr, "record\n"); if (getset == 0) { string buf; // get filename const char* home; char gfilename[256]; home = getenv ("HOME"); if (home == 0) home = "."; if (strcmp(path, " ") == 0) { snprintf(gfilename, 256, "%s%src", home, "/.jack_capture/ja_ca_set"); } else { snprintf(gfilename, 256, "%s%s", home, path); } // open file and read the line into const char* capturas ifstream f(gfilename); if (f.good()) { getline(f, buf); capturas = buf.c_str(); f.close(); capture(capturas); } } // read comandline from string else { capturas = com.c_str(); capture(capturas); } rec3 = 1; } } GTKUI::GTKUI(char * name, int* pargc, char*** pargv) { //------- check if save path exists ---------------- Exists(); if (!fInitialized) { gtk_init(pargc, pargv); fInitialized = true; } //-------- create main window fWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); // modify colors mainwindow GdkColor colorOwn; gdk_color_parse ("#a8acae", &colorOwn); gtk_widget_modify_bg (GTK_WIDGET(fWindow), GTK_STATE_NORMAL, &colorOwn); // gtk_window_set_position (GTK_WINDOW(fWindow), GTK_WIN_POS_MOUSE); //gtk_window_set_icon_from_file (GTK_WINDOW(fWindow), "jack_capture.png", NULL); //gtk_rc_parse("jack_capture.rc"); status = gtk_statusbar_new (); st = gtk_statusbar_get_context_id (GTK_STATUSBAR(status), ""); st = gtk_statusbar_push(GTK_STATUSBAR(status), st, "Ready"); gtk_window_set_resizable(GTK_WINDOW (fWindow) , FALSE); gtk_window_set_title (GTK_WINDOW (fWindow), "jack_capture settings"); gtk_signal_connect (GTK_OBJECT (fWindow), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL); gtk_signal_connect (GTK_OBJECT (fWindow), "destroy", GTK_SIGNAL_FUNC (destroy_event), NULL); fTop = 0; fBox[fTop] = gtk_vbox_new (homogene, 4); fMode[fTop] = kBoxMode; gtk_box_pack_end (GTK_BOX ( fBox[fTop]), status, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (fWindow), fBox[fTop]); gtk_widget_show(status); fStopped = false; } // empilement des boites void GTKUI::pushBox(int mode, GtkWidget* w) { assert(++fTop < stackSize); fMode[fTop] = mode; fBox[fTop] = w; } void GTKUI::closeBox() { assert(--fTop >= 0); } // les differentes boites void GTKUI::addDrawBox(int i) { FILE *readports; char bufer[256]; const char* b; string a; if (strcmp(serverpath, " ") == 0) { a = "jack_capture -v"; } else { a = serverpath; a += " -v 2>/dev/null"; } b = a.c_str(); readports = popen(b, "r"); fgets (bufer, 256, readports); pclose(readports); a = bufer; std::string e = "\n"; std::string::size_type isn = a.find(e); if (isn != -1) { a.erase(isn); } else a = "not found ?"; snprintf(bufer, 256, "%s%s", " jack_capture \n v. ", a.c_str()); GtkWidget* label = gtk_label_new (bufer); GdkColor colorOwn; gdk_color_parse ("#c1c1c8", &colorOwn); gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &colorOwn); gtk_container_add (GTK_CONTAINER(fBox[fTop]), label); gtk_widget_show(label); } void GTKUI::openEventBox(int i, const char* label) { GtkWidget * box = gtk_hbox_new (homogene, 0); gtk_container_set_border_width (GTK_CONTAINER (box), 0); if (fMode[fTop] != kTabMode && label[0] != 0) { GtkWidget * frame = addWidget(label, gtk_event_box_new ()); if (i == 1) { GdkColor colorOwn; gdk_color_parse ("#c1c1c8", &colorOwn); gtk_widget_modify_bg (frame, GTK_STATE_NORMAL, &colorOwn); } else if (i == 2) { GdkColor colorOwn; gdk_color_parse ("#acacb2", &colorOwn); gtk_widget_modify_bg (frame, GTK_STATE_NORMAL, &colorOwn); } else if (i == 3) { GdkColor colorOwn; gdk_color_parse ("#8f969a", &colorOwn); gtk_widget_modify_bg (frame, GTK_STATE_NORMAL, &colorOwn); } else if (i == 4) { GdkColor colorOwn; gdk_color_parse ("#555555", &colorOwn); gtk_widget_modify_bg (frame, GTK_STATE_NORMAL, &colorOwn); } gtk_container_add (GTK_CONTAINER(frame), box); gtk_widget_show(box); pushBox(kBoxMode, box); } else { pushBox(kBoxMode, addWidget(label, box)); } } //--------------- expander box ------------------------------- struct uiExpanderBox : public uiItem { GtkExpander* fButton; uiExpanderBox(UI* ui, float* zone, GtkExpander* b) : uiItem(ui, zone), fButton(b) {} static void expanded (GtkWidget *widget, gpointer data) { float v = gtk_expander_get_expanded (GTK_EXPANDER(widget)); if (v == 1.000000) { v = 0; } else v = 1; ((uiItem*)data)->modifyZone(v); } virtual void reflectZone() { float v = *fZone; fCache = v; gtk_expander_set_expanded(GTK_EXPANDER(fButton), v); } }; void GTKUI::openExpanderBox(const char* label, float* zone) { *zone = 0.0; GtkWidget * box = gtk_vbox_new (homogene, 4); gtk_container_set_border_width (GTK_CONTAINER (box), 4); if (fMode[fTop] != kTabMode && label[0] != 0) { GtkWidget * frame = addWidget(label, gtk_expander_new_with_mnemonic (label)); GdkColor colorOwn; gdk_color_parse ("#c1c6c8", &colorOwn); gtk_widget_modify_bg (frame, GTK_STATE_PRELIGHT, &colorOwn); gtk_container_add (GTK_CONTAINER(frame), box); gtk_widget_show(box); pushBox(kBoxMode, box); uiExpanderBox* c = new uiExpanderBox(this, zone, GTK_EXPANDER(frame)); gtk_signal_connect (GTK_OBJECT (frame), "activate", GTK_SIGNAL_FUNC (uiExpanderBox::expanded), (gpointer) c); } else { pushBox(kBoxMode, addWidget(label, box)); } } void GTKUI::openHorizontalBox(const char* label) { GtkWidget * box = gtk_hbox_new (homogene, 4); if (fMode[fTop] != kTabMode && label[0] != 0) { gtk_container_set_border_width (GTK_CONTAINER (box), 2); GtkWidget * frame = addWidget(label, gtk_frame_new (label)); gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.0); GdkColor colorOwn; gdk_color_parse ("#002200", &colorOwn); gtk_widget_modify_fg (frame, GTK_STATE_NORMAL, &colorOwn); gtk_container_add (GTK_CONTAINER(frame), box); gtk_widget_show(box); pushBox(kBoxMode, box); } else { gtk_container_set_border_width (GTK_CONTAINER (box), 0); GtkWidget * frame = addWidget(NULL, gtk_frame_new (NULL)); gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.0); GdkColor colorOwn; gdk_color_parse ("#000022", &colorOwn); gtk_widget_modify_fg (frame, GTK_STATE_NORMAL, &colorOwn); gtk_container_add (GTK_CONTAINER(frame), box); gtk_widget_show(box); pushBox(kBoxMode, box); } } void GTKUI::openVerticalBox(const char* label) { GtkWidget * box = gtk_vbox_new (homogene, 4); if (fMode[fTop] != kTabMode && label[0] != 0) { gtk_container_set_border_width (GTK_CONTAINER (box), 2); GtkWidget * frame = addWidget(label, gtk_frame_new (label)); gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.0); GdkColor colorOwn; gdk_color_parse ("#002200", &colorOwn); gtk_widget_modify_fg (frame, GTK_STATE_NORMAL, &colorOwn); gtk_container_add (GTK_CONTAINER(frame), box); gtk_widget_show(box); pushBox(kBoxMode, box); } else { gtk_container_set_border_width (GTK_CONTAINER (box), 0); GtkWidget * frame = addWidget(NULL, gtk_frame_new (NULL)); gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.0); GdkColor colorOwn; gdk_color_parse ("#000022", &colorOwn); gtk_widget_modify_fg (frame, GTK_STATE_NORMAL, &colorOwn); gtk_container_add (GTK_CONTAINER(frame), box); gtk_widget_show(box); pushBox(kBoxMode, box); } } GtkWidget* GTKUI::addWidget(const char* label, GtkWidget* w) { switch (fMode[fTop]) { case kSingleMode : gtk_container_add (GTK_CONTAINER(fBox[fTop]), w); break; case kBoxMode : gtk_box_pack_start (GTK_BOX(fBox[fTop]), w, expand, fill, 0); break; case kTabMode : gtk_notebook_append_page (GTK_NOTEBOOK(fBox[fTop]), w, gtk_label_new_with_mnemonic(label)); break; } gtk_widget_show (w); return w; } // --------------------------- Combo Box --------------------------- struct uiComboBox : public uiItem { GtkComboBox* fButton; uiComboBox(UI* ui, float* zone, GtkComboBox* b) : uiItem(ui, zone), fButton(b) {} static void changed (GtkWidget *widget, gpointer data) { float v = gtk_combo_box_get_active (GTK_COMBO_BOX(widget)); ((uiItem*)data)->modifyZone(v); } virtual void reflectZone() { float v = *fZone; fCache = v; if (v == -1.000000) v = 0; gtk_combo_box_set_active(GTK_COMBO_BOX(fButton), v); } }; void GTKUI::addComboBox(int i, const char* label, float* zone) { if (i == 0) { *zone = 0.0; combo = gtk_combo_box_new_text(); gtk_rc_parse_string ("style \"background\"\n" "{\n" "bg[NORMAL] = \"#c1c1c8\"\n" "bg[ACTIVE] = \"#c1c6c8\"\n" "bg[PRELIGHT] = \"#c1c6c8\"\n" "}" "widget_class \"*GtkCombo*\" style \"background\"" "widget_class \"*GtkMenu*\" style \"background\"" ); addWidget(label, combo); FILE *readports; char bufer[100]; char bufer1[100]; string a; readports = popen("jack_lsp -t", "r"); while (fgets (bufer, 100, readports) != NULL) { std::string e(bufer); a = "\n"; std::string::size_type isn = e.find(a); if (isn != -1) e.erase(isn); a = "audio"; isn = e.find(a); if ((isn == -1) ) snprintf(bufer1, 100, e.c_str()); if ((isn != -1) ) { gtk_combo_box_append_text(GTK_COMBO_BOX(combo), bufer1); intext++; } } pclose(readports); uiComboBox* c = new uiComboBox(this, zone, GTK_COMBO_BOX(combo)); gtk_signal_connect (GTK_OBJECT (combo), "changed", GTK_SIGNAL_FUNC (uiComboBox::changed), (gpointer) c); gtk_signal_connect (GTK_OBJECT (combo), "changed", GTK_SIGNAL_FUNC (restore_plug), (gpointer) combo); } else if (i == 1) { *zone = 0.0; combo1 = gtk_combo_box_new_text(); addWidget(label, combo1); FILE *readports; char bufer[100]; char bufer1[100]; string a; readports = popen("jack_lsp -t", "r"); while (fgets (bufer, 100, readports) != NULL) { std::string e(bufer); a = "\n"; std::string::size_type isn = e.find(a); if (isn != -1) e.erase(isn); a = "audio"; isn = e.find(a); if ((isn == -1) ) snprintf(bufer1, 100, e.c_str()); if ((isn != -1) ) { gtk_combo_box_append_text(GTK_COMBO_BOX(combo1), bufer1); } } pclose(readports); uiComboBox* c = new uiComboBox(this, zone, GTK_COMBO_BOX(combo1)); gtk_signal_connect (GTK_OBJECT (combo1), "changed", GTK_SIGNAL_FUNC (uiComboBox::changed), (gpointer) c); gtk_signal_connect (GTK_OBJECT (combo1), "changed", GTK_SIGNAL_FUNC (restore_plug), (gpointer) combo1); } } // --------------------------- Press button --------------------------- struct uiButton : public uiItem { GtkButton* fButton; uiButton (UI* ui, float* zone, GtkButton* b) : uiItem(ui, zone), fButton(b) {} static void pressed( GtkWidget *widget, gpointer data ) { uiItem* c = (uiItem*) data; c->modifyZone(1.0); } static void released( GtkWidget *widget, gpointer data ) { uiItem* c = (uiItem*) data; c->modifyZone(0.0); } virtual void reflectZone() { float v = *fZone; fCache = v; if (v > 0.0) gtk_button_pressed(fButton); else gtk_button_released(fButton); } }; void GTKUI::addButton(const char* label, float* zone) { *zone = 0.0; GtkWidget* button = gtk_button_new_with_mnemonic (label); GtkWidget * box = gtk_hbox_new (homogene, 4); GtkWidget * box1 = gtk_vbox_new (homogene, 4); gtk_container_set_border_width (GTK_CONTAINER (box), 0); gtk_container_set_border_width (GTK_CONTAINER (box1), 0); gtk_container_add (GTK_CONTAINER(box), box1); gtk_container_add (GTK_CONTAINER(box), button); gtk_widget_set_size_request (GTK_WIDGET(box1), 72.0, 22.0); gtk_widget_set_size_request (GTK_WIDGET(button), 52.0, 22.0); gtk_widget_show (button); gtk_widget_show (box1); addWidget(label, box); uiButton* c = new uiButton(this, zone, GTK_BUTTON(button)); GdkColor colorOwn; GdkColor colorwn; gdk_color_parse ("#c4c4c8", &colorwn); gtk_widget_modify_bg (button, GTK_STATE_PRELIGHT, &colorwn); gdk_color_parse ("#c0c0c7", &colorOwn); gtk_widget_modify_bg (button, GTK_STATE_NORMAL, &colorOwn); gtk_signal_connect (GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC (uiButton::pressed), (gpointer) c); gtk_signal_connect (GTK_OBJECT (button), "released", GTK_SIGNAL_FUNC (uiButton::released), (gpointer) c); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (delet_plug), (gpointer) c); } void GTKUI::addExitButton(const char* label, float* zone) { *zone = 0.0; GtkWidget* button = gtk_button_new_with_mnemonic (label); addWidget(label, button); GdkColor colorOwn; GdkColor colorwn; gdk_color_parse ("#c1c1c8", &colorwn); gtk_widget_modify_bg (button, GTK_STATE_PRELIGHT, &colorwn); gdk_color_parse ("#c1c6c8", &colorOwn); gtk_widget_modify_bg (button, GTK_STATE_NORMAL, &colorOwn); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (destroy_event), (gpointer) button); } // --------------------------- Toggle Buttons --------------------------- struct uiToggleButton : public uiItem { GtkToggleButton* fButton; uiToggleButton(UI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {} static void toggled (GtkWidget *widget, gpointer data) { float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0; ((uiItem*)data)->modifyZone(v); } virtual void reflectZone() { float v = *fZone; fCache = v; gtk_toggle_button_set_active(fButton, v > 0.0); } }; void GTKUI::addRecToggleButton(const char* label, float* zone) { *zone = 0.0; // make the record button red GdkColor colorRed; GdkColor colorwn; GdkColor colorOwn; gdk_color_parse ("#c1c6c8", &colorwn); gdk_color_parse ("#f48784", &colorRed); gdk_color_parse ("#fba6a6", &colorOwn); recbutton = gtk_toggle_button_new_with_mnemonic (label); addWidget(label, recbutton); gtk_widget_modify_bg (recbutton, GTK_STATE_NORMAL, &colorwn); gtk_widget_modify_bg (recbutton, GTK_STATE_PRELIGHT, &colorOwn); gtk_widget_modify_bg (recbutton, GTK_STATE_ACTIVE, &colorRed); //uiToggleButton* c = new uiToggleButton(this, zone, GTK_TOGGLE_BUTTON(button)); gtk_signal_connect (GTK_OBJECT (recbutton), "toggled", GTK_SIGNAL_FUNC (recordit), (gpointer) recbutton); } // --------------------------- Check Button --------------------------- struct uiCheckButton : public uiItem { GtkToggleButton* fButton; uiCheckButton(UI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {} static void toggled (GtkWidget *widget, gpointer data) { float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0; ((uiItem*)data)->modifyZone(v); } virtual void reflectZone() { float v = *fZone; fCache = v; gtk_toggle_button_set_active(fButton, v > 0.0); } }; void GTKUI::addCheckButton(const char* label, float* zone) { *zone = 0.0; GtkWidget* button = gtk_check_button_new_with_mnemonic (label); addWidget(label, button); GdkColor colorOwn; gdk_color_parse ("#c0c0c7", &colorOwn); gtk_widget_modify_bg (button, GTK_STATE_PRELIGHT, &colorOwn); uiCheckButton* c = new uiCheckButton(this, zone, GTK_TOGGLE_BUTTON(button)); gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled), (gpointer) c); } void GTKUI::addRadioButton(GtkWidget* group, const char* label, float* zone) { *zone = 0.0; GtkWidget* button = gtk_radio_button_new_with_mnemonic (gtk_radio_button_group (GTK_RADIO_BUTTON (group)), label); addWidget(label, button); GdkColor colorOwn; gdk_color_parse ("#c0c0c7", &colorOwn); gtk_widget_modify_bg (button, GTK_STATE_PRELIGHT, &colorOwn); uiCheckButton* c = new uiCheckButton(this, zone, GTK_TOGGLE_BUTTON(button)); gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled), (gpointer) c); } void GTKUI::addRadioButtonwithGroup( const char* label, float* zone) { *zone = 0.0; buttongroup = gtk_radio_button_new_with_mnemonic (NULL, label); addWidget(label, buttongroup); GdkColor colorOwn; gdk_color_parse ("#c0c0c7", &colorOwn); gtk_widget_modify_bg (buttongroup, GTK_STATE_PRELIGHT, &colorOwn); uiCheckButton* c = new uiCheckButton(this, zone, GTK_TOGGLE_BUTTON(buttongroup)); gtk_signal_connect (GTK_OBJECT (buttongroup), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled), (gpointer) c); } void GTKUI::show() { assert(fTop == 0); gtk_widget_show (fBox[0]); gtk_widget_show (fWindow); } /** * Update all user items reflecting zone z */ static gboolean callUpdateAllGuis(gpointer) { UI::updateAllGuis(); return TRUE; } void GTKUI::run() { assert(fTop == 0); gtk_widget_show (fBox[0]); gtk_widget_show (fWindow); gtk_timeout_add(40, callUpdateAllGuis, 0); gtk_main (); stop(); } /****************************************************************************** ******************************************************************************* interface GUI ******************************************************************************* *******************************************************************************/ //---------------------------------------------------------------- // définition du processeur de signal //---------------------------------------------------------------- class GUI { protected: public: GUI() {} virtual ~GUI() {} virtual void buildUserInterface(UI* interface) = 0; }; //---------------------------------------------------------------------------- // FAUST generated code //---------------------------------------------------------------------------- class myGUI : public GUI { private: float fbutton1; float fcheckbox0; float fcheckbox1; float fcheckbox2; float fcheckbox3; float fcheckbox4; float fcheckbox5; float fcheckbox6; float fcheckbox7; float fcheckbox8; float fcheckbox9; float fcheckbox10; float fcheckbox11; float fcheckbox12; float fcheckbox13; float fcheckbox14; float fcheckbox15; float fcheckbox16; float fbutton2; float fbutton3; float fsocket1; float fcbox1; float fcbox2; float fbutton4; float fexpand; public: static void metadata(Meta* m) { m->declare("name", "jack_capture_settings"); m->declare("version", "1.00"); m->declare("author", "brummer"); m->declare("license", "BSD"); m->declare("copyright", "it's a present to Kjetil S. Matheussen"); } virtual void buildUserInterface(UI* interface) { // here we insert the channels to lounch given by argv[] appname // set_appname(); //----------------------- start build interface ----------------------- interface->openVerticalBox(""); if (strcmp(sett, "yes") != 0) { interface->openExpanderBox("_settings", &fexpand); interface->openEventBox(1, " "); interface->openVerticalBox(""); } else interface->openVerticalBox("settings"); interface->openHorizontalBox(""); interface->openVerticalBox(""); interface->openEventBox(3, " "); interface->openVerticalBox("fileformat"); interface->openEventBox(2, " "); interface->openVerticalBox(""); interface->addRadioButtonwithGroup("_wav", &fcheckbox0); interface->addRadioButton(buttongroup, "_ogg", &fcheckbox1); interface->addRadioButton(buttongroup, "_mp3", &fcheckbox2); interface->addRadioButton(buttongroup, "_flac", &fcheckbox3); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->openVerticalBox(""); interface->openEventBox(3, " "); interface->openVerticalBox("bitdepth\nwav only"); interface->openEventBox(2, " "); interface->openVerticalBox(""); interface->addRadioButtonwithGroup("f_loat", &fcheckbox4); interface->addRadioButton(buttongroup,"_32", &fcheckbox5); interface->addRadioButton(buttongroup, "_24", &fcheckbox6); interface->addRadioButton(buttongroup, "_16", &fcheckbox7); interface->addRadioButton(buttongroup, "_8", &fcheckbox8); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->openHorizontalBox(""); interface->openEventBox(3, " "); interface->openVerticalBox("Channels"); interface->openEventBox(2, " "); interface->openVerticalBox(""); interface->addRadioButtonwithGroup("2 _Channels", &fcheckbox14); interface->addRadioButton(buttongroup, "1 C_hannel", &fcheckbox15); interface->addRadioButton(buttongroup, "_auto", &fcheckbox16); interface->openVerticalBox(""); interface->openEventBox(4, " "); interface->addDrawBox(0); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->openVerticalBox(""); interface->openEventBox(3, " "); interface->openVerticalBox("capture"); interface->openEventBox(2, " "); interface->openVerticalBox(""); interface->addRadioButtonwithGroup(output, &fcheckbox9); interface->addRadioButton(buttongroup, input, &fcheckbox10); interface->openVerticalBox(""); interface->addRadioButton(buttongroup, "selec_t Channels", &fcheckbox12); interface->addComboBox(0, "Channel", &fcbox1); interface->addComboBox(1, "Channel", &fcbox2); interface->addButton(" r_efresh ", &fbutton4); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->openVerticalBox(""); interface->openVerticalBox(""); interface->openEventBox(3, " "); interface->openHorizontalBox(""); interface->openEventBox(2, " "); interface->openHorizontalBox(""); interface->addCheckButton( "enable console _db-meter", &fcheckbox13); interface->addCheckButton("enable meter_bridge", &fbutton1); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); interface->closeBox(); if (strcmp(sett, "yes") != 0) { interface->closeBox(); interface->closeBox(); } interface->closeBox(); if (strcmp(sett, "yes") != 0) { interface->openHorizontalBox(""); interface->openHorizontalBox(""); interface->addRecToggleButton("\n _record \n", &fbutton3); interface->closeBox(); interface->openHorizontalBox(""); interface->addExitButton(" e_xit ", &fbutton2); interface->closeBox(); interface->closeBox(); } else interface->addExitButton("O_k", &fbutton2); interface->closeBox(); } }; myGUI GUI; //------------------------------------------------------------------------- // MAIN //------------------------------------------------------------------------- int main(int argc, char *argv[] ) { { OPTARGS_BEGIN("\033[1;34m jack_capture_settings useage\033[0m\n all parameters are optional\n\n[\033[1;31m--only-settings -o\033[0m] [\033[1;31m--name -n\033[0m] [\033[1;31m--filename -f\033[0m] [\033[1;31m--path -p\033[0m] [\033[1;31m--serverpath -s\033[0m]\n\n" // "\033[1;34mGive a name and a path where you would save the sound file\033[0m\n\n" "[\033[1;31m--filname\033[0m] or [\033[1;31m-f\033[0m] ->the name and path where to save the sound file, default is ~/sessionX.xxx\n\n" // "\033[1;34mYou can set the path to jack_capture if you dont have it installed in your $PATH\nThis argument must include jack_capture and could include parameter witch will give direct to jack_capture\033[0m\n\n" "[\033[1;31m--serverpath\033[0m] or [\033[1;31m-s\033[0m] ->the path to jack_capture include jack_capture. \nUse like this \033[1;31m -s '\033[0m/path/to/jack_capture parameter parameter\033[1;31m' \033[0mto include parameters to jack_capture\n\n" // "\033[1;34mGive a name to launch a spezified programm\033[0m\n\n" "[\033[1;31m--name\033[0m] or [\033[1;31m-n\033[0m] ->the name from the program you wont lounch the channels.\n\n" // "\033[1;34mThis settings are most usefull to lounch jack_capture_gui from a other program\033[0m\n\n" "[\033[1;31m--only-settings\033[0m] or [\033[1;31m-o\033[0m] ->'\033[1;31myes\033[0m' disable the record and exit button, default is no\n\n" "[\033[1;31m--path\033[0m] or [\033[1;31m-p\033[0m] ->the path where to save the comandline for jack_capture, default is ~/.jack_capture/ja_ca_setrc\n\n" "\n" ) { OPTARG("--only-settins","-o") sett=OPTARG_GETSTRING(); OPTARG("--name","-n") appname=OPTARG_GETSTRING(); OPTARG("--filename","-f") fname=OPTARG_GETSTRING(); OPTARG("--path","-p") path=OPTARG_GETSTRING(); OPTARG("--serverpath","-s") serverpath=OPTARG_GETSTRING(); } OPTARGS_END; } char rcfilename[256]; const char* home; interface = new GTKUI (NULL, &argc, &argv); GUI.buildUserInterface(interface); //------ path to save the gui settings ---------------- home = getenv ("HOME"); if (home == 0) home = "."; snprintf(rcfilename, 256, "%s/.%src", home, "jack_capture/settings"); interface->recallState(rcfilename); interface->readactuellState(fname, path, input, output, serverpath, sett); interface->run(); // save all values on exit. interface->saveState(rcfilename); readState(path); return 0; } jack_capture-0.9.73/make_and_run_debug.sh000077500000000000000000000007631310754750000204050ustar00rootroot00000000000000#!/bin/sh export LSAN_OPTIONS=suppressions=`pwd`/SanitizerSuppr.txt export ASAN_OPTIONS="detect_leaks=0,allocator_may_return_null=1,suppressions=SanitizerSuppr.txt,abort_on_error=1,new_delete_type_mismatch=0,alloc_dealloc_mismatch=0" # new_delete_type_mismatch=0 because of qt5. export UBSAN_OPTIONS="suppressions=`pwd`/SanitizerSuppr.txt:print_stacktrace=1:halt_on_error=1" export TSAN_OPTIONS="history_size=7,second_deadlock_stack=1,suppressions=SanitizerSuppr.txt" make && gdb ./jack_capture jack_capture-0.9.73/osc.c000066400000000000000000000077301310754750000152060ustar00rootroot00000000000000/* jack_capture - OSC remote control Kjetil Matheussen, 2005-2010. Written 2012 by Robin Gareus 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 "das_config.h" #if HAVE_LIBLO #include #include #include #include #include #include extern bool verbose; extern bool silent; /* message flags */ extern bool queued_file_rotate; void osc_stop(); void osc_tm_start(); void osc_tm_stop(); /*************************************************************************** * specific OSC callbacks */ int oscb_tm_start (const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data){ if (verbose==true) fprintf(stderr, "OSC: %s <- s:%s\n", path, &argv[0]->s); osc_tm_start(); return(0); } int oscb_tm_stop (const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data){ if (verbose==true) fprintf(stderr, "OSC: %s <- s:%s\n", path, &argv[0]->s); osc_tm_stop(); return(0); } int oscb_stop (const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data){ if (verbose==true) fprintf(stderr, "OSC: %s\n", path); osc_stop(); return(0); } int oscb_frotate (const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data){ if (verbose==true) fprintf(stderr, "OSC: %s\n", path); queued_file_rotate=true; return(0); } /*************************************************************************** * general callbacks */ static void oscb_error(int num, const char *m, const char *path) { fprintf(stderr, "liblo server error %d in path %s: %s\n", num, path, m); } /* ///////////////////////////////////////////////////////////////////////// */ /* main OSC server code */ lo_server_thread osc_server = NULL; int init_osc(int osc_port) { char tmp[8]; if (osc_port < 0) return(1); if (osc_server) return (1); uint32_t port = (osc_port>100 && osc_port< 60000)?osc_port:7654; snprintf(tmp, sizeof(tmp), "%d", port); if(verbose==true) fprintf(stderr, "OSC trying port:%i\n",port); osc_server = lo_server_thread_new (tmp, oscb_error); if (!osc_server) { if(silent==false) fprintf(stderr, "OSC start failed."); return(1); } if(silent==false) { char *urlstr; urlstr = lo_server_thread_get_url (osc_server); fprintf(stderr, "OSC server name: %s\n",urlstr); free (urlstr); } lo_server_thread_add_method(osc_server, "/jack_capture/tm/start", "", &oscb_tm_start, NULL); lo_server_thread_add_method(osc_server, "/jack_capture/tm/stop", "", &oscb_tm_stop, NULL); lo_server_thread_add_method(osc_server, "/jack_capture/stop", "", &oscb_stop, NULL); lo_server_thread_add_method(osc_server, "/jack_capture/rotate", "", &oscb_frotate, NULL); lo_server_thread_start(osc_server); if(verbose==true) fprintf(stderr, "OSC server started on port %i\n",port); return (0); } void shutdown_osc(void) { if (!osc_server) return; lo_server_thread_stop(osc_server); lo_server_thread_free(osc_server); if(verbose==true) fprintf(stderr, "OSC server shut down.\n"); osc_server=NULL; } #else int init_osc(int osc_port) {(void)osc_port;return(1);} void shutdown_osc(void) {;} #endif /* vi:set ts=8 sts=2 sw=2: */ jack_capture-0.9.73/testsndfile.c000066400000000000000000000014021310754750000167340ustar00rootroot00000000000000/* Kjetil Matheussen, 2005-2009. 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 jack_capture-0.9.73/upwaker.c000066400000000000000000000015071310754750000160740ustar00rootroot00000000000000#include "upwaker.h" #include upwaker_t *create_upwaker(void) { upwaker_t *upwaker = calloc(1,sizeof(upwaker_t)); ATOMIC_SET(upwaker->please_wakeup, false); pthread_mutex_init(&upwaker->mutex,NULL); pthread_cond_init(&upwaker->cond,NULL); return upwaker; } void upwaker_sleep(upwaker_t *upwaker) { if(ATOMIC_GET(upwaker->please_wakeup)==false) pthread_cond_wait(&upwaker->cond,&upwaker->mutex); ATOMIC_SET(upwaker->please_wakeup, false); } void upwaker_wake_up(upwaker_t *upwaker) { ATOMIC_SET(upwaker->please_wakeup, true); pthread_cond_broadcast(&upwaker->cond); // Must call, in case the other thread had finished the 'if' test before this function was called. // (i.e. it is not safe to trylock the mutex to check wether to call pthread_cond_broadcast). } jack_capture-0.9.73/upwaker.h000066400000000000000000000017721310754750000161050ustar00rootroot00000000000000 /* An upwaker is something inbetween semaphores and conditionals. Similarly to semaphores, it guarantees upwaker_sleep() to wake up, even if the upwaker isn't currently sleeping (the next call to upwaker_sleep() will not actually go to sleep). And similarly to conditionals, it will wake the sleeper up _only_ one time when upwaker_wake_up() is called several times in a row, where upwaker_sleep() hasn't got a chance to wake up inbetween those calls. This implementation only supports one thread calling upwaker_sleep() and one thread calling upwaker_wake_up(). Using several sleepers and upwakers on the same upwaker_t variable has not been tested or given any thought about whether it would work. */ #include #include "atomic.h" typedef struct { DEFINE_ATOMIC(bool, please_wakeup); pthread_mutex_t mutex; pthread_cond_t cond; } upwaker_t; upwaker_t *create_upwaker(void); void upwaker_sleep(upwaker_t *upwaker); void upwaker_wake_up(upwaker_t *upwaker); jack_capture-0.9.73/vringbuffer.c000066400000000000000000000236541310754750000167440ustar00rootroot00000000000000 /* Kjetil Matheussen, 2010-2011. 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. */ /* vringbuffer is convenient to use when sending data from a realtime thread to a non-realtime thread. */ #include #include #include /* Increaser: while true: if (ringbuffer_size(used)>...) buffer=my_calloc(1,sizeof(buffer)) write_ringbuffer(free_new,buffer) sleep(...) -> Increaser: while true: if (ringbuffer_size(used)>...) buffer=my_calloc(1,sizeof(buffer)) increase_vringbuffer(vringbuffer_t *vrb,1,&buffer) sleep(...) Disk: while true: buffer=read_ringbuffer(used) write_to_disk(buffer) write_ringbuffer(free_used,buffer) -> Disk: while true: buffer=read_vringbuffer(vringbuffer) write_to_disk(buffer) return_vringbuffer(vringbuffer,buffer) Process(data): if(ringbuffer_size(free_new)>0) buffer = read_ringbuffer(free_new) else buffer = read_ringbuffer(free_used) fill_buffer(buffer,data) write_ringbuffer(used,buffer) -> Process(data): buffer = read_vringbuffer(vringbuffer); fill_buffer(buffer,data); return_buffer(vringbuffer,buffer) vringbuffer_t *vringbuffer_create (int num_elements_during_startup, int max_num_elements); void vringbuffer_increase (vringbuffer_t *vrb, int num_elements, void **elements); int vrginbuffer_size (vringbuffer_t *vrb); void* vringbuffer_get_reading (vringbuffer_t *vrb); void vringbuffer_return_reading (vringbuffer_t *vrb, void *data); int vringbuffer_reading_size (vringbuffer_t *vrb); void* vringbuffer_get_writing (vringbuffer_t *vrb); void vrginbuffer_return_writing (vringbuffer_t *vrb, void *data); int vringbuffer_writing_size (vringbuffer_t *vrb); */ #include "vringbuffer.h" ////////////////// Implementation /////////////////////////////////// static void* my_malloc(size_t size1,size_t size2){ size_t size=size1*size2; void* ret=malloc(size); if(ret==NULL){ fprintf(stderr,"\nOut of memory. Try a smaller buffer.\n"); return NULL; } // Touch all pages. (earlier all memory was nulled out, but that puts a strain on the memory bus) { long pagesize = sysconf(_SC_PAGESIZE); char *cret=ret; size_t i=0; for(i=0;icurr_num_elements > vrb->max_num_elements) num_elements = vrb->max_num_elements - vrb->curr_num_elements; if(num_elements==0) return true; pthread_mutex_lock(&vrb->increase_lock);{ int i; vringbuffer_list_t *element=my_malloc(1,sizeof(vringbuffer_list_t) + (num_elements*vrb->element_size)); element->next=vrb->allocated_mem; vrb->allocated_mem=element; char *das_buffer=(char*)(element+1); if(das_buffer==NULL) return false; if(first_call){ // Make sure at least a certain amount of the buffer is in a cache. // Might create a less shocking startup. int num=8; if(num>num_elements) num=num_elements; memset(das_buffer,0,num*vrb->element_size); } for(i=0;ielement_size); jack_ringbuffer_write(vrb->for_writer1, (char*)&pointer, sizeof(char*)); } vrb->curr_num_elements += num_elements; }pthread_mutex_unlock(&vrb->increase_lock); return true; } vringbuffer_t* vringbuffer_create (int num_elements_during_startup, int max_num_elements,size_t element_size){ //fprintf(stderr,"Creating %d %d %d\n",num_elements_during_startup,max_num_elements,element_size); vringbuffer_t *vrb=calloc(1,sizeof(struct vringbuffer_t)); vrb->for_writer1 = jack_ringbuffer_create(sizeof(void*) * max_num_elements); vrb->for_writer2 = jack_ringbuffer_create(sizeof(void*) * max_num_elements); vrb->for_reader = jack_ringbuffer_create(sizeof(void*) * max_num_elements); vrb->element_size = element_size; vrb->max_num_elements = max_num_elements; vrb->please_stop=false; pthread_mutex_init(&vrb->increase_lock,NULL); if(vringbuffer_increase_writer1(vrb,num_elements_during_startup,true)==false) return NULL; vrb->receiver_trigger = create_upwaker(); vrb->autoincrease_trigger = create_upwaker(); return vrb; } void vringbuffer_stop_callbacks(vringbuffer_t* vrb){ vrb->please_stop=true; if(vrb->autoincrease_callback!=NULL){ upwaker_wake_up(vrb->autoincrease_trigger); pthread_join(vrb->autoincrease_thread, NULL); vrb->autoincrease_callback=NULL; } if(vrb->receiver_callback!=NULL){ upwaker_wake_up(vrb->receiver_trigger); pthread_join(vrb->receiver_thread, NULL); vrb->receiver_callback=NULL; } } void vringbuffer_delete(vringbuffer_t* vrb){ vringbuffer_stop_callbacks(vrb); while(vrb->allocated_mem!=NULL){ vringbuffer_list_t *next=vrb->allocated_mem->next; free(vrb->allocated_mem); vrb->allocated_mem=next; } jack_ringbuffer_free(vrb->for_writer1); jack_ringbuffer_free(vrb->for_writer2); jack_ringbuffer_free(vrb->for_reader); free(vrb); } static void *autoincrease_func(void* arg){ vringbuffer_t *vrb=arg; vrb->autoincrease_callback(vrb,true,0,0); #ifdef __APPLE__ semaphore_signal(vrb->autoincrease_started); #else sem_post(&vrb->autoincrease_started); #endif while(vrb->please_stop==false){ int reading_size = vringbuffer_reading_size(vrb); int writing_size = vringbuffer_writing_size(vrb); int num_new_elements = vrb->autoincrease_callback(vrb,false,reading_size,writing_size); if(num_new_elements>0) vringbuffer_increase_writer1(vrb,num_new_elements,false); if(vrb->autoincrease_interval==0) upwaker_sleep(vrb->autoincrease_trigger); else usleep(vrb->autoincrease_interval); } return NULL; } void vringbuffer_trigger_autoincrease_callback(vringbuffer_t *vrb){ upwaker_wake_up(vrb->autoincrease_trigger); } void vringbuffer_set_autoincrease_callback (vringbuffer_t *vrb, Vringbuffer_autoincrease_callback callback, useconds_t interval){ vrb->autoincrease_callback = callback; vrb->autoincrease_interval = interval; #ifdef __APPLE__ semaphore_create(mach_task_self(), &vrb->autoincrease_started, SYNC_POLICY_FIFO, 0); #else sem_init(&vrb->autoincrease_started,0,0); #endif pthread_create(&vrb->autoincrease_thread, NULL, autoincrease_func, vrb); #ifdef __APPLE__ semaphore_wait(vrb->autoincrease_started); #else sem_wait(&vrb->autoincrease_started); #endif } void vringbuffer_increase (vringbuffer_t *vrb, int num_elements, void **elements){ if(num_elements+vrb->curr_num_elements > vrb->max_num_elements) num_elements = vrb->max_num_elements - vrb->curr_num_elements; if(num_elements==0) return; pthread_mutex_lock(&vrb->increase_lock);{ jack_ringbuffer_write(vrb->for_writer1,(char*)elements,sizeof(void*) * num_elements); vrb->curr_num_elements += num_elements; }pthread_mutex_unlock(&vrb->increase_lock); } /* Non-realtime */ void* vringbuffer_get_reading (vringbuffer_t *vrb){ void *ret=NULL; jack_ringbuffer_read(vrb->for_reader,(char*)&ret,sizeof(void*)); return ret; } void vringbuffer_return_reading (vringbuffer_t *vrb, void *data){ jack_ringbuffer_write(vrb->for_writer2,(char*)&data,sizeof(void*)); } int vringbuffer_reading_size (vringbuffer_t *vrb){ return (jack_ringbuffer_read_space(vrb->for_reader)/sizeof(void*)); } static void *receiver_func(void* arg){ vringbuffer_t *vrb=arg; vrb->receiver_callback(vrb,true,NULL); #ifdef __APPLE__ semaphore_signal(vrb->receiver_started); #else sem_post(&vrb->receiver_started); #endif void *buffer = NULL; while(vrb->please_stop==false){ upwaker_sleep(vrb->receiver_trigger); while(vringbuffer_reading_size(vrb) > 0){ if (buffer==NULL) buffer=vringbuffer_get_reading(vrb); if (vrb->receiver_callback(vrb,false,buffer) == VRB_CALLBACK_DIDNT_USE_BUFFER) break; vringbuffer_return_reading(vrb,buffer); buffer = NULL; } } return NULL; } void vringbuffer_set_receiver_callback(vringbuffer_t *vrb,Vringbuffer_receiver_callback receiver_callback){ vrb->receiver_callback=receiver_callback; #ifdef __APPLE__ semaphore_create(mach_task_self(), &vrb->receiver_started, SYNC_POLICY_FIFO, 0); #else sem_init(&vrb->receiver_started,0,0); #endif pthread_create(&vrb->receiver_thread, NULL, receiver_func, vrb); #ifdef __APPLE__ semaphore_wait(vrb->receiver_started); #else sem_wait(&vrb->receiver_started); #endif } /* Realtime */ void* vringbuffer_get_writing (vringbuffer_t *vrb){ void *ret=NULL; if(jack_ringbuffer_read(vrb->for_writer2,(char*)&ret,sizeof(void*))==0) // Checking writer2 first since that memory is more likely to already be in the cache. jack_ringbuffer_read(vrb->for_writer1,(char*)&ret,sizeof(void*)); return ret; } void vringbuffer_return_writing (vringbuffer_t *vrb, void *data){ jack_ringbuffer_write(vrb->for_reader,(char*)&data,sizeof(void*)); upwaker_wake_up(vrb->receiver_trigger); } int vringbuffer_writing_size (vringbuffer_t *vrb){ return ( (jack_ringbuffer_read_space(vrb->for_writer1) + jack_ringbuffer_read_space(vrb->for_writer2)) / sizeof(void*)); } jack_capture-0.9.73/vringbuffer.h000066400000000000000000000075541310754750000167520ustar00rootroot00000000000000 #include #include #include #ifdef __APPLE__ #include #else #include #endif #include "upwaker.h" #include enum vringbuffer_receiver_callback_return_t { VRB_CALLBACK_DIDNT_USE_BUFFER, VRB_CALLBACK_USED_BUFFER }; struct vringbuffer_t; typedef int (*Vringbuffer_autoincrease_callback) (struct vringbuffer_t *vrb, bool first_time, int reading_size, int writing_size); typedef enum vringbuffer_receiver_callback_return_t (*Vringbuffer_receiver_callback) (struct vringbuffer_t *vrb, bool first_time, void *buffer); typedef struct vringbuffer_list_t{ struct vringbuffer_list_t *next; }vringbuffer_list_t; typedef struct vringbuffer_t{ jack_ringbuffer_t *for_writer1; jack_ringbuffer_t *for_writer2; jack_ringbuffer_t *for_reader; size_t element_size; int curr_num_elements; int max_num_elements; vringbuffer_list_t *allocated_mem; pthread_mutex_t increase_lock; bool please_stop; // Receiver callback pthread_t receiver_thread; upwaker_t *receiver_trigger; #ifdef __APPLE__ semaphore_t receiver_started; #else sem_t receiver_started; #endif Vringbuffer_receiver_callback receiver_callback; // Autoincrease callback pthread_t autoincrease_thread; upwaker_t *autoincrease_trigger; #ifdef __APPLE__ semaphore_t autoincrease_started; #else sem_t autoincrease_started; #endif Vringbuffer_autoincrease_callback autoincrease_callback; useconds_t autoincrease_interval; }vringbuffer_t; vringbuffer_t* vringbuffer_create (int num_elements_during_startup, int max_num_elements,size_t element_size); void vringbuffer_stop_callbacks(vringbuffer_t* vrb); void vringbuffer_delete(vringbuffer_t* vrb); void vringbuffer_increase (vringbuffer_t *vrb, int num_elements, void **elements); // Creates a new autoincrease thread. The callback is called each 'interval' microseconds. // The callback does not have to allocate memory itself, but instead the function returns the number of elements (not bytes) to allocate. // The callback is also responsible for setting nicety and priority of the thread. This should be done when called the first time (i.e. when first_time==true). // // The return value for autoincrease_callback is also ignored the first time, and both reading_size and writing_size will have the values 0. // Furthermore, the first time autoincrease_callback is called, it is called before vringbuffer_set_autoincrease_callback() returns // // An autoincrease callback could be implemented like this: /* static int autoincrease_callback(vringbuffer_t *vrb, bool first_call, size_t reading_size, size_t writing_size){ if(first_call){ set_high_priority(); return 0; } if(writing_size < 1024) return 2; return 0; } int main(){ ... vringbuffer_t *vrg = vrngbuffer_create(quite_large_number,very_large_number,element_size); vringbuffer_set_autoincrease_callback(vrg,autoincrease_callback,quite_often); ... } */ // Set interval==0 if you wan't the callback to be triggered if using the trigger_autoincrease_callback function. void vringbuffer_set_autoincrease_callback (vringbuffer_t *vrg, Vringbuffer_autoincrease_callback callback, useconds_t interval); // Don't call unless interval==0 void vringbuffer_trigger_autoincrease_callback(vringbuffer_t *vrb); void* vringbuffer_get_reading (vringbuffer_t *vrb); void vringbuffer_return_reading (vringbuffer_t *vrb, void *data); // Returns available number of elements in the ringbuffer int vringbuffer_reading_size (vringbuffer_t *vrb); void vringbuffer_set_receiver_callback(vringbuffer_t *vrb,Vringbuffer_receiver_callback receiver_callback); void* vringbuffer_get_writing (vringbuffer_t *vrb); void vringbuffer_return_writing (vringbuffer_t *vrb, void *data); // Returns available number of elements in the ringbuffer int vringbuffer_writing_size (vringbuffer_t *vrb);