brutefir-1.0o/0002775000175000017500000000000012752354364012704 5ustar andersandersbrutefir-1.0o/CHANGES0000644000175000017500000005310312752354353013673 0ustar andersandersBruteFIR v1.0o August 9, 2016 * Applied some minor fixes reported by Debian package maintainer. BruteFIR v1.0n August 9, 2016 * Minor code cleanups, no functional change. * Don't set the executable flag of bfio and bflogic modules (security). BruteFIR v1.0m November 27, 2013 * Fixed SSE2 bug introduced in 1.0l. * Added safety_limit feature to protect speakers. * Fixed a rare race condition on multi-core. * More sample format fixing, removed S16_4*, changed S24_4* to represent low bits in sample, so it matches ALSA formats. If you used S24_4LE in earlier versions you should now use S32_LE. BruteFIR v1.0l October 6, 2013 * Made the code compile well on x86-64. * Replaced legacy assembler code with new SSE/SSE2 C code, 3DNow support dropped. * Fixed filter indexing bug in the 'cffa' CLI command. * S24_LE now maps to Alsa SND_PCM_FORMAT_S24_3LE and S24_4LE to SND_PCM_FORMAT_S24_LE (same for BE of course). Also added possibility to use Alsa syntax "S24_3LE" in config file (means same as the old "S24_LE"). * Refreshed JACK I/O module to make up to date with current API versions. BruteFIR v1.0k March 31, 2009 * Refreshed ALSA and JACK I/O modules to make them up to date with current API versions. BruteFIR v1.0j March 5, 2009 * Fixed a memory leak in the CLI. BruteFIR v1.0i October 8, 2006 * Adjusted how sleeping works in CLI script mode. * Minor sleep/script parse bug fixes in CLI logic module. * Minor ALSA I/O module bug fix. * No longer complain when trying to change static delays to same value. BruteFIR v1.0h July 12, 2006 * Added subsample delay feature * Added support for naming JACK ports. * Added support for text format in file I/O module. * Adjusted realtime priority logic modules (lowered it compared to the filter process. * output_timed logic module callbacks now works. * Getting inplace plans from convolver_fftplan() now works. BruteFIR v1.0g March 30, 2006 * Fixed bug in input mixer causing garbled result when inputs exceeded four. * Fixed minor delay setting bug. BruteFIR v1.0f August 11, 2005 * Improved filter configuration error messages. * Fixed filter parse bug. BruteFIR v1.0e June 28, 2005 * Made it compile with GCC 4.0 BruteFIR v1.0d June 12, 2005 * Fixed a fatal file descriptor bug introduced in v1.0b, causing problems with multiple inputs/outputs. * Added 'priority' parameter to JACK I/O module and made some minor adjustments. * Fixed ALSA I/O module bug, causing it to get stuck in infinit loop on some hardware. * Fixed minor CLI config parsing bug concerning the 'port' field. BruteFIR v1.0c January 4, 2005 * Ooops, CLI module did not accept '\r' in v1.0b, fixed that. BruteFIR v1.0b November 21, 2004 * JACK I/O module now allows for multiple BruteFIR instances, and it no longer necessary to connect to external ports at startup. * Serial line and pipe support for the CLI module. * Bug fix in equaliser rendering, which was triggered in some situations (thanks to Tero Kontkanen for the bug fix). * Fixed directory creation in install target in the Makefile. * Fixed typo in CLI module help text. * Some code cleanups. BruteFIR v1.0a August 7, 2004 * Got rid of BF_MAXCOEFFS (that is no longer any coeff limit). * The CLI module will now try to remove an existing local socket when it is restarted. * Updated the example files xtc_config and massive_config, and included two example coefficient files. BruteFIR v1.0 April 21, 2004 * An OSS I/O module was added. * Powersave now supports analog inputs (set a noise level). * Fixed some ugly castings and other stuff causing compiler warnings. * Fixed -fPIC likning issue. * Fixed I/O subsystem bug which caused full duplex file descriptors to fail (used by the new OSS I/O module). * Now it is possible to compile on FreeBSD and Solaris again. * Clearer license statement (no license change though, still GPL). BruteFIR v0.99n February 22, 2004 * Removed merge function, it was not good enough. * Added crossfade function which makes what the merge function did but considerably better. The drawback is that roughly twice the CPU time is required per filter at coefficient change (only when crossfade is active of course). * Added a new benchmark config, bench5_config, which is a copy of bench2_config but with crossfade activated and a script using it, so the crossfade cost (ends up in the convolve column) can easily be benchmarked. * Do not use posix_memalign() in buggy glibc versions. * Corrected ALSA pcm linking code. BruteFIR v0.99m January 17, 2004 * Fixed merge config bug (only activated if dither was activated too). * If the CLI runs in script mode, it is run directly from the filter process for better timing precision, and it is now possible to sleep in blocks (sleep b10; will skip ten blocks). * Removed truncation/extension warning when loading coefficients from files, and made loading more efficient (don't load whole file, only as much as to fill the coefficient space). * Fixed rare deadlock bug which could be triggered at startup. * Updated ALSA code to support 1.0 version of the API. * Replaced OOM killer hack with /proc/meminfo tracker. BruteFIR v0.99l October 26, 2003 * Added discontinuity merger function, which hides clicks that may occur when coefficients are changed in runtime. * Added skip feature to coeff loading, to skip given amount of bytes in the beginning when loading a filter from file. * Fixed load balancing info bug. BruteFIR v0.99k August 10, 2003 * Added daemon mode. * Fixed buffer allocation bug for fftw3 plan creation. * Fixed ALSA I/O module logging bug. * Fixed another load balancing bug. * Fixed array overrun read bug. * Fixed power save bug which could cause noise output. BruteFIR v0.99j July 11, 2003 * Migrated to FFTW3 * Support for 32 bit and 64 bit precision in the same binary (use the new float_bits setting). * Buffer underflows/overflows can optionally be ignored in the ALSA I/O module, by using the new ignore_xrun setting. * Improved powersave function, filters will now be turned off on an individual basis. * Issue a warning if realtime priority could not be set, instead of aborting. A good behaviour in situations where root access is not feasible. * Support for a benchmark mode (use the new benchmark setting). * Included some sample benchmark configuration files. * ALSA card linking can be disabled with the new link setting for the ALSA I/O module. This is only to be used as a work-around if problems are experienced when linking is used (on per default). * Delay buffer can now optionally be specified per virtual channel with the new setting individual_maxdelay. The old maxdelay setting can still be used and is interpreted as before. * Added version check for loadable modules * Added loop option to the file I/O module (for looping input). * Now setting proper magnitudes at equaliser edges in equaliser module. * FFTW wisdom is now extended cumulatively (as it should). * Fixed string parsing bug. * Fixed input mute bug. * Fixed bug in the automatic load balancer. * Fixed tausworthe random state init bug. * Some fixes to compile nicely with GCC 3.3. BruteFIR v0.99i February 11, 2003 * Fixed lots of stupid mistakes made in previous release. BruteFIR v0.99h February 9, 2003 * Added new sample native endian (NE) sample formats. * Support for auto sample format (only JACK I/O module so far) * Real-time index and overflow detection now works with callback I/O. * Mixing blocking input with callback output now works. * Added a simple automatic load balancer for multiple CPUs. BruteFIR v0.99g February 2, 2003 * Added support for callback I/O. * Added JACK I/O module. * Proper synchronised start of CLI script. * Added 'echo' option to CLI module, for printing commands to console. * Fixed multiple input devices input mute bug. * Setting suid now works again. * Configuration file can contain international characters again. * Fixed broken pipe start bug with multiple alsa output devices. * Fixed broken syntax error handling in CLI module. BruteFIR v0.99f January 5, 2003 * Peak meter more exact, writes -inf instead of +0.0 when there is zero output. * Peak meter now works for floating point output. * Removed obsolete configuration file parse warning. BruteFIR v0.99e December 25, 2002 * Added debug trace code for debugging buffer over/underflows. * Added support for having shorter filter partitions than the sound card hardware supports natively. * Various latency improvements, should be less risk for underflow now. Thanks to Denis Sbragion for testing on ice1712 hardware. * Added optional "input poll mode", which provides support for strange hardware period sizes on some sound cards, and the possibility to run with shorter software period size than the hardware supports. * Added '-nodefault' option which will make BruteFIR ignore the default configuration file. * Properly terminate if one process is lost due to the computer is out of memory or similar. * Try to terminate nicely when the computer runs out of memory, and give a correct error code if that happens. * It is now possible to have more than 31 bands in the equaliser module (bug fixed by Andreas Johansson). * Real-time index now works with sound cards again. * Removed bfconf_lexical.c (flex output) which mistakingly has been included in the distributions. * Improved configuration file parser. It is now faster, and can accept very large strings (with line breaks), and floating point values with exponent (example -2.0e-05). * It is now possible to sleep in millisecond resolution in the CLI module. * Sample clock drift check removed (did not work with new code). May be added later. BruteFIR v0.99d November 28, 2002 * Improved real-time index, much simpler and more reliable algorithm. * Improved filter rendering performance in equaliser module. * Fixed ALSA I/O module bug causing it not to work with odd sound card period sizes (for example ice1712 sound cards). * Added a powersave feature, which will suspend convolution when there is zero input. BruteFIR v0.99c October 10, 2002 * Added double buffer support to the equaliser module. * Added simple script support to the CLI module. * Fixed format conversion errors (on the read side) for S24_LE and S24_BE. * Fixed a couple of config file parsing bugs. * Corrected a slight synchronisation problem between input and output. * Reduced risk of buffer underflow at startup. * Fixed bug (outdated input buffers) when changing coefficients in partitioned convolution when coefficients are of different lengths. BruteFIR v0.99b September 12, 2002 * Fixed bug which caused buffer underflow in ALSA I/O module when software buffer is larger than hardware buffer. * Linking problem with ALSA module resolved. BruteFIR v0.99a August 25, 2002 * Attach coefficient shared memory segments with read/write permissions at all times in order to allow modules such as "eq" to operate on them. * Fixed array overrun bug seen with some virtual channel configurations * Improved handling of piped commands in the "eq" module. BruteFIR v0.99 August 4, 2002 * Fixed I/O delay ("fixed" as in constant, not as in "fixed a bug"). For properly designed sound cards, the I/O delay will always be exactly twice the filter block length. * An equaliser module is now included. Equalisation can be changed in runtime through the CLI. * Slight change in configuration file format in order to support advanced configuration of modules. * Some bug fixes. BruteFIR v0.98e July 26, 2002 * Added the possibility to scale input/outputs with negative values BruteFIR v0.98d July 21, 2002 * Fixed scaling bug in coefficient parser * Fixed sample rate set problem in ALSA code BruteFIR v0.98c June 14, 2002 * Fixed mixbuffer assignment bug (caused looping sound with some filter configurations) * Fixed large integer parsing bug * Added runtime FFTW link check to see if the common mistake linking 32 bit BruteFIR with 64 bit FFTW (or the other way around) has been made. BruteFIR v0.98b May 5, 2002 * Native SSE2 support for Pentium 4 processors * Added monitor_rate option * Better Makefile * Compiles and runs on Solaris on Sparc * Code style more strict * Compiles cleanly with gcc3 * Yet another exit program bug fix BruteFIR v0.98a April 16, 2002 * Fixed bug which sometimes left a stray process after exit * Improved real-time index calculation to handle SMP properly * Measure sample rate and exit if it is altered during runtime * The program now has meaningful exit codes (exited due to sample rate change, out of memory, buffer underflow etc) BruteFIR v0.98 March 25, 2002 * Support for virtual channels (new mapping field in input and output structures) * Support for muting input channels (also in CLI, new 'tmi' command) * Support for the old skip and append parameters in bfio_file module * Fixed error message passing bug in ALSA code * Fixed double close bug BruteFIR v0.97d December 20, 2001 * Fixed mute array bug (channels could be muted without saying so) * Added support for -quiet parameter to suppress title and warnings BruteFIR v0.97c December 17, 2001 * ALSA I/O module now accepts raw ALSA device names as module parameter. If you want to do the same thing as before, just at "hw:" at the start of the string, forming "alsa"/"hw:0,0" for example. BruteFIR v0.97b December 16, 2001 * Added new sample formats S24_4LE/BE and S16_4LE/BE * Fixed I/O module load bug (if two types of modules was mixed, the program crashed) * Fixed integer overflow bug for 32 bit formats (caused clipping to fail with disasterous results: a loud tone instead of proper clipping on overflow) * Fixed overflow update bug * Fixed missing error message bug in ALSA I/O module * Added check for dither feasibility BruteFIR v0.97a December 14, 2001 * Fixed dither bug (if dither not activated, the program crashed) * Fixed default file parse bug (channel fields without '/' lead to crash) * Fixed mute array parse bug (all output structures shared the same mute array) * Support for short delay arrays in the configuration files, now they are extended with zeros (instead of calling it a parse error) BruteFIR v0.97 December 9, 2001 * Major architectural change: all sample I/O moved to external modules, support for adaptive filtering and other things through logic modules. * ALSA and raw PCM support now in I/O modules. * CLI is in a logic module. * Changed sample format strings, example: "16s2l0" is now "S16_LE". * Added support for floating point raw pcm formats. * A few minor bugfixes BruteFIR v0.96a September 27, 2001 * Fixed minor configuration parse bugs. * Added "dirac pulse" feature. * Added proper signal handler for termination of the program. * Fixed file write bug that could cause last part of output file to be zero. * Added automatic detection of processor capabilities. BruteFIR v0.96 August 20, 2001 * It is now possible to make filter networks, that is put filters in series, and so on, as long as no loops are made. * Variable length filters (actually variable length coefficient sets). * Predelay per filter setting. * Fixed mute setting bug. * Fixed severe digital audio setup bug. BruteFIR v0.95b July 18, 2001 * Thanks to Doug Kelly a severe bug (block bounds violation) in the conversion from 32 bit to floating point format was found and fixed. * Realtime priorites of the processes was changed, in order to keep the CLI responsive even in realtime operation. This may be bad for extremely low latencies (sub 100 ms), haven't tested that. It is utterly necessary for higher latencies though. BruteFIR v0.95a June 3, 2001 * Fixed severe bug in raw filter reading (caused core dump with long filters). * Added lock_memory parameter, so the mlock call can be controlled (it is unnecessary to run mlock when there is no swap, and mlock can think it is out of memory when lots of shared memory is used(?) ). BruteFIR v0.95 May 26, 2001 * Updated ALSA support, non-interleaved hardware (like RME9652) is now supported. * Updated and added some CLI commands. * Added support for delay changes in runtime. * Added support for giving shared memory references for coefficient sets, which is useful when using BruteFIR from another program. * Fixed severe bug which caused choppy sound when combining files with sound cards for inputs/outputs. * Improved the Makefile. BruteFIR v0.94a April 11, 2001 * Fixed a severe bug in the ALSA support code, which caused BruteFIR to report "Hardware does not support enough fragments" together with common sound cards. BruteFIR v0.94 April 8, 2001 * New convolution algorithm which allows for even higher output. Hand- coded assembler optimisations for SSE and 3DNow are included (also for x87, but that code is naive and slow). * Now it is possible to mute channels from the CLI. * Configuration file can be read from stdin (filename = stdin) * Some bugfixes. BruteFIR v0.93 March 3, 2001 * I/O-delay reduction algorithm implmented. BruteFIR v0.92 February 17, 2001 * Fixed bug causing long delays not to work. * ALSA support updated. * Made double precision compilation the default. BruteFIR v0.91a January 30, 2001 * Fixed a major bug, channels could be lost when running in more than one process, which could for example result in mono playback. BruteFIR v0.91 January 28, 2001 * ALSA support updated * Added nosound target to makefile 'make brutefir_nosound' * Mixing and volume now applied in the frequency domain, which leads to large performance improvements for common configurations * Improved realtime reliability through better process synchronisation and setting processes to realtime priority * Some code cleanups BruteFIR v0.9 January 15, 2001 * ALSA support implemented (ALSA v0.6, that is the CVS version) * Isolated FFT and sound card interfaces so it should be simple to port to use other FFT libraries / sound card APIs. * Some code cleanups BruteFIR v0.6 January 13, 2001 * Dither support implemented (high pass TPDF) * Sample requantisation now works as a proper mid-tread requantiser * Optimised delay buffer handling when delays are short * Fixed minor bugs in sample format parsing/conversion * Some code cleanups BruteFIR v0.52 January 8, 2001 * Now conversion to and from all supported raw sample formats should be complete and correct for both little and big endian architectures. BruteFIR v0.51 January 7, 2001 * Added customisable delay for input and output channels * Removed some meaningless settings from configuration * Added printout of I/O-delay approximation * Fixed file ending bug BruteFIR v0.5 January 6, 2001 * Initial release. No ALSA support yet. brutefir-1.0o/LICENSE0000644000175000017500000000067512752354353013713 0ustar andersanders This collective work, is Copyright (C) 2001 - 2004 by Anders Torger. Individual portions may be copyright by individual contributors, and are included in this collective work with permission of the copyright owners. This collective work is licensed under the GNU General Public License version 2.0. A copy of the license is found in the file "GPL-2.0". A copy of this work can be downloaded from http://www.ludd.luth.se/~torger/brutefir.html brutefir-1.0o/GPL-2.00000644000175000017500000004311012752354353013537 0ustar andersanders GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General Public License instead of this License. brutefir-1.0o/Makefile0000644000175000017500000001051612752354353014341 0ustar andersanders################################### CFLAGS += CPPFLAGS += LDFLAGS += ################################### # Where to install INSTALL_PREFIX = $(DESTDIR)/usr/local ################################### # Where to find libraries, and their header files. LIBPATHS = -L/usr/local/lib INCLUDE = -I/usr/local/include ifdef FFTW_PATH LIBPATHS += -L$(FFTW_PATH)/lib INCLUDE += -I$(FFTW_PATH)/include endif ################################### # FFTW3 libraries for single and double precision FFTW_LIB = -lfftw3 -lfftw3f ################################### # Binaries FLEX = flex LD = gcc CC = gcc CHMOD = chmod ################################### # Flags CC_WARN = -Wall -Wpointer-arith -Wshadow \ -Wcast-align -Wwrite-strings -Wstrict-prototypes \ -Wmissing-prototypes -Wmissing-declarations -Wnested-externs CC_FLAGS = $(DEFINE) -O2 $(CFLAGS) $(CPPFLAGS) CC_FPIC = -fPIC LD_SHARED = -shared CHMOD_REMOVEX = -x ################################### # Objects and libs for targets BRUTEFIR_LIBS = $(FFTW_LIB) -lm BRUTEFIR_OBJS = brutefir.o fftw_convolver.o bfconf.o bfrun.o firwindow.o \ emalloc.o shmalloc.o dai.o bfconf_lexical.o inout.o dither.o delay.o BRUTEFIR_SSE_OBJS = convolver_xmm.o BFIO_FILE_OBJS = bfio_file.fpic.o BFIO_ALSA_LIBS = -lasound BFIO_ALSA_OBJS = bfio_alsa.fpic.o emalloc.fpic.o inout.fpic.o BFIO_OSS_OBJS = bfio_oss.fpic.o emalloc.fpic.o BFIO_JACK_LIBS = -ljack BFIO_JACK_OBJS = bfio_jack.fpic.o emalloc.fpic.o inout.fpic.o BFLOGIC_CLI_OBJS = bflogic_cli.fpic.o inout.fpic.o BFLOGIC_EQ_OBJS = bflogic_eq.fpic.o emalloc.fpic.o shmalloc.fpic.o BIN_TARGETS = brutefir LIB_TARGETS = cli.bflogic eq.bflogic file.bfio ################################### # System-specific settings UNAME = $(shell uname) UNAME_P = $(shell uname -p) UNAME_M = $(shell uname -m) # Linux ifeq ($(UNAME),Linux) ifeq ($(UNAME_M),i586) BRUTEFIR_OBJS += $(BRUTEFIR_SSE_OBJS) CC_FLAGS += -msse endif ifeq ($(UNAME_M),i686) BRUTEFIR_OBJS += $(BRUTEFIR_SSE_OBJS) CC_FLAGS += -msse endif ifeq ($(UNAME_M),x86_64) BRUTEFIR_OBJS += $(BRUTEFIR_SSE_OBJS) CC_FLAGS += -msse endif ifneq (,$(findstring sparc,$(UNAME_M))) CC_FLAGS += -Wa,-xarch=v8plus endif BRUTEFIR_LIBS += -ldl LDMULTIPLEDEFS = -Xlinker --allow-multiple-definition # assume that we have oss and jack, alsa being linux-only ifeq ($(UNAME),Linux) LIB_TARGETS += alsa.bfio endif LIB_TARGETS += oss.bfio LIB_TARGETS += jack.bfio endif # FreeBSD ifeq ($(UNAME),FreeBSD) ifeq ($(UNAME_M),i386) BRUTEFIR_OBJS += $(BRUTEFIR_IA32_OBJS) endif # assume that we have oss LIB_TARGETS += oss.bfio endif TARGETS = $(BIN_TARGETS) $(LIB_TARGETS) ################################### # Targets all: $(TARGETS) %.fpic.o: %.c $(CC) -o $@ -c $(LDFLAGS) $(INCLUDE) $(CC_FPIC) $(CC_WARN) $(CC_FLAGS) $< %.o: %.c $(CC) -o $@ -c $(LDFLAGS) $(INCLUDE) $(CC_WARN) $(CC_FLAGS) $< # special rule to avoid to get warnings from code generated by flex bfconf_lexical.o: bfconf_lexical.c $(CC) -o $@ -c $(LDFLAGS) $(INCLUDE) $(CC_FLAGS) $< %.c: %.lex $(FLEX) -o$@ $< brutefir: $(BRUTEFIR_OBJS) $(CC) $(LDFLAGS) $(LIBPATHS) $(LDMULTIPLEDEFS) -o $@ $(BRUTEFIR_OBJS) $(BRUTEFIR_LIBS) alsa.bfio: $(BFIO_ALSA_OBJS) $(LD) $(LD_SHARED) $(LDFLAGS) $(CC_FPIC) $(LIBPATHS) -o $@ $(BFIO_ALSA_OBJS) $(BFIO_ALSA_LIBS) -lc $(CHMOD) $(CHMOD_REMOVEX) $@ oss.bfio: $(BFIO_OSS_OBJS) $(LD) $(LD_SHARED) $(LDFLAGS) $(CC_FPIC) $(LIBPATHS) -o $@ $(BFIO_OSS_OBJS) -lc $(CHMOD) $(CHMOD_REMOVEX) $@ jack.bfio: $(BFIO_JACK_OBJS) $(LD) $(LD_SHARED) $(LDFLAGS) $(CC_FPIC) $(LIBPATHS) -o $@ $(BFIO_JACK_OBJS) $(BFIO_JACK_LIBS) -lc $(CHMOD) $(CHMOD_REMOVEX) $@ file.bfio: $(BFIO_FILE_OBJS) $(LD) $(LD_SHARED) $(LDFLAGS) $(CC_FPIC) $(LIBPATHS) -o $@ $(BFIO_FILE_OBJS) -lc $(CHMOD) $(CHMOD_REMOVEX) $@ cli.bflogic: $(BFLOGIC_CLI_OBJS) $(LD) $(LD_SHARED) $(LDFLAGS) $(CC_FPIC) $(LIBPATHS) -o $@ $(BFLOGIC_CLI_OBJS) -lc $(CHMOD) $(CHMOD_REMOVEX) $@ eq.bflogic: $(BFLOGIC_EQ_OBJS) $(LD) $(LD_SHARED) $(LDFLAGS) $(CC_FPIC) $(LIBPATHS) -o $@ $(BFLOGIC_EQ_OBJS) -lc $(CHMOD) $(CHMOD_REMOVEX) $@ install: $(BIN_TARGETS) $(LIB_TARGETS) install -d $(INSTALL_PREFIX)/bin $(INSTALL_PREFIX)/lib/brutefir install $(BIN_TARGETS) $(INSTALL_PREFIX)/bin install $(LIB_TARGETS) $(INSTALL_PREFIX)/lib/brutefir clean: rm -f *.core core bfconf_lexical.c $(BRUTEFIR_OBJS) $(BFIO_FILE_OBJS) \ $(BFLOGIC_CLI_OBJS) $(BFLOGIC_EQ_OBJS) $(BFIO_ALSA_OBJS) $(BFIO_OSS_OBJS) \ $(BFIO_JACK_OBJS) $(TARGETS) brutefir-1.0o/README0000644000175000017500000000070012752354353013553 0ustar andersanders BruteFIR ================================================================================ This is the source code for a generic FIR filter (convolution) engine, licensed under the GNU General Public License version 2.0. The documentation is found on the web, http://www.ludd.ltu.se/~torger/brutefir.html A copy of the web page is included in this archive, but the one found on the web may be more up to date. Anders Torger, torger@ludd.ltu.se brutefir-1.0o/asmprot.h0000644000175000017500000000115112752354353014532 0ustar andersanders/* * (c) Copyright 2001, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _ASMPROT_H_ #define _ASMPROT_H_ void convolver_sse_convolve_add(void *input_cbuf, void *coeffs, void *output_cbuf, int loop_counter); void convolver_sse2_convolve_add(void *input_cbuf, void *coeffs, void *output_cbuf, int loop_counter); void convolver_3dnow_convolve_add(void *input_cbuf, void *coeffs, void *output_cbuf, int loop_counter); #endif brutefir-1.0o/bench1_config0000644000175000017500000000163712752354353015315 0ustar andersanderssampling_rate: 44100; filter_length: 8192,8; benchmark: true; modules_path: "."; convolver_config: ".fftw3wisdom"; coeff 0 { filename: "dirac pulse"; }; coeff 1 { filename: "dirac pulse"; }; coeff 2 { filename: "dirac pulse"; }; coeff 3 { filename: "dirac pulse"; }; coeff 4 { filename: "dirac pulse"; }; coeff 5 { filename: "dirac pulse"; }; input 0, 1 { device: "file" { path: "/dev/zero"; }; sample: "S24_4LE"; channels: 2; }; output 0, 1 { device: "file" { path: "/dev/null"; }; sample: "S24_4LE"; dither: false; channels: 2; }; filter 0 { from_filters: 2, 5; to_outputs: 0; coeff: 0; }; filter 1 { from_filters: 3, 4; to_outputs: 1; coeff: 1; }; filter 2 { from_inputs: 0; to_filters: 0; coeff: 2; }; filter 3 { from_inputs: 0; to_filters: 1; coeff: 3; }; filter 4 { from_inputs: 1; to_filters: 1; coeff: 4; }; filter 5 { from_inputs: 1; to_filters: 0; coeff: 5; }; brutefir-1.0o/bench2_config0000644000175000017500000000410012752354353015302 0ustar andersanderssampling_rate: 44100; filter_length: 8192,8; benchmark: true; modules_path: "."; convolver_config: ".fftw3wisdom"; coeff 0 { filename: "dirac pulse"; }; input 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "file" { path: "/dev/zero"; }; sample: "S24_4LE"; channels: 26; }; output 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "file" { path: "/dev/null"; }; sample: "S24_4LE"; channels: 26; dither: false; }; filter 0 { from_inputs: 0; to_outputs: 0; coeff: 0; }; filter 1 { from_inputs: 1; to_outputs: 1; coeff: 0; }; filter 2 { from_inputs: 2; to_outputs: 2; coeff: 0; }; filter 3 { from_inputs: 3; to_outputs: 3; coeff: 0; }; filter 4 { from_inputs: 4; to_outputs: 4; coeff: 0; }; filter 5 { from_inputs: 5; to_outputs: 5; coeff: 0; }; filter 6 { from_inputs: 6; to_outputs: 6; coeff: 0; }; filter 7 { from_inputs: 7; to_outputs: 7; coeff: 0; }; filter 8 { from_inputs: 8; to_outputs: 8; coeff: 0; }; filter 9 { from_inputs: 9; to_outputs: 9; coeff: 0; }; filter 10 { from_inputs: 10; to_outputs: 10; coeff: 0; }; filter 11 { from_inputs: 11; to_outputs: 11; coeff: 0; }; filter 12 { from_inputs: 12; to_outputs: 12; coeff: 0; }; filter 13 { from_inputs: 13; to_outputs: 13; coeff: 0; }; filter 14 { from_inputs: 14; to_outputs: 14; coeff: 0; }; filter 15 { from_inputs: 15; to_outputs: 15; coeff: 0; }; filter 16 { from_inputs: 16; to_outputs: 16; coeff: 0; }; filter 17 { from_inputs: 17; to_outputs: 17; coeff: 0; }; filter 18 { from_inputs: 18; to_outputs: 18; coeff: 0; }; filter 19 { from_inputs: 19; to_outputs: 19; coeff: 0; }; filter 20 { from_inputs: 20; to_outputs: 20; coeff: 0; }; filter 21 { from_inputs: 21; to_outputs: 21; coeff: 0; }; filter 22 { from_inputs: 22; to_outputs: 22; coeff: 0; }; filter 23 { from_inputs: 23; to_outputs: 23; coeff: 0; }; filter 24 { from_inputs: 24; to_outputs: 24; coeff: 0; }; filter 25 { from_inputs: 25; to_outputs: 25; coeff: 0; }; brutefir-1.0o/bench3_config0000644000175000017500000000407712752354353015320 0ustar andersanderssampling_rate: 44100; filter_length: 65536; benchmark: true; modules_path: "."; convolver_config: ".fftw3wisdom"; coeff 0 { filename: "dirac pulse"; }; input 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "file" { path: "/dev/zero"; }; sample: "S24_4LE"; channels: 26; }; output 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "file" { path: "/dev/null"; }; sample: "S24_4LE"; channels: 26; dither: false; }; filter 0 { from_inputs: 0; to_outputs: 0; coeff: 0; }; filter 1 { from_inputs: 1; to_outputs: 1; coeff: 0; }; filter 2 { from_inputs: 2; to_outputs: 2; coeff: 0; }; filter 3 { from_inputs: 3; to_outputs: 3; coeff: 0; }; filter 4 { from_inputs: 4; to_outputs: 4; coeff: 0; }; filter 5 { from_inputs: 5; to_outputs: 5; coeff: 0; }; filter 6 { from_inputs: 6; to_outputs: 6; coeff: 0; }; filter 7 { from_inputs: 7; to_outputs: 7; coeff: 0; }; filter 8 { from_inputs: 8; to_outputs: 8; coeff: 0; }; filter 9 { from_inputs: 9; to_outputs: 9; coeff: 0; }; filter 10 { from_inputs: 10; to_outputs: 10; coeff: 0; }; filter 11 { from_inputs: 11; to_outputs: 11; coeff: 0; }; filter 12 { from_inputs: 12; to_outputs: 12; coeff: 0; }; filter 13 { from_inputs: 13; to_outputs: 13; coeff: 0; }; filter 14 { from_inputs: 14; to_outputs: 14; coeff: 0; }; filter 15 { from_inputs: 15; to_outputs: 15; coeff: 0; }; filter 16 { from_inputs: 16; to_outputs: 16; coeff: 0; }; filter 17 { from_inputs: 17; to_outputs: 17; coeff: 0; }; filter 18 { from_inputs: 18; to_outputs: 18; coeff: 0; }; filter 19 { from_inputs: 19; to_outputs: 19; coeff: 0; }; filter 20 { from_inputs: 20; to_outputs: 20; coeff: 0; }; filter 21 { from_inputs: 21; to_outputs: 21; coeff: 0; }; filter 22 { from_inputs: 22; to_outputs: 22; coeff: 0; }; filter 23 { from_inputs: 23; to_outputs: 23; coeff: 0; }; filter 24 { from_inputs: 24; to_outputs: 24; coeff: 0; }; filter 25 { from_inputs: 25; to_outputs: 25; coeff: 0; }; brutefir-1.0o/bench4_config0000644000175000017500000000174412752354353015317 0ustar andersanderssampling_rate: 44100; filter_length: 4096,13; benchmark: true; modules_path: "."; convolver_config: ".fftw3wisdom"; coeff 0 { filename: "dirac pulse"; }; coeff 1 { filename: "dirac pulse"; blocks: 1; }; input 0,1,2,3,4,5 { device: "file" { path: "/dev/zero"; }; sample: "S24_4LE"; channels: 4/0,3; mapping: 0,1,0,1,0,1; }; input 6,7 { device: "file" { path: "/dev/zero"; }; sample: "S8"; channels: 2; }; output 0 { device: "file" { path: "/dev/null"; }; sample: "S16_LE"; channels: 1; dither: true; }; output 1 { device: "file" { path: "/dev/null"; }; sample: "FLOAT_NE"; channels: 1; }; filter 0 { from_inputs: 0//-1, 1//2; delay: 1; to_outputs: 0/3; to_filters: 2, 1; coeff: 0; }; filter 1 { from_inputs: 6, 2; from_filters: 0/3; to_outputs: 1/5.32; to_filters: 2; coeff: 1; }; filter 2 { from_filters: 0, 1; from_inputs: 3, 4, 5, 7; to_outputs: 0, 1; coeff: 0; }; brutefir-1.0o/bench5_config0000644000175000017500000000611512752354353015315 0ustar andersanderssampling_rate: 44100; filter_length: 8192,8; benchmark: true; modules_path: "."; logic: "cli" { echo: false; script: "cfc 0 0; cfc 1 0; cfc 2 0; cfc 3 0; cfc 4 0; cfc 5 0; cfc 6 0; cfc 7 0; cfc 8 0; cfc 9 0; cfc 10 0; cfc 11 0; cfc 12 0; cfc 13 0; cfc 14 0; cfc 15 0; cfc 16 0; cfc 17 0; cfc 18 0; cfc 19 0; cfc 20 0; cfc 21 0; cfc 22 0; cfc 23 0; cfc 24 0; cfc 25 0; cfc 0 -1; cfc 1 -1; cfc 2 -1; cfc 3 -1; cfc 4 -1; cfc 5 -1; cfc 6 -1; cfc 7 -1; cfc 8 -1; cfc 9 -1; cfc 10 -1; cfc 11 -1; cfc 12 -1; cfc 13 -1; cfc 14 -1; cfc 15 -1; cfc 16 -1; cfc 17 -1; cfc 18 -1; cfc 19 -1; cfc 20 -1; cfc 21 -1; cfc 22 -1; cfc 23 -1; cfc 24 -1; cfc 25 -1;"; }; convolver_config: ".fftw3wisdom"; coeff 0 { filename: "dirac pulse"; }; input 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "file" { path: "/dev/zero"; }; sample: "S24_4LE"; channels: 26; }; output 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "file" { path: "/dev/null"; }; sample: "S24_4LE"; channels: 26; dither: false; }; filter 0 { crossfade: true; from_inputs: 0; to_outputs: 0; coeff: 0; }; filter 1 { crossfade: true; from_inputs: 1; to_outputs: 1; coeff: 0; }; filter 2 { crossfade: true; from_inputs: 2; to_outputs: 2; coeff: 0; }; filter 3 { crossfade: true; from_inputs: 3; to_outputs: 3; coeff: 0; }; filter 4 { crossfade: true; from_inputs: 4; to_outputs: 4; coeff: 0; }; filter 5 { crossfade: true; from_inputs: 5; to_outputs: 5; coeff: 0; }; filter 6 { crossfade: true; from_inputs: 6; to_outputs: 6; coeff: 0; }; filter 7 { crossfade: true; from_inputs: 7; to_outputs: 7; coeff: 0; }; filter 8 { crossfade: true; from_inputs: 8; to_outputs: 8; coeff: 0; }; filter 9 { crossfade: true; from_inputs: 9; to_outputs: 9; coeff: 0; }; filter 10 { crossfade: true; from_inputs: 10; to_outputs: 10; coeff: 0; }; filter 11 { crossfade: true; from_inputs: 11; to_outputs: 11; coeff: 0; }; filter 12 { crossfade: true; from_inputs: 12; to_outputs: 12; coeff: 0; }; filter 13 { crossfade: true; from_inputs: 13; to_outputs: 13; coeff: 0; }; filter 14 { crossfade: true; from_inputs: 14; to_outputs: 14; coeff: 0; }; filter 15 { crossfade: true; from_inputs: 15; to_outputs: 15; coeff: 0; }; filter 16 { crossfade: true; from_inputs: 16; to_outputs: 16; coeff: 0; }; filter 17 { crossfade: true; from_inputs: 17; to_outputs: 17; coeff: 0; }; filter 18 { crossfade: true; from_inputs: 18; to_outputs: 18; coeff: 0; }; filter 19 { crossfade: true; from_inputs: 19; to_outputs: 19; coeff: 0; }; filter 20 { crossfade: true; from_inputs: 20; to_outputs: 20; coeff: 0; }; filter 21 { crossfade: true; from_inputs: 21; to_outputs: 21; coeff: 0; }; filter 22 { crossfade: true; from_inputs: 22; to_outputs: 22; coeff: 0; }; filter 23 { crossfade: true; from_inputs: 23; to_outputs: 23; coeff: 0; }; filter 24 { crossfade: true; from_inputs: 24; to_outputs: 24; coeff: 0; }; filter 25 { crossfade: true; from_inputs: 25; to_outputs: 25; coeff: 0; }; brutefir-1.0o/bfconf.c0000664000175000017500000031517512752354353014315 0ustar andersanders/* * (c) Copyright 2001 - 2006, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include "defs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bfrun.h" #include "bfconf.h" #include "emalloc.h" #include "log2.h" #include "bit.h" #include "dai.h" #include "bfconf_grammar.h" #include "timermacros.h" #include "shmalloc.h" #include "dither.h" #include "inout.h" #include "swap.h" #include "bfmod.h" #include "pinfo.h" #include "numunion.h" #include "delay.h" #define PATH_SEPARATOR_CHAR '/' #define PATH_SEPARATOR_STR "/" #define CONVOLVER_NEEDS_CONFIGFILE 1 #define MINFILTERLEN 4 #define MAXFILTERLEN (1 << 30) struct bflex { int line; int token; union bflexval lexval; }; struct coeff { struct bfcoeff coeff; #define COEFF_FORMAT_RAW 1 #define COEFF_FORMAT_TEXT 3 #define COEFF_FORMAT_PROCESSED 4 int format; int skip; struct sample_format rawformat; char filename[PATH_MAX]; int shm_shmids[BF_MAXCOEFFPARTS]; int shm_offsets[BF_MAXCOEFFPARTS]; int shm_blocks[BF_MAXCOEFFPARTS]; int shm_elements; double scale; }; struct filter { struct bffilter filter; struct bffilter_control fctrl; char coeff_name[BF_MAXOBJECTNAME]; char *channel_name[2][BF_MAXCHANNELS]; char *filter_name[2][BF_MAXCHANNELS]; int process; }; struct iodev { int virtual_channels; int channel_intname[BF_MAXCHANNELS]; char *channel_name[BF_MAXCHANNELS]; int virt2phys[BF_MAXCHANNELS]; struct dai_channels ch; /* physical channels */ struct bflex *device_params; char device_name[BF_MAXOBJECTNAME]; int maxdelay; bool_t apply_dither; bool_t auto_format; }; union bflexval yylval; struct bfconf *bfconf = NULL; uint64_t base_ts = 0; static struct coeff *default_coeff = NULL; static struct filter *default_filter = NULL; static struct iodev *default_iodev[2] = { NULL, NULL }; static char *convolver_config = NULL; static char default_config_file[PATH_MAX]; static char current_filename[PATH_MAX]; static char *modules_path = NULL; static char *logic_names[BF_MAXMODULES]; static struct bflex *logic_params[BF_MAXMODULES]; static struct bflex *config_params; static int config_params_pos; static bool_t has_defaults = false; #define FROM_DB(db) (pow(10, (db) / 20.0)) void parse_error(const char msg[]) { fprintf(stderr, "Parse error on line %d in file \"%s\":\n %s", lexlineno, current_filename, msg); exit(BF_EXIT_INVALID_CONFIG); } static char * tilde_expansion(const char path[]) { static char real_path[PATH_MAX]; struct passwd *pw; char *homedir = NULL, *p; int pos, slashpos = -1; /* FIXME: cleanup the code */ if (path[0] != '~') { strncpy(real_path, path, PATH_MAX); real_path[PATH_MAX-1] = '\0'; return real_path; } if ((p = strchr(path, PATH_SEPARATOR_CHAR)) != NULL) { slashpos = p - path; } if (path[1] == '\0' || slashpos == 1) { if ((homedir = getenv("HOME")) == NULL) { if ((pw = getpwuid(getuid())) != NULL) { homedir = pw->pw_dir; } } } else { strncpy(real_path, &path[1], PATH_MAX); real_path[PATH_MAX-1] = '\0'; if (slashpos != -1) { real_path[slashpos-1] = '\0'; } if ((pw = getpwnam(real_path)) != NULL) { homedir = pw->pw_dir; } } if (homedir == NULL) { strncpy(real_path, path, PATH_MAX); real_path[PATH_MAX-1] = '\0'; return real_path; } real_path[PATH_MAX-1] = '\0'; strncpy(real_path, homedir, PATH_MAX - 2); pos = strlen(homedir); if (homedir[0] == '\0' || homedir[strlen(homedir)-1] != PATH_SEPARATOR_CHAR) { strcat(real_path, PATH_SEPARATOR_STR); pos += 1; } if (slashpos != -1) { strncpy(&real_path[pos], &path[slashpos+1], PATH_MAX - pos - 1); } else { strncpy(&real_path[pos], &path[1], PATH_MAX - pos - 1); } real_path[PATH_MAX-1] = '\0'; return real_path; } static void create_default_config(void) { FILE *stream; if ((stream = fopen(tilde_expansion(DEFAULT_BFCONF_NAME), "wt")) == NULL) { fprintf(stderr, "Could not create default configuration file (" DEFAULT_BFCONF_NAME "): %s.\n", strerror(errno)); exit(BF_EXIT_OTHER); } fprintf(stream, "## DEFAULT GENERAL SETTINGS ##\n\ \n\ float_bits: 32; # internal floating point precision\n\ sampling_rate: 44100; # sampling rate in Hz of audio interfaces\n\ filter_length: 65536; # length of filters\n\ config_file: \"~/.brutefir_config\"; # standard location of main config file\n\ overflow_warnings: true; # echo warnings to stderr if overflow occurs\n\ show_progress: true; # echo filtering progress to stderr\n\ max_dither_table_size: 0; # maximum size in bytes of precalculated dither\n\ allow_poll_mode: false; # allow use of input poll mode\n\ modules_path: \".\"; # extra path where to find BruteFIR modules\n\ monitor_rate: false; # monitor sample rate\n\ powersave: false; # pause filtering when input is zero\n\ lock_memory: true; # try to lock memory if realtime prio is set\n\ sdf_length: -1; # subsample filter half length in samples\n\ safety_limit: 20; # if non-zero max dB in output before aborting\n" #ifdef CONVOLVER_NEEDS_CONFIGFILE "convolver_config: \"~/.brutefir_convolver\"; # location of " "convolver config file\n" #endif "\n\ ## COEFF DEFAULTS ##\n\ \n\ coeff {\n\ \tformat: \"TEXT\"; # file format\n\ \tattenuation: 0.0; # attenuation in dB\n\ \tblocks: -1; # how long in blocks\n\ \tskip: 0; # how many bytes to skip\n\ \tshared_mem: false; # allocate in shared memory\n\ };\n\ \n\ ## INPUT DEFAULTS ##\n\ \n\ input {\n\ \tdevice: \"file\" {}; # module and parameters to get audio\n\ \tsample: \"S16_LE\"; # sample format\n\ \tchannels: 2/0,1; # number of open channels / which to use\n\ \tdelay: 0,0; # delay in samples for each channel\n\ \tmaxdelay: -1; # max delay for variable delays\n\ \tsubdelay: 0,0; # subsample delay in 1/%dth sample for each channel\n\ \tmute: false,false; # mute active on startup for each channel\n\ };\n\ \n\ ## OUTPUT DEFAULTS ##\n\ \n\ output {\n\ \tdevice: \"file\" {}; # module and parameters to put audio\n\ \tsample: \"S16_LE\"; # sample format\n\ \tchannels: 2/0,1; # number of open channels / which to use\n\ \tdelay: 0,0; # delay in samples for each channel\n\ \tmaxdelay: -1; # max delay for variable delays\n\ \tsubdelay: 0,0; # subsample delay in 1/%dth sample for each channel\n\ \tmute: false,false; # mute active on startup for each channel\n\ \tdither: false; # apply dither\n\ };\n\ \n\ ## FILTER DEFAULTS ##\n\ \n\ filter {\n\ \tprocess: -1; # process index to run in (-1 means auto)\n\ \tdelay: 0; # predelay, in blocks\n\ \tcrossfade: false; # crossfade when coefficient is changed\n\ };\n\ ", BF_SAMPLE_SLOTS, BF_SAMPLE_SLOTS); fclose(stream); } static const char * token_name(int token) { switch (token) { case REAL: return "number"; case BOOLEAN: return "boolean"; case STRING: return "string"; case FIELD: return "field"; case EOS: return "end of statement (;)"; case LBRACE: return "left brace ({)"; case RBRACE: return "right brace (})"; case COMMA: return "comma (,)"; case SLASH: return "slash (/)"; case EOF: return "end of file"; case COEFF: return "coeff"; case INPUT: return "input"; case OUTPUT: return "output"; case FILTER: return "filter"; default: return "UNKNOWN"; } } static void unexpected_token(int expected_token, int yylex_ret) { char msg[4096]; sprintf(msg, "unexpected token, expected %s, got %s.\n", token_name(expected_token), token_name(yylex_ret)); parse_error(msg); } static void unrecognised_token(const char name[], const char string[]) { char msg[4096]; sprintf(msg, "unrecognised %s: \"%s\".\n", name, string); parse_error(msg); } static int make_integer(double number) { if (rint(number) != number) { parse_error("Expected integer, got floating point.\n"); } return (int)number; } static void get_token(int token) { int _token; if ((_token = yylex()) != token) { unexpected_token(token, _token); } } static void field_repeat_test(uint32_t *bitset, int bit) { if (bit_isset(bitset, bit)) { parse_error("Field is already set.\n"); } bit_set(bitset, bit); } static void field_mandatory_test(uint32_t bitset, uint32_t bits, const char name[]) { char msg[200]; if ((bitset & bits) != bits) { sprintf(msg, "At least one mandatory field is missing in %s.\n", name); parse_error(msg); } } static bool_t parse_sample_format(struct sample_format *sf, const char s[], bool_t allow_auto) { bool_t little_endian, native_endian; little_endian = true; native_endian = false; memset(sf, 0, sizeof(struct sample_format)); if (strcasecmp(s, "AUTO") == 0) { if (allow_auto) { return true; } parse_error("Cannot have \"AUTO\" sample format here.\n"); } /* new sample format string format */ if (strcasecmp(s, "S8") == 0) { sf->format = BF_SAMPLE_FORMAT_S8; sf->bytes = 1; sf->sbytes = 1; little_endian = false; } else if (strcasecmp(s, "S16_LE") == 0) { sf->format = BF_SAMPLE_FORMAT_S16_LE; sf->bytes = 2; sf->sbytes = 2; } else if (strcasecmp(s, "S16_BE") == 0) { sf->format = BF_SAMPLE_FORMAT_S16_BE; sf->bytes = 2; sf->sbytes = 2; little_endian = false; } else if (strcasecmp(s, "S16_NE") == 0) { sf->format = BF_SAMPLE_FORMAT_S16_NE; sf->bytes = 2; sf->sbytes = 2; native_endian = true; } else if (strcasecmp(s, "S24_LE") == 0 || strcasecmp(s, "S24_3LE") == 0) { sf->format = BF_SAMPLE_FORMAT_S24_LE; sf->bytes = 3; sf->sbytes = 3; } else if (strcasecmp(s, "S24_BE") == 0 || strcasecmp(s, "S24_3BE") == 0) { sf->format = BF_SAMPLE_FORMAT_S24_BE; sf->bytes = 3; sf->sbytes = 3; little_endian = false; } else if (strcasecmp(s, "S24_NE") == 0 || strcasecmp(s, "S24_3NE") == 0) { sf->format = BF_SAMPLE_FORMAT_S24_NE; sf->bytes = 3; sf->sbytes = 3; native_endian = true; } else if (strcasecmp(s, "S24_4LE") == 0) { sf->format = BF_SAMPLE_FORMAT_S24_4LE; sf->bytes = 4; sf->sbytes = 3; } else if (strcasecmp(s, "S24_4BE") == 0) { sf->format = BF_SAMPLE_FORMAT_S24_4BE; sf->bytes = 4; sf->sbytes = 3; little_endian = false; } else if (strcasecmp(s, "S24_4NE") == 0) { sf->format = BF_SAMPLE_FORMAT_S24_4NE; sf->bytes = 4; sf->sbytes = 3; native_endian = true; } else if (strcasecmp(s, "S32_LE") == 0) { sf->format = BF_SAMPLE_FORMAT_S32_LE; sf->bytes = 4; sf->sbytes = 4; } else if (strcasecmp(s, "S32_BE") == 0) { sf->format = BF_SAMPLE_FORMAT_S32_BE; sf->bytes = 4; sf->sbytes = 4; little_endian = false; } else if (strcasecmp(s, "S32_NE") == 0) { sf->format = BF_SAMPLE_FORMAT_S32_NE; sf->bytes = 4; sf->sbytes = 4; native_endian = true; } else if (strcasecmp(s, "FLOAT_LE") == 0) { sf->format = BF_SAMPLE_FORMAT_FLOAT_LE; sf->bytes = 4; sf->sbytes = 4; sf->isfloat = true; } else if (strcasecmp(s, "FLOAT_BE") == 0) { sf->format = BF_SAMPLE_FORMAT_FLOAT_BE; sf->bytes = 4; sf->sbytes = 4; sf->isfloat = true; little_endian = false; } else if (strcasecmp(s, "FLOAT_NE") == 0) { sf->format = BF_SAMPLE_FORMAT_FLOAT_NE; sf->bytes = 4; sf->sbytes = 4; native_endian = true; } else if (strcasecmp(s, "FLOAT64_LE") == 0) { sf->format = BF_SAMPLE_FORMAT_FLOAT64_LE; sf->bytes = 8; sf->sbytes = 8; sf->isfloat = true; } else if (strcasecmp(s, "FLOAT64_BE") == 0) { sf->format = BF_SAMPLE_FORMAT_FLOAT64_BE; sf->bytes = 8; sf->sbytes = 8; sf->isfloat = true; little_endian = false; } else if (strcasecmp(s, "FLOAT64_NE") == 0) { sf->format = BF_SAMPLE_FORMAT_FLOAT64_NE; sf->bytes = 4; sf->sbytes = 4; native_endian = true; } else { parse_error("Unknown sample format.\n"); } if (sf->isfloat) { sf->scale = 1.0; } else { sf->scale = 1.0 / (double)((uint64_t)1 << ((sf->sbytes << 3) - 1)); } #ifdef __BIG_ENDIAN__ if (native_endian) { sf->swap = false; switch (sf->format) { case BF_SAMPLE_FORMAT_S16_NE: sf->format = BF_SAMPLE_FORMAT_S16_BE; break; case BF_SAMPLE_FORMAT_S24_NE: sf->format = BF_SAMPLE_FORMAT_S24_BE; break; case BF_SAMPLE_FORMAT_S24_4NE: sf->format = BF_SAMPLE_FORMAT_S24_4LE; break; case BF_SAMPLE_FORMAT_S32_NE: sf->format = BF_SAMPLE_FORMAT_S32_BE; break; case BF_SAMPLE_FORMAT_FLOAT_NE: sf->format = BF_SAMPLE_FORMAT_FLOAT_BE; break; case BF_SAMPLE_FORMAT_FLOAT64_NE: sf->format = BF_SAMPLE_FORMAT_FLOAT64_BE; break; } } else { sf->swap = little_endian; } #endif #ifdef __LITTLE_ENDIAN__ if (native_endian) { switch (sf->format) { case BF_SAMPLE_FORMAT_S16_NE: sf->format = BF_SAMPLE_FORMAT_S16_LE; break; case BF_SAMPLE_FORMAT_S24_NE: sf->format = BF_SAMPLE_FORMAT_S24_LE; break; case BF_SAMPLE_FORMAT_S24_4NE: sf->format = BF_SAMPLE_FORMAT_S24_4LE; break; case BF_SAMPLE_FORMAT_S32_NE: sf->format = BF_SAMPLE_FORMAT_S32_LE; break; case BF_SAMPLE_FORMAT_FLOAT_NE: sf->format = BF_SAMPLE_FORMAT_FLOAT_LE; break; case BF_SAMPLE_FORMAT_FLOAT64_NE: sf->format = BF_SAMPLE_FORMAT_FLOAT64_LE; break; } } else { sf->swap = !little_endian; } #endif return false; } static void free_params(struct bflex *bflex) { int n; if (bflex == NULL) { return; } for (n = 0; bflex[n].token != 0; n++) { switch (bflex[n].token) { case BF_LEXVAL_STRING: efree(bflex[n].lexval.string); break; case BF_LEXVAL_FIELD: efree(bflex[n].lexval.field); break; } } efree(bflex); } static struct bflex * get_params(void) { int brace_depth, n, capacity; struct bflex *bflex; get_token(LBRACE); bflex = NULL; brace_depth = 0; capacity = 0; n = 0; while (true) { if (n == capacity) { capacity += 10; bflex = erealloc(bflex, capacity * sizeof(struct bflex)); } bflex[n].token = yylex(); bflex[n].line = lexlineno; memcpy(&bflex[n].lexval, &yylval, sizeof(yylval)); switch (bflex[n].token) { case BF_LEX_LBRACE: brace_depth++; break; case BF_LEX_RBRACE: if (brace_depth == 0) { /* finished */ bflex[n].token = 0; return bflex; } brace_depth--; break; case BF_LEXVAL_STRING: bflex[n].lexval.string = estrdup(yylval.string); break; case BF_LEXVAL_FIELD: bflex[n].lexval.field = estrdup(yylval.field); break; } n++; } } static int get_config_token(union bflexval *lexval) { if (config_params[config_params_pos].token == 0) { return 0; } lexlineno = config_params[config_params_pos].line; memcpy(lexval, &config_params[config_params_pos].lexval, sizeof(union bflexval)); return config_params[config_params_pos++].token; } /* FIXME: get_string_list and get_integer_list is very similar */ static int get_string_list(char firststring[], char *list[], int maxelements, int ending_token) { char *str = firststring; int pos = 0, token; bool_t expecting_string = false; do { if (!expecting_string) { if (pos == maxelements) { parse_error("String array is too long.\n"); } list[pos] = estrdup(str); pos++; } switch (token = yylex()) { case STRING: if (!expecting_string) { unexpected_token(ending_token, token); } str = yylval.string; expecting_string = false; break; default: if (token != ending_token) { unexpected_token(expecting_string ? STRING : ending_token, token); } /* fall through */ case COMMA: if (expecting_string) { unexpected_token(STRING, token); } expecting_string = true; break; } } while (token != ending_token); return pos; } static int get_integer_list(double firstnum, int list[], int maxelements, int ending_token) { double num = firstnum; int pos = 0, token; bool_t expecting_real = false; do { if (!expecting_real) { if (pos == maxelements) { parse_error("Integer array is too long.\n"); } list[pos] = make_integer(num); pos++; } switch (token = yylex()) { case REAL: if (!expecting_real) { unexpected_token(ending_token, token); } num = yylval.real; expecting_real = false; break; default: if (token != ending_token) { unexpected_token(expecting_real ? REAL : ending_token, token); } /* fall through */ case COMMA: if (expecting_real) { unexpected_token(REAL, token); } expecting_real = true; break; } } while (token != ending_token); return pos; } /* returns true if integer, returns false if string */ static bool_t get_string_or_int(char str[], int strmax, int *integer) { int token; switch (token = yylex()) { case REAL: *integer = make_integer(yylval.real); str[0] = '\0'; return true; case STRING: strncpy(str, yylval.string, strmax); str[strmax-1] = '\0'; *integer = 0; return false; default: unexpected_token(STRING, token); } /* never reached */ return false; } static struct coeff * parse_coeff(bool_t parse_default, int intname) { struct coeff *coeff; uint32_t bitset = 0; int token, n; coeff = emalloc(sizeof(struct coeff)); if (!parse_default) { if (has_defaults) { memcpy(coeff, default_coeff, sizeof(struct coeff)); } else { memset(coeff, 0, sizeof(struct coeff)); coeff->scale = 1.0; coeff->coeff.n_blocks = -1; } if (get_string_or_int(coeff->coeff.name, BF_MAXOBJECTNAME, &coeff->coeff.intname)) { if (coeff->coeff.intname != intname) { parse_error("Incorrect integer name.\n"); } sprintf(coeff->coeff.name, "%d", intname); } coeff->coeff.intname = intname; } else { memset(coeff, 0, sizeof(struct coeff)); coeff->scale = 1.0; } get_token(LBRACE); do { switch (token = yylex()) { case FIELD: if (strcmp(yylval.field, "format") == 0) { field_repeat_test(&bitset, 0); get_token(STRING); coeff->rawformat.isfloat = true; coeff->rawformat.swap = false; coeff->rawformat.bytes = sizeof(float); coeff->rawformat.sbytes = sizeof(float); coeff->rawformat.scale = 1.0; if (strcasecmp(yylval.string, "text") == 0) { coeff->format = COEFF_FORMAT_TEXT; } else if (strcasecmp(yylval.string, "processed") == 0) { coeff->format = COEFF_FORMAT_PROCESSED; } else { coeff->format = COEFF_FORMAT_RAW; parse_sample_format(&coeff->rawformat, yylval.string, false); } get_token(EOS); } else if (strcmp(yylval.field, "attenuation") == 0) { field_repeat_test(&bitset, 1); get_token(REAL); coeff->scale = FROM_DB(-yylval.real); get_token(EOS); } else if (strcmp(yylval.field, "filename") == 0) { field_repeat_test(&bitset, 2); if (parse_default) { parse_error("cannot give coeff filename in default " "configuration.\n"); } switch (token = yylex()) { case STRING: strncpy(coeff->filename, yylval.string, PATH_MAX); coeff->filename[PATH_MAX-1] = '\0'; get_token(EOS); break; case REAL: coeff->filename[0] = '\0'; n = 0; do { coeff->shm_shmids[n] = make_integer(yylval.real); get_token(SLASH); get_token(REAL); coeff->shm_offsets[n] = make_integer(yylval.real); get_token(SLASH); get_token(REAL); coeff->shm_blocks[n] = make_integer(yylval.real); n++; if (n == BF_MAXCOEFFPARTS) { parse_error("too many shared memory blocks.\n"); } if ((token = yylex()) == COMMA) { get_token(REAL); } } while (token == COMMA); if (token != EOS) { unexpected_token(EOS, token); } coeff->shm_elements = n; break; default: unexpected_token(STRING, token); break; } } else if (strcmp(yylval.field, "blocks") == 0) { field_repeat_test(&bitset, 3); get_token(REAL); coeff->coeff.n_blocks = make_integer(yylval.real); get_token(EOS); } else if (strcmp(yylval.field, "shared_mem") == 0) { field_repeat_test(&bitset, 4); get_token(BOOLEAN); coeff->coeff.is_shared = yylval.boolean; get_token(EOS); } else if (strcmp(yylval.field, "skip") == 0) { field_repeat_test(&bitset, 5); get_token(REAL); coeff->skip = make_integer(yylval.real); get_token(EOS); } else { unrecognised_token("coeff field", yylval.field); } break; case RBRACE: break; default: unexpected_token(FIELD, token); } } while (token != RBRACE); get_token(EOS); if (parse_default) { field_mandatory_test(bitset, 0x1B, "coeff"); } else { if (!has_defaults) { if (strcmp(coeff->filename, "dirac pulse") == 0) { if (!bit_isset(&bitset, 0)) { coeff->format = COEFF_FORMAT_PROCESSED; } field_mandatory_test(bitset, 0x04, "coeff"); } else { field_mandatory_test(bitset, 0x05, "coeff"); } } else { field_mandatory_test(bitset, 0x04, "coeff"); } } if (coeff->format == COEFF_FORMAT_PROCESSED) { if (coeff->scale != 1.0) { parse_error("cannot have non-zero attenuation on processed " "format.\n"); } } if (coeff->shm_elements > 0 && coeff->format != COEFF_FORMAT_PROCESSED) { parse_error("shared memory coefficients must be in processed " "format.\n"); } if (!parse_default && coeff->shm_elements > 0) { coeff->coeff.is_shared = true; } return coeff; } static void parse_filter_io_array(struct filter *filter, bool_t parse_default, int io, bool_t isfilter) { int len = 0, token; char name[BF_MAXOBJECTNAME]; int io_array[BF_MAXCHANNELS]; char msg[200]; if (parse_default) { sprintf(msg, "cannot give filter %s in default configuration.\n", (io == IN) ? "inputs" : "outputs"); parse_error(msg); } do { if (len == BF_MAXCHANNELS) { parse_error("array is too long.\n"); } if (!get_string_or_int(name, BF_MAXOBJECTNAME, &io_array[len])) { io_array[len] = 0; if (isfilter) { filter->filter_name[io][len] = estrdup(name); } else { filter->channel_name[io][len] = estrdup(name); } } else { if (isfilter) { filter->filter_name[io][len] = NULL; } else { filter->channel_name[io][len] = NULL; } } if (isfilter) { if (io == IN) { filter->fctrl.fscale[len] = 1.0; } } else { filter->fctrl.scale[io][len] = 1.0; } switch (token = yylex()) { case SLASH: if (io == OUT && isfilter) { parse_error("cannot scale filter outputs which are connected " "to other filter inputs.\n"); } switch (token = yylex()) { case SLASH: goto parse_scalar; case REAL: if (isfilter) { filter->fctrl.fscale[len] *= FROM_DB(-yylval.real); } else { filter->fctrl.scale[io][len] *= FROM_DB(-yylval.real); } switch (token = yylex()) { case EOS: case COMMA: break; case SLASH: parse_scalar: get_token(REAL); if (isfilter) { filter->fctrl.fscale[len] *= yylval.real; } else { filter->fctrl.scale[io][len] *= yylval.real; } switch (token = yylex()) { case EOS: case COMMA: break; default: unexpected_token(EOS, token); } break; } break; default: unexpected_token(REAL, token); } break; case EOS: case COMMA: break; default: unexpected_token(EOS, token); } len++; } while (token != EOS); if (isfilter) { filter->filter.n_filters[io] = len; filter->filter.filters[io] = emalloc(len * sizeof(int)); memcpy(filter->filter.filters[io], io_array, len * sizeof(int)); } else { filter->filter.n_channels[io] = len; filter->filter.channels[io] = emalloc(len * sizeof(int)); memcpy(filter->filter.channels[io], io_array, len * sizeof(int)); } } static struct filter * parse_filter(bool_t parse_default, int intname) { struct filter *filter; uint32_t bitset = 0; char msg[200]; int token; filter = emalloc(sizeof(struct filter)); if (!parse_default) { if (has_defaults) { memcpy(filter, default_filter, sizeof(struct filter)); } else { memset(filter, 0, sizeof(struct filter)); filter->fctrl.coeff = -1; filter->process = -1; } if (get_string_or_int(filter->filter.name, BF_MAXOBJECTNAME, &filter->filter.intname)) { if (filter->filter.intname != intname) { parse_error("incorrect integer name.\n"); } sprintf(filter->filter.name, "%d", intname); } filter->filter.intname = intname; } else { memset(filter, 0, sizeof(struct filter)); filter->fctrl.coeff = -1; filter->process = -1; } get_token(LBRACE); do { switch (token = yylex()) { case FIELD: if (strcmp(yylval.field, "process") == 0) { field_repeat_test(&bitset, 0); get_token(REAL); filter->process = make_integer(yylval.real); if (filter->process >= BF_MAXPROCESSES) { sprintf(msg, "process is less than 0 " "or larger than %d.\n", BF_MAXPROCESSES - 1); parse_error(msg); } if (filter->process < 0) { filter->process = -1; } get_token(EOS); } else if (strcmp(yylval.field, "coeff") == 0) { field_repeat_test(&bitset, 1); if (parse_default) { parse_error("cannot give filter coeff in default " "configuration.\n"); } get_string_or_int(filter->coeff_name, BF_MAXOBJECTNAME, &filter->fctrl.coeff); get_token(EOS); } else if (strcmp(yylval.field, "from_inputs") == 0 || strcmp(yylval.field, "inputs") == 0) { field_repeat_test(&bitset, 2); parse_filter_io_array(filter, parse_default, IN, false); } else if (strcmp(yylval.field, "to_outputs") == 0 || strcmp(yylval.field, "outputs") == 0) { field_repeat_test(&bitset, 3); parse_filter_io_array(filter, parse_default, OUT, false); } else if (strcmp(yylval.field, "from_filters") == 0) { field_repeat_test(&bitset, 4); parse_filter_io_array(filter, parse_default, IN, true); } else if (strcmp(yylval.field, "to_filters") == 0) { field_repeat_test(&bitset, 5); parse_filter_io_array(filter, parse_default, OUT, true); } else if (strcmp(yylval.field, "delay") == 0) { field_repeat_test(&bitset, 6); get_token(REAL); filter->fctrl.delayblocks = make_integer(yylval.real); if (filter->fctrl.delayblocks < 0) { filter->fctrl.delayblocks = 0; } /* dividing with block size when all configration has be read */ get_token(EOS); } else if (strcmp(yylval.field, "crossfade") == 0) { field_repeat_test(&bitset, 7); get_token(BOOLEAN); filter->filter.crossfade = yylval.boolean; get_token(EOS); } else { unrecognised_token("filter field", yylval.field); } break; case RBRACE: break; default: unexpected_token(FIELD, token); } } while (token != RBRACE); get_token(EOS); if (!parse_default) { if (!bit_isset(&bitset, 5) && !bit_isset(&bitset, 3)) { parse_error("no outputs for filter.\n"); } if (!bit_isset(&bitset, 4) && !bit_isset(&bitset, 2)) { parse_error("no inputs for filter.\n"); } field_mandatory_test(bitset, 0x2, "filter"); } /* some sanity checks and completion of inputs, outputs and coeff fields cannot be done until whole configuration has been read */ return filter; } static struct iodev * parse_iodev(bool_t parse_default, int io, int physical_intname_base, int virtual_intname_base, int *maxdelay) { struct iodev *iodev; int token, n, io_array[BF_MAXCHANNELS]; char name[BF_MAXOBJECTNAME]; uint32_t bitset = 0, inuse[BF_MAXCHANNELS / 32 + 1]; int maxdelay_setting = -2, indmaxd_count = 0; iodev = emalloc(sizeof(struct iodev)); if (!parse_default) { if (has_defaults) { memcpy(iodev, default_iodev[io], sizeof(struct iodev)); iodev->device_params = default_iodev[io]->device_params; iodev->ch.channel_selection = emalloc(default_iodev[io]->ch.used_channels * sizeof(int)); memcpy(iodev->ch.channel_selection, default_iodev[io]->ch.channel_selection, default_iodev[io]->ch.used_channels * sizeof(int)); iodev->ch.channel_name = emalloc(default_iodev[io]->ch.used_channels * sizeof(int)); for (n = 0; n < default_iodev[io]->ch.used_channels; n++) { iodev->ch.channel_name[n] = physical_intname_base + n; } } else { memset(iodev, 0, sizeof(struct iodev)); } if (get_string_or_int(name, BF_MAXOBJECTNAME, &n)) { iodev->virtual_channels = get_integer_list((double)n, io_array, BF_MAXCHANNELS, LBRACE); memcpy(iodev->channel_intname, io_array, iodev->virtual_channels * sizeof(int)); for (n = 0; n < iodev->virtual_channels; n++) { if (iodev->channel_intname[n] != virtual_intname_base + n) { parse_error("incorrect integer name.\n"); } iodev->virt2phys[n] = n; } for (n = 0; n < iodev->virtual_channels; n++) { sprintf(name, "%d", iodev->channel_intname[n]); iodev->channel_name[n] = estrdup(name); } } else { iodev->virtual_channels = get_string_list(name, iodev->channel_name, BF_MAXCHANNELS, LBRACE); for (n = 0; n < iodev->virtual_channels; n++) { iodev->channel_intname[n] = virtual_intname_base + n; iodev->virt2phys[n] = n; } } } else { memset(iodev, 0, sizeof(struct iodev)); get_token(LBRACE); } do { switch (token = yylex()) { case FIELD: if (strcmp(yylval.field, "device") == 0) { field_repeat_test(&bitset, 0); get_token(STRING); strncpy(iodev->device_name, yylval.string, BF_MAXOBJECTNAME); iodev->device_name[BF_MAXOBJECTNAME - 1] = '\0'; if (strchr(iodev->device_name, PATH_SEPARATOR_CHAR) != NULL) { parse_error("path separator not allowed in device name.\n"); } iodev->device_params = get_params(); get_token(EOS); } else if (strcmp(yylval.field, "sample") == 0) { field_repeat_test(&bitset, 1); get_token(STRING); iodev->auto_format = parse_sample_format(&iodev->ch.sf, yylval.string, true); get_token(EOS); } else if (strcmp(yylval.field, "channels") == 0) { field_repeat_test(&bitset, 2); get_token(REAL); iodev->ch.open_channels = make_integer(yylval.real); if (iodev->ch.open_channels < 1 || iodev->ch.open_channels > BF_MAXCHANNELS) { parse_error("too few or too many channels.\n"); } if (iodev->ch.channel_selection != NULL) { efree(iodev->ch.channel_selection); } if (iodev->ch.channel_name != NULL) { efree(iodev->ch.channel_name); } switch (token = yylex()) { case SLASH: get_token(REAL); n = get_integer_list(yylval.real, io_array, BF_MAXCHANNELS, EOS); iodev->ch.used_channels = n; if (n > iodev->ch.open_channels) { parse_error("channel amount mismatch.\n"); } iodev->ch.channel_selection = emalloc(n * sizeof(int)); memcpy(iodev->ch.channel_selection, io_array, n * sizeof(int)); memset(inuse, 0, sizeof(inuse)); for (n = 0; n < iodev->ch.used_channels; n++) { if (iodev->ch.channel_selection[n] < 0 || iodev->ch.channel_selection[n] >= iodev->ch.open_channels) { parse_error("channel out of range.\n"); } if (bit_isset(inuse, iodev->ch.channel_selection[n])) { parse_error("duplicate channel selection.\n"); } bit_set(inuse, iodev->ch.channel_selection[n]); } break; case EOS: iodev->ch.used_channels = iodev->ch.open_channels; iodev->ch.channel_selection = emalloc(iodev->ch.used_channels * sizeof(int)); for (n = 0; n < iodev->ch.used_channels; n++) { iodev->ch.channel_selection[n] = n; } break; default: unexpected_token(EOS, token); } if (!parse_default && iodev->ch.used_channels > iodev->virtual_channels) { parse_error("channel amount exceeds allocated.\n"); } iodev->ch.channel_name = emalloc(iodev->ch.used_channels * sizeof(int)); for (n = 0; n < iodev->ch.used_channels; n++) { iodev->ch.channel_name[n] = physical_intname_base + n; } } else if (strcmp(yylval.field, "delay") == 0) { field_repeat_test(&bitset, 3); get_token(REAL); n = get_integer_list(yylval.real, &bfconf->delay[io][virtual_intname_base], BF_MAXCHANNELS - virtual_intname_base, EOS); while (n--) { if (bfconf->delay[io][virtual_intname_base + n] < 0) { parse_error("negative delay.\n"); } if (bfconf->delay[io][virtual_intname_base + n] > *maxdelay) { *maxdelay = bfconf->delay[io][virtual_intname_base + n]; } } } else if (strcmp(yylval.field, "dither") == 0) { field_repeat_test(&bitset, 4); if (io == IN) { unrecognised_token("input field", yylval.field); } get_token(BOOLEAN); iodev->apply_dither = yylval.boolean; get_token(EOS); } else if (strcmp(yylval.field, "mute") == 0) { field_repeat_test(&bitset, 5); for (n = virtual_intname_base; n < BF_MAXCHANNELS; n++) { get_token(BOOLEAN); bfconf->mute[io][n] = yylval.boolean; if ((token = yylex()) == COMMA) { continue; } else if (token == EOS) { break; } else { unexpected_token(EOS, token); } } } else if (strcmp(yylval.field, "maxdelay") == 0) { field_repeat_test(&bitset, 6); get_token(REAL); maxdelay_setting = make_integer(yylval.real); if (maxdelay_setting > *maxdelay) { *maxdelay = maxdelay_setting; } if (maxdelay_setting < 0) { maxdelay_setting = -1; } get_token(EOS); } else if (strcmp(yylval.field, "individual_maxdelay") == 0) { field_repeat_test(&bitset, 7); get_token(REAL); n = get_integer_list(yylval.real, &bfconf->maxdelay[io] [virtual_intname_base], BF_MAXCHANNELS - virtual_intname_base, EOS); indmaxd_count = n; while (n--) { if (bfconf->maxdelay[io][virtual_intname_base + n] < 0) { bfconf->maxdelay[io][virtual_intname_base + n] = -1; } if (bfconf->maxdelay[io][virtual_intname_base + n] > *maxdelay) { *maxdelay = bfconf->maxdelay[io][virtual_intname_base + n]; } } } else if (strcmp(yylval.field, "mapping") == 0) { field_repeat_test(&bitset, 8); if (parse_default) { unrecognised_token("default io device field", yylval.field); } get_token(REAL); n = get_integer_list(yylval.real, iodev->virt2phys, BF_MAXCHANNELS, EOS); if (n != iodev->virtual_channels) { parse_error("channel amount mismatch.\n"); } } else if (strcmp(yylval.field, "merge") == 0) { field_repeat_test(&bitset, 9); pinfo("Warning: \"merge\" setting is deprecated.\n"); if (io == IN) { unrecognised_token("input field", yylval.field); } get_token(BOOLEAN); get_token(EOS); } else if (strcmp(yylval.field, "subdelay") == 0) { field_repeat_test(&bitset, 10); get_token(REAL); n = get_integer_list(yylval.real, &bfconf->subdelay[io] [virtual_intname_base], BF_MAXCHANNELS - virtual_intname_base, EOS); while (n--) { if (bfconf->subdelay[io][virtual_intname_base + n] <= -BF_SAMPLE_SLOTS) { bfconf->subdelay[io][virtual_intname_base + n] = BF_UNDEFINED_SUBDELAY; } else { bfconf->use_subdelay[io] = true; } if (bfconf->subdelay[io][virtual_intname_base + n] >= BF_SAMPLE_SLOTS) { parse_error("too large subdelay.\n"); } } } else { unrecognised_token((io == IN) ? "input field" : "output filed", yylval.field); } break; case RBRACE: break; default: unexpected_token(FIELD, token); return NULL; } } while (token != RBRACE); get_token(EOS); if (io == IN) { if (parse_default || !has_defaults) { field_mandatory_test(bitset, 0x07, "input"); } } else { if (parse_default || !has_defaults) { field_mandatory_test(bitset, 0x07, "output"); } } if (!parse_default) { if (maxdelay_setting != -2) { for (n = indmaxd_count; n < iodev->virtual_channels; n++) { bfconf->maxdelay[io][virtual_intname_base + n] = maxdelay_setting; } } for (n = 0; n < iodev->virtual_channels; n++) { if (iodev->virt2phys[n] < 0 || iodev->virt2phys[n] >= iodev->ch.used_channels) { parse_error("invalid channel mapping.\n"); } } if (bit_isset(&bitset, 8) && iodev->virtual_channels <= iodev->ch.used_channels) { parse_error("virtual mapping only allowed when virtual channel " "amount exceeds physical.\n"); } } for (n = 0; n < iodev->virtual_channels; n++) { if (bfconf->maxdelay[io][virtual_intname_base + n] >= 0 && bfconf->delay[io][virtual_intname_base + n] > bfconf->maxdelay[io][virtual_intname_base + n]) { parse_error("delay exceeds specified maximum delay.\n"); } } return iodev; } static void parse_setting(char field[], bool_t parse_default, uint32_t *repeat_bitset) { char msg[200]; int token, n; if (strcmp(field, "sampling_rate") == 0) { field_repeat_test(repeat_bitset, 0); get_token(REAL); bfconf->sampling_rate = make_integer(yylval.real); if (bfconf->sampling_rate <= 0) { parse_error("invalid sampling_rate.\n"); } get_token(EOS); } else if (strcmp(field, "config_file") == 0) { field_repeat_test(repeat_bitset, 1); if (!parse_default) { parse_error("cannot set config_file setting in this file.\n"); } get_token(STRING); strcpy(default_config_file, tilde_expansion(yylval.string)); get_token(EOS); } else if (strcmp(field, "logic") == 0) { field_repeat_test(repeat_bitset, 2); do { get_token(STRING); for (n = 0; n < bfconf->n_logicmods; n++) { if (strcmp(yylval.string, logic_names[n]) == 0) { pinfo("Warning: overriding parameters for module \"%s\".\n", yylval.string); efree(logic_names[n]); free_params(logic_params[n]); break; } } logic_names[n] = estrdup(yylval.string); if (strchr(yylval.string, PATH_SEPARATOR_CHAR) != NULL) { parse_error("path separator not allowed in module name.\n"); } logic_params[n] = get_params(); token = yylex(); switch (token) { case COMMA: case EOS: if (n == bfconf->n_logicmods) { if (++bfconf->n_logicmods == BF_MAXMODULES) { parse_error("too many modules.\n"); } } break; default: unexpected_token(EOS, token); break; } } while (token != EOS); } else if (strcmp(field, "overflow_warnings") == 0) { field_repeat_test(repeat_bitset, 3); get_token(BOOLEAN); bfconf->overflow_warnings = yylval.boolean; get_token(EOS); } else if (strcmp(field, "show_progress") == 0) { field_repeat_test(repeat_bitset, 4); get_token(BOOLEAN); bfconf->show_progress = yylval.boolean; get_token(EOS); } else if (strcmp(field, "n_processors") == 0) { field_repeat_test(repeat_bitset, 5); pinfo("Warning: \"n_processors\" setting is deprecated.\n"); get_token(REAL); n = make_integer(yylval.real); if (n < 1) { parse_error("invalid number of processors.\n"); } get_token(EOS); } else if (strcmp(field, "max_dither_table_size") == 0) { field_repeat_test(repeat_bitset, 6); get_token(REAL); bfconf->max_dither_table_size = make_integer(yylval.real); get_token(EOS); } else if (strcmp(field, "filter_length") == 0) { field_repeat_test(repeat_bitset, 7); get_token(REAL); bfconf->filter_length = make_integer(yylval.real); switch (token = yylex()) { case EOS: bfconf->n_blocks = 1; break; case COMMA: get_token(REAL); bfconf->n_blocks = make_integer(yylval.real); get_token(EOS); break; default: unexpected_token(EOS, token); break; } if (log2_get(bfconf->filter_length) == -1 || bfconf->n_blocks * bfconf->filter_length < MINFILTERLEN || bfconf->n_blocks * bfconf->filter_length > MAXFILTERLEN) { sprintf(msg, "filter length is not within %d - %d " "or not a power of 2.\n", MINFILTERLEN, MAXFILTERLEN); parse_error(msg); } } else if (strcmp(field, "lock_memory") == 0) { field_repeat_test(repeat_bitset, 8); get_token(BOOLEAN); bfconf->lock_memory = yylval.boolean; get_token(EOS); } else if (strcmp(field, "modules_path") == 0) { field_repeat_test(repeat_bitset, 9); get_token(STRING); if (modules_path == NULL) { modules_path = emalloc(PATH_MAX + 1); } strcpy(modules_path, tilde_expansion(yylval.string)); n = strlen(modules_path); if (modules_path[n - 1] != PATH_SEPARATOR_CHAR) { modules_path[n] = PATH_SEPARATOR_CHAR; modules_path[n + 1] = '\0'; } get_token(EOS); } else if (strcmp(field, "monitor_rate") == 0) { field_repeat_test(repeat_bitset, 10); get_token(BOOLEAN); bfconf->monitor_rate = yylval.boolean; get_token(EOS); } else if (strcmp(field, "debug") == 0) { field_repeat_test(repeat_bitset, 11); get_token(BOOLEAN); bfconf->debug = yylval.boolean; get_token(EOS); } else if (strcmp(field, "powersave") == 0) { field_repeat_test(repeat_bitset, 12); switch (token = yylex()) { case REAL: bfconf->analog_powersave = FROM_DB(yylval.real); if (bfconf->analog_powersave < 1.0) { bfconf->powersave = true; } break; case BOOLEAN: bfconf->analog_powersave = 1.0; bfconf->powersave = yylval.boolean; break; default: unexpected_token(BOOLEAN, token); break; } get_token(EOS); } else if (strcmp(field, "allow_poll_mode") == 0) { field_repeat_test(repeat_bitset, 13); get_token(BOOLEAN); bfconf->allow_poll_mode = yylval.boolean; get_token(EOS); } else if (strcmp(field, "float_bits") == 0) { field_repeat_test(repeat_bitset, 14); get_token(REAL); bfconf->realsize = make_integer(yylval.real); if (bfconf->realsize != sizeof(float) * 8 && bfconf->realsize != sizeof(double) * 8) { sprintf(msg, "invalid float_bits, must be %zd or %zd.\n", sizeof(float) * 8, sizeof(double) * 8); parse_error(msg); } bfconf->realsize /= 8; get_token(EOS); #ifdef CONVOLVER_NEEDS_CONFIGFILE } else if (strcmp(field, "convolver_config") == 0) { field_repeat_test(repeat_bitset, 15); get_token(STRING); if (convolver_config == NULL) { convolver_config = emalloc(PATH_MAX); } strcpy(convolver_config, tilde_expansion(yylval.string)); get_token(EOS); #endif } else if (strcmp(field, "benchmark") == 0) { if (parse_default) { parse_error("cannot set benchmark setting in this file.\n"); } field_repeat_test(repeat_bitset, 16); get_token(BOOLEAN); bfconf->benchmark = yylval.boolean; get_token(EOS); if (has_defaults && bfconf->benchmark) { fprintf(stderr, "The benchmark option requires the " "\"-nodefault\" switch.\n"); exit(BF_EXIT_INVALID_CONFIG); } } else if (strcmp(field, "sdf_length") == 0) { field_repeat_test(repeat_bitset, 17); get_token(REAL); bfconf->sdf_length = make_integer(yylval.real); if (bfconf->sdf_length <= 0) { bfconf->sdf_length = -1; } switch (token = yylex()) { case EOS: bfconf->sdf_beta = 9.0; break; case COMMA: get_token(REAL); bfconf->sdf_beta = yylval.real; get_token(EOS); break; default: unexpected_token(EOS, token); break; } } else if (strcmp(field, "safety_limit") == 0) { field_repeat_test(repeat_bitset, 18); get_token(REAL); bfconf->safety_limit = pow(10.0, yylval.real / 20.0); if (!isfinite(bfconf->safety_limit)) { fprintf(stderr, "invalid safety_limit.\n"); exit(BF_EXIT_INVALID_CONFIG); } get_token(EOS); } else { parse_error("unrecognised setting name.\n"); } } static void get_defaults(void) { struct stat filestat; uint32_t repeat_bitset = 0; #ifdef CONVOLVER_NEEDS_CONFIGFILE uint32_t bits = 0x85DB; #else uint32_t bits = 0x05DB; #endif int token, io, dummy; strcpy(current_filename, tilde_expansion(DEFAULT_BFCONF_NAME)); if (stat(current_filename, &filestat) != 0) { create_default_config(); } if ((yyin = fopen(current_filename, "rt")) == NULL) { fprintf(stderr, "Could not open file \"" DEFAULT_BFCONF_NAME "\" for reading.\n"); exit(BF_EXIT_OTHER); } do { switch (token = yylex()) { case FIELD: parse_setting(yylval.field, true, &repeat_bitset); break; case COEFF: if (default_coeff != NULL) { fprintf(stderr, "More than one coeff structure in " "default configuration.\n"); exit(BF_EXIT_INVALID_CONFIG); } default_coeff = parse_coeff(true, 0); break; case INPUT: case OUTPUT: io = (token == INPUT) ? IN : OUT; if (default_iodev[io] != NULL) { fprintf(stderr, "More than one %s structure in " "default configuration.\n", (io == IN) ? "input" : "output"); exit(BF_EXIT_INVALID_CONFIG); } dummy = 0; default_iodev[io] = parse_iodev(true, io, 0, 0, &dummy); break; case FILTER: if (default_filter != NULL) { fprintf(stderr, "More than one filter structure in " "default configuration.\n"); exit(BF_EXIT_INVALID_CONFIG); } default_filter = parse_filter(true, 0); break; case EOF: break; default: unexpected_token(FIELD, token); } } while (token != EOF); fclose(yyin); FOR_IN_AND_OUT { if (default_iodev[IO] == NULL) { fprintf(stderr, "No %s defined in %s.\n", (IO == IN) ? "input" : "output", current_filename); exit(BF_EXIT_INVALID_CONFIG); } } if (default_filter == NULL) { default_filter = emalloc(sizeof(struct filter)); memset(default_filter, 0, sizeof(struct filter)); } if (default_coeff == NULL) { fprintf(stderr, "No coeff defined in %s.\n", current_filename); exit(BF_EXIT_INVALID_CONFIG); } field_mandatory_test(repeat_bitset, bits, current_filename); } static void * real_read(FILE *stream, int *len, char filename[], int realsize, int maxitems) { char str[1024], *p, *s; void *realbuf = NULL; int capacity = 0; *len = 0; str[1023] = '\0'; while (fgets(str, 1023, stream) != NULL) { s = str; while (*s == ' ' || *s == '\t') s++; if (*s == '\n' || *s == '\0') { continue; } if (*len == capacity) { capacity += 1024; realbuf = erealloc(realbuf, capacity * realsize); } if (realsize == 4) { ((float *)realbuf)[*len] = (float)strtod(s, &p); } else { ((double *)realbuf)[*len] = strtod(s, &p); } (*len) += 1; if (p == s) { fprintf(stderr, "Parse error on line %d in file %s: invalid " "floating point number.\n", *len, filename); exit(BF_EXIT_INVALID_CONFIG); } if (maxitems > 0 && (*len) == maxitems) { break; } } realbuf = erealloc(realbuf, (*len) * realsize); return realbuf; } #define REALSIZE 4 #define RAW2REAL_NAME raw2realf #include "raw2real.h" #undef REALSIZE #undef RAW2REAL_NAME #define REALSIZE 8 #define RAW2REAL_NAME raw2reald #include "raw2real.h" #undef REALSIZE #undef RAW2REAL_NAME static void * raw_read(FILE *stream, int *totitems, struct sample_format *sf, int realsize, int maxitems) { int n_items; uint8_t *rawbuf = NULL, *curp = NULL; void *realbuf; *totitems = 0; curp = rawbuf = emalloc(1024 * sf->bytes); while ((n_items = fread(curp, sf->bytes, 1024, stream)) != 0) { *totitems += n_items; if (maxitems > 0 && *totitems >= maxitems) { *totitems = maxitems; break; } rawbuf = erealloc(rawbuf, (*totitems + 1024) * sf->bytes); curp = rawbuf + (*totitems) * sf->bytes; } rawbuf = erealloc(rawbuf, (*totitems) * sf->bytes); if (sf->isfloat && !sf->swap && sf->bytes == realsize) { return (void *)rawbuf; } realbuf = emalloc((*totitems) * realsize); if (realsize == 4) { raw2realf(realbuf, rawbuf, sf->bytes, sf->isfloat, 1, sf->swap, *totitems); for (n_items = 0; n_items < *totitems; n_items++) { ((float *)realbuf)[n_items] *= (float)sf->scale; } } else { raw2reald(realbuf, rawbuf, sf->bytes, sf->isfloat, 1, sf->swap, *totitems); for (n_items = 0; n_items < *totitems; n_items++) { ((double *)realbuf)[n_items] *= sf->scale; } } efree(rawbuf); return realbuf; } static void * get_sharedmem(int shmid, int offset) { static void **shmsegs = NULL; static int *shmid_array = NULL; static int n_shmids = 0; static int cap = 0; void *buf, *p; int n; buf = NULL; for (n = 0; n < n_shmids; n++) { if (shmid == shmid_array[n]) { buf = shmsegs[n]; break; } } if (n == n_shmids) { if ((buf = shmat(shmid, NULL, 0)) == (void *)-1) { fprintf(stderr, "Failed to attach to shared memory with id %d: " "%s.\n", shmid, strerror(errno)); exit(BF_EXIT_OTHER); } if (n_shmids == cap) { cap += 16; shmsegs = erealloc(shmsegs, cap * sizeof (void *)); shmid_array = erealloc(shmid_array, cap * sizeof (int)); } shmid_array[n_shmids] = shmid; shmsegs[n_shmids] = buf; n_shmids++; } p = &((uint8_t *)buf)[offset]; if (((ptrdiff_t)p & (ALIGNMENT - 1)) != 0) { fprintf(stderr, "Shared memory pointer with id %d and offset %d " " is not aligned at a %d byte boundary.\n", shmid, offset, ALIGNMENT); exit(BF_EXIT_OTHER); } return (void *)&((uint8_t *)buf)[offset]; } static void * load_coeff(struct coeff *coeff, int cindex, int realsize) { void *coeffs, *zbuf = NULL; FILE *stream = NULL; void **cbuf, *buf; int n, i, j, len; uint8_t *dest; if (coeff->shm_elements <= 0 && strcmp(coeff->filename, "dirac pulse") != 0) { if ((stream = fopen(coeff->filename, coeff->format == COEFF_FORMAT_TEXT ? "rt" : "rb")) == NULL) { fprintf(stderr, "Could not open \"%s\" for reading.\n", coeff->filename); exit(BF_EXIT_OTHER); } if (coeff->skip > 0) { fseek(stream, coeff->skip, SEEK_SET); if (ftell(stream) != coeff->skip) { fprintf(stderr, "Failed to skip %d bytes of file \"%s\".\n", coeff->skip, coeff->filename); exit(BF_EXIT_OTHER); } } } else if (coeff->skip > 0) { fprintf(stderr, "Cannot use skip field for coeff \"%s\" " "(skip only works on files).\n", coeff->coeff.name); exit(BF_EXIT_INVALID_CONFIG); } cbuf = emalloc(coeff->coeff.n_blocks * sizeof(void **)); if (strcmp(coeff->filename, "dirac pulse") == 0) { len = coeff->coeff.n_blocks * bfconf->filter_length; coeffs = emalloc(len * realsize); memset(coeffs, 0, len * realsize); if (realsize == 4) { ((float *)coeffs)[0] = 1.0; } else { ((double *)coeffs)[0] = 1.0; } } else { switch (coeff->format) { case COEFF_FORMAT_TEXT: coeffs = real_read(stream, &len, coeff->filename, realsize, coeff->coeff.n_blocks * bfconf->filter_length); break; case COEFF_FORMAT_RAW: coeffs = raw_read(stream, &len, &coeff->rawformat, realsize, coeff->coeff.n_blocks * bfconf->filter_length); break; case COEFF_FORMAT_PROCESSED: if (coeff->shm_elements > 0) { for (i = j = 0; i < coeff->shm_elements; i++) { j += coeff->shm_blocks[i]; } if (j != coeff->coeff.n_blocks) { fprintf(stderr, "Shared memory block count mismatch in " "coeff %d.\n", coeff->coeff.intname); exit(BF_EXIT_INVALID_CONFIG); } for (i = j = 0; i < coeff->shm_elements; i++) { buf = get_sharedmem(coeff->shm_shmids[i], coeff->shm_offsets[i]); for (n = 0; n < coeff->shm_blocks[i]; n++) { cbuf[j++] = (void *)&((uint8_t *)buf)[n * convolver_cbufsize()]; } } } else { buf = raw_read(stream, &len, &coeff->rawformat, realsize, coeff->coeff.n_blocks * convolver_cbufsize() + 1); if (coeff->coeff.n_blocks * convolver_cbufsize() != len) { fprintf(stderr, "Length mismatch of file \"%s\", expected " "%d, got %d.\n", coeff->filename, coeff->coeff.n_blocks * convolver_cbufsize(), len); exit(BF_EXIT_INVALID_CONFIG); } for (n = 0; n < coeff->coeff.n_blocks; n++) { cbuf[n] = (void *)&((uint8_t *)buf)[n * convolver_cbufsize()]; } } if (!convolver_verify_cbuf(cbuf, coeff->coeff.n_blocks)) { fprintf(stderr, "Coeff %d is invalid.\n", coeff->coeff.intname); exit(BF_EXIT_INVALID_CONFIG); } #if 0 if (bfconf->debug) { char filename[1024]; sprintf(filename, "brutefir-%d-coeffs-%d.txt", getpid(), cindex); convolver_debug_dump_cbuf(filename, cbuf, coeff->coeff.n_blocks); } #endif return cbuf; default: fprintf(stderr, "Invalid format: %d.\n", coeff->format); exit(BF_EXIT_INVALID_CONFIG); } fclose(stream); } if (len < coeff->coeff.n_blocks * bfconf->filter_length) { zbuf = emalloc(bfconf->filter_length * realsize); memset(zbuf, 0, bfconf->filter_length * realsize); } if (coeff->coeff.is_shared) { dest = shmalloc(2 * coeff->coeff.n_blocks * bfconf->filter_length * realsize); if (dest == NULL) { exit(BF_EXIT_NO_MEMORY); } } else { dest = NULL; } for (n = 0; n < coeff->coeff.n_blocks; n++) { if (n * bfconf->filter_length > len) { cbuf[n] = convolver_coeffs2cbuf(zbuf, bfconf->filter_length, coeff->scale, dest); } else if ((n + 1) * bfconf->filter_length > len) { cbuf[n] = convolver_coeffs2cbuf (&((uint8_t *)coeffs)[n * bfconf->filter_length * realsize], len - n * bfconf->filter_length, coeff->scale, dest); } else { cbuf[n] = convolver_coeffs2cbuf (&((uint8_t *)coeffs)[n * bfconf->filter_length * realsize], bfconf->filter_length, coeff->scale, dest); } if (cbuf[n] == NULL) { fprintf(stderr, "Failed to preprocess coefficients in file %s.\n", coeff->filename); exit(BF_EXIT_OTHER); } if (coeff->coeff.is_shared) { dest += 2 * bfconf->filter_length * realsize; } } efree(zbuf); efree(coeffs); #if 0 if (bfconf->debug) { char filename[1024]; sprintf(filename, "brutefir-%d-coeffs-%d.txt", getpid(), cindex); convolver_debug_dump_cbuf(filename, cbuf, coeff->coeff.n_blocks); } #endif return cbuf; } static bool_t filter_loop(int source_intname, int search_intname) { int n; for (n = 0; n < bfconf->filters[search_intname].n_filters[OUT]; n++) { if (bfconf->filters[search_intname].filters[OUT][n] == source_intname || filter_loop(source_intname, bfconf->filters[search_intname].filters[OUT][n])) { return true; } } return false; } static void * load_module_function(void *handle, const char modname[], const char name[], bool_t required) { char *funname; void *fun; funname = alloca(strlen(name) + 1); strcpy(funname, name); if ((fun = dlsym(handle, funname)) == NULL && required) { fprintf(stderr, "Failed to resolve function \"%s\" in module " "\"%s\": %s.\n", name, modname, dlerror()); exit(BF_EXIT_OTHER); } return fun; } static void find_module(char path[], const char name[], const char suffix[]) { struct stat filestat; if (modules_path != NULL) { sprintf(path, "%s%s%s", modules_path, name, suffix); } if (stat(path, &filestat) == 0) { return; } sprintf(path, "/usr/local/lib/brutefir/%s%s", name, suffix); if (stat(path, &filestat) == 0) { return; } sprintf(path, "/usr/local/lib/%s%s", name, suffix); if (stat(path, &filestat) == 0) { return; } sprintf(path, "/usr/lib/brutefir/%s%s", name, suffix); if (stat(path, &filestat) == 0) { return; } sprintf(path, "/usr/lib/%s%s", name, suffix); if (stat(path, &filestat) == 0) { return; } fprintf(stderr, "Failed to find module \"%s\". " "None of the following files existed:\n", name); if (modules_path != NULL) { fprintf(stderr, " \"%s%s%s\"\n", modules_path, name, suffix); } fprintf(stderr, " \"/usr/local/lib/brutefir/%s%s\"\n", name, suffix); fprintf(stderr, " \"/usr/local/lib/%s%s\"\n", name, suffix); fprintf(stderr, " \"/usr/lib/brutefir/%s%s\"\n", name, suffix); fprintf(stderr, " \"/usr/lib/%s%s\"\n", name, suffix); exit(BF_EXIT_OTHER); } static void load_bfio_module(struct bfio_module *m, const char name[]) { char path[BF_MAXOBJECTNAME + PATH_MAX + 100]; int (*iscallback)(void); memset(m, 0, sizeof(struct bfio_module)); find_module(path, name, ".bfio"); if ((m->handle = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL) { fprintf(stderr, "Failed to open module \"%s\" in \"%s\": %s.\n", name, path, dlerror()); exit(BF_EXIT_OTHER); } iscallback = load_module_function(m->handle, name, BF_FUN_BFIO_ISCALLBACK, false); if (iscallback != NULL) { m->iscallback = iscallback(); } else { m->iscallback = false; } m->preinit = load_module_function(m->handle, name, BF_FUN_BFIO_PREINIT, true); m->command = load_module_function(m->handle, name, BF_FUN_BFIO_COMMAND, false); m->init = load_module_function(m->handle, name, BF_FUN_BFIO_INIT, true); m->read = load_module_function(m->handle, name, BF_FUN_BFIO_READ, false); m->write = load_module_function(m->handle, name, BF_FUN_BFIO_WRITE, false); m->synch_start = load_module_function(m->handle, name, BF_FUN_BFIO_SYNCH_START, m->iscallback); m->synch_stop = load_module_function(m->handle, name, BF_FUN_BFIO_SYNCH_STOP, m->iscallback); m->start = load_module_function(m->handle, name, BF_FUN_BFIO_START, false); m->stop = load_module_function(m->handle, name, BF_FUN_BFIO_STOP, false); m->message = load_module_function(m->handle, name, BF_FUN_BFIO_MESSAGE, false); if (!m->iscallback && m->read == NULL && m->write == NULL) { fprintf(stderr, "Module \"%s\" in \"%s\" lacks both read and write " "functions.\n", name, path); exit(BF_EXIT_OTHER); } if (m->iscallback && m->command != NULL) { fprintf(stderr, "Command function for callback I/O not supported.\n"); exit(BF_EXIT_OTHER); } if ((m->synch_start != NULL && m->synch_stop == NULL) || (m->synch_start == NULL && m->synch_stop != NULL) || (m->start != NULL && m->stop == NULL) || (m->start == NULL && m->stop != NULL) || (m->start == NULL && m->synch_start == NULL)) { fprintf(stderr, "Module \"%s\" in \"%s\" lacks start and/or stop " "functions.\n", name, path); exit(BF_EXIT_OTHER); } if (m->command != NULL && m->message == NULL) { fprintf(stderr, "Module \"%s\" in \"%s\" has command function but " "lacks message function.\n", name, path); } } static void load_bflogic_module(struct bflogic_module *m, const char name[]) { char path[BF_MAXOBJECTNAME + PATH_MAX + 100]; memset(m, 0, sizeof(struct bflogic_module)); m->fork_mode = BF_FORK_DONT_FORK; find_module(path, name, ".bflogic"); if ((m->handle = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL) { fprintf(stderr, "Failed to open module \"%s\" in \"%s\": %s.\n", name, path, dlerror()); exit(BF_EXIT_OTHER); } m->preinit = load_module_function(m->handle, name, BF_FUN_BFLOGIC_PREINIT, true); m->command = load_module_function(m->handle, name, BF_FUN_BFLOGIC_COMMAND, false); m->init = load_module_function(m->handle, name, BF_FUN_BFLOGIC_INIT, true); m->message = load_module_function(m->handle, name, BF_FUN_BFLOGIC_MESSAGE, false); if (m->command != NULL && m->message == NULL) { fprintf(stderr, "Module \"%s\" in \"%s\" has command function but " "lacks message function.\n", name, path); } } static int number_of_cpus(void) { FILE *stream; char s[1000]; int n_cpus = 0; /* This code is Linux specific... */ if ((stream = fopen("/proc/cpuinfo", "rt")) == NULL) { fprintf(stderr, "Warning: could not determine number of processors, " "guessing 1.\n"); return 1; } s[999] = '\0'; while (fgets(s, 999, stream) != NULL) { if (strncasecmp(s, "processor", 9) == 0) { n_cpus++; } } fclose(stream); if (n_cpus == 0) { n_cpus = 1; } return n_cpus; } static int load_balance_filters(struct filter *pfilters[]) { uint32_t used_channels[BF_MAXCHANNELS / 32 + 1]; int n, i, j, k, process; bool_t set; /* Step 1: make as many processes as possible, that is only follow the requirements: a) connected filters within same process, and b) mixed output within same process. */ process = 0; for (n = 0; n < bfconf->n_filters; n++) { set = false; if (pfilters[n]->process != -1) { continue; } pfilters[n]->process = process; do { set = false; /* which filters are connected to the current? */ for (i = 0; i < bfconf->n_filters; i++) { if (pfilters[i]->process != process) { continue; } FOR_IN_AND_OUT { for (j = 0; j < bfconf->filters[i].n_filters[IO]; j++) { k = bfconf->filters[i].filters[IO][j]; if (pfilters[k]->process != process) { pfilters[k]->process = process; set = true; } } } } /* which filters mix to the same output? */ memset(used_channels, 0, sizeof(used_channels)); /* which outputs are in the current process? */ for (i = 0; i < bfconf->n_filters; i++) { if (pfilters[i]->process != process) { continue; } for (j = 0; j < bfconf->filters[i].n_channels[OUT]; j++) { bit_set(used_channels, bfconf->filters[i].channels[OUT][j]); } } /* move filters with same outputs to the current process */ for (i = 0; i < bfconf->n_filters; i++) { if (pfilters[i]->process == process) { continue; } for (j = 0; j < bfconf->filters[i].n_channels[OUT]; j++) { if (bit_isset(used_channels, bfconf->filters[i].channels[OUT][j])) { pfilters[i]->process = process; set = true; } } } } while (set); process++; } /* Step 2: reduce the number of processes to the same as the number of CPUs. Since coefficients can be changed in runtime and so on, we just make a simple estimate, which may be dead wrong, but in those cases, the user have to configure manually. */ j = 0; for (n = 0; n < process; n++) { for (i = 0; i < bfconf->n_filters; i++) { if (pfilters[i]->process == n) { pfilters[i]->process = j; } } if (++j == bfconf->n_cpus) { j = 0; } } return process < bfconf->n_cpus ? process - 1 : bfconf->n_cpus - 1; } void bfconf_init(char filename[], bool_t quiet, bool_t nodefault) { struct iodev *iodevs[2][BF_MAXCHANNELS]; struct filter *pfilters[BF_MAXFILTERS]; struct coeff **coeffs = NULL; struct bffilter filters[BF_MAXFILTERS]; struct dither_state *dither_state[BF_MAXCHANNELS]; struct timeval tv1, tv2; int coeffs_capacity = 0; int largest_process = -1; int n_channels[2], min_prio, max_prio; int version_minor, version_major; uint32_t used_channels[2][BF_MAXCHANNELS / 32 + 1]; uint32_t used_filters[BF_MAXFILTERS / 32 + 1]; uint32_t local_used_channels[BF_MAXCHANNELS / 32 + 1]; uint32_t apply_dither[BF_MAXCHANNELS / 32 + 1]; uint32_t used_processes[BF_MAXPROCESSES / 32 + 1]; uint32_t repeat_bitset = 0; int channels[2][BF_MAXCHANNELS]; int n, i, j, k, io, token, virtch, physch, maxdelay[2]; bool_t load_balance = false; uint64_t t1, t2; char str[200]; gettimeofday(&tv1, NULL); timestamp(&t1); has_defaults = !nodefault; bfconf = emalloc(sizeof(struct bfconf)); memset(bfconf, 0, sizeof(struct bfconf)); FOR_IN_AND_OUT { bfconf->delay[IO] = emalloc(BF_MAXCHANNELS * sizeof(int)); memset(bfconf->delay[IO], 0, BF_MAXCHANNELS * sizeof(int)); bfconf->maxdelay[IO] = emalloc(BF_MAXCHANNELS * sizeof(int)); memset(bfconf->maxdelay[IO], -1, BF_MAXCHANNELS * sizeof(int)); bfconf->subdelay[IO] = emalloc(BF_MAXCHANNELS * sizeof(int)); memset(bfconf->subdelay[IO], 0, BF_MAXCHANNELS * sizeof(int)); bfconf->mute[IO] = emalloc(BF_MAXCHANNELS * sizeof(bool_t)); memset(bfconf->mute[IO], 0, BF_MAXCHANNELS * sizeof(bool_t)); } bfconf->sdf_length = -1; bfconf->quiet = quiet; bfconf->realsize = sizeof(float); bfconf->safety_limit = 0; if (!nodefault) { get_defaults(); } if (filename != NULL && strcasecmp(filename, "stdin") == 0) { strncpy(current_filename, filename, PATH_MAX); current_filename[PATH_MAX-1] = '\0'; yyin = stdin; } else { if (filename == NULL) { strcpy(current_filename, default_config_file); } else { strncpy(current_filename, filename, PATH_MAX); current_filename[PATH_MAX-1] = '\0'; } if ((yyin = fopen(current_filename, "rt")) == NULL) { fprintf(stderr, "Could not open file \"%s\" for reading: %s.\n", current_filename, strerror(errno)); exit(BF_EXIT_OTHER); } } lexlineno = 1; maxdelay[IN] = maxdelay[OUT] = 0; do { switch (token = yylex()) { case FIELD: parse_setting(yylval.field, false, &repeat_bitset); break; case COEFF: if (bfconf->n_coeffs == coeffs_capacity) { coeffs_capacity += 16; coeffs = erealloc(coeffs, coeffs_capacity * sizeof(void *)); } coeffs[bfconf->n_coeffs] = parse_coeff(false, bfconf->n_coeffs); bfconf->n_coeffs++; break; case INPUT: case OUTPUT: io = (token == INPUT) ? IN : OUT; if (bfconf->n_subdevs[io] == BF_MAXCHANNELS) { sprintf(str, "too many %s.\n", (io == IN) ? "inputs" : "outputs"); parse_error(str); } iodevs[io][bfconf->n_subdevs[io]] = parse_iodev(false, io, bfconf->n_physical_channels[io], bfconf->n_channels[io], &maxdelay[io]); bfconf->n_physical_channels[io] += iodevs[io][bfconf->n_subdevs[io]]->ch.used_channels; bfconf->n_channels[io] += iodevs[io][bfconf->n_subdevs[io]]->virtual_channels; bfconf->n_subdevs[io]++; break; case FILTER: if (bfconf->n_filters == BF_MAXFILTERS) { parse_error("too many filters.\n"); } pfilters[bfconf->n_filters] = parse_filter(false, bfconf->n_filters); bfconf->n_filters++; break; case EOF: break; default: parse_error("unexpected token.\n"); break; } } while (token != EOF); fclose(yyin); efree(default_coeff); efree(default_filter); if (!has_defaults) { #ifdef CONVOLVER_NEEDS_CONFIGFILE field_mandatory_test(repeat_bitset, 0x8281, current_filename); #else field_mandatory_test(repeat_bitset, 0x0281, current_filename); #endif } pinfo("Internal resolution is %d bit floating point.\n", bfconf->realsize * 8); /* do some sanity checks */ FOR_IN_AND_OUT { if (bfconf->n_subdevs[IO] == 0) { fprintf(stderr, "No %s defined.\n", (IO == IN) ? "inputs" : "outputs"); exit(BF_EXIT_INVALID_CONFIG); } } if (bfconf->n_filters == 0) { fprintf(stderr, "No filters defined.\n"); exit(BF_EXIT_INVALID_CONFIG); } if (bfconf->benchmark && bfconf->powersave) { fprintf(stderr, "The benchmark and powersave setting cannot " "both be set to true.\n"); exit(BF_EXIT_INVALID_CONFIG); } /* create the channel arrays */ FOR_IN_AND_OUT { bfconf->channels[IO] = emalloc(bfconf->n_channels[IO] * sizeof(struct bfchannel)); memset(bfconf->channels[IO], 0, bfconf->n_channels[IO] * sizeof(struct bfchannel)); bfconf->virt2phys[IO] = emalloc(bfconf->n_channels[IO] * sizeof(int)); bfconf->n_virtperphys[IO] = emalloc(bfconf->n_physical_channels[IO] * sizeof(int)); memset(bfconf->n_virtperphys[IO], 0, bfconf->n_physical_channels[IO] * sizeof(int)); bfconf->phys2virt[IO] = emalloc(bfconf->n_physical_channels[IO] * sizeof(int *)); for (n = 0; n < bfconf->n_subdevs[IO]; n++) { for (i = 0; i < iodevs[IO][n]->virtual_channels; i++) { virtch = iodevs[IO][n]->channel_intname[i]; physch = iodevs[IO][n]->ch.channel_name[0] + iodevs[IO][n]->virt2phys[i]; bfconf->channels[IO][virtch].intname = virtch; bfconf->virt2phys[IO][virtch] = physch; bfconf->n_virtperphys[IO][physch]++; strcpy(bfconf->channels[IO][virtch].name, iodevs[IO][n]->channel_name[i]); efree(iodevs[IO][n]->channel_name[i]); } for (i = 0; i < iodevs[IO][n]->ch.used_channels; i++) { physch = iodevs[IO][n]->ch.channel_name[i]; bfconf->phys2virt[IO][physch] = emalloc(bfconf->n_virtperphys[IO][physch] * sizeof(int)); /* set all values to -1 */ memset(bfconf->phys2virt[IO][physch], 0xFF, bfconf->n_virtperphys[IO][physch] * sizeof(int)); } for (i = 0; i < iodevs[IO][n]->virtual_channels; i++) { physch = iodevs[IO][n]->ch.channel_name[0] + iodevs[IO][n]->virt2phys[i]; for (k = 0; bfconf->phys2virt[IO][physch][k] != -1; k++); bfconf->phys2virt[IO][physch][k] = iodevs[IO][n]->channel_intname[i]; } } } /* check for duplicate names */ for (n = 0; n < bfconf->n_coeffs; n++) { for (i = 0; i < bfconf->n_coeffs; i++) { if (n != i && strcmp(coeffs[i]->coeff.name, coeffs[n]->coeff.name) == 0) { fprintf(stderr, "Duplicate coefficient set names.\n"); exit(BF_EXIT_INVALID_CONFIG); } } } for (n = 0; n < bfconf->n_filters; n++) { for (i = 0; i < bfconf->n_filters; i++) { if (n != i && strcmp(pfilters[i]->filter.name, pfilters[n]->filter.name) == 0) { fprintf(stderr, "Duplicate filter names.\n"); exit(BF_EXIT_INVALID_CONFIG); } } } FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_channels[IO]; n++) { for (i = 0; i < bfconf->n_channels[IO]; i++) { if (n != i && strcmp(bfconf->channels[IO][i].name, bfconf->channels[IO][n].name) == 0) { fprintf(stderr, "Duplicate channel names.\n"); exit(BF_EXIT_INVALID_CONFIG); } } } } /* finish and sanity check filter structures */ memset(used_channels, 0, sizeof(used_channels)); memset(used_processes, 0, sizeof(used_processes)); for (n = 0; n < bfconf->n_filters; n++) { /* coefficient name */ if (pfilters[n]->coeff_name[0] == '\0') { if (pfilters[n]->fctrl.coeff >= bfconf->n_coeffs) { fprintf(stderr, "Coeff index %d in filter %d/\"%s\" is out of " "range.\n", pfilters[n]->fctrl.coeff, n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } } else { pfilters[n]->fctrl.coeff = -1; for (i = 0; coeffs[i] != NULL && coeffs[i]->coeff.name != NULL; i++) { if (strcmp(coeffs[i]->coeff.name, pfilters[n]->coeff_name) == 0) { pfilters[n]->fctrl.coeff = i; break; } } if (pfilters[n]->fctrl.coeff == -1) { fprintf(stderr, "Coeff with name \"%s\" (in filter %d/\"%s\") " "does not exist.\n", pfilters[n]->coeff_name, n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } } if (pfilters[n]->process == -1) { if (n > 0 && !load_balance) { fprintf(stderr, "Cannot mix manual process settings with " "automatic.\n"); exit(BF_EXIT_INVALID_CONFIG); } load_balance = true; } else { if (pfilters[n]->process > largest_process) { largest_process = pfilters[n]->process; } bit_set(used_processes, pfilters[n]->process); if (n > 0 && load_balance) { fprintf(stderr, "Cannot mix manual process settings with " "automatic.\n"); exit(BF_EXIT_INVALID_CONFIG); } load_balance = false; } /* input/output names */ FOR_IN_AND_OUT { /* channel input/outputs */ memset(local_used_channels, 0, sizeof(local_used_channels)); for (j = 0; j < pfilters[n]->filter.n_channels[IO]; j++) { if (pfilters[n]->channel_name[IO][j] == NULL) { if (pfilters[n]->filter.channels[IO][j] < 0 || pfilters[n]->filter.channels[IO][j] >= bfconf->n_channels[IO]) { fprintf(stderr, "%s channel index %d in filter " "%d/\"%s\" is out of range.\n", (IO == IN) ? "Input" : "Output", pfilters[n]->filter.channels[IO][j], n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } } else { pfilters[n]->filter.channels[IO][j] = -1; for (i = 0; i < bfconf->n_channels[IO]; i++) { if (strcmp(bfconf->channels[IO][i].name, pfilters[n]->channel_name[IO][j]) == 0) { pfilters[n]->filter.channels[IO][j] = i; break; } } if (pfilters[n]->filter.channels[IO][j] == -1) { fprintf(stderr, "%s channel with name \"%s\" (in " "filter %d/\"%s\") does not exist.\n", (IO == IN) ? "Input" : "Output", pfilters[n]->channel_name[IO][j], n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } efree(pfilters[n]->channel_name[IO][j]); } bit_set(used_channels[IO], pfilters[n]->filter.channels[IO][j]); if (bit_isset(local_used_channels, pfilters[n]->filter.channels[IO][j])) { fprintf(stderr, "Duplicate channels in filter %d/\"%s\".\n", n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } bit_set(local_used_channels, pfilters[n]->filter.channels[IO][j]); } /* filter input/outputs */ memset(used_filters, 0, sizeof(used_filters)); for (j = 0; j < pfilters[n]->filter.n_filters[IO]; j++) { if (pfilters[n]->filter_name[IO][j] == NULL) { if (pfilters[n]->filter.filters[IO][j] < 0 || pfilters[n]->filter.filters[IO][j] >= bfconf->n_filters) { fprintf(stderr, "%s filter index %d in filter " "%d/\"%s\" is out of range.\n", (IO == IN) ? "Input" : "Output", pfilters[n]->filter.filters[IO][j], n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } } else { pfilters[n]->filter.filters[IO][j] = -1; for (i = 0; i < bfconf->n_filters; i++) { if (strcmp(pfilters[i]->filter.name, pfilters[n]->filter_name[IO][j]) == 0) { pfilters[n]->filter.filters[IO][j] = i; break; } } if (pfilters[n]->filter.filters[IO][j] == -1) { fprintf(stderr, "%s filter with name \"%s\" (in " "filter %d/\"%s\") does not exist.\n", (IO == IN) ? "Input" : "Output", pfilters[n]->filter_name[IO][j], n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } efree(pfilters[n]->filter_name[IO][j]); } if (bit_isset(used_filters, pfilters[n]->filter.filters[IO][j])) { fprintf(stderr, "Duplicate filters in filter %d/\"%s\".\n", n, pfilters[n]->filter.name); exit(BF_EXIT_INVALID_CONFIG); } bit_set(used_filters, pfilters[n]->filter.filters[IO][j]); } } /* finish delayblocks number */ if (pfilters[n]->fctrl.delayblocks > bfconf->n_blocks - 1) { fprintf(stderr, "Delay in filter %d/\"%s\" is too large (max " "allowed is %d blocks, max blocks - 1).\n", n, pfilters[n]->filter.name, bfconf->n_blocks - 1); exit(BF_EXIT_INVALID_CONFIG); } } /* check if all in/out channels are used in the filters */ FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_channels[IO]; n++) { if (!bit_isset(used_channels[IO], n)) { pinfo("Warning: %s channel \"%s\" is not used.\n", (IO == IN) ? "input" : "output", bfconf->channels[IO][n].name); } } } /* check if all processes are used (if manually set) */ for (n = 0; n <= largest_process; n++) { if (!bit_isset(used_processes, n)) { fprintf(stderr, "The range of process indexes among filters is " "broken.\n"); exit(BF_EXIT_INVALID_CONFIG); } } /* fill in the filters and initfctrl array */ bfconf->filters = emalloc(bfconf->n_filters * sizeof(struct bffilter)); bfconf->initfctrl = emalloc(bfconf->n_filters * sizeof(struct bffilter_control)); for (n = 0; n < bfconf->n_filters; n++) { bfconf->filters[n] = pfilters[n]->filter; bfconf->initfctrl[n] = pfilters[n]->fctrl; } /* check so filters are connected, that is that output to a filter shows as input from source filter at the destination filter */ for (n = 0; n < bfconf->n_filters; n++) { for (i = 0; i < bfconf->filters[n].n_filters[OUT]; i++) { k = bfconf->filters[n].filters[OUT][i]; for (j = 0; j < bfconf->filters[k].n_filters[IN]; j++) { if (bfconf->filters[k].filters[IN][j] == n) { break; } } if (j == bfconf->filters[k].n_filters[IN]) { fprintf(stderr, "Output to filter %d/\"%s\" from filter %d/\"%s\" must exist\n\ as input at at the destination filter %d/\"%s\".\n", k, bfconf->filters[k].name, n, bfconf->filters[n].name, k, bfconf->filters[k].name); exit(BF_EXIT_INVALID_CONFIG); } } for (i = 0; i < bfconf->filters[n].n_filters[IN]; i++) { k = bfconf->filters[n].filters[IN][i]; for (j = 0; j < bfconf->filters[k].n_filters[OUT]; j++) { if (bfconf->filters[k].filters[OUT][j] == n) { break; } } if (j == bfconf->filters[k].n_filters[OUT]) { fprintf(stderr, "Input from filter %d/\"%s\" in filter %d/\"%s\" must exist\n\ as output in the source filter %d/\"%s\".\n", k, bfconf->filters[k].name, n, bfconf->filters[n].name, k, bfconf->filters[k].name); exit(BF_EXIT_INVALID_CONFIG); } } } /* check so there are no filter loops */ for (n = 0; n < bfconf->n_filters; n++) { if (filter_loop(n, n)) { fprintf(stderr, "Filter %d is involved in a loop.\n", n); exit(BF_EXIT_INVALID_CONFIG); } } /* estimate a load balancing for filters (if not manually set) */ bfconf->n_cpus = number_of_cpus(); if (load_balance) { largest_process = load_balance_filters(pfilters); } /* if (convolver_init != NULL) {*/ /* initialise convolver */ if (!convolver_init(convolver_config, bfconf->filter_length, bfconf->realsize)) { fprintf(stderr, "Convolver initialisation failed.\n"); exit(BF_EXIT_OTHER); } efree(convolver_config); /* }*/ /* init subdelay */ if (bfconf->sdf_length < 0) { bfconf->use_subdelay[IN] = false; bfconf->use_subdelay[OUT] = false; } else if (2 * bfconf->sdf_length + 1 > bfconf->filter_length) { /* note: this check depends on the internals of delay_subsample_init() */ fprintf(stderr, "The filter_length must be larger than " "2 x sdf_length + 1.\n"); exit(BF_EXIT_INVALID_CONFIG); } if (bfconf->use_subdelay[IN] || bfconf->use_subdelay[OUT]) { if (!delay_subsample_init(BF_SAMPLE_SLOTS, bfconf->sdf_length, bfconf->sdf_beta, bfconf->filter_length, bfconf->realsize)) { bf_exit(BF_EXIT_OTHER); return; } } /* load coefficients */ bfconf->coeffs_data = emalloc(bfconf->n_coeffs * sizeof(void **)); bfconf->coeffs = emalloc(bfconf->n_coeffs * sizeof(struct bfcoeff)); if (bfconf->n_coeffs == 1) { pinfo("Loading coefficient set..."); } else if (bfconf->n_coeffs > 1) { pinfo("Loading %d coefficient sets...", bfconf->n_coeffs); } for (n = 0; n < bfconf->n_coeffs; n++) { if (coeffs[n]->coeff.n_blocks <= 0) { coeffs[n]->coeff.n_blocks = bfconf->n_blocks; } else if (coeffs[n]->coeff.n_blocks > bfconf->n_blocks) { fprintf(stderr, "Too many blocks in coeff %d.\n", n); exit(BF_EXIT_INVALID_CONFIG); } bfconf->coeffs_data[n] = load_coeff(coeffs[n], n, bfconf->realsize); bfconf->coeffs[n] = coeffs[n]->coeff; efree(coeffs[n]); } if (bfconf->n_coeffs > 0) { pinfo("finished.\n"); } efree(coeffs); /* shorten mute array */ FOR_IN_AND_OUT { bfconf->mute[IO] = erealloc(bfconf->mute[IO], bfconf->n_channels[IO] * sizeof(bool_t)); bfconf->delay[IO] = erealloc(bfconf->delay[IO], bfconf->n_channels[IO] * sizeof(int)); } /* derive filter_process from filters */ bfconf->n_processes = largest_process + 1; bfconf->fproc = emalloc(bfconf->n_processes * sizeof(struct filter_process)); memset(bfconf->fproc, 0, bfconf->n_processes * sizeof(struct filter_process)); for (n = k = 0; n < bfconf->n_processes; n++) { n_channels[IN] = n_channels[OUT] = 0; memset(used_channels, 0, sizeof(used_channels)); for (i = 0; i < bfconf->n_filters; i++) { if (pfilters[i]->process == n) { filters[bfconf->fproc[n].n_filters] = pfilters[i]->filter; bfconf->fproc[n].n_filters++; FOR_IN_AND_OUT { for (j = 0; j < pfilters[i]->filter.n_channels[IO]; j++) { if (!bit_isset(used_channels[IO], pfilters[i]->filter.channels[IO][j])) { channels[IO][n_channels[IO]] = pfilters[i]->filter.channels[IO][j]; n_channels[IO]++; } bit_set(used_channels[IO], pfilters[i]->filter.channels[IO][j]); } } } } FOR_IN_AND_OUT { bfconf->fproc[n].n_unique_channels[IO] = n_channels[IO]; bfconf->fproc[n].unique_channels[IO] = emalloc(n_channels[IO] * sizeof(int)); memcpy(bfconf->fproc[n].unique_channels[IO], channels[IO], n_channels[IO] * sizeof(int)); } bfconf->fproc[n].filters = emalloc(bfconf->fproc[n].n_filters * sizeof(struct bffilter)); memcpy(bfconf->fproc[n].filters, filters, bfconf->fproc[n].n_filters * sizeof(struct bffilter)); } /* check so there are no output mixing between processes */ for (n = 0; n < bfconf->n_processes; n++) { memset(used_channels, 0, sizeof(used_channels)); memset(used_filters, 0, sizeof(used_filters)); for (i = 0; i < bfconf->fproc[n].n_filters; i++) { for (j = 0; j < bfconf->fproc[n].filters[i].n_channels[OUT]; j++) { bit_set(used_channels[OUT], bfconf->fproc[n].filters[i].channels[OUT][j]); } for (j = 0; j < bfconf->fproc[n].filters[i].n_filters[OUT]; j++) { bit_set(used_filters, bfconf->fproc[n].filters[i].filters[OUT][j]); } } for (k = 0; k < bfconf->n_processes; k++) { if (k != n) { for (i = 0; i < bfconf->fproc[k].n_filters; i++) { if (bit_isset(used_filters, bfconf->fproc[k].filters[i].intname)) { fprintf(stderr, "Connected filters must be processed " "within the same process.\n"); exit(BF_EXIT_INVALID_CONFIG); } for (j = 0; j < bfconf->fproc[k].filters[i].n_channels[OUT]; j++) { if (bit_isset(used_channels[OUT], bfconf-> fproc[k].filters[i].channels[OUT][j])) { fprintf(stderr, "Mixed outputs must be processed " "within the same process.\n"); exit(BF_EXIT_INVALID_CONFIG); } } } } } } /* sort filters in processes in the correct process order */ for (n = 0; n < bfconf->n_processes; n++) { memset(used_filters, 0, sizeof(used_filters)); j = 0; while (j < bfconf->fproc[n].n_filters) { for (i = 0; i < bfconf->fproc[n].n_filters; i++) { if (bit_isset(used_filters, bfconf->fproc[n].filters[i].intname)) { continue; } for (k = 0; k < bfconf->fproc[n].filters[i].n_filters[IN]; k++) { if (!bit_isset(used_filters, bfconf->fproc[n].filters[i].filters[IN][k])) { k = -1; break; } } if (k != -1) { filters[j] = bfconf->fproc[n].filters[i]; bit_set(used_filters, filters[j].intname); j++; } } } memcpy(bfconf->fproc[n].filters, filters, bfconf->fproc[n].n_filters * sizeof(struct bffilter)); } /* load bflogic modules */ if (bfconf->n_logicmods > 0) { bfconf->logicmods = emalloc(bfconf->n_logicmods * sizeof(struct bflogic_module)); memset(bfconf->logicmods, 0, bfconf->n_logicmods * sizeof(struct bflogic_module)); bfconf->logicnames = emalloc(bfconf->n_logicmods * sizeof(char *)); } for (n = i = 0; n < bfconf->n_logicmods; n++) { load_bflogic_module(&bfconf->logicmods[n], logic_names[n]); config_params = logic_params[n]; config_params_pos = 0; version_major = BF_VERSION_MAJOR; version_minor = BF_VERSION_MINOR; if (bfconf->logicmods[n].preinit(&version_major, &version_minor, get_config_token, bfconf->sampling_rate, bfconf->filter_length, bfconf->n_blocks, bfconf->n_coeffs, bfconf->coeffs, bfconf->n_channels, (const struct bfchannel **) bfconf->channels, bfconf->n_filters, bfconf->filters, &bfconf->logicmods[n].bfevents, &bfconf->logicmods[n].fork_mode, (int)bfconf->debug) == -1) { fprintf(stderr, "Error at line %d for logic module \"%s\".\n", lexlineno, logic_names[n]); exit(BF_EXIT_INVALID_CONFIG); } if (version_major != BF_VERSION_MAJOR) { fprintf(stderr, "Logic module \"%s\" reported an incompatible " "major version (got %d, expected %d).\n", logic_names[n], version_major, BF_VERSION_MAJOR); exit(BF_EXIT_OTHER); } free_params(logic_params[n]); if (bfconf->logicmods[n].fork_mode != BF_FORK_DONT_FORK) { i++; } if (bfconf->logicmods[n].bfevents.fdevents != 0) { if (pipe(bfconf->logicmods[n].event_pipe) == -1) { fprintf(stderr, "Failed to create pipe: %s.\n", strerror(errno)); exit(BF_EXIT_OTHER); } } else { bfconf->logicmods[n].event_pipe[0] = -1; bfconf->logicmods[n].event_pipe[1] = -1; } bfconf->logicnames[n] = logic_names[n]; } if (bfconf->n_processes + i >= BF_MAXPROCESSES) { fprintf(stderr, "Too many processes.\n"); exit(BF_EXIT_INVALID_CONFIG); } /* load bfio modules */ bfconf->iomods = emalloc(BF_MAXMODULES * sizeof(struct bfio_module)); memset(bfconf->iomods, 0, BF_MAXMODULES * sizeof(struct bfio_module)); bfconf->ionames = emalloc(BF_MAXMODULES * sizeof(char *)); FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_subdevs[IO]; n++) { for (i = 0; i < bfconf->n_iomods; i++) { if (strcmp(bfconf->ionames[i], iodevs[IO][n]->device_name) == 0) { break; } } if (i != bfconf->n_iomods) { continue; } load_bfio_module(&bfconf->iomods[bfconf->n_iomods], iodevs[IO][n]->device_name); if (bfconf->iomods[bfconf->n_iomods].iscallback) { bfconf->callback_io = true; } else { bfconf->blocking_io = true; } bfconf->ionames[bfconf->n_iomods] = estrdup(iodevs[IO][n]->device_name); if (++bfconf->n_iomods == BF_MAXMODULES) { fprintf(stderr, "Too many modules.\n"); exit(BF_EXIT_INVALID_CONFIG); } } } bfconf->iomods = erealloc(bfconf->iomods, bfconf->n_iomods * sizeof(struct bfio_module)); bfconf->ionames = erealloc(bfconf->ionames, bfconf->n_iomods * sizeof(char *)); /* derive dai_subdevices from inputs and outputs */ FOR_IN_AND_OUT { bfconf->subdevs[IO] = emalloc(bfconf->n_subdevs[IO] * sizeof(struct dai_subdevice)); memset(bfconf->subdevs[IO], 0, bfconf->n_subdevs[IO] * sizeof(struct dai_subdevice)); for (n = 0; n < bfconf->n_subdevs[IO]; n++) { for (i = 0; i < bfconf->n_iomods; i++) { if (strcmp(bfconf->ionames[i], iodevs[IO][n]->device_name) == 0) { break; } } bfconf->subdevs[IO][n].module = i; config_params = iodevs[IO][n]->device_params; config_params_pos = 0; bfconf->subdevs[IO][n].sched_policy = -1; if (iodevs[IO][n]->auto_format) { iodevs[IO][n]->ch.sf.format = BF_SAMPLE_FORMAT_AUTO; } version_major = BF_VERSION_MAJOR; version_minor = BF_VERSION_MINOR; bfconf->subdevs[IO][n].params = bfconf->iomods[i].preinit(&version_major, &version_minor, get_config_token, IO, &iodevs[IO][n]->ch.sf.format, bfconf->sampling_rate, iodevs[IO][n]->ch.open_channels, &bfconf->subdevs[IO][n].uses_clock, &bfconf->subdevs[IO][n].sched_policy, &bfconf->subdevs[IO][n].sched_param, (int)bfconf->debug); if (default_iodev[IO] == NULL || config_params != default_iodev[IO]->device_params) { free_params(config_params); } if (version_major != BF_VERSION_MAJOR) { fprintf(stderr, "Module \"%s\" reported an incompatible " "major version (got %d, expected %d).\n", bfconf->ionames[i], version_major, BF_VERSION_MAJOR); exit(BF_EXIT_OTHER); } if (bfconf->subdevs[IO][n].params == NULL) { fprintf(stderr, "Error at line %d for %s device using " "module \"%s\".\n", lexlineno, (IO == IN) ? "input" : "output", bfconf->ionames[i]); exit(BF_EXIT_INVALID_CONFIG); } if (IO == IN) { if (!bfconf->iomods[i].iscallback && bfconf->iomods[i].read == NULL) { fprintf(stderr, "Module \"%s\" does not support input.\n", bfconf->ionames[i]); exit(BF_EXIT_INVALID_CONFIG); } } else { if (!bfconf->iomods[i].iscallback && bfconf->iomods[i].write == NULL) { fprintf(stderr, "Module \"%s\" does not support output.\n", bfconf->ionames[i]); exit(BF_EXIT_INVALID_CONFIG); } } bfconf->subdevs[IO][n].uses_clock = !!bfconf->subdevs[IO][n].uses_clock; if (bfconf->subdevs[IO][n].uses_clock) { bfconf->realtime_priority = true; } if ((bfconf->subdevs[IO][n].uses_clock || bfconf->iomods[i].iscallback) && bfconf->iomods[i].synch_start == NULL) { fprintf(stderr, "Module \"%s\" lacks the synch_start/stop " "functions.\n", bfconf->ionames[i]); exit(BF_EXIT_INVALID_CONFIG); } if (!bfconf->subdevs[IO][n].uses_clock && bfconf->iomods[i].start == NULL && !bfconf->iomods[i].iscallback) { fprintf(stderr, "Module \"%s\" lacks the start/stop " "functions.\n", bfconf->ionames[i]); exit(BF_EXIT_INVALID_CONFIG); } if (IO == OUT && bfconf->subdevs[IO][n].uses_clock && !bfconf->iomods[i].iscallback) { bfconf->synched_write = true; } if (iodevs[IO][n]->auto_format) { if (bf_sampleformat_size(iodevs[IO][n]->ch.sf.format) <= 0) { fprintf(stderr, "Module \"%s\" could not decide sample " "format.\n", bfconf->ionames[i]); exit(BF_EXIT_OTHER); } parse_sample_format(&iodevs[IO][n]->ch.sf, bf_strsampleformat(iodevs[IO][n]-> ch.sf.format), false); } bfconf->subdevs[IO][n].channels = iodevs[IO][n]->ch; } } /* check which output channels that have dither applied */ memset(apply_dither, 0, sizeof(apply_dither)); for (n = j = 0; n < bfconf->n_subdevs[OUT]; n++) { if (!iodevs[OUT][n]->apply_dither) { continue; } /* verify if it is feasible to apply dither */ if (iodevs[OUT][n]->ch.sf.isfloat) { if (!iodevs[OUT][n]->auto_format) { pinfo("Warning: cannot dither floating point format " "(outputs %d - %d).\n", iodevs[OUT][n]->channel_intname[0], iodevs[OUT][n]->channel_intname[iodevs[OUT][n]-> virtual_channels - 1]); } continue; } if (bfconf->realsize == sizeof(float) && iodevs[OUT][n]->ch.sf.sbytes > 2) { if (!iodevs[OUT][n]->auto_format) { pinfo("Warning: internal resolution not high enough to " "dither (outputs %d - %d).\n", iodevs[OUT][n]->channel_intname[0], iodevs[OUT][n]->channel_intname[iodevs[OUT][n]-> virtual_channels - 1]); } continue; } if (iodevs[OUT][n]->ch.sf.sbytes >= 4) { if (!iodevs[OUT][n]->auto_format) { pinfo("Warning: cannot apply dither to 32 bit format " "(outputs %d - %d).\n", iodevs[OUT][n]->channel_intname[0], iodevs[OUT][n]->channel_intname[iodevs[OUT][n]-> virtual_channels - 1]); } continue; } /* which physical channels to apply dither on */ for (i = 0; i < iodevs[OUT][n]->ch.used_channels; i++) { bit_set(apply_dither, iodevs[OUT][n]->ch.channel_name[i]); j++; } } /* initialise dither array */ bfconf->dither_state = emalloc(bfconf->n_physical_channels[OUT] * sizeof(struct dither_state *)); memset(bfconf->dither_state, 0, bfconf->n_physical_channels[OUT] * sizeof(struct dither_state *)); if (j > 0) { if (!dither_init(j, bfconf->sampling_rate, bfconf->realsize, bfconf->max_dither_table_size, bfconf->filter_length, dither_state)) { exit(BF_EXIT_OTHER); } for (n = j = 0; n < bfconf->n_physical_channels[OUT]; n++) { if (bit_isset(apply_dither, n)) { bfconf->dither_state[n] = dither_state[j]; j++; } else { bfconf->dither_state[n] = NULL; } } } /* calculate which sched priorities to use */ bfconf->realtime_maxprio = 4; bfconf->realtime_midprio = 3; bfconf->realtime_minprio = 2; bfconf->realtime_usermaxprio = 1; min_prio = -1; max_prio = 0; FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_subdevs[IO]; n++) { if (bfconf->subdevs[IO][n].sched_policy != -1) { if (bfconf->realtime_priority && bfconf->subdevs[IO][n].uses_clock && bfconf->subdevs[IO][n].sched_policy != SCHED_FIFO && bfconf->subdevs[IO][n].sched_policy != SCHED_RR) { fprintf(stderr, "\ External scheduling belonging to module \"%s\" must be realtime.\n", bfconf->ionames[bfconf->subdevs[IO][n].module]); exit(BF_EXIT_INVALID_CONFIG); } if (bfconf->realtime_priority) { if (max_prio < bfconf->subdevs[IO][n].sched_param.sched_priority) { max_prio = bfconf->subdevs[IO][n]. sched_param.sched_priority; } if (bfconf->subdevs[IO][n].sched_param.sched_priority < min_prio || min_prio == -1) { min_prio = bfconf->subdevs[IO][n]. sched_param.sched_priority; } if (bfconf->realtime_midprio > bfconf->subdevs[IO][n].sched_param.sched_priority) { fprintf(stderr, "\ External scheduling priority for module \"%s\" is lower than %d.\n", bfconf->ionames[bfconf->subdevs[IO][n].module], bfconf->realtime_midprio); exit(BF_EXIT_INVALID_CONFIG); } } } } } if (min_prio != -1) { bfconf->realtime_midprio = max_prio; bfconf->realtime_minprio = min_prio - 1; bfconf->realtime_usermaxprio = min_prio - 2; } if (bfconf->realtime_midprio > bfconf->realtime_maxprio) { bfconf->realtime_maxprio = bfconf->realtime_midprio + 1; } if (bfconf->realtime_maxprio > sched_get_priority_max(SCHED_FIFO)) { fprintf(stderr, "Max priority exceeds maximum possible (%d > %d).\n", bfconf->realtime_maxprio, sched_get_priority_max(SCHED_FIFO)); exit(BF_EXIT_INVALID_CONFIG); } if (bfconf->realtime_priority) { pinfo("Realtime priorities are min = %d, usermax = %d, " "mid = %d and max = %d.\n", bfconf->realtime_minprio, bfconf->realtime_usermaxprio, bfconf->realtime_midprio, bfconf->realtime_maxprio); } /* calculate maximum possible flowthrough blocks */ bfconf->flowthrough_blocks = bfconf->n_blocks + (maxdelay[IN] + bfconf->filter_length - 1) / bfconf->filter_length + (maxdelay[OUT] + bfconf->filter_length - 1) / bfconf->filter_length; FOR_IN_AND_OUT { if (default_iodev[IO] == NULL) { continue; } free_params(default_iodev[IO]->device_params); efree(default_iodev[IO]->ch.channel_selection); efree(default_iodev[IO]->ch.channel_name); efree(default_iodev[IO]); } /* estimate CPU clock rate */ gettimeofday(&tv2, NULL); timestamp(&t2); timersub(&tv2, &tv1, &tv2); if (tv2.tv_sec == 0 && tv2.tv_usec < 100000) { usleep(100000 - tv2.tv_usec); gettimeofday(&tv2, NULL); timestamp(&t2); timersub(&tv2, &tv1, &tv2); } bfconf->cpu_mhz = (double) ((long double)(t2 - t1) / (long double)(tv2.tv_sec * 1000000 + tv2.tv_usec)); #ifdef TIMESTAMP_NOT_CLOCK_CYCLE_COUNTER pinfo("Warning: no support for clock cycle counter on this platform.\n" " Timers for benchmarking may be unreliable.\n"); #else pinfo("Estimated CPU clock rate is %.3f MHz. CPU count is %d.\n", bfconf->cpu_mhz, bfconf->n_cpus); #endif if (load_balance && bfconf->n_cpus > 1) { for (n = 0; n <= largest_process; n++) { pinfo("Filters in process %d: ", n); for (i = 0; i < bfconf->n_filters; i++) { if (pfilters[i]->process == n) { pinfo("%d ", i); } } pinfo("\n"); } } /* free allocated memory */ FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_subdevs[IO]; n++) { efree(iodevs[IO][n]); } } } brutefir-1.0o/bfconf.h0000664000175000017500000000367612752354353014322 0ustar andersanders/* * (c) Copyright 2001 - 2004, 2006, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _BFCONF_H_ #define _BFCONF_H_ #include #include #include "defs.h" #include "dai.h" #include "bfrun.h" #include "bfmod.h" #include "dither.h" #include "timestamp.h" #define DEFAULT_BFCONF_NAME "~/.brutefir_defaults" struct bfconf { double cpu_mhz; int n_cpus; int sampling_rate; int filter_length; int n_blocks; int max_dither_table_size; int flowthrough_blocks; int realtime_maxprio; int realtime_midprio; int realtime_usermaxprio; int realtime_minprio; int realsize; bool_t callback_io; bool_t blocking_io; bool_t powersave; double analog_powersave; bool_t benchmark; bool_t debug; bool_t quiet; bool_t overflow_warnings; bool_t show_progress; bool_t realtime_priority; bool_t lock_memory; bool_t monitor_rate; bool_t synched_write; bool_t allow_poll_mode; struct dither_state **dither_state; int n_coeffs; struct bfcoeff *coeffs; void ***coeffs_data; int n_channels[2]; struct bfchannel *channels[2]; int n_physical_channels[2]; int *n_virtperphys[2]; int **phys2virt[2]; int *virt2phys[2]; int n_subdevs[2]; struct dai_subdevice *subdevs[2]; int *delay[2]; int *maxdelay[2]; bool_t *mute[2]; int n_filters; struct bffilter *filters; struct bffilter_control *initfctrl; int n_processes; struct filter_process *fproc; int n_iomods; struct bfio_module *iomods; char **ionames; int n_logicmods; struct bflogic_module *logicmods; char **logicnames; bool_t use_subdelay[2]; int *subdelay[2]; int sdf_length; double sdf_beta; double safety_limit; }; extern struct bfconf *bfconf; void bfconf_init(char filename[], bool_t quiet, bool_t nodefault); #endif brutefir-1.0o/bfconf_grammar.h0000644000175000017500000000143312752354353016013 0ustar andersanders/* * (c) Copyright 2001, 2002 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _BFCONF_GRAMMAR_H_ #define _BFCONF_GRAMMAR_H_ #include #include "defs.h" #include "bfmod.h" #define EOS BF_LEX_EOS /* end of statement (;) */ #define LBRACE BF_LEX_LBRACE /* { */ #define RBRACE BF_LEX_RBRACE /* } */ #define COMMA BF_LEX_COMMA /* , */ #define SLASH BF_LEX_SLASH /* / */ #define REAL BF_LEXVAL_REAL #define BOOLEAN BF_LEXVAL_BOOLEAN #define STRING BF_LEXVAL_STRING #define FIELD BF_LEXVAL_FIELD #define COEFF 200 #define INPUT 201 #define OUTPUT 202 #define FILTER 203 extern union bflexval yylval; extern FILE *yyin; extern int lexlineno; int yylex(void); void parse_error(const char msg[]); #endif brutefir-1.0o/bfconf_lexical.lex0000644000175000017500000000353312752354353016352 0ustar andersanders/* * (c) Copyright 2001 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ %{ #include #include #include "defs.h" #include "bfconf_grammar.h" int lexlineno = 0; %} %option nounput %option noyywrap %option noyylineno %option noreject %option fast %option 8bit DIGIT [0-9] ANUM [A-Za-z_0-9] %% "\n" { lexlineno++; } "{" { return LBRACE; } "}" { return RBRACE; } "," { return COMMA; } "/" { return SLASH; } ";" { return EOS; } "#"[^\n]* /* remove one-line comments */ [ \t\r]+ /* remove whitespace */ "coeff" { return COEFF; } "input" { return INPUT; } "output" { return OUTPUT; } "filter" { return FILTER; } "route" { return FILTER; } /* backwards compability */ "true" { yylval.boolean = true; return BOOLEAN; } "false" { yylval.boolean = false; return BOOLEAN; } "\""("\\\""|[^\"])*"\"" { int n, slen; slen = strlen(yytext) - 1; yytext[slen] = '\0'; for (n = 0; n < slen - 1; n++) { switch ((int)yytext[n]) { case '\\': memmove(&yytext[n], &yytext[n+1], slen - n); slen--; switch ((int)yytext[n]) { case 'n': yytext[n] = '\n'; break; case 't': yytext[n] = '\t'; break; default: break; } break; case '\n': lexlineno++; break; } } yylval.string = &yytext[1]; return STRING; } {ANUM}+":" { yylval.field = yytext; yylval.field[strlen(yylval.field)-1] = '\0'; return FIELD; } ("+"|"-")?{DIGIT}*"."?{DIGIT}+("e"("+"|"-"){DIGIT}{2})? { yylval.real = atof(yytext); return REAL; } <> { return EOF; } . { parse_error("unrecognised token.\n"); } %% brutefir-1.0o/bfio_alsa.c0000664000175000017500000004671212752354353014775 0ustar andersanders/* * (c) Copyright 2001 - 2005, 2009, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #define index _index /* hack to avoid shadowing of index() */ #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API #include #undef index #include "defs.h" #include "log2.h" #include "bit.h" #include "emalloc.h" #define IS_BFIO_MODULE #include "bfmod.h" #include "inout.h" #define ERRORSIZE 1024 struct alsa_access_state { snd_pcm_t *handle; bool_t isinterleaved; bool_t ignore_xrun; int sw_period_size; int sample_size; int used_channels; int open_channels; /* This union is a hack to be able to 'cast' between const and non const without the compiler noticing and complaining about it. This is required because ALSA's write functions do not have const buffers for input */ union { void **ptr; const void **const_ptr; } bufs; int *channel_selection; char *device; bool_t restart; }; struct settings { bool_t ignore_xrun; char *device; }; static snd_pcm_t *handles[2][BF_MAXCHANNELS]; static int n_handles[2]; static struct alsa_access_state fd2as[FD_SETSIZE]; static snd_pcm_t *base_handle = NULL; static bool_t debug = false; static bool_t link_handles = true; static snd_output_t *out; static bool_t set_params(snd_pcm_t *handle, int sample_format, int sample_rate, int open_channels, int period_size, int *hardware_period_size, int *sample_size, bool_t *isinterleaved, char errstr[]) { snd_pcm_uframes_t try_hw_period_size, hw_period_size, frames; snd_pcm_sw_params_t *swparams; snd_pcm_hw_params_t *params; int format, err; unsigned int un; if (log2_get(period_size) == -1) { sprintf(errstr, " Invalid software period size (%d): must be a power " "of 2.\n", period_size); return false; } switch (sample_format) { case BF_SAMPLE_FORMAT_S8: format = SND_PCM_FORMAT_S8; *sample_size = 1; break; case BF_SAMPLE_FORMAT_S16_LE: format = SND_PCM_FORMAT_S16_LE; *sample_size = 2; break; case BF_SAMPLE_FORMAT_S16_BE: format = SND_PCM_FORMAT_S16_BE; *sample_size = 2; break; case BF_SAMPLE_FORMAT_S24_LE: format = SND_PCM_FORMAT_S24_3LE; *sample_size = 3; break; case BF_SAMPLE_FORMAT_S24_BE: format = SND_PCM_FORMAT_S24_3BE; *sample_size = 3; break; case BF_SAMPLE_FORMAT_S24_4LE: format = SND_PCM_FORMAT_S24_LE; *sample_size = 4; break; case BF_SAMPLE_FORMAT_S24_4BE: format = SND_PCM_FORMAT_S24_BE; *sample_size = 4; break; case BF_SAMPLE_FORMAT_S32_LE: format = SND_PCM_FORMAT_S32_LE; *sample_size = 4; break; case BF_SAMPLE_FORMAT_S32_BE: format = SND_PCM_FORMAT_S32_BE; *sample_size = 4; break; case BF_SAMPLE_FORMAT_FLOAT_LE: format = SND_PCM_FORMAT_FLOAT_LE; *sample_size = 4; break; case BF_SAMPLE_FORMAT_FLOAT_BE: format = SND_PCM_FORMAT_FLOAT_BE; *sample_size = 4; break; case BF_SAMPLE_FORMAT_FLOAT64_LE: format = SND_PCM_FORMAT_FLOAT64_LE; *sample_size = 8; break; case BF_SAMPLE_FORMAT_FLOAT64_BE: format = SND_PCM_FORMAT_FLOAT64_BE; *sample_size = 8; break; default: sprintf(errstr, " Unsupported sample format.\n"); return false; } snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); if ((err = snd_pcm_hw_params_any(handle, params)) < 0) { sprintf(errstr, " Could not get any hardware configuration: %s.\n", snd_strerror(err)); return false; } if (snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { if ((err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED)) < 0) { sprintf(errstr, " Failed to set interleaved and non-interleaved " "access mode: %s.\n", snd_strerror(err)); return false; } *isinterleaved = false; } else { *isinterleaved = true; } /* It seems like it is best to set_rate_near instead of exact, have had problems with ens1371 */ un = sample_rate; if ((err = snd_pcm_hw_params_set_rate_near(handle, params, &un, NULL)) < 0) { sprintf(errstr, " Failed to set sample rate to %d Hz: %s.\n", sample_rate, snd_strerror(err)); return false; } /* accept a minor variation in sample rate */ if (un != sample_rate && !((int)((double)sample_rate * 0.99) < un && (int)((double)sample_rate / 0.99) > un)) { sprintf(errstr, " Failed to set sample rate to %d Hz, device " "suggested %u Hz instead.\n", sample_rate, un); return false; } if ((err = snd_pcm_hw_params_set_format(handle, params, format)) < 0) { sprintf(errstr, " Failed to set sample format to %s: %s.\n", bf_strsampleformat(sample_format), snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_set_channels(handle, params, open_channels)) < 0) { sprintf(errstr, " Failed to set channel count to %d: %s.\n", open_channels, snd_strerror(err)); return false; } snd_pcm_hw_params_get_periods_max(params, &un, NULL); if (un < 2) { /* really strange hardware if this happens */ sprintf(errstr, " Hardware does not support enough periods. At least 2 is required, but the\n\ hardware supports only %u.\n", un); return false; } /* try to get a hardware fragment size close to the software size */ hw_period_size = period_size; snd_pcm_hw_params_set_period_size_near(handle, params, &hw_period_size, NULL); /* if the number of periods is only one, decrease the period size until we get at least two periods */ snd_pcm_hw_params_get_periods(params, &un, NULL); try_hw_period_size = hw_period_size; while (un == 1 && try_hw_period_size != 0) { try_hw_period_size /= 2; hw_period_size = try_hw_period_size; snd_pcm_hw_params_set_period_size_near(handle, params, &hw_period_size, NULL); snd_pcm_hw_params_get_periods(params, &un, NULL); } if (hw_period_size == 0) { /* this should never happen, since we have checked that the hardware supports at least two periods */ sprintf(errstr, " Could not set period size.\n"); return false; } if (snd_pcm_hw_params(handle, params) < 0) { sprintf(errstr, " Unable to install hw params.\n"); return false; } /* configure to start when explicitly told so */ snd_pcm_sw_params_current(handle, swparams); if ((err = snd_pcm_sw_params_set_start_threshold(handle, swparams, ~0U)) < 0) { sprintf(errstr, " Failed to set start threshold: %s.\n", snd_strerror(err)); return false; } /* configure to stop when buffer underflow is detected */ snd_pcm_hw_params_get_buffer_size(params, &frames); if ((err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, frames)) < 0) { sprintf(errstr, " Failed to set stop threshold: %s.\n", snd_strerror(err)); return false; } snd_pcm_hw_params_get_period_size(params, &hw_period_size, NULL); *hardware_period_size = (int)hw_period_size; if ((err = snd_pcm_sw_params_set_avail_min(handle, swparams, 1)) < 0) { sprintf(errstr, " Failed to set min avail to 1: %s.\n", snd_strerror(err)); return false; } if ((err = snd_pcm_sw_params(handle, swparams)) < 0) { sprintf(errstr, " Unable to install sw params: %s.\n", snd_strerror(err)); return false; } if ((err = snd_pcm_prepare(handle)) < 0) { sprintf(errstr, " Unable to prepare audio: %s.\n", snd_strerror(err)); return false; } if (debug) { snd_pcm_dump(handle, out); } return true; } #define GET_TOKEN(token, errstr) \ if (get_config_token(&lexval) != token) { \ fprintf(stderr, "ALSA I/O: Parse error: " errstr); \ return NULL; \ } void * bfio_preinit(int *version_major, int *version_minor, int (*get_config_token)(union bflexval *lexval), int io, int *sample_format, int sample_rate, int open_channels, int *uses_sample_clock, int *callback_sched_policy, struct sched_param *callback_sched, int _debug) { static bool_t has_been_called = false; struct settings *settings; union bflexval lexval; int err, token, ver; ver = *version_major; *version_major = BF_VERSION_MAJOR; *version_minor = BF_VERSION_MINOR; if (ver != BF_VERSION_MAJOR) { return NULL; } debug = !!_debug; if (!has_been_called && (err = snd_output_stdio_attach(&out, stderr, 0)) != 0) { fprintf(stderr, "ALSA I/O: Unable to attach output: %s.\n", snd_strerror(err)); return NULL; } settings = malloc(sizeof(struct settings)); memset(settings, 0, sizeof(struct settings)); while ((token = get_config_token(&lexval)) > 0) { if (token != BF_LEXVAL_FIELD) { fprintf(stderr, "ALSA I/O: Parse error: expected field.\n"); return NULL; } if (strcmp(lexval.field, "param") == 0 || /* param for compability */ strcmp(lexval.field, "device") == 0) { if (settings->device != NULL) { fprintf(stderr, "ALSA I/O: Parse error: device already set.\n"); return NULL; } GET_TOKEN(BF_LEXVAL_STRING, "expected string.\n"); settings->device = estrdup(lexval.string); } else if (strcmp(lexval.field, "ignore_xrun") == 0) { GET_TOKEN(BF_LEXVAL_BOOLEAN, "expected boolean value.\n"); settings->ignore_xrun = lexval.boolean; } else if (strcmp(lexval.field, "link") == 0) { GET_TOKEN(BF_LEXVAL_BOOLEAN, "expected boolean value.\n"); if (has_been_called && lexval.boolean != link_handles) { fprintf(stderr, "ALSA I/O: \"link\" is a global setting, " "if set on more than one device, the\n value must " "be the same.\n"); return NULL; } link_handles = lexval.boolean; } else { fprintf(stderr, "ALSA I/O: Parse error: unknown field.\n"); return NULL; } GET_TOKEN(BF_LEX_EOS, "expected end of statement (;).\n"); } if (settings->device == NULL) { fprintf(stderr, "ALSA I/O: Parse error: device not set.\n"); return NULL; } if (*sample_format == BF_SAMPLE_FORMAT_AUTO) { fprintf(stderr, "ALSA I/O: No support for AUTO sample format.\n"); return NULL; } *uses_sample_clock = 1; has_been_called = true; return settings; } int bfio_init(void *params, int io, int sample_format, int sample_rate, int open_channels, int used_channels, const int channel_selection[], int period_size, int *device_period_size, int *isinterleaved, void *callback_state, int (*process_callback)(void **callback_states[2], int callback_state_count[2], void **buffers[2], int frame_count, int event)) { struct settings *settings; char errstr[ERRORSIZE]; struct pollfd pollfd; int err, sample_size; snd_pcm_t *handle; settings = (struct settings *)params; if ((err = snd_pcm_open(&handles[io][n_handles[io]], settings->device, (io == BF_IN) ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { fprintf(stderr, "ALSA I/O: Could not open audio %s \"%s\": %s.\n", io == BF_IN ? "input" : "output", settings->device, snd_strerror(err)); return -1; } handle = handles[io][n_handles[io]]; if (!set_params(handle, sample_format, sample_rate, open_channels, period_size, device_period_size, &sample_size, isinterleaved, errstr)) { fprintf(stderr, "ALSA I/O: Could not set audio %s parameters for " "\"%s\":\n%s", io == BF_IN ? "input" : "output", settings->device, errstr); snd_pcm_close(handle); return -1; } if (snd_pcm_poll_descriptors(handle, &pollfd, 1) != 1) { fprintf(stderr, "ALSA I/O: Could not get file descriptor.\n"); snd_pcm_close(handle); return -1; } if (base_handle == NULL) { base_handle = handle; } else if (link_handles) { if ((err = snd_pcm_link(base_handle, handle)) < 0) { fprintf(stderr, "ALSA I/O: Could not link alsa devices: %s.\n", snd_strerror(err)); snd_pcm_close(handle); return -1; } } n_handles[io]++; fd2as[pollfd.fd].handle = handle; fd2as[pollfd.fd].isinterleaved = *isinterleaved; fd2as[pollfd.fd].ignore_xrun = settings->ignore_xrun; fd2as[pollfd.fd].sw_period_size = period_size; fd2as[pollfd.fd].sample_size = sample_size; fd2as[pollfd.fd].open_channels = open_channels; fd2as[pollfd.fd].used_channels = used_channels; fd2as[pollfd.fd].device = settings->device; if (*isinterleaved) { fd2as[pollfd.fd].bufs.ptr = NULL; fd2as[pollfd.fd].channel_selection = NULL; } else { fd2as[pollfd.fd].bufs.ptr = emalloc(open_channels * sizeof(void *)); memset(fd2as[pollfd.fd].bufs.ptr, 0, open_channels * sizeof(void *)); fd2as[pollfd.fd].channel_selection = emalloc(used_channels * sizeof(int)); memcpy(fd2as[pollfd.fd].channel_selection, channel_selection, used_channels * sizeof(int)); } efree(settings->device); efree(settings); return pollfd.fd; } int bfio_synch_start(void) { snd_pcm_status_t *status; int err, n; if (base_handle == NULL) { return 0; } /* FIXME: the SND_PCM_STATE_RUNNING code would not be needed if the bfio_write autostart hack was not there */ snd_pcm_status_alloca(&status); if (link_handles) { if ((err = snd_pcm_status(base_handle, status)) < 0) { fprintf(stderr, "ALSA I/O: Could not get status: %s.\n", snd_strerror(err)); return -1; } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_RUNNING) { return 0; } if ((err = snd_pcm_start(base_handle)) < 0) { fprintf(stderr, "ALSA I/O: Could not start audio: %s.\n", snd_strerror(err)); return -1; } return 0; } FOR_IN_AND_OUT { for (n = 0; n < n_handles[IO]; n++) { if ((err = snd_pcm_status(handles[IO][n], status)) < 0) { fprintf(stderr, "ALSA I/O: Could not get status: %s.\n", snd_strerror(err)); return -1; } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_RUNNING) { continue; } if ((err = snd_pcm_start(handles[IO][n])) < 0) { fprintf(stderr, "ALSA I/O: Could not start audio: %s.\n", snd_strerror(err)); return -1; } } } return 0; } void bfio_synch_stop(void) { int n; if (base_handle == NULL) { return; } FOR_IN_AND_OUT { for (n = 0; n < n_handles[IO]; n++) { snd_pcm_close(handles[IO][n]); } } } int bfio_read(int fd, void *buf, int offset, int count) { struct alsa_access_state *as = &fd2as[fd]; int n, i, err; uint8_t *ptr; bfio_read_restart: if (as->isinterleaved) { i = as->sample_size * as->open_channels; if ((n = snd_pcm_readi(as->handle, &((uint8_t *)buf)[offset], count / i)) < 0) { goto bfio_read_error; } } else { ptr = (uint8_t *)buf; ptr += offset / as->used_channels; for (n = 0; n < as->used_channels; n++) { as->bufs.ptr[as->channel_selection[n]] = ptr; ptr += as->sw_period_size * as->sample_size; } i = as->sample_size * as->used_channels; if ((n = snd_pcm_readn(as->handle, as->bufs.ptr, count / i)) < 0) { goto bfio_read_error; } } return n * i; bfio_read_error: switch (n) { case -EPIPE: if (as->ignore_xrun) { fprintf(stderr, "ALSA I/O: overflow! (read on %s)\n", as->device); if ((err = snd_pcm_prepare(as->handle)) < 0) { fprintf(stderr, "ALSA I/O: Unable to prepare audio: %s.\n", snd_strerror(err)); errno = EPIPE; return -1; } if ((err = snd_pcm_start(as->handle)) < 0) { fprintf(stderr, "ALSA I/O: Could not restart audio: %s.\n", snd_strerror(err)); errno = -EPIPE; return -1; } goto bfio_read_restart; } /* assume and indicate buffer overflow */ errno = EPIPE; break; case -EAGAIN: errno = EAGAIN; return -1; default: errno = 0; break; } fprintf(stderr, "ALSA I/O: Could not read audio: %s.\n", snd_strerror(n)); return -1; } int bfio_write(int fd, const void *buf, int offset, int count) { struct alsa_access_state *as = &fd2as[fd]; const uint8_t *ptr; int n, i, err; if (as->isinterleaved) { i = as->sample_size * as->open_channels; if ((n = snd_pcm_writei(as->handle, &((const uint8_t *)buf)[offset], count / i)) < 0) { goto bfio_write_error; } } else { ptr = buf; ptr += offset / as->used_channels; for (n = 0; n < as->used_channels; n++) { as->bufs.const_ptr[as->channel_selection[n]] = ptr; ptr += as->sw_period_size * as->sample_size; } i = as->sample_size * as->used_channels; if ((n = snd_pcm_writen(as->handle, as->bufs.ptr, count / i)) < 0) { goto bfio_write_error; } } if (as->restart) { as->restart = false; if ((err = snd_pcm_start(as->handle)) < 0) { fprintf(stderr, "ALSA I/O: Could not restart audio: %s.\n", snd_strerror(err)); errno = -EPIPE; return -1; } } return n * i; bfio_write_error: switch (n) { case -EPIPE: if (as->ignore_xrun) { fprintf(stderr, "ALSA I/O: underflow! (write on %s)\n", as->device); if ((err = snd_pcm_prepare(as->handle)) < 0) { fprintf(stderr, "ALSA I/O: Unable to prepare audio: %s.\n", snd_strerror(err)); errno = EPIPE; return -1; } as->restart = true; return count; } /* assume and indicate buffer underflow */ errno = EPIPE; break; case -EAGAIN: errno = EAGAIN; return -1; default: errno = 0; break; } fprintf(stderr, "ALSA I/O: Could not write audio: %s.\n", snd_strerror(n)); return -1; } void do_init(void); void __attribute__((constructor)) do_init(void) { memset(handles, 0, sizeof(handles)); memset(n_handles, 0, sizeof(n_handles)); memset(fd2as, 0, sizeof(fd2as)); } brutefir-1.0o/bfio_file.c0000664000175000017500000004414012752354353014765 0ustar andersanders/* * (c) Copyright 2001 - 2004, 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include #include #include #include #include #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif #define IS_BFIO_MODULE #include "bfmod.h" #include "bit.h" #define TEXT_BUFFER_SIZE 4096 #define OUTTEXT_FORMAT "%+.16e" struct readstate { off_t filesize; off_t skipbytes; off_t curpos; bool_t loop; bool_t use_text; struct { int dupfd; int filefd; int bufsize; int offset; int parse_offset; char *buffer; } text; }; struct writestate { int open_channels; bool_t use_text; struct { int bufsize; int offset; int print_offset; int partial_offset; char *buffer; char *partial_buffer; } text; }; static struct readstate *readstate[FD_SETSIZE]; static struct writestate *writestate[FD_SETSIZE]; static fd_set fds[2]; static int fdmax[2] = { -1, -1 }; static bool_t debug = false; static int outtext_len; static int read_ready_fd = -1; struct settings { off_t skipbytes; bool_t append; bool_t loop; bool_t text; char *path; }; #define GET_TOKEN(token, errstr) \ if (get_config_token(&lexval) != token) { \ fprintf(stderr, "File I/O: Parse error: " errstr); \ return NULL; \ } void * bfio_preinit(int *version_major, int *version_minor, int (*get_config_token)(union bflexval *lexval), int io, int *sample_format, int sample_rate, int open_channels, int *uses_sample_clock, int *callback_sched_policy, struct sched_param *callback_sched, int _debug) { struct settings *settings; union bflexval lexval; int token, ver, dummypipe[2]; ver = *version_major; *version_major = BF_VERSION_MAJOR; *version_minor = BF_VERSION_MINOR; if (ver != BF_VERSION_MAJOR) { return NULL; } debug = !!_debug; settings = malloc(sizeof(struct settings)); memset(settings, 0, sizeof(struct settings)); while ((token = get_config_token(&lexval)) > 0) { if (token == BF_LEXVAL_FIELD) { if (strcmp(lexval.field, "path") == 0) { if (settings->path != NULL) { fprintf(stderr, "File I/O: Parse error: path " "already set.\n"); return NULL; } GET_TOKEN(BF_LEXVAL_STRING, "expected string.\n"); settings->path = strdup(lexval.string); } else if (strcmp(lexval.field, "skip") == 0) { GET_TOKEN(BF_LEXVAL_REAL, "expected integer.\n"); settings->skipbytes = (off_t)lexval.real; } else if (strcmp(lexval.field, "append") == 0) { if (io == BF_IN) { fprintf(stderr, "File I/O: Append on input makes " "no sense.\n"); return NULL; } GET_TOKEN(BF_LEXVAL_BOOLEAN, "expected boolean value.\n"); settings->append = lexval.boolean; } else if (strcmp(lexval.field, "loop") == 0) { if (io == BF_OUT) { fprintf(stderr, "File I/O: Loop on output makes " "no sense.\n"); return NULL; } GET_TOKEN(BF_LEXVAL_BOOLEAN, "expected boolean value.\n"); settings->loop = lexval.boolean; } else if (strcmp(lexval.field, "text") == 0) { GET_TOKEN(BF_LEXVAL_BOOLEAN, "expected boolean value.\n"); settings->text = lexval.boolean; } else { fprintf(stderr, "File I/O: Parse error: unknown field.\n"); return NULL; } GET_TOKEN(BF_LEX_EOS, "expected end of statement (;).\n"); } else { fprintf(stderr, "File I/O: Parse error: expected field.\n"); return NULL; } } if (settings->path == NULL) { fprintf(stderr, "File I/O: Parse error: path not set.\n"); return NULL; } if (settings->text) { if (read_ready_fd == -1) { /* make read file descriptor that immediately causes a return if used in select() */ if (pipe(dummypipe) == -1) { fprintf(stderr, "File I/O: Could not create pipe: %s.\n", strerror(errno)); return NULL; } close(dummypipe[1]); read_ready_fd = dummypipe[0]; } if (*sample_format == BF_SAMPLE_FORMAT_AUTO) { #ifdef __LITTLE_ENDIAN__ *sample_format = BF_SAMPLE_FORMAT_FLOAT64_LE; #endif #ifdef __BIG_ENDIAN__ *sample_format = BF_SAMPLE_FORMAT_FLOAT64_BE; #endif } switch (*sample_format) { #ifdef __LITTLE_ENDIAN__ case BF_SAMPLE_FORMAT_FLOAT64_LE: #endif #ifdef __BIG_ENDIAN__ case BF_SAMPLE_FORMAT_FLOAT64_BE: #endif break; default: fprintf(stderr, "File I/O: No support for text conversion of given" " sample format.\n"); return NULL; } } else { if (*sample_format == BF_SAMPLE_FORMAT_AUTO) { fprintf(stderr, "File I/O: No support for AUTO sample format.\n"); return NULL; } } *uses_sample_clock = 0; return settings; } int bfio_init(void *params, int io, int sample_format, int sample_rate, int open_channels, int used_channels, const int channel_selection[], int period_size, int *device_period_size, int *isinterleaved, void *callback_state, int (*process_callback)(void **callback_states[2], int callback_state_count[2], void **buffers[2], int frame_count, int event)) { struct settings *settings; struct writestate *ws; struct readstate *rs; struct stat buf; int fd, mode; settings = (struct settings *)params; *device_period_size = 0; *isinterleaved = 1; if (io == BF_IN) { if ((fd = open(settings->path, O_RDONLY | O_NONBLOCK | O_LARGEFILE)) == -1) { fprintf(stderr, "File I/O: Could not open file \"%s\" for " "reading: %s.\n", settings->path, strerror(errno)); return -1; } rs = malloc(sizeof(struct readstate)); memset(rs, 0, sizeof(struct readstate)); rs->filesize = 0; if (settings->loop) { if (stat(settings->path, &buf) != 0) { fprintf(stderr, "File I/O: Could not stat file \"%s\": %s.\n", settings->path, strerror(errno)); return -1; } rs->filesize = buf.st_size; if (rs->filesize == 0) { fprintf(stderr, "File I/O: Cannot loop empty file \"%s\".\n", settings->path); return -1; } } rs->curpos = 0; rs->skipbytes = settings->skipbytes; rs->loop = settings->loop; rs->use_text = settings->text; if (settings->skipbytes > 0) { if (lseek(fd, settings->skipbytes, SEEK_SET) == -1) { fprintf(stderr, "File seek failed.\n"); return -1; } rs->curpos = settings->skipbytes; } if (settings->text) { rs->text.filefd = fd; rs->text.buffer = malloc(TEXT_BUFFER_SIZE + 1); rs->text.buffer[0] = '\0'; rs->text.bufsize = TEXT_BUFFER_SIZE; if ((fd = dup(read_ready_fd)) == -1) { fprintf(stderr, "File I/O: Dup failed: %s.\n", strerror(errno)); return -1; } rs->text.dupfd = read_ready_fd; } readstate[fd] = rs; } else { if (settings->append) { mode = O_APPEND; } else { mode = O_TRUNC; } if ((fd = open(settings->path, O_WRONLY | O_CREAT | mode | O_NONBLOCK | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { fprintf(stderr, "File I/O: Could not create file \"%s\" for " "writing: %s.\n", settings->path, strerror(errno)); return -1; } ws = malloc(sizeof(struct writestate)); memset(ws, 0, sizeof(struct writestate)); ws->open_channels = open_channels; ws->use_text = settings->text; if (settings->text) { ws->text.partial_buffer = malloc(open_channels * (outtext_len + 1)); ws->text.bufsize = TEXT_BUFFER_SIZE; while (ws->text.bufsize < open_channels * (outtext_len + 1)) { ws->text.bufsize += TEXT_BUFFER_SIZE; } ws->text.buffer = malloc(ws->text.bufsize); } writestate[fd] = ws; } FD_SET(fd, &fds[io]); if (fd > fdmax[io]) { fdmax[io] = fd; } free(settings->path); free(settings); return fd; } static int text_read(int fd, void *buf, int count) { char *p, *p1, *parsebuf, *filebuf; int retval, i, rest, size; struct readstate *rs; double *a; rs = readstate[fd]; a = (double *)buf; count >>= 3; i = 0; while (i < count) { parsebuf = &rs->text.buffer[rs->text.parse_offset]; for (p = parsebuf; *p != '\n' && *p != ' ' && *p != '\t' && *p != '\0'; p++); if (*p == '\0') { if (p != &rs->text.buffer[rs->text.offset]) { fprintf(stderr, "File I/O: Read failed: null character " "in text input.\n"); errno = EIO; return -1; } if (rs->text.offset == rs->text.bufsize) { /* move tail to start of buffer (normally 'rest' is <30 so it is a cheap operation) */ rest = rs->text.bufsize - rs->text.parse_offset; if (rest > (rs->text.bufsize >> 3)) { fprintf(stderr, "File I/O: Read failed: too long entry " "in text input.\n"); errno = EIO; return -1; } memcpy(rs->text.buffer, parsebuf, rest); rs->text.offset = rest; rs->text.parse_offset = 0; } filebuf = &rs->text.buffer[rs->text.offset]; size = rs->text.bufsize - rs->text.offset; if ((retval = read(rs->text.filefd, filebuf, size)) == -1) { if (errno != EAGAIN && errno != EINTR) { fprintf(stderr, "File I/O: Read failed: %s.\n", strerror(errno)); return -1; } /* continue */ } if (retval < size) { if (rs->text.dupfd != rs->text.filefd) { if (dup2(rs->text.filefd, fd) == -1) { fprintf(stderr, "File I/O: Dup2 failed: %s.\n", strerror(errno)); errno = EINVAL; return -1; } rs->text.dupfd = rs->text.filefd; } if (retval == -1) { break; } } rs->curpos += retval;; if (rs->loop && rs->curpos == rs->filesize) { if (lseek(rs->text.filefd, rs->skipbytes, SEEK_SET) == -1) { fprintf(stderr, "File I/O: seek failed: %s.\n", strerror(errno)); return -1; } rs->curpos = rs->skipbytes; } if (retval == 0 && !rs->loop) { break; } rs->text.offset += retval; rs->text.buffer[rs->text.offset] = '\0'; continue; } rs->text.parse_offset += p - parsebuf + 1; *p = '\0'; while (*parsebuf == ' ' || *parsebuf == '\t') parsebuf++; if (parsebuf == p) { /* skip empty lines */ continue; } a[i++] = strtod(parsebuf, &p1); if (p1 == parsebuf) { fprintf(stderr, "File I/O: Read failed: bad text format.\n"); errno = EIO; return -1; } } if (i == count) { if (rs->text.dupfd != read_ready_fd) { if (dup2(read_ready_fd, fd) == -1) { fprintf(stderr, "File I/O: Dup2 failed: %s.\n", strerror(errno)); errno = EBADF; return -1; } rs->text.dupfd = read_ready_fd; } } return i << 3; } int bfio_read(int fd, void *buf, int offset, int count) { int retval; if (readstate[fd]->use_text) { return text_read(fd, &((uint8_t *)buf)[offset], count); } retry: if ((retval = read(fd, &((uint8_t *)buf)[offset], count)) == -1) { if (errno != EAGAIN && errno != EINTR) { fprintf(stderr, "File I/O: Read failed: %s.\n", strerror(errno)); } return -1; } readstate[fd]->curpos += retval;; if (readstate[fd]->loop && readstate[fd]->curpos == readstate[fd]->filesize) { if (lseek(fd, readstate[fd]->skipbytes, SEEK_SET) == -1) { fprintf(stderr, "File I/O: seek failed: %s.\n", strerror(errno)); return -1; } readstate[fd]->curpos = readstate[fd]->skipbytes; if (retval == 0) { goto retry; } } return retval; } static int text_write_helper(int fd, int linelen) { int retval, i, partial, whole; struct writestate *ws; struct iovec iovec[2]; ws = writestate[fd]; i = 0; if (ws->text.partial_offset != 0) { iovec[i].iov_base = &ws->text.partial_buffer[ws->text.partial_offset]; iovec[i].iov_len = linelen - ws->text.partial_offset; i++; } if (ws->text.print_offset - ws->text.offset > 0) { iovec[i].iov_base = &ws->text.buffer[ws->text.offset]; iovec[i].iov_len = ws->text.print_offset - ws->text.offset; i++; } if (i == 0) { return 0; } if ((retval = writev(fd, iovec, i)) == -1) { if (errno != EAGAIN && errno != EINTR) { fprintf(stderr, "File I/O: Write failed: %s.\n", strerror(errno)); } return -1; } partial = 0; if (ws->text.partial_offset != 0) { if (ws->text.partial_offset + retval >= linelen) { retval -= linelen - ws->text.partial_offset; ws->text.partial_offset = 0; partial = 1; } else { ws->text.partial_offset += retval; errno = EAGAIN; return -1; } } whole = retval / linelen; ws->text.partial_offset = retval % linelen; if (ws->text.partial_offset != 0) { memcpy(ws->text.partial_buffer, &ws->text.buffer[whole * linelen], linelen); retval += linelen - ws->text.partial_offset; } ws->text.offset += retval; if (ws->text.offset == ws->text.print_offset) { ws->text.print_offset = 0; ws->text.offset = 0; } return whole + partial; } static int text_write(int fd, const void *buf, int count) { int retval, i, j, k, linelen; struct writestate *ws; const double *a; char *printbuf; ws = writestate[fd]; count >>= 3; a = (const double *)buf; if (ws->text.partial_offset != 0) { a += ws->open_channels; count -= ws->open_channels; } retval = 1; i = 0; j = 0; linelen = ws->open_channels * (outtext_len + 1); while (i < count) { if (ws->text.print_offset + linelen > ws->text.bufsize) { if ((retval = text_write_helper(fd, linelen)) <= 0) { if (retval == -1 && errno != EAGAIN && errno != EINTR) { return -1; } break; } j += retval; } else { printbuf = &ws->text.buffer[ws->text.print_offset]; for (k = 0; k < ws->open_channels; k++) { sprintf(printbuf, OUTTEXT_FORMAT, a[i++]); printbuf += outtext_len + 1; printbuf[-1] = '\t'; } printbuf[-1] = '\n'; ws->text.print_offset += linelen; } } if ((retval > 0) && (ws->text.partial_offset != 0 || ws->text.print_offset != 0)) { if ((retval = text_write_helper(fd, linelen)) <= 0) { if (retval == -1 && errno != EAGAIN && errno != EINTR) { return -1; } retval = 0; } j += retval; } if (j == 0) { errno = EAGAIN; return -1; } return ws->open_channels * (j << 3); } int bfio_write(int fd, const void *buf, int offset, int count) { int retval; if (writestate[fd]->use_text) { return text_write(fd, &((const uint8_t *)buf)[offset], count); } if ((retval = write(fd, &((const uint8_t *)buf)[offset], count)) == -1) { if (errno != EAGAIN && errno != EINTR) { fprintf(stderr, "File I/O: Write failed: %s.\n", strerror(errno)); } } return retval; } int bfio_start(int io) { /* do nothing */ return 0; } void bfio_stop(int io) { int fd; for (fd = 0; fd <= fdmax[io]; fd++) { if (FD_ISSET(fd, &fds[io])) { close(fd); } } } void do_init(void); void __attribute__((constructor)) do_init(void) { char s[1024]; memset(readstate, 0, sizeof(readstate)); memset(fds, 0, sizeof(fds)); sprintf(s, OUTTEXT_FORMAT, 1.0); outtext_len = strlen(s); } brutefir-1.0o/bfio_jack.c0000644000175000017500000004545612752354353014767 0ustar andersanders/* * (c) Copyright 2003 - 2006, 2009, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include #include #include #include "defs.h" #include "emalloc.h" #define IS_BFIO_MODULE #include "bfmod.h" #include "inout.h" struct jack_state { int n_channels; jack_port_t *ports[BF_MAXCHANNELS]; char *port_name[BF_MAXCHANNELS]; char *local_port_name[BF_MAXCHANNELS]; char *dest_name[BF_MAXCHANNELS]; }; #define DEFAULT_CLIENTNAME "brutefir" #define DEFAULT_JACK_CB_THREAD_PRIORITY 5 static bool_t debug; static volatile bool_t stopped = false; static volatile bool_t has_started = false; static struct jack_state *handles[2][BF_MAXCHANNELS]; static int expected_priority = -1; static void **states[2]; static int n_handles[2]; static bool_t hasio[2]; static jack_client_t *client = NULL; static char *client_name = NULL; static int (*process_cb)(void **_states[2], int state_count[2], void **bufs[2], int count, int event); static void error_callback(const char *msg) { if (stopped) { /* we don't care about errors after we have stopped */ return; } if (msg[strlen(msg)-1] == '\n') { fprintf(stderr, "JACK I/O: JACK reported an error: %s", msg); } else { fprintf(stderr, "JACK I/O: JACK reported an error: %s\n", msg); } if (has_started) { stopped = true; process_cb(states, n_handles, NULL, 0, BF_CALLBACK_EVENT_ERROR); } } static void shutdown_callback(void *arg) { fprintf(stderr, "JACK I/O: JACK daemon shut down.\n"); stopped = true; process_cb(states, n_handles, NULL, 0, BF_CALLBACK_EVENT_ERROR); } static void init_callback_(void) { struct sched_param schp; int policy; pthread_getschedparam(pthread_self(), &policy, &schp); if (policy != SCHED_FIFO && policy != SCHED_RR) { fprintf(stderr, "JACK I/O: Warning: JACK is not running with " "SCHED_FIFO or SCHED_RR (realtime).\n"); } else if (schp.sched_priority != expected_priority) { fprintf(stderr, "\ JACK I/O: Warning: JACK thread has priority %d, but BruteFIR expected %d.\n\ In order to make correct realtime scheduling BruteFIR must know what\n\ priority JACK uses. At the time of writing the JACK API does not support\n\ getting that information so BruteFIR must be manually configured with that.\n\ Use the \"priority\" setting in the first \"jack\" device clause in your\n\ BruteFIR configuration file.\n", (int)schp.sched_priority, (int)expected_priority); } } static void init_callback(void *arg) { static pthread_once_t once_control = PTHREAD_ONCE_INIT; pthread_once(&once_control, init_callback_); } static void latency_callback(jack_latency_callback_mode_t mode, void *arg) { const int period_size = (intptr_t)arg; jack_latency_range_t range; struct jack_state *js; int n, i; // same latency for all ports, regardless of how they are connected if (mode == JackPlaybackLatency) { // do nothing } else if (mode == JackCaptureLatency) { for (n = 0; n < n_handles[OUT]; n++) { js = handles[OUT][n]; for (i = 0; i < js->n_channels; i++) { range.min = period_size; range.max = period_size; jack_port_set_latency_range(js->ports[i], mode, &range); } } } } static int process_callback(jack_nframes_t n_frames, void *arg) { static int frames_left = 0; void *in_bufs[BF_MAXCHANNELS], *out_bufs[BF_MAXCHANNELS], **iobufs[2]; struct jack_state *js; void *buffer = NULL; int n, i; iobufs[IN] = n_handles[IN] > 0 ? in_bufs : NULL; iobufs[OUT] = n_handles[OUT] > 0 ? out_bufs : NULL; FOR_IN_AND_OUT { for (n = 0; n < n_handles[IO]; n++) { js = handles[IO][n]; for (i = 0; i < js->n_channels; i++) { iobufs[IO][i] = jack_port_get_buffer(js->ports[i], n_frames); } } } if (frames_left != 0) { process_cb(states, n_handles, NULL, 0, BF_CALLBACK_EVENT_FINISHED); for (n = 0; n < n_handles[OUT]; n++) { js = handles[OUT][n]; for (i = 0; i < js->n_channels; i++) { buffer = jack_port_get_buffer(js->ports[i], n_frames); memset(buffer, 0, n_frames * sizeof(jack_default_audio_sample_t)); } } stopped = true; return -1; } frames_left = process_cb(states, n_handles, iobufs, n_frames, BF_CALLBACK_EVENT_NORMAL); if (frames_left == -1) { stopped = true; return -1; } return 0; } static bool_t global_init(void) { jack_status_t status; jack_set_error_function(error_callback); if ((client = jack_client_open(client_name, JackNoStartServer, &status)) == NULL) { fprintf(stderr, "\ JACK I/O: Could not become JACK client (status: 0x%2.0x). Error message(s):\n", status); if ((status & JackFailure) != 0) { fprintf(stderr, "\ Overall operation failed.\n"); } if ((status & JackInvalidOption) != 0) { fprintf(stderr, "\ Likely bug in BruteFIR: the operation contained an invalid or unsupported\n\ option.\n"); } if ((status & JackNameNotUnique) != 0) { fprintf(stderr, "\ Client name \"%s\" not unique, try another name.\n", client_name); } if ((status & JackServerFailed) != 0) { fprintf(stderr, "\ Unable to connect to the JACK server. Perhaps it is not running? BruteFIR\n\ requires that a JACK server is started in advance.\n"); } if ((status & JackServerError) != 0) { fprintf(stderr, " Communication error with the JACK server.\n"); } if ((status & JackNoSuchClient) != 0) { fprintf(stderr, " Requested client does not exist.\n"); } if ((status & JackLoadFailure) != 0) { fprintf(stderr, " Unable to load internal client.\n"); } if ((status & JackInitFailure) != 0) { fprintf(stderr, " Unable initialize client.\n"); } if ((status & JackShmFailure) != 0) { fprintf(stderr, " Unable to access shared memory.\n"); } if ((status & JackVersionError) != 0) { fprintf(stderr, "\ The version of the JACK server is not compatible with the JACK client\n\ library used by BruteFIR.\n"); } return false; } jack_set_thread_init_callback(client, init_callback, NULL); jack_set_process_callback(client, process_callback, NULL); jack_on_shutdown(client, shutdown_callback, NULL); return true; } int bfio_iscallback(void) { return true; } #define GET_TOKEN(token, errstr) \ if (get_config_token(&lexval) != token) { \ fprintf(stderr, "JACK I/O: Parse error: " errstr); \ return NULL; \ } void * bfio_preinit(int *version_major, int *version_minor, int (*get_config_token)(union bflexval *lexval), int io, int *sample_format, int sample_rate, int open_channels, int *uses_sample_clock, int *callback_sched_policy, struct sched_param *callback_sched_param, int _debug) { int rate, n, ver, token; union bflexval lexval; struct jack_state *js; ver = *version_major; *version_major = BF_VERSION_MAJOR; *version_minor = BF_VERSION_MINOR; if (ver != BF_VERSION_MAJOR) { return NULL; } debug = !!_debug; if (*sample_format == BF_SAMPLE_FORMAT_AUTO) { #ifdef __LITTLE_ENDIAN__ if (sizeof(jack_default_audio_sample_t) == 4) { *sample_format = BF_SAMPLE_FORMAT_FLOAT_LE; } else if (sizeof(jack_default_audio_sample_t) == 8) { *sample_format = BF_SAMPLE_FORMAT_FLOAT64_LE; } #endif #ifdef __BIG_ENDIAN__ if (sizeof(jack_default_audio_sample_t) == 4) { *sample_format = BF_SAMPLE_FORMAT_FLOAT_BE; } else if (sizeof(jack_default_audio_sample_t) == 8) { *sample_format = BF_SAMPLE_FORMAT_FLOAT64_BE; } #endif } if (sizeof(jack_default_audio_sample_t) == 4) { #ifdef __LITTLE_ENDIAN__ if (*sample_format != BF_SAMPLE_FORMAT_FLOAT_LE) { fprintf(stderr, "JACK I/O: Sample format must be %s or %s.\n", bf_strsampleformat(BF_SAMPLE_FORMAT_FLOAT_LE), bf_strsampleformat(BF_SAMPLE_FORMAT_AUTO)); return NULL; } #endif #ifdef __BIG_ENDIAN__ if (*sample_format != BF_SAMPLE_FORMAT_FLOAT_BE) { fprintf(stderr, "JACK I/O: Sample format must be %s or %s.\n", bf_strsampleformat(BF_SAMPLE_FORMAT_FLOAT_BE), bf_strsampleformat(BF_SAMPLE_FORMAT_AUTO)); return NULL; } #endif } else if (sizeof(jack_default_audio_sample_t) == 8) { #ifdef __LITTLE_ENDIAN__ if (*sample_format != BF_SAMPLE_FORMAT_FLOAT64_LE) { fprintf(stderr, "JACK I/O: Sample format must be %s or %s.\n", bf_strsampleformat(BF_SAMPLE_FORMAT_FLOAT64_LE), bf_strsampleformat(BF_SAMPLE_FORMAT_AUTO)); return NULL; } #endif #ifdef __BIG_ENDIAN__ if (*sample_format != BF_SAMPLE_FORMAT_FLOAT64_BE) { fprintf(stderr, "JACK I/O: Sample format must be %s or %s.\n", bf_strsampleformat(BF_SAMPLE_FORMAT_FLOAT64_BE), bf_strsampleformat(BF_SAMPLE_FORMAT_AUTO)); return NULL; } #endif } js = emalloc(sizeof(struct jack_state)); memset(js, 0, sizeof(struct jack_state)); js->n_channels = open_channels; while ((token = get_config_token(&lexval)) > 0) { if (token != BF_LEXVAL_FIELD) { fprintf(stderr, "JACK I/O: Parse error: expected field.\n"); return NULL; } if (strcmp(lexval.field, "ports") == 0) { for (n = 0; n < open_channels; n++) { GET_TOKEN(BF_LEXVAL_STRING, "expected string.\n"); if (lexval.string[0] != '\0') { js->dest_name[n] = estrdup(lexval.string); } if ((token = get_config_token(&lexval)) == BF_LEX_SLASH) { GET_TOKEN(BF_LEXVAL_STRING, "expected string.\n"); if (lexval.string[0] != '\0') { js->local_port_name[n] = estrdup(lexval.string); } token = get_config_token(&lexval); } if (n < open_channels - 1) { if (token != BF_LEX_COMMA) { fprintf(stderr, "JACK I/O: Parse error: " "expected comma (,).\n"); return NULL; } } else { if (token != BF_LEX_EOS) { fprintf(stderr, "JACK I/O: Parse error: " "expected end of statement (;).\n"); return NULL; } } } } else if (strcmp(lexval.field, "clientname") == 0) { GET_TOKEN(BF_LEXVAL_STRING, "expected string.\n"); if (client != NULL && strcmp(lexval.string, client_name) != 0) { fprintf(stderr, "JACK I/O: clientname setting is global and " "must be set in the first jack device.\n"); return NULL; } if (client_name == NULL) { client_name = estrdup(lexval.string); } GET_TOKEN(BF_LEX_EOS, "expected end of statement (;).\n"); } else if (strcmp(lexval.field, "priority") == 0) { GET_TOKEN(BF_LEXVAL_REAL, "expected integer.\n"); if (client != NULL && expected_priority != (int)lexval.real) { fprintf(stderr, "JACK I/O: priority setting is global and " "must be set in the first jack device.\n"); return NULL; } expected_priority = (int)lexval.real; GET_TOKEN(BF_LEX_EOS, "expected end of statement (;).\n"); } } hasio[io] = true; if (expected_priority < 0) { expected_priority = DEFAULT_JACK_CB_THREAD_PRIORITY; } if (client == NULL) { if (client_name == NULL) { client_name = estrdup(DEFAULT_CLIENTNAME); } if (!global_init()) { return NULL; } } memset(callback_sched_param, 0, sizeof(*callback_sched_param)); callback_sched_param->sched_priority = expected_priority; *callback_sched_policy = SCHED_FIFO; if ((rate = (int)jack_get_sample_rate(client)) == 0) { *uses_sample_clock = 0; } else { *uses_sample_clock = 1; } if (rate != 0 && rate != sample_rate) { fprintf(stderr, "JACK I/O: JACK sample rate is %d, BruteFIR is %d, " "they must be same.\n", rate, sample_rate); return NULL; } return (void *)js; } int bfio_init(void *params, int io, int sample_format, int sample_rate, int open_channels, int used_channels, const int channel_selection[], int period_size, int *device_period_size, int *isinterleaved, void *callback_state, int (*bf_process_callback)(void **callback_states[2], int callback_state_count[2], void **buffers[2], int frame_count, int event)) { static void *_states[2][BF_MAXCHANNELS]; static int io_idx[2] = { 0, 0 }; char *name, _name[128], longname[1024]; struct jack_state *js; jack_port_t *port; int n; process_cb = bf_process_callback; *device_period_size = jack_get_buffer_size(client); *isinterleaved = false; js = (struct jack_state *)params; if (used_channels != open_channels) { fprintf(stderr, "JACK I/O: Open channels must be equal to used " "channels for this I/O module.\n"); return -1; } for (n = 0; n < used_channels; n++) { if (js->dest_name[n] != NULL) { if ((port = jack_port_by_name(client, js->dest_name[n])) == NULL) { fprintf(stderr, "JACK I/O: Failed to open JACK port \"%s\".\n", js->dest_name[n]); return -1; } if ((io == IN && (jack_port_flags(port) & JackPortIsOutput) == 0) || (io == OUT && (jack_port_flags(port) & JackPortIsInput) == 0)) { fprintf(stderr, "JACK I/O: JACK port \"%s\" is not an %s.\n", js->dest_name[n], io == IN ? "Output" : "Input"); return -1; } } if (js->local_port_name[n] != NULL) { name = js->local_port_name[n]; } else { name = _name; sprintf(name, "%s-%d", io == IN ? "input" : "output", io_idx[io]++); } port = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, (io == IN ? JackPortIsInput : JackPortIsOutput), 0); if (port == NULL) { fprintf(stderr, "JACK I/O: Failed to open new JACK port.\n"); return -1; } // if (io == OUT) { // jack_port_set_latency(port, period_size); // } js->ports[n] = port; snprintf(longname, sizeof(longname), "%s:%s", client_name, name); longname[sizeof(longname) - 1] = '\0'; js->port_name[n] = estrdup(longname); } _states[io][n_handles[io]] = callback_state; handles[io][n_handles[io]++] = js; states[IN] = n_handles[IN] > 0 ? _states[IN] : NULL; states[OUT] = n_handles[OUT] > 0 ? _states[OUT] : NULL; if (io == OUT && hasio[IN]) { jack_set_latency_callback(client, latency_callback, (void *)(intptr_t)period_size); } return 0; } int bfio_synch_start(void) { struct jack_state *js; sigset_t signals; int n, i; if (has_started) { return 0; } if (client == NULL) { fprintf(stderr, "JACK I/O: client is NULL\n"); return -1; } has_started = true; /* * jack_activate() will start a new pthread. We block all signals before * calling to make sure that we get all signals to our thread. This is a * bit ugly, since it assumes that the JACK library does not mess up the * signal handlers later. */ sigfillset(&signals); pthread_sigmask(SIG_BLOCK, &signals, NULL); n = jack_activate(client); pthread_sigmask(SIG_UNBLOCK, &signals, NULL); if (n != 0) { fprintf(stderr, "JACK I/O: Could not activate local JACK client.\n"); has_started = false; return -1; } for (n = 0; n < n_handles[IN]; n++) { js = handles[IN][n]; for (i = 0; i < js->n_channels; i++) { if (js->dest_name[i] == NULL) { continue; } if (jack_connect(client, js->dest_name[i], js->port_name[i]) != 0) { fprintf(stderr, "JACK I/O: Could not connect local port \"%s\" " "to \"%s\".\n", js->port_name[i], js->dest_name[i]); has_started = false; return -1; } } } for (n = 0; n < n_handles[OUT]; n++) { js = handles[OUT][n]; for (i = 0; i < js->n_channels; i++) { if (js->dest_name[i] == NULL) { continue; } if (jack_connect(client, js->port_name[i], js->dest_name[i]) != 0) { fprintf(stderr, "JACK I/O: Could not connect local port \"%s\" " "to \"%s\".\n", js->port_name[i], js->dest_name[i]); has_started = false; return -1; } } } return 0; } void bfio_synch_stop(void) { if (!stopped) { stopped = true; jack_client_close(client); } } void do_init(void); void __attribute__((constructor)) do_init(void) { memset(hasio, 0, sizeof(hasio)); memset(handles, 0, sizeof(handles)); memset(n_handles, 0, sizeof(n_handles)); } brutefir-1.0o/bfio_oss.c0000644000175000017500000003165212752354353014654 0ustar andersanders/* * (c) Copyright 2004 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include #include #include #include "defs.h" #include "log2.h" #include "bit.h" #include "emalloc.h" #define IS_BFIO_MODULE #include "bfmod.h" #include "inout.h" #define ERRORSIZE 1024 struct oss_device_info { char *device; bool_t dir[2]; bool_t trigger; int sample_format; int sample_rate; int open_channels; int period_size; int fd; }; struct settings { char *device; }; static struct oss_device_info *devices[2 * BF_MAXCHANNELS]; static int n_devices = 0; static bool_t debug = false; static bool_t set_params(int fd, int sample_format, int sample_rate, int open_channels, int period_size, int *hardware_period_size, char errstr[]) { int format, n; /* Set the fragment size */ n = period_size * open_channels * bf_sampleformat_size(sample_format); n = (0x7FFF << 16) | n; /* we check the actual result later (n does not get the true value) */ if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &n) == -1) { sprintf(errstr, " Could not set fragment size: %s.\n", strerror(errno)); return false; } /* Set sample format */ switch (sample_format) { case BF_SAMPLE_FORMAT_S8: format = AFMT_S8; break; case BF_SAMPLE_FORMAT_S16_LE: format = AFMT_S16_LE; break; case BF_SAMPLE_FORMAT_S16_BE: format = AFMT_S16_BE; break; #ifdef AFMT_S24_LE case BF_SAMPLE_FORMAT_S24_LE: format = AFMT_S24_LE; break; case BF_SAMPLE_FORMAT_S24_BE: format = AFMT_S24_BE; break; #endif #ifdef AFMT_S32_LE case BF_SAMPLE_FORMAT_S24_4LE: case BF_SAMPLE_FORMAT_S32_LE: format = AFMT_S32_LE; break; case BF_SAMPLE_FORMAT_S24_4BE: case BF_SAMPLE_FORMAT_S32_BE: format = AFMT_S32_BE; break; #endif default: sprintf(errstr, " Unsupported sample format.\n"); return false; } n = format; if (ioctl(fd, SNDCTL_DSP_SETFMT, &n) == -1) { sprintf(errstr, " Could not set sample format: %s.\n", strerror(errno)); return false; } if (n != format) { sprintf(errstr, " Sample format %s is not supported by the device.\n", bf_strsampleformat(sample_format)); return false; } /* Set channel count */ n = open_channels; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &n) == -1) { sprintf(errstr, " Could not set channel count: %s.\n", strerror(errno)); return false; } if (n != open_channels) { sprintf(errstr, " Failed to open %d interleaved channels, device " "suggested %d channels instead.\n", open_channels, n); return false; } /* Set sample rate */ n = sample_rate; if (ioctl(fd, SNDCTL_DSP_SPEED, &n) == -1) { sprintf(errstr, " Could not set sample rate: %s.\n", strerror(errno)); return false; } /* accept a minor variation in sample rate */ if (n != sample_rate && !((int)((double)sample_rate * 0.99) < n && (int)((double)sample_rate / 0.99) > n)) { sprintf(errstr, " Failed to set sample rate to %d Hz, device " "suggested %d Hz instead.\n", sample_rate, n); return false; } /* Get the fragment size */ if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &n) == -1) { sprintf(errstr, " Could not get fragment size: %s.\n", strerror(errno)); return false; } *hardware_period_size = n; return true; } #define GET_TOKEN(token, errstr) \ if (get_config_token(&lexval) != token) { \ fprintf(stderr, "OSS I/O: Parse error: " errstr); \ return NULL; \ } void * bfio_preinit(int *version_major, int *version_minor, int (*get_config_token)(union bflexval *lexval), int io, int *sample_format, int sample_rate, int open_channels, int *uses_sample_clock, int *callback_sched_policy, struct sched_param *callback_sched, int _debug) { struct settings *settings; union bflexval lexval; int n, token, ver; ver = *version_major; *version_major = BF_VERSION_MAJOR; *version_minor = BF_VERSION_MINOR; if (ver != BF_VERSION_MAJOR) { return NULL; } debug = !!_debug; *uses_sample_clock = 1; settings = malloc(sizeof(struct settings)); memset(settings, 0, sizeof(struct settings)); while ((token = get_config_token(&lexval)) > 0) { if (token != BF_LEXVAL_FIELD) { fprintf(stderr, "OSS I/O: Parse error: expected field.\n"); return NULL; } if (strcmp(lexval.field, "device") == 0) { if (settings->device != NULL) { fprintf(stderr, "OSS I/O: Parse error: device already set.\n"); return NULL; } GET_TOKEN(BF_LEXVAL_STRING, "expected string.\n"); settings->device = estrdup(lexval.string); } else { fprintf(stderr, "OSS I/O: Parse error: unknown field.\n"); return NULL; } GET_TOKEN(BF_LEX_EOS, "expected end of statement (;).\n"); } if (settings->device == NULL) { fprintf(stderr, "OSS I/O: Parse error: device not set.\n"); return NULL; } if (*sample_format == BF_SAMPLE_FORMAT_AUTO) { fprintf(stderr, "OSS I/O: No support for AUTO sample format.\n"); return NULL; } for (n = 0; n < n_devices; n++) { if (strcmp(devices[n]->device, settings->device) == 0) { if (devices[n]->dir[io]) { fprintf(stderr, "OSS I/O: Device \"%s\" already used for " "audio %s.\n", settings->device, io == BF_IN ? "input" : "output"); return NULL; } devices[n]->dir[io] = true; } } if (n == n_devices) { devices[n] = emalloc(sizeof(struct oss_device_info)); memset(devices[n], 0, sizeof(struct oss_device_info)); devices[n]->device = estrdup(settings->device); devices[n]->dir[io] = true; devices[n]->fd = -1; n_devices++; } return settings; } int bfio_init(void *params, int io, int sample_format, int sample_rate, int open_channels, int used_channels, const int channel_selection[], int period_size, int *device_period_size, int *isinterleaved, void *callback_state, int (*process_callback)(void **callback_states[2], int callback_state_count[2], void **buffers[2], int frame_count, int event)) { struct oss_device_info *devinfo; struct settings *settings; char errstr[ERRORSIZE]; int n, fd, enable_bits; settings = (struct settings *)params; *isinterleaved = 1; devinfo = NULL; for (n = 0; n < n_devices; n++) { if (strcmp(settings->device, devices[n]->device) == 0) { devinfo = devices[n]; break; } } if (devinfo == NULL) { fprintf(stderr, "OSS I/O: Bug: device not found.\n"); return -1; } if (devinfo->fd != -1) { /* already opened, check that the parameters matches */ if (sample_format != devinfo->sample_format) { fprintf(stderr, "OSS I/O: Sample formats for input and output " "on device \"%s\" do not match.\n", devinfo->device); return -1; } if (sample_rate != devinfo->sample_rate) { fprintf(stderr, "OSS I/O: Sample rate for input and output " "on device \"%s\" do not match.\n", devinfo->device); return -1; } if (open_channels != devinfo->open_channels) { fprintf(stderr, "OSS I/O: Channel amount for input and output " "on device \"%s\" do not match.\n", devinfo->device); return -1; } *device_period_size = devinfo->period_size; return devinfo->fd; } if (devinfo->dir[BF_IN] && devinfo->dir[BF_OUT]) { /* open device in full duplex */ if ((fd = open(devinfo->device, O_RDWR)) == -1) { fprintf(stderr, "OSS I/O: Could not open device \"%s\" in full " "duplex mode: %s.\n", devinfo->device, strerror(errno)); return -1; } /* On *BSD, full duplex is enabled per default, which leads to that this ioctl() always fails with EINVAL, so we ignore that. */ if (ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0) == -1 && errno != EINVAL) { fprintf(stderr, "OSS I/O: Could not set device \"%s\" to full " "duplex mode: %s.\n", devinfo->device, strerror(errno)); return -1; } enable_bits = 0; } else { /* open device in half duplex */ if ((fd = open(devinfo->device, devinfo->dir[BF_IN] ? O_RDONLY : O_WRONLY)) == -1) { fprintf(stderr, "OSS I/O: Could not open device \"%s\" for audio " "%s: %s.\n", devinfo->device, devinfo->dir[BF_IN] ? "input" : "output", strerror(errno)); return -1; } enable_bits = devinfo->dir[BF_IN] ? ~PCM_ENABLE_INPUT : ~PCM_ENABLE_OUTPUT; } if (ioctl(fd, SNDCTL_DSP_GETCAPS, &n) == -1) { fprintf(stderr, "OSS I/O: Could not get device \"%s\" " "capabilities: %s.\n", devinfo->device, strerror(errno)); return -1; } if (devinfo->dir[BF_IN] && devinfo->dir[BF_OUT] && (n & DSP_CAP_DUPLEX) == 0) { fprintf(stderr, "OSS I/O: Device \"%s\" does not support full " "duplex.\n", devinfo->device); return -1; } /* We use the trigger if it exists. It will work without triggering though (at least on tested platform/card) */ if ((n & DSP_CAP_TRIGGER) != 0) { devinfo->trigger = true; if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1) { fprintf(stderr, "OSS I/O: Could not set enable bits for device " "\"%s\": %s.\n", devinfo->device, strerror(errno)); return -1; } } if (!set_params(fd, sample_format, sample_rate, open_channels, period_size, device_period_size, errstr)) { fprintf(stderr, "OSS I/O: Could not set audio %s parameters for " "\"%s\":\n%s", io == BF_IN ? "input" : "output", devinfo->device, errstr); close(fd); return -1; } devinfo->fd = fd; devinfo->sample_format = sample_format; devinfo->sample_rate = sample_rate; devinfo->open_channels = open_channels; devinfo->period_size = *device_period_size; efree(settings->device); efree(settings); return fd; } int bfio_synch_start(void) { int n, enable_bits; for (n = 0; n < n_devices; n++) { if (!devices[n]->trigger) { continue; } enable_bits = 0; if (devices[n]->dir[BF_IN]) { enable_bits |= PCM_ENABLE_INPUT; } if (devices[n]->dir[BF_OUT]) { enable_bits |= PCM_ENABLE_OUTPUT; } if (ioctl(devices[n]->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1) { fprintf(stderr, "OSS I/O: Could not trigger device " "\"%s\": %s.\n", devices[n]->device, strerror(errno)); return -1; } } return 0; } void bfio_synch_stop(void) { int n; for (n = 0; n < n_devices; n++) { close(devices[n]->fd); } } int bfio_read(int fd, void *buf, int offset, int count) { audio_buf_info info; if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) { fprintf(stderr, "OSS I/O: Could not get ispace info: %s.\n", strerror(errno)); return -1; } if (info.bytes == 0) { errno = EAGAIN; return -1; } if (count > info.bytes) { count = info.bytes; } count = read(fd, &((uint8_t *)buf)[offset], count); return count; } int bfio_write(int fd, const void *buf, int offset, int count) { audio_buf_info info; if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == -1) { fprintf(stderr, "OSS I/O: Could not get ospace info: %s.\n", strerror(errno)); return -1; } if (info.bytes == 0) { errno = EAGAIN; return -1; } if (count > info.bytes) { count = info.bytes; } count = write(fd, &((const uint8_t *)buf)[offset], count); return count; } brutefir-1.0o/bflogic_cli.c0000644000175000017500000012101412752354353015275 0ustar andersanders/* * (c) Copyright 2001 - 2006, 2009, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IS_BFLOGIC_MODULE #include "timermacros.h" #include "bfmod.h" #include "defs.h" #include "log2.h" #include "bit.h" #include "inout.h" #include "fdrw.h" #define WELCOME_TEXT "\nWelcome to BruteFIR, type \"help\" for help.\n\n" #define PROMPT_TEXT "> " #define HELP_TEXT "\n\ Commands:\n\ \n\ lf -- list filters.\n\ lc -- list coeffient sets.\n\ li -- list inputs.\n\ lo -- list outputs.\n\ lm -- list modules.\n\ \n\ cfoa -- change filter output attenuation.\n\ cfoa \n\ cfia -- change filter input attenuation.\n\ cfia \n\ cffa -- change filter filter-input attenuation.\n\ cffa \n\ cfc -- change filter coefficients.\n\ cfc \n\ cfd -- change filter delay. (may truncate coeffs!)\n\ cfd \n\ cod -- change output delay.\n\ cod []\n\ cid -- change input delay.\n\ cid []\n\ tmo -- toggle mute output.\n\ tmo \n\ tmi -- toggle mute input.\n\ tmi \n\ imc -- issue input module command.\n\ imc \n\ omc -- issue output module command.\n\ omc \n\ lmc -- issue logic module command.\n\ lmc \n\ \n\ sleep -- sleep for the given number of seconds [and ms], or blocks.\n\ sleep 10 (sleep 10 seconds).\n\ sleep b10 (sleep 10 blocks).\n\ sleep 0 300 (sleep 300 milliseconds).\n\ abort -- terminate immediately.\n\ tp -- toggle prompt.\n\ ppk -- print peak info, channels/samples/max dB.\n\ rpk -- reset peak meters.\n\ upk -- toggle print peak info on changes.\n\ rti -- print current realtime index.\n\ quit -- close connection.\n\ help -- print this text.\n\ \n\ Notes:\n\ \n\ - When entering several commands on a single line,\n\ separate them with semicolons (;).\n\ - Inputs/outputs/filters can be given as index\n\ numbers or as strings between quotes (\"\").\n\ \n\ " #define MAXCMDLINE 4096 #define FILTER_ID 1 #define INPUT_ID 2 #define OUTPUT_ID 3 #define COEFF_ID 4 static volatile struct bffilter_control *fctrl; static int n_filters; static const struct bffilter *filters; static int n_coeffs; static const struct bfcoeff *coeffs; static const int *n_channels; static const struct bfchannel **channels; static int line_speed; static int port, port2; static char *lport = NULL; static char *script = NULL; static struct bfaccess *bfaccess; static int n_maxblocks; static int block_length; static bool_t print_peak_updates = false; static bool_t print_prompt = true; static bool_t print_commands = false; static bool_t debug = false; static char error[1024]; struct sleep_task { bool_t do_sleep; bool_t block_sleep; unsigned int blocks; unsigned int seconds; unsigned int useconds; }; struct state { struct bffilter_control fctrl[BF_MAXFILTERS]; bool_t fchanged[BF_MAXFILTERS]; int delay[2][BF_MAXCHANNELS]; int subdelay[2][BF_MAXCHANNELS]; bool_t toggle_mute[2][BF_MAXCHANNELS]; }; static struct state newstate; static void clear_changes(void) { int n, i; if (fctrl == NULL) { return; } for (n = 0; n < n_filters; n++) { newstate.fctrl[n].coeff = fctrl[n].coeff; newstate.fctrl[n].delayblocks = fctrl[n].delayblocks; for (i = 0; i < filters[n].n_channels[OUT]; i++) { newstate.fctrl[n].scale[OUT][i] = fctrl[n].scale[OUT][i]; } for (i = 0; i < filters[n].n_channels[IN]; i++) { newstate.fctrl[n].scale[IN][i] = fctrl[n].scale[IN][i]; } for (i = 0; i < filters[n].n_filters[IN]; i++) { newstate.fctrl[n].fscale[i] = fctrl[n].fscale[i]; } } memset(newstate.toggle_mute, 0, sizeof(newstate.toggle_mute)); memset(newstate.fchanged, 0, sizeof(newstate.fchanged)); memset(newstate.delay, -1, sizeof(newstate.delay)); for (n = 0; n < BF_MAXCHANNELS; n++) { newstate.subdelay[IN][n] = BF_UNDEFINED_SUBDELAY; newstate.subdelay[OUT][n] = BF_UNDEFINED_SUBDELAY; } } static void commit_changes(FILE *stream) { int n, i; FOR_IN_AND_OUT { for (n = 0; n < n_channels[IO]; n++) { if (newstate.delay[IO][n] != -1) { if (bfaccess->set_delay(IO, n, newstate.delay[IO][n]) == -1) { fprintf(stream, "Could not change %s delay.\n", IO == IN ? "input" : "output"); } } if (newstate.subdelay[IO][n] != BF_UNDEFINED_SUBDELAY) { if (bfaccess->set_subdelay(IO, n, newstate.subdelay[IO][n]) == -1) { fprintf(stream, "Could not change %s subdelay.\n", IO == IN ? "input" : "output"); } } if (newstate.toggle_mute[IO][n]) { bfaccess->toggle_mute(IO, n); } } } for (n = 0; n < n_filters; n++) { if (!newstate.fchanged[n]) { continue; } fctrl[n].coeff = newstate.fctrl[n].coeff; fctrl[n].delayblocks = newstate.fctrl[n].delayblocks; for (i = 0; i < filters[n].n_channels[OUT]; i++) { fctrl[n].scale[OUT][i] = newstate.fctrl[n].scale[OUT][i]; } for (i = 0; i < filters[n].n_channels[IN]; i++) { fctrl[n].scale[IN][i] = newstate.fctrl[n].scale[IN][i]; } for (i = 0; i < filters[n].n_filters[IN]; i++) { fctrl[n].fscale[i] = newstate.fctrl[n].fscale[i]; } } } static bool_t are_changes(void) { int n; FOR_IN_AND_OUT { for (n = 0; n < n_channels[IO]; n++) { if (newstate.delay[IO][n] != -1 || newstate.subdelay[IO][n] != BF_UNDEFINED_SUBDELAY || newstate.toggle_mute[IO][n]) { return true; } } } for (n = 0; n < n_filters; n++) { if (newstate.fchanged[n]) { return true; } } return false; } static void print_overflows(FILE *stream) { double peak; int n; fprintf(stream, "peak: "); for (n = 0; n < n_channels[OUT]; n++) { peak = (volatile double)bfaccess->overflow[n].largest; if (peak < (double)(volatile int32_t)bfaccess->overflow[n].intlargest) { peak = (double)(volatile int32_t)bfaccess->overflow[n].intlargest; } if (peak != 0.0) { if ((peak = 20.0 * log10(peak / bfaccess->overflow[n].max)) == 0.0) { peak = -0.0; } fprintf(stream, "%d/%u/%+.2f ", n, (volatile unsigned int)bfaccess->overflow[n].n_overflows, peak); } else { fprintf(stream, "%d/%u/-Inf ", n, (volatile unsigned int)bfaccess->overflow[n].n_overflows); } } fprintf(stream, "\n"); } static char * strtrim(char s[]) { char *p; while (*s == ' ' || *s == '\t') { s++; } if (*s == '\0') { return s; } p = s + strlen(s) - 1; while ((*p == ' ' || *p == '\t') && p != s) { p--; } *(p + 1) = '\0'; return s; } static bool_t get_id(FILE *stream, char str[], char **p, int *id, int type, int rid) { int io = -1, n; str = strtrim(str); if (str[0] == '\"') { str += 1; if ((*p = strchr(str, '\"')) == NULL) { fprintf(stream, "Invalid string.\n"); return false; } **p = '\0'; switch (type) { case FILTER_ID: io = -2; for (*id = 0; *id < n_filters; (*id)++) { if (strcmp(filters[*id].name, str) == 0) { break; } } if (*id == n_filters) { fprintf(stream, "There is no filter with name \"%s\".\n", str); return false; } break; case COEFF_ID: for (*id = 0; *id < n_coeffs; (*id)++) { if (strcmp(coeffs[*id].name, str) == 0) { break; } } if (*id == n_coeffs) { fprintf(stream, "There is no coefficient set with name " "\"%s\".\n", str); return false; } break; case INPUT_ID: case OUTPUT_ID: io = (type == INPUT_ID) ? IN : OUT; for (*id = 0; *id < n_channels[io]; (*id)++) { if (strcmp(channels[io][*id].name, str) == 0) { break; } } if (*id == n_channels[io]) { fprintf(stream, "There is no %s with name \"%s\".\n", (io == IN) ? "input" : "output", str); return false; } break; } } else { *id = strtol(str, p, 10); if (*p == str) { fprintf(stream, "Invalid number.\n"); return false; } if (*id < 0 && type != COEFF_ID) { fprintf(stream, "Negative number (%d) is not allowed.\n", *id); return false; } switch (type) { case FILTER_ID: io = -2; if (*id >= n_filters) { fprintf(stream, "Filter id %d is out of range.\n", *id); return false; } break; case COEFF_ID: if (*id >= (int)n_coeffs) { fprintf(stream, "Coefficient set id %d is out of range.\n", *id); return false; } break; case INPUT_ID: case OUTPUT_ID: io = (type == INPUT_ID) ? IN : OUT; if (*id >= n_channels[io]) { fprintf(stream, "%s id %d is out of range.\n", (io == IN) ? "Input" : "Output", *id); return false; } break; } } if (io != -1 && rid != -1) { if (io == -2) { for (n = 0; n < filters[rid].n_filters[IN]; n++) { if (filters[rid].filters[IN][n] == *id) { break; } } if (n == filters[rid].n_filters[IN]) { fprintf(stream, "Filter id %d does not exist in the given " "filter.\n", *id); return false; } } else { for (n = 0; n < filters[rid].n_channels[io]; n++) { if (filters[rid].channels[io][n] == *id) { break; } } if (n == filters[rid].n_channels[io]) { fprintf(stream, "%s id %d does not exist in the given " "filter.\n", (io == IN) ? "Input" : "Output", *id); return false; } } *id = n; } *p += 1; while (**p == ' ' || **p == '\t') { (*p)++; } return true; } static bool_t parse_command(FILE *stream, char cmd[], struct sleep_task *_sleep_task) { struct sleep_task sleep_task; int n, i, rid, id, range[2]; const char **names; double att; char *p; if (strcmp(cmd, "lf") == 0) { fprintf(stream, "Filters:\n"); for (n = 0; n < n_filters; n++) { fprintf(stream, " %d: \"%s\"\n", n, filters[n].name); if (fctrl[n].coeff < 0) { fprintf(stream, " coeff set: %d (no filter)\n", fctrl[n].coeff); } else { fprintf(stream, " coeff set: %d\n", fctrl[n].coeff); } fprintf(stream, " delay blocks: %d (%d samples)\n", fctrl[n].delayblocks, fctrl[n].delayblocks * block_length); FOR_IN_AND_OUT { fprintf(stream, (IO == IN) ? " from inputs: " : " to outputs: "); for (i = 0; i < filters[n].n_channels[IO]; i++) { if (fctrl[n].scale[IO][i] < 0) { att = -20.0 * log10(-fctrl[n].scale[IO][i]); } else { att = -20.0 * log10(fctrl[n].scale[IO][i]); } if (att == 0.0) { att = 0.0000001; /* to show up as 0.0 and not -0.0 */ } fprintf(stream, "%d/%.1f", filters[n].channels[IO][i], att); if (fctrl[n].scale[IO][i] < 0) { fprintf(stream, "/-1 "); } else { fprintf(stream, " "); } } fprintf(stream, "\n"); } FOR_IN_AND_OUT { fprintf(stream, (IO == IN) ? " from filters: " : " to filters: "); for (i = 0; i < filters[n].n_filters[IO]; i++) { if (IO == IN) { if (fctrl[n].fscale[i] < 0) { att = -20.0 * log10(-fctrl[n].fscale[i]); } else { att = -20.0 * log10(fctrl[n].fscale[i]); } if (att == 0.0) { att = 0.0000001; } fprintf(stream, "%d/%.1f", filters[n].filters[IO][i], att); if (fctrl[n].fscale[i] < 0) { fprintf(stream, "/-1 "); } else { fprintf(stream, " "); } } else { fprintf(stream, "%d ", filters[n].filters[IO][i]); } } fprintf(stream, "\n"); } } fprintf(stream, "\n"); } else if (strcmp(cmd, "lc") == 0) { fprintf(stream, "Coefficient sets:\n"); for (n = 0; n < n_coeffs; n++) { fprintf(stream, " %d: \"%s\" (%d blocks)\n", n, coeffs[n].name, coeffs[n].n_blocks); } fprintf(stream, "\n"); } else if (strcmp(cmd, "li") == 0) { fprintf(stream, "Input channels:\n"); for (n = 0; n < n_channels[IN]; n++) { fprintf(stream, " %d: \"%s\" (delay: %d:%d) %s\n", n, channels[IN][n].name, bfaccess->get_delay(IN, n), bfaccess->get_subdelay(IN, n), bfaccess->ismuted(IN, n) ? "(muted)" : ""); } fprintf(stream, "\n"); } else if (strcmp(cmd, "lo") == 0) { fprintf(stream, "Output channels:\n"); for (n = 0; n < n_channels[OUT]; n++) { fprintf(stream, " %d: \"%s\" (delay: %d:%d) %s\n", n, channels[OUT][n].name, bfaccess->get_delay(OUT, n), bfaccess->get_subdelay(OUT, n), bfaccess->ismuted(OUT, n) ? "(muted)" : ""); } fprintf(stream, "\n"); } else if (strcmp(cmd, "lm") == 0) { names = bfaccess->bflogic_names(&i); if (names != NULL) { fprintf(stream, "Logic modules:\n"); for (n = 0; n < i; n++) { fprintf(stream, " %d: \"%s\"\n", n, names[n]); } fprintf(stream, "\n"); } FOR_IN_AND_OUT { names = bfaccess->bfio_names(IO, &i); if (names != NULL) { fprintf(stream, "%s modules:\n", (IO == IN) ? "Input" : "Output"); for (n = 0; n < i; n++) { bfaccess->bfio_range(IO, n, range); fprintf(stream, " %d (%d - %d): \"%s\"\n", n, range[0], range[1], names[n]); } fprintf(stream, "\n"); } } } else if (strstr(cmd, "cffa") == cmd) { if (get_id(stream, cmd + 4, &cmd, &rid, FILTER_ID, -1) && get_id(stream, cmd, &cmd, &id, FILTER_ID, rid)) { if (*cmd == 'M' || *cmd == 'm') { cmd++; att = strtod(cmd, &p); if (cmd == p) { fprintf(stream, "Invalid input multiplier.\n"); } else { newstate.fctrl[rid].fscale[id] = att; newstate.fchanged[rid] = true; } } else { att = strtod(cmd, &p); if (cmd == p) { fprintf(stream, "Invalid input attenuation.\n"); } else { if (newstate.fctrl[rid].fscale[id] < 0) { newstate.fctrl[rid].fscale[id] = -pow(10, -att / 20); } else { newstate.fctrl[rid].fscale[id] = pow(10, -att / 20); } newstate.fchanged[rid] = true; } } } } else if (strstr(cmd, "cfia") == cmd) { if (get_id(stream, cmd + 4, &cmd, &rid, FILTER_ID, -1) && get_id(stream, cmd, &cmd, &id, INPUT_ID, rid)) { if (*cmd == 'M' || *cmd == 'm') { cmd++; att = strtod(cmd, &p); if (cmd == p) { fprintf(stream, "Invalid input multiplier.\n"); } else { newstate.fctrl[rid].scale[IN][id] = att; newstate.fchanged[rid] = true; } } else { att = strtod(cmd, &p); if (cmd == p) { fprintf(stream, "Invalid input attenuation.\n"); } else { if (newstate.fctrl[rid].scale[IN][id] < 0) { newstate.fctrl[rid].scale[IN][id] = -pow(10, -att / 20); } else { newstate.fctrl[rid].scale[IN][id] = pow(10, -att / 20); } newstate.fchanged[rid] = true; } } } } else if (strstr(cmd, "cfoa") == cmd) { if (get_id(stream, cmd + 4, &cmd, &rid, FILTER_ID, -1) && get_id(stream, cmd, &cmd, &id, OUTPUT_ID, rid)) { if (*cmd == 'M' || *cmd == 'm') { cmd++; att = strtod(cmd, &p); if (cmd == p) { fprintf(stream, "Invalid output multiplier.\n"); } else { newstate.fctrl[rid].scale[OUT][id] = att; newstate.fchanged[rid] = true; } } else { att = strtod(cmd, &p); if (cmd == p) { fprintf(stream, "Invalid output attenuation.\n"); } else { if (newstate.fctrl[rid].scale[OUT][id] < 0) { newstate.fctrl[rid].scale[OUT][id] = -pow(10, -att / 20); } else { newstate.fctrl[rid].scale[OUT][id] = pow(10, -att / 20); } newstate.fchanged[rid] = true; } } } } else if (strstr(cmd, "cid") == cmd) { if (get_id(stream, cmd + 3, &cmd, &id, INPUT_ID, -1)) { n = strtol(cmd, &p, 10); if (cmd == p || n < 0) { fprintf(stream, "Invalid input delay.\n"); } else { newstate.delay[IN][id] = n; } cmd = p; n = strtol(cmd, &p, 10); if (cmd != p) { if (n <= -BF_SAMPLE_SLOTS || n >= BF_SAMPLE_SLOTS) { fprintf(stream, "Invalid input subdelay.\n"); } else { newstate.subdelay[IN][id] = n; } } } } else if (strstr(cmd, "cod") == cmd) { if (get_id(stream, cmd + 3, &cmd, &id, OUTPUT_ID, -1)) { n = strtol(cmd, &p, 10); if (cmd == p || n < 0) { fprintf(stream, "Invalid output delay.\n"); } else { newstate.delay[OUT][id] = n; } cmd = p; n = strtol(cmd, &p, 10); if (cmd != p) { if (n <= -BF_SAMPLE_SLOTS || n >= BF_SAMPLE_SLOTS) { fprintf(stream, "Invalid output subdelay.\n"); } else { newstate.subdelay[OUT][id] = n; } } } } else if (strstr(cmd, "cfc") == cmd) { if (get_id(stream, cmd + 3, &cmd, &rid, FILTER_ID, -1) && get_id(stream, cmd, &cmd, &id, COEFF_ID, rid)) { newstate.fctrl[rid].coeff = id; newstate.fchanged[rid] = true; } } else if (strstr(cmd, "cfd") == cmd) { if (get_id(stream, cmd + 3, &cmd, &rid, FILTER_ID, -1)) { n = strtol(cmd, &p, 10); if (cmd == p || n < 0 || n > n_maxblocks - 1) { fprintf(stream, "Invalid filter delay.\n"); } else { newstate.fctrl[rid].delayblocks = n; newstate.fchanged[rid] = true; } } } else if (strstr(cmd, "tmo") == cmd) { if (get_id(stream, cmd + 3, &cmd, &id, OUTPUT_ID, -1)) { newstate.toggle_mute[OUT][id] = !newstate.toggle_mute[OUT][id]; } } else if (strstr(cmd, "tmi") == cmd) { if (get_id(stream, cmd + 3, &cmd, &id, INPUT_ID, -1)) { newstate.toggle_mute[IN][id] = !newstate.toggle_mute[IN][id]; } } else if (strstr(cmd, "imc") == cmd) { id = strtol(cmd + 3, &p, 10); if (p == cmd + 3) { id = -1; } if (bfaccess->bfio_command(IN, id, p, &p) == -1) { fprintf(stream, "Command failed: %s\n", p); } else { fprintf(stream, "%s", p); } } else if (strstr(cmd, "omc") == cmd) { id = strtol(cmd + 3, &p, 10); if (p == cmd + 3) { id = -1; } if (bfaccess->bfio_command(OUT, id, p, &p) == -1) { fprintf(stream, "Command failed: %s\n", p); } else { fprintf(stream, "%s", p); } } else if (strstr(cmd, "lmc") == cmd) { id = strtol(cmd + 3, &p, 10); if (p == cmd + 3) { id = -1; names = bfaccess->bflogic_names(&i); if (names != NULL) { p = strtrim(cmd + 3); for (n = 0; n < i; n++) { if (strstr(p, names[n]) == p) { id = n; p += strlen(names[n]); break; } } } } if (bfaccess->bflogic_command(id, p, &p) == -1) { fprintf(stream, "Command failed: %s\n", p); } else { fprintf(stream, "%s", p); } } else if (strcmp(cmd, "ppk") == 0) { print_overflows(stream); } else if (strcmp(cmd, "rpk") == 0) { bfaccess->reset_peak(); } else if (strcmp(cmd, "upk") == 0) { print_peak_updates = !print_peak_updates; } else if (strcmp(cmd, "tp") == 0) { print_prompt = !print_prompt; } else if (strcmp(cmd, "rti") == 0) { fprintf(stream, "Realtime index: %.3f\n", bfaccess->realtime_index()); } else if (strcmp(cmd, "quit") == 0) { return false; } else if (strstr(cmd, "sleep") == cmd) { memset(&sleep_task, 0, sizeof(struct sleep_task)); if ((p = strchr(cmd + 5, 'b')) != NULL) { n = strtol(p + 1, &p, 10); if (p != NULL && n >= 0) { sleep_task.block_sleep = true; sleep_task.do_sleep = true; sleep_task.blocks = n; } } else { n = strtol(cmd + 5, &p, 10); if (p != NULL && n >= 0) { sleep_task.do_sleep = true; sleep_task.seconds = n; sleep_task.useconds = atoi(p) * 1000; } } if (sleep_task.do_sleep) { if (_sleep_task == NULL) { if (sleep_task.block_sleep) { fprintf(stream, "Block sleep only valid in scripts\n"); } else { if (sleep_task.seconds > 0) { sleep(sleep_task.seconds); } if (sleep_task.useconds > 0) { usleep(sleep_task.useconds); } } } else { memcpy(_sleep_task, &sleep_task, sizeof(struct sleep_task)); } } } else if (strstr(cmd, "abort") == cmd) { bfaccess->exit(BF_EXIT_OK); } else if (strcmp(cmd, "help") == 0) { fprintf(stream, HELP_TEXT); } else { fprintf(stream, "Unknown command \"%s\", type \"help\" for help.\n", cmd); } return true; } static void wait_data(FILE *client_stream, int client_fd, int callback_fd) { fd_set rfds; uint32_t msg; int n; FD_ZERO(&rfds); do { FD_SET(client_fd, &rfds); FD_SET(callback_fd, &rfds); if (client_stream != NULL) { fflush(client_stream); } while ((n = select(client_fd < callback_fd ? callback_fd + 1 : client_fd + 1, &rfds, NULL, NULL, NULL)) == -1 && errno == EINTR); if (n == -1) { fprintf(stderr, "CLI: Select failed: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } if (FD_ISSET(callback_fd, &rfds)) { if (!readfd(callback_fd, &msg, 4)) { bfaccess->exit(BF_EXIT_OK); } switch (msg) { case BF_FDEVENT_PEAK: if (print_peak_updates) { print_overflows(client_stream); } break; default: fprintf(stderr, "CLI: Invalid callback code: %d.\n", msg); bfaccess->exit(BF_EXIT_OTHER); break; } } } while (!FD_ISSET(client_fd, &rfds)); } static bool_t parse(FILE *stream, const char cmd[], struct sleep_task *sleep_task) { char *p, *s, *s1, *buf; bool_t do_quit; int len; if (sleep_task != NULL) { memset(sleep_task, 0, sizeof(struct sleep_task)); } do_quit = false; len = strlen(cmd); if (len == 0) { return true; } buf = alloca(len + 1); memcpy(buf, cmd, len + 1); if (buf[len - 1] == '\n') { buf[len - 1] = '\0'; } if (len > 1 && buf[len - 2] == '\r') { buf[len - 2] = '\0'; } s = buf; clear_changes(); do { if ((p = strchr(s, ';')) != NULL) { *p = '\0'; } while (*s == ' ' || *s == '\t') s++; if (*s != '\0') { s1 = strtrim(s); if (print_commands) { fprintf(stream, "%s\n", s1); } if (!parse_command(stream, s1, sleep_task)) { do_quit = true; } } else if (are_changes()) { bfaccess->control_mutex(1); commit_changes(stream); bfaccess->control_mutex(0); clear_changes(); } } while ((s = p + 1) != (char *)1); if (are_changes()) { bfaccess->control_mutex(1); commit_changes(stream); bfaccess->control_mutex(0); } return !do_quit; } static void block_start(struct bfaccess *_bfaccess, unsigned int block_index, struct timeval *current_time) { static char *p = NULL, *p1, *p2; static bool_t do_quit = false, do_sleep = false, sleep_block; static unsigned int sleep_block_index; static struct timeval sleep_time; static bool_t retchr, cmdchr; static char restore_char; struct sleep_task sleep_task; struct timeval tv; bfaccess = _bfaccess; fctrl = _bfaccess->fctrl; if (do_quit) { return; } if (do_sleep) { if (sleep_block) { if (block_index <= sleep_block_index) { return; } } else { if (timercmp(current_time, &sleep_time, <)) { return; } } do_sleep = false; } if (p == NULL) { p = script; } cmdchr = false; retchr = false; p1 = p; /* this loop extracts the next non-empty line, and handles wrap */ while ((*p1 != '\0' && *p1 != '\n') || !cmdchr) { switch ((int)*p1) { case ' ': case '\t': case '\r': break; case '\n': p = &p1[1]; break; case '\0': if (p == script) { fprintf(stderr, "CLI: the script is empty.\n"); bfaccess->exit(BF_EXIT_INVALID_CONFIG); } p = script; p1 = &p[-1]; break; default: cmdchr = true; break; } p1++; } /* search for empty statements on the line */ cmdchr = false; p2 = p; while (p2 < p1) { switch ((int)*p2) { case ';': if (!cmdchr) { /* empty statement, treat as linebreak */ p1 = p2; } cmdchr = false; break; case ' ': case '\t': case '\r': break; default: cmdchr = true; break; } p2++; } /* handle \r\n line ends */ restore_char = *p1; *p1 = '\0'; if (p1 != script && p1[-1] == '\r') { retchr = true; p1[-1] = '\0'; } if (!parse(stderr, p, &sleep_task)) { do_quit = true; } if (retchr) { p1[-1] = '\r'; } *p1 = restore_char; if (*p1 == '\0') { /* wrap */ p = script; } else { p = &p1[1]; } if (sleep_task.do_sleep) { do_sleep = true; if (sleep_task.block_sleep) { sleep_block = true; sleep_block_index = block_index + sleep_task.blocks; } else { sleep_block = false; tv.tv_sec = sleep_task.seconds; tv.tv_usec = sleep_task.useconds; timeradd(current_time, &tv, &sleep_time); } } } static void parse_string(FILE *stream, const char inbuf[MAXCMDLINE], char cmd[MAXCMDLINE]) { int slen, n, i; slen = strlen(inbuf); cmd[0] = '\0'; for (n = 0, i = 0; n < slen; n++) { switch ((int)inbuf[n]) { case 27: fprintf(stream, "ESC sequences not supported, " "discarding line.\n"); cmd[0] = '\0'; return; case '\b': if (i > 0) { i--; } break; default: if (inbuf[n] == '\n' || inbuf[n] == '\r' || (inbuf[n] > 31 && inbuf[n] < 127)) { cmd[i++] = inbuf[n]; } else { fprintf(stream, "unsupported character in input (%u), " "discarding line.\n", (unsigned int)inbuf[n]); cmd[0] = '\0'; return; } break; } } cmd[i] = '\0'; } static void stream_loop(int event_fd, int infd, FILE *instream, FILE *outstream) { char inbuf[MAXCMDLINE], cmd[MAXCMDLINE]; inbuf[MAXCMDLINE - 1] = '\0'; while (true) { wait_data(outstream, infd, event_fd); if (fgets(inbuf, MAXCMDLINE - 1, instream) == NULL) { fprintf(stderr, "CLI: fgets failed: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } parse_string(outstream, inbuf, cmd); parse(outstream, cmd, NULL); } } static void socket_loop(int event_fd, int lsock) { char inbuf[MAXCMDLINE], cmd[MAXCMDLINE]; FILE *stream; int sock; while (true) { wait_data(NULL, lsock, event_fd); if ((sock = accept(lsock, NULL, NULL)) == -1) { fprintf(stderr, "CLI: Accept failed: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } if ((stream = fdopen(sock, "r+")) == NULL) { fprintf(stderr, "CLI: fdopen failed: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } setvbuf(stream, NULL, _IOLBF, 0); fprintf(stream, WELCOME_TEXT); fprintf(stream, PROMPT_TEXT); wait_data(stream, sock, event_fd); cmd[MAXCMDLINE - 1] = '\0'; while (fgets(inbuf, MAXCMDLINE - 1, stream) != NULL) { parse_string(stream, inbuf, cmd); if (!parse(stream, cmd, NULL)) { break; } if (print_prompt) { fprintf(stream, PROMPT_TEXT); } wait_data(stream, sock, event_fd); } print_peak_updates = false; fclose(stream); } } int bflogic_preinit(int *version_major, int *version_minor, int (*get_config_token)(union bflexval *lexval), int sample_rate, int _block_length, int _n_maxblocks, int _n_coeffs, const struct bfcoeff _coeffs[], const int _n_channels[2], const struct bfchannel *_channels[2], int _n_filters, const struct bffilter _filters[], struct bfevents *bfevents, int *fork_mode, int _debug) { union bflexval lexval; int token, ver; ver = *version_major; *version_major = BF_VERSION_MAJOR; *version_minor = BF_VERSION_MINOR; if (ver != BF_VERSION_MAJOR) { return -1; } memset(&newstate, 0, sizeof(newstate)); error[0] = '\0'; port = -1; port2 = -1; lport = NULL; script = NULL; line_speed = 9600; debug = !!_debug; while ((token = get_config_token(&lexval)) > 0) { if (token != BF_LEXVAL_FIELD) { fprintf(stderr, "CLI: Parse error: expected field.\n"); return -1; } if (strcmp(lexval.field, "port") == 0) { switch (get_config_token(&lexval)) { case BF_LEXVAL_STRING: lport = strdup(lexval.string); break; case BF_LEXVAL_REAL: port = (int)lexval.real; switch (get_config_token(&lexval)) { case BF_LEX_COMMA: if (get_config_token(&lexval) != BF_LEXVAL_REAL) { fprintf(stderr, "CLI: Parse error: expected integer.\n"); return -1; } port2 = (int)lexval.real; break; case BF_LEX_EOS: continue; default: fprintf(stderr, "CLI: Parse error: expected end of " "statement (;).\n"); return -1; } break; default: fprintf(stderr, "CLI: Parse error: expected string or " "integer.\n"); return -1; } } else if (strcmp(lexval.field, "script") == 0) { if (get_config_token(&lexval) != BF_LEXVAL_STRING) { fprintf(stderr, "CLI: Parse error: expected string.\n"); return -1; } script = strdup(lexval.string); } else if (strcmp(lexval.field, "echo") == 0) { if (get_config_token(&lexval) != BF_LEXVAL_BOOLEAN) { fprintf(stderr, "CLI: Parse error: expected boolean.\n"); return -1; } print_commands = lexval.boolean; } else if (strcmp(lexval.field, "line_speed") == 0) { if (get_config_token(&lexval) != BF_LEXVAL_REAL) { fprintf(stderr, "CLI: Parse error: expected integer.\n"); return -1; } line_speed = (int)lexval.real; } else { fprintf(stderr, "CLI: Parse error: unknown field \"%s\".\n", lexval.field); return -1; } if (get_config_token(&lexval) != BF_LEX_EOS) { fprintf(stderr, "CLI: Parse error: expected end of " "statement (;).\n"); return -1; } } n_coeffs = _n_coeffs; n_maxblocks = _n_maxblocks; block_length = _block_length; coeffs = _coeffs; n_channels = _n_channels; channels = _channels; n_filters = _n_filters; filters = _filters; if (script == NULL) { if (port == -1 && lport == NULL) { fprintf(stderr, "CLI: \"port\" or \"script\" must be set.\n"); return -1; } bfevents->fdevents = BF_FDEVENT_PEAK; *fork_mode = BF_FORK_PRIO_MAX; } else { if (port != -1 || lport != NULL) { fprintf(stderr, "CLI: Cannot have both \"script\" and \"port\" " "set.\n"); return -1; } bfevents->block_start = block_start; *fork_mode = BF_FORK_DONT_FORK; } return 0; } #define WRITE_TO_SYNCH_FD \ dummy = 0; \ if (!writefd(synch_fd, &dummy, 1)) { \ bfaccess->exit(BF_EXIT_OTHER); \ } \ close(synch_fd); int bflogic_init(struct bfaccess *_bfaccess, int sample_rate, int _block_length, int _n_maxblocks, int _n_coeffs, const struct bfcoeff _coeffs[], const int _n_channels[2], const struct bfchannel *_channels[2], int _n_filters, const struct bffilter _filters[], int event_fd, int synch_fd) { FILE *stream, *instream, *outstream; int lsock, fd, speed, opt; struct sockaddr_in s_in; struct sockaddr_un s_un; struct termios newtio; uint8_t dummy; bfaccess = _bfaccess; fctrl = _bfaccess->fctrl; if (script != NULL) { return 0; } if (lport != NULL && strstr(lport, "/dev/") == lport) { /* serial line interface */ if ((fd = open(lport, O_RDWR | O_NOCTTY)) == -1) { fprintf(stderr, "CLI: Failed to open serial device: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } speed = B9600; switch (line_speed) { case 0: speed = B9600; break; case 1200: speed = B1200; break; case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; case 57600: speed = B57600; break; case 115200: speed = B115200; break; case 230400: speed = B230400; break; default: fprintf(stderr, "CLI: Invalid/unsupported serial line speed %d.\n", speed); bfaccess->exit(BF_EXIT_OTHER); return 0; } memset(&newtio, 0, sizeof(newtio)); newtio.c_cflag = CS8 | CLOCAL | CREAD; cfsetispeed(&newtio, speed); cfsetospeed(&newtio, speed); newtio.c_iflag = IGNPAR | ICRNL | ISTRIP; newtio.c_oflag = OPOST | ONLCR; newtio.c_lflag = ICANON; #ifdef _POSIX_VDISABLE { int _i, _n; _i = sizeof(newtio.c_cc); _i /= sizeof(newtio.c_cc[0]); for (_n = 0; _n < _i; _n++) { newtio.c_cc[_n] = _POSIX_VDISABLE; } } #endif if (tcflush(fd, TCIFLUSH) == -1) { fprintf(stderr, "CLI: tcflush failed: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } if (tcsetattr(fd, TCSANOW, &newtio) == -1) { fprintf(stderr, "CLI: tcsetattr failed: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } if ((stream = fdopen(fd, "r+")) == NULL) { fprintf(stderr, "CLI: fdopen failed: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } setvbuf(stream, NULL, _IOLBF, 0); WRITE_TO_SYNCH_FD; stream_loop(event_fd, fd, stream, stream); } else if (port != -1 && port2 != -1) { /* pipe interface */ if ((instream = fdopen(port, "r")) == NULL) { fprintf(stderr, "CLI: fdopen 'r' on fd %d failed: %s.\n", port, strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } setvbuf(instream, NULL, _IOLBF, 0); if ((outstream = fdopen(port2, "w")) == NULL) { fprintf(stderr, "CLI: fdopen 'w' on fd %d failed: %s.\n", port2, strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } setvbuf(outstream, NULL, _IOLBF, 0); WRITE_TO_SYNCH_FD; stream_loop(event_fd, port, instream, outstream); } else if (port != -1) { /* TCP interface */ memset(&s_in, 0, sizeof(s_in)); s_in.sin_family = AF_INET; s_in.sin_addr.s_addr = INADDR_ANY; s_in.sin_port = htons(port); if ((lsock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "CLI: Failed to create socket: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } opt = 1; if (setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) { fprintf(stderr, "CLI: Failed to set socket options: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } if (bind(lsock, (struct sockaddr *)&s_in, sizeof(struct sockaddr_in)) == -1) { fprintf(stderr, "CLI: Failed to bind name to socket: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } if (listen(lsock, 1) != 0) { fprintf(stderr, "CLI: Failed to listen on port %d: %s.\n", port, strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } WRITE_TO_SYNCH_FD; socket_loop(event_fd, lsock); } else if (lport != NULL) { /* local socket interface */ remove(lport); memset(&s_un, 0, sizeof(s_un)); s_un.sun_family = AF_UNIX; strncpy(s_un.sun_path, lport, sizeof(s_un.sun_path)); s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0'; if ((lsock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "CLI: Failed to create socket: %s.\n", strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } if (bind(lsock, (struct sockaddr *)&s_un, sizeof(struct sockaddr_un)) == -1) { if (errno == EADDRINUSE) { fprintf(stderr, "CLI: Failed to create local socket: " "path \"%s\" already exists.\n", s_un.sun_path); } else { fprintf(stderr, "CLI: Failed to bind name to socket: %s.\n", strerror(errno)); } bfaccess->exit(BF_EXIT_OTHER); } if (listen(lsock, 1) != 0) { fprintf(stderr, "CLI: Failed to listen on local " "socket \"%s\": %s.\n", s_un.sun_path, strerror(errno)); bfaccess->exit(BF_EXIT_OTHER); } free(lport); WRITE_TO_SYNCH_FD; socket_loop(event_fd, lsock); } else { fprintf(stderr, "CLI: No port specified.\n"); bfaccess->exit(BF_EXIT_OTHER); } return 0; } brutefir-1.0o/bflogic_eq.c0000644000175000017500000007253012752354353015143 0ustar andersanders/* * (c) Copyright 2002 - 2005 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include #include #include #include #include #define IS_BFLOGIC_MODULE #include "bfmod.h" #include "emalloc.h" #include "shmalloc.h" #include "defs.h" #include "log2.h" #include "bit.h" #include "fdrw.h" #include "timermacros.h" #define MAX_EQUALISERS 64 #define MAX_BANDS 128 #define MSGSIZE (MAX_BANDS * 20) #define CMD_CHANGE_MAGNITUDE 1 #define CMD_CHANGE_PHASE 2 #define CMD_GET_INFO 3 struct realtime_eq { void *ifftplan; int band_count; int taps; volatile int coeff[2]; volatile int active_coeff; volatile bool_t not_changed; double *freq; double *mag; double *phase; }; static struct realtime_eq *equalisers; static int n_equalisers = 0; static char msg[MSGSIZE]; static struct bfaccess *bfaccess; static int sample_rate; static int block_length; static int n_maxblocks; static int n_coeffs; static int n_filters; static const struct bfcoeff *coeffs; static char *debug_dump_filter_path = NULL; static int cmdpipe[2], cmdpipe_reply[2]; static bool_t debug = false; static void *rbuf; #define real_t float #define REALSIZE 4 #define COSINE_INT_NAME cosine_int_f #define RENDER_EQUALISER_NAME render_equaliser_f #include "rendereq.h" #undef COSINE_INT_NAME #undef RENDER_EQUALISER_NAME #undef REALSIZE #undef real_t #define real_t double #define REALSIZE 8 #define COSINE_INT_NAME cosine_int_d #define RENDER_EQUALISER_NAME render_equaliser_d #include "rendereq.h" #undef COSINE_INT_NAME #undef RENDER_EQUALISER_NAME #undef REALSIZE #undef real_t static char * strtrim(char s[]) { char *p; while (*s == ' ' || *s == '\t') { s++; } if (*s == '\0') { return s; } p = s + strlen(s) - 1; while ((*p == ' ' || *p == '\t') && p != s) { p--; } *(p + 1) = '\0'; return s; } static void coeff_final(int filter, int *coeff) { int n, c[2], active; for (n = 0; n < n_equalisers; n++) { c[0] = equalisers[n].coeff[0]; c[1] = equalisers[n].coeff[1]; active = c[equalisers[n].active_coeff]; if (*coeff == c[0] || *coeff == c[1]) { *coeff = active; equalisers[n].not_changed = false; } } } static bool_t finalise_equaliser(struct realtime_eq *eq, double mfreq[], double mag[], int n_mag, double pfreq[], double phase[], int n_phase, double bands[], int n_bands) { int n, i, band_count; band_count = n_bands + 2; eq->freq = emalloc(band_count * sizeof(double)); eq->mag = emalloc(band_count * sizeof(double)); eq->phase = emalloc(band_count * sizeof(double)); eq->freq[0] = 0.0; for (n = 0; n < n_bands; n++) { eq->freq[1+n] = bands[n]; } eq->freq[1+n] = (double)sample_rate / 2.0; memset(eq->mag, 0, band_count * sizeof(double)); for (n = 0, i = 0; n < n_mag; n++) { while (mfreq[n] > eq->freq[i]) { i++; } if (mfreq[n] != eq->freq[i]) { fprintf(stderr, "EQ: %.1f Hz is not a band frequency, " "use %.1f instead.\n", mfreq[n], eq->freq[i]); return false; } eq->mag[i++] = mag[n]; } eq->mag[0] = eq->mag[1]; eq->mag[band_count - 1] = eq->mag[band_count - 2]; memset(eq->phase, 0, band_count * sizeof(double)); for (n = 0, i = 0; n < n_phase; n++) { while (pfreq[n] > eq->freq[i]) { i++; } if (pfreq[n] != eq->freq[i]) { fprintf(stderr, "EQ: %.1f Hz is not a band frequency, " "use %.1f instead.\n", pfreq[n], eq->freq[i]); return false; } eq->phase[i++] = phase[n]; } for (n = 0; n < band_count; n++) { eq->freq[n] /= (double)sample_rate; eq->mag[n] = pow(10, eq->mag[n] / 20); eq->phase[n] = eq->phase[n] / (180 * M_PI); } eq->band_count = band_count; for (n = i = 0; n < 2; n++) { if (!coeffs[eq->coeff[n]].is_shared) { fprintf(stderr, "EQ: Coefficient %d must be in shared memory.\n", eq->coeff[n]); return false; } if ((i = log2_get(block_length * coeffs[eq->coeff[n]].n_blocks)) == -1) { fprintf(stderr, "EQ: Coefficient %d length is not a power " "of two.\n", eq->coeff[n]); return false; } } eq->taps = 1 << i; if (coeffs[eq->coeff[0]].n_blocks != coeffs[eq->coeff[1]].n_blocks) { fprintf(stderr, "EQ: Coefficient %d and %d must be the same length.\n", eq->coeff[0], eq->coeff[1]); return false; } return true; } #define GET_TOKEN(token, errstr) \ if (get_config_token(&lexval) != token) { \ fprintf(stderr, "EQ: Parse error: " errstr); \ return -1; \ } static int parse_freq_val(int (*get_config_token)(union bflexval *lexval), double freq[], double val[]) { union bflexval lexval; int n, token; token = BF_LEX_COMMA; for (n = 0; n < MAX_BANDS && token == BF_LEX_COMMA; n++) { GET_TOKEN(BF_LEXVAL_REAL, "expected real.\n"); freq[n] = lexval.real; if (freq[n] < 0) { fprintf(stderr, "EQ: Parse error: negative frequency.\n"); return -1; } if (freq[n] > (double)sample_rate / 2.0) { fprintf(stderr, "EQ: Parse error: frequency larger than " "nykvist.\n"); return -1; } if (n > 0 && freq[n] <= freq[n-1]) { fprintf(stderr, "EQ: Parse error: frequencies not sorted.\n"); return -1; } GET_TOKEN(BF_LEX_SLASH, "expected slash (/).\n"); GET_TOKEN(BF_LEXVAL_REAL, "expected real.\n"); val[n] = lexval.real; token = get_config_token(&lexval); } if (token != BF_LEX_EOS) { fprintf(stderr, "EQ: Parse error: expected end of statement (;).\n"); return -1; } return n; } int bflogic_preinit(int *version_major, int *version_minor, int (*get_config_token)(union bflexval *lexval), int _sample_rate, int _block_length, int _n_maxblocks, int _n_coeffs, const struct bfcoeff _coeffs[], const int _n_channels[2], const struct bfchannel *_channels[2], int _n_filters, const struct bffilter _filters[], struct bfevents *bfevents, int *fork_mode, int _debug) { double mag[2][MAX_BANDS]; double phase[2][MAX_BANDS]; double bands[MAX_BANDS]; int n_mag, n_phase, n_bands; union bflexval lexval; int token, n, i, ver; char *p; ver = *version_major; *version_major = BF_VERSION_MAJOR; *version_minor = BF_VERSION_MINOR; if (ver != BF_VERSION_MAJOR) { return -1; } debug = !!_debug; sample_rate = _sample_rate; block_length = _block_length; n_maxblocks = n_maxblocks; n_coeffs = _n_coeffs; coeffs = _coeffs; n_filters = _n_filters; *fork_mode = BF_FORK_PRIO_OTHER; bfevents->coeff_final = coeff_final; memset(msg, 0, sizeof(msg)); if ((equalisers = shmalloc(MAX_EQUALISERS * sizeof(struct realtime_eq))) == NULL) { fprintf(stderr, "EQ: Could not allocate shared memory\n"); return -1; } while ((token = get_config_token(&lexval)) > 0) { switch (token) { case BF_LEX_LBRACE: if (n_equalisers == MAX_EQUALISERS) { fprintf(stderr, "EQ: Too many equalisers.\n"); return -1; } memset(&equalisers[n_equalisers], 0, sizeof(struct realtime_eq)); equalisers[n_equalisers].coeff[0] = -1; equalisers[n_equalisers].coeff[1] = -1; n_mag = 0; n_phase = 0; n_bands = -1; while ((token = get_config_token(&lexval)) > 0) { if (token == BF_LEX_RBRACE) { if (equalisers[n_equalisers].coeff[0] == -1) { fprintf(stderr, "EQ: Parse error: coeff not set.\n"); return -1; } if (n_bands == -1) { fprintf(stderr, "EQ: Parse error: bands not set.\n"); return -1; } if (!finalise_equaliser(&equalisers[n_equalisers], mag[0], mag[1], n_mag, phase[0], phase[1], n_phase, bands, n_bands)) { return -1; } n_equalisers++; break; } if (token != BF_LEXVAL_FIELD) { fprintf(stderr, "EQ: Parse error: expected field.\n"); return -1; } if (strcmp(lexval.field, "bands") == 0) { token = get_config_token(&lexval); switch (token) { case BF_LEXVAL_STRING: if (strcmp("ISO octave", lexval.string) == 0) { n_bands = 10; bands[0] = 31.5; bands[1] = 63; bands[2] = 125; bands[3] = 250; bands[4] = 500; bands[5] = 1000; bands[6] = 2000; bands[7] = 4000; bands[8] = 8000; bands[9] = 16000; } else if (strcmp("ISO 1/3 octave", lexval.string) == 0) { n_bands = 31; bands[0] = 20; bands[1] = 25; bands[2] = 31; bands[3] = 40; bands[4] = 50; bands[5] = 63; bands[6] = 80; bands[7] = 100; bands[8] = 125; bands[9] = 160; bands[10] = 200; bands[11] = 250; bands[12] = 315; bands[13] = 400; bands[14] = 500; bands[15] = 630; bands[16] = 800; bands[17] = 1000; bands[18] = 1250; bands[19] = 1600; bands[20] = 2000; bands[21] = 2500; bands[22] = 3150; bands[23] = 4000; bands[24] = 5000; bands[25] = 6300; bands[26] = 8000; bands[27] = 10000; bands[28] = 12500; bands[29] = 16000; bands[30] = 20000; } else { fprintf(stderr, "EQ: Parse error: expected \"ISO " "octave\" or \"ISO 1/3 octave\".\n"); return -1; } GET_TOKEN(BF_LEX_EOS, "expected end of statement " "(;).\n"); for (n = n_bands - 1; n > 0; n--) { if (bands[n] < (double)sample_rate / 2) { break; } n_bands--; } break; case BF_LEXVAL_REAL: bands[0] = lexval.real; if (bands[0] <= 0.0) { fprintf(stderr, "EQ: Parse error: band frequencies " "must be larger than 0 Hz.\n"); return -1; } token = get_config_token(&lexval); for (n = 1; n < MAX_BANDS && token == BF_LEX_COMMA; n++) { GET_TOKEN(BF_LEXVAL_REAL, "expected real.\n"); bands[n] = lexval.real; if (bands[n-1] >= bands[n]) { fprintf(stderr, "EQ: Parse error: frequencies " "not sorted.\n"); return -1; } token = get_config_token(&lexval); } n_bands = n; if (token != BF_LEX_EOS) { fprintf(stderr, "EQ: Parse error: expected end of " "statement (;).\n"); return -1; } break; default: fprintf(stderr, "EQ: Parse error: expected real.\n"); return -1; } if (bands[n_bands-1] >= (double)sample_rate / 2.0) { fprintf(stderr, "EQ: Parse error: band frequencies " "must be less than sample rate / 2.\n"); return -1; } } else if (strcmp(lexval.field, "coeff") == 0) { for (i = 0; i < 2; i++) { token = get_config_token(&lexval); if (token != BF_LEXVAL_STRING && token != BF_LEXVAL_REAL) { fprintf(stderr, "EQ: Parse error: expected integer " "or string.\n"); return -1; } if (token == BF_LEXVAL_STRING) { for (n = 0; n < n_coeffs; n++) { if (strcmp(coeffs[n].name, lexval.string) == 0) { equalisers[n_equalisers].coeff[i] = n; break; } } if (n == n_coeffs) { fprintf(stderr, "EQ: Unknown coefficient " "name.\n"); return -1; } } else { equalisers[n_equalisers].coeff[i] = (int)lexval.real; if (equalisers[n_equalisers].coeff[i] < 0 || equalisers[n_equalisers].coeff[i] >= n_coeffs) { fprintf(stderr, "EQ: Invalid coefficient " "index.\n"); return -1; } } token = get_config_token(&lexval); if (i == 0) { if (token == BF_LEX_EOS) { equalisers[n_equalisers].coeff[1] = equalisers[n_equalisers].coeff[0]; break; } else if (token != BF_LEX_COMMA) { fprintf(stderr, "EQ: Parse error: expected " "comma.\n"); return -1; } } else if (token != BF_LEX_EOS) { fprintf(stderr, "EQ: Parse error: expected end of " "statement (;).\n"); return -1; } } } else if (strcmp(lexval.field, "magnitude") == 0) { if ((n_mag = parse_freq_val(get_config_token, mag[0], mag[1])) == -1) { return -1; } } else if (strcmp(lexval.field, "phase") == 0) { if ((n_phase = parse_freq_val(get_config_token, phase[0], phase[1])) == -1) { return -1; } } else { fprintf(stderr, "EQ: Parse error: unknown field \"%s\".\n", lexval.field); return -1; } } break; case BF_LEXVAL_FIELD: if (strcmp(lexval.field, "debug_dump_filter") == 0) { GET_TOKEN(BF_LEXVAL_STRING, "expected string.\n"); debug_dump_filter_path = estrdup(lexval.string); if (strstr(debug_dump_filter_path, "%d") == NULL) { fprintf(stderr, "EQ: Parse error: %%d is missing in " "name.\n"); return -1; } p = strchr(debug_dump_filter_path, '%'); if (strchr(&p[1], '%')) { fprintf(stderr, "EQ: Parse error: more than one %% in " "name.\n"); return -1; } } else { fprintf(stderr, "EQ: Parse error: unknown field.\n"); return -1; } break; default: fprintf(stderr, "EQ: Parse error: expected field.\n"); return -1; } GET_TOKEN(BF_LEX_EOS, "expected end of statement (;).\n"); } for (n = 0; n < n_equalisers; n++) { for (i = 0; i < n_equalisers; i++) { if (i != n && (equalisers[n].coeff[0] == equalisers[i].coeff[0] || equalisers[n].coeff[0] == equalisers[i].coeff[1] || equalisers[n].coeff[1] == equalisers[i].coeff[0] || equalisers[n].coeff[1] == equalisers[i].coeff[1])) { fprintf(stderr, "EQ: At least two equalisers has at least one " "coefficient set in common.\n"); return -1; } } } if (pipe(cmdpipe) == -1 || pipe(cmdpipe_reply) == -1) { fprintf(stderr, "EQ: Failed to create pipe: %s.\n", strerror(errno)); return -1; } return 0; } int bflogic_init(struct bfaccess *_bfaccess, int _sample_rate, int _block_length, int _n_maxblocks, int _n_coeffs, const struct bfcoeff _coeffs[], const int _n_channels[2], const struct bfchannel *_channels[2], int _n_filters, const struct bffilter _filters[], int event_fd, int synch_fd) { int n, maxblocks, command, eq_index, n_bands, i; double bands[MAX_BANDS], values[MAX_BANDS], freq; int render_postponed_index = -1; struct realtime_eq *eq; char rmsg[MSGSIZE], *p; uint8_t dummy = 0; struct timeval tv; fd_set rfds; bfaccess = _bfaccess; FD_ZERO(&rfds); maxblocks = 0; for (n = 0; n < n_equalisers; n++) { if (maxblocks < coeffs[equalisers[n].coeff[0]].n_blocks) { maxblocks = coeffs[equalisers[n].coeff[0]].n_blocks; } } rbuf = emallocaligned(maxblocks * block_length * bfaccess->realsize); for (n = 0; n < n_equalisers; n++) { equalisers[n].ifftplan = bfaccess->convolver_fftplan(log2_get(equalisers[n].taps), true, true); if (bfaccess->realsize == 4) { render_equaliser_f(&equalisers[n]); } else { render_equaliser_d(&equalisers[n]); } } if (!writefd(synch_fd, &dummy, 1)) { fprintf(stderr, "EQ: write failed.\n"); return -1; } close(synch_fd); while (true) { if (!readfd(cmdpipe[0], &command, sizeof(int)) || !readfd(cmdpipe[0], &eq_index, sizeof(int))) { fprintf(stderr, "EQ: read failed.\n"); return -1; } eq = &equalisers[eq_index]; switch (command) { case CMD_CHANGE_MAGNITUDE: case CMD_CHANGE_PHASE: if (!readfd(cmdpipe[0], &n_bands, sizeof(int)) || !readfd(cmdpipe[0], bands, n_bands * sizeof(double)) || !readfd(cmdpipe[0], values, n_bands * sizeof(double))) { fprintf(stderr, "EQ: read failed.\n"); return -1; } for (n = 0, i = 0; i < n_bands && n < eq->band_count; n++) { if (bands[i] > 0.99 * eq->freq[n] && bands[i] < 1.01 * eq->freq[n]) { if (command == CMD_CHANGE_MAGNITUDE) { eq->mag[n] = values[i]; } else { eq->phase[n] = values[i]; } i++; } } if (render_postponed_index == eq_index) { render_postponed_index = -1; } tv.tv_sec = 0; tv.tv_usec = 0; FD_SET(cmdpipe[0], &rfds); if (select(cmdpipe[0] + 1, &rfds, NULL, NULL, &tv) == 1) { /* we have a command waiting, so we postpone render equaliser a while */ if (render_postponed_index != -1) { if (bfaccess->realsize == 4) { render_equaliser_f(&equalisers[render_postponed_index]); } else { render_equaliser_d(&equalisers[render_postponed_index]); } } render_postponed_index = eq_index; continue; } if (bfaccess->realsize == 4) { render_equaliser_f(eq); } else { render_equaliser_d(eq); } break; case CMD_GET_INFO: p = rmsg; memset(rmsg, 0, sizeof(rmsg)); if (eq->coeff[0] == eq->coeff[1]) { sprintf(p, "coefficient %d:\n band: ", eq->coeff[0]); } else { sprintf(p, "coefficient %d,%d:\n band: ", eq->coeff[0], eq->coeff[1]); } p += strlen(p); for (n = 1; n < eq->band_count - 1; n++) { freq = eq->freq[n] * (double)sample_rate; if (freq < 100) { sprintf(p, "%6.1f", freq); } else { sprintf(p, "%6.0f", freq); } p += strlen(p); } sprintf(p, "\n mag: "); p += strlen(p); for (n = 1; n < eq->band_count - 1; n++) { sprintf(p, "%6.1f", 20 * log10(eq->mag[n])); p += strlen(p); } sprintf(p, "\nphase: "); p += strlen(p); for (n = 1; n < eq->band_count - 1; n++) { sprintf(p, "%6.1f", M_PI * 180 * eq->phase[n]); p += strlen(p); } sprintf(p, "\n"); if (!writefd(cmdpipe_reply[1], rmsg, sizeof(rmsg))) { fprintf(stderr, "EQ: write failed.\n"); return -1; } break; } if (render_postponed_index != -1) { if (bfaccess->realsize == 4) { render_equaliser_f(&equalisers[render_postponed_index]); } else { render_equaliser_d(&equalisers[render_postponed_index]); } render_postponed_index = -1; } } return 0; } int bflogic_command(const char params[]) { int command, coeff, n, i, n_bands, eq_index; char *p, *params_copy, *cmd; double bands[MAX_BANDS], values[MAX_BANDS]; struct realtime_eq *eq; params_copy = estrdup(params); cmd = strtrim(params_copy); coeff = -1; /* /[,, ...] */ if (cmd[0] == '\"') { p = strchr(cmd + 1, '\"'); if (p == NULL) { sprintf(msg, "Invalid coefficient.\n"); free(params_copy); return -1; } *p = '\0'; p++; for (n = 0; n < n_coeffs; n++) { if (strcmp(cmd + 1, coeffs[n].name) == 0) { coeff = coeffs[n].intname; break; } } if (n == n_coeffs) { sprintf(msg, "Coefficient with name \"%s\" does not exist.\n", cmd + 1); free(params_copy); return -1; } } else { coeff = strtol(cmd, &p, 10); if (p == cmd) { sprintf(msg, "Invalid number.\n"); free(params_copy); return -1; } } for (n = 0; n < n_equalisers; n++) { if (equalisers[n].coeff[0] == coeff || equalisers[n].coeff[1] == coeff) { eq_index = n; break; } } if (n == n_equalisers) { sprintf(msg, "The given coefficient is not controlled.\n"); free(params_copy); return -1; } cmd = strtrim(p); if (strstr(cmd, "mag") == cmd) { command = CMD_CHANGE_MAGNITUDE; cmd = strtrim(cmd + 3); } else if (strstr(cmd, "phase") == cmd) { command = CMD_CHANGE_PHASE; cmd = strtrim(cmd + 5); } else if (strstr(cmd, "info") == cmd) { command = CMD_GET_INFO; } else { sprintf(msg, "Unknown command.\n"); free(params_copy); return -1; } switch (command) { case CMD_CHANGE_MAGNITUDE: case CMD_CHANGE_PHASE: for (n = 0; n < MAX_BANDS && cmd[0] != '\0'; n++) { bands[n] = strtod(cmd, &p); if (p == cmd || *p != '/') { sprintf(msg, "Invalid frequency/value list.\n"); free(params_copy); return -1; } if (n > 1 && bands[n] <= bands[n-1]) { sprintf(msg, "Frequency bands not sorted.\n"); free(params_copy); return -1; } cmd = p + 1; values[n] = strtod(cmd, &p); if (p == cmd) { sprintf(msg, "Invalid frequency/value list.\n"); free(params_copy); return -1; } cmd = strtrim(p); if (cmd[0] != ',' && cmd[0] != '\0') { sprintf(msg, "Invalid frequency/value list.\n"); free(params_copy); return -1; } if (cmd[0] == ',') { cmd++; } } free(params_copy); n_bands = n; eq = &equalisers[eq_index]; for (n = i = 0; i < n_bands && n < eq->band_count; n++) { if (bands[i] / (double)sample_rate > 0.99 * eq->freq[n] && bands[i] / (double)sample_rate < 1.01 * eq->freq[n]) { bands[i] /= (double)sample_rate; if (command == CMD_CHANGE_MAGNITUDE) { values[i] = pow(10, values[i] / 20); } else { values[i] = values[i] / (180 * M_PI); } i++; } } if (i != n_bands) { sprintf(msg, "At least one invalid frequency band.\n"); return -1; } /* */ if (!writefd(cmdpipe[1], &command, sizeof(int)) || !writefd(cmdpipe[1], &eq_index, sizeof(int)) || !writefd(cmdpipe[1], &n_bands, sizeof(int)) || !writefd(cmdpipe[1], bands, n_bands * sizeof(double)) || !writefd(cmdpipe[1], values, n_bands * sizeof(double))) { sprintf(msg, "Write failed: %s.\n", strerror(errno)); return -1; } sprintf(msg, "ok\n"); break; case CMD_GET_INFO: if (!writefd(cmdpipe[1], &command, sizeof(int)) || !writefd(cmdpipe[1], &eq_index, sizeof(int))) { sprintf(msg, "Write failed: %s.\n", strerror(errno)); return -1; } if (!readfd(cmdpipe_reply[0], msg, sizeof(msg))) { sprintf(msg, "Write failed: %s.\n", strerror(errno)); return -1; } break; } return 0; } const char * bflogic_message(void) { return msg; } brutefir-1.0o/bfmod.h0000664000175000017500000003307212752354353014145 0ustar andersanders/* * (c) Copyright 2001 - 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _BFMOD_H_ #define _BFMOD_H_ #ifdef __cplusplus extern "C" { #endif #include #include #include #include #define BF_VERSION_MAJOR 2 #define BF_VERSION_MINOR 0 /* limits */ #define BF_MAXCHANNELS 256 #define BF_MAXFILTERS 256 #define BF_MAXMODULES 256 #define BF_MAXOBJECTNAME 128 #define BF_MAXCOEFFPARTS 128 #define BF_MAXPROCESSES 64 #define BF_IN 0 #define BF_OUT 1 #define BF_SAMPLE_FORMAT_MIN BF_SAMPLE_FORMAT_S8 #define BF_SAMPLE_FORMAT_S8 1 #define BF_SAMPLE_FORMAT_S16_LE 2 #define BF_SAMPLE_FORMAT_S16_BE 3 #define BF_SAMPLE_FORMAT_S16_4LE 4 // unused since 1.0m #define BF_SAMPLE_FORMAT_S16_4BE 5 // unused since 1.0m #define BF_SAMPLE_FORMAT_S24_LE 6 #define BF_SAMPLE_FORMAT_S24_BE 7 #define BF_SAMPLE_FORMAT_S24_4LE 8 #define BF_SAMPLE_FORMAT_S24_4BE 9 #define BF_SAMPLE_FORMAT_S32_LE 10 #define BF_SAMPLE_FORMAT_S32_BE 11 #define BF_SAMPLE_FORMAT_FLOAT_LE 12 #define BF_SAMPLE_FORMAT_FLOAT_BE 13 #define BF_SAMPLE_FORMAT_FLOAT64_LE 14 #define BF_SAMPLE_FORMAT_FLOAT64_BE 15 #define BF_SAMPLE_FORMAT_MAX BF_SAMPLE_FORMAT_FLOAT64_BE /* macro sample formats */ #define BF_SAMPLE_FORMAT_S16_NE 16 #define BF_SAMPLE_FORMAT_S16_4NE 17 // unused since 1.0m #define BF_SAMPLE_FORMAT_S24_NE 18 #define BF_SAMPLE_FORMAT_S24_4NE 19 #define BF_SAMPLE_FORMAT_S32_NE 20 #define BF_SAMPLE_FORMAT_FLOAT_NE 21 #define BF_SAMPLE_FORMAT_FLOAT64_NE 22 #define BF_SAMPLE_FORMAT_AUTO 23 #define BF_SAMPLE_FORMAT_MACRO_MAX 23 /* exit values */ #define BF_EXIT_OK 0 #define BF_EXIT_OTHER 1 #define BF_EXIT_INVALID_CONFIG 2 #define BF_EXIT_NO_MEMORY 3 #define BF_EXIT_INVALID_INPUT 4 #define BF_EXIT_BUFFER_UNDERFLOW 5 /* callback events */ #define BF_CALLBACK_EVENT_NORMAL 0 #define BF_CALLBACK_EVENT_ERROR 1 #define BF_CALLBACK_EVENT_LAST_INPUT 2 #define BF_CALLBACK_EVENT_FINISHED 3 #define BF_LEX_EOS 1 /* end of statement (;) */ #define BF_LEX_LBRACE 2 /* { */ #define BF_LEX_RBRACE 3 /* } */ #define BF_LEX_COMMA 4 /* , */ #define BF_LEX_SLASH 5 /* / */ #define BF_LEXVAL_REAL 100 #define BF_LEXVAL_BOOLEAN 101 #define BF_LEXVAL_STRING 102 #define BF_LEXVAL_FIELD 103 #define BF_SAMPLE_SLOTS 100 #define BF_UNDEFINED_SUBDELAY (-BF_SAMPLE_SLOTS) union bflexval { double real; int boolean; char *string; char *field; }; struct bfoverflow { unsigned int n_overflows; int32_t intlargest; double largest; double max; }; struct bfcoeff { int is_shared; char name[BF_MAXOBJECTNAME]; int intname; int n_blocks; }; struct bfchannel { char name[BF_MAXOBJECTNAME]; int intname; }; struct bffilter { char name[BF_MAXOBJECTNAME]; int intname; int crossfade; int n_channels[2]; int *channels[2]; int n_filters[2]; int *filters[2]; }; struct bffilter_control { int coeff; int delayblocks; double scale[2][BF_MAXCHANNELS]; double fscale[BF_MAXFILTERS]; }; struct bfaccess { volatile struct bffilter_control *fctrl; volatile struct bfoverflow *overflow; int realsize; void ***coeffs_data; void (*control_mutex)(int lock); void (*reset_peak)(void); void (*exit)(int bf_exit_code); /* * Mute/unmute the given input/output channel. If the channel index is invalid, * nothing happens. */ void (*toggle_mute)(int io, int channel); int (*ismuted)(int io, int channel); /* * Change delay of the given channel. If the delay or channel is out of range, * -1 is returned, else 0. */ int (*set_delay)(int io, int channel, int delay); int (*get_delay)(int io, int channel); double (*realtime_index)(void); const char **(*bfio_names)(int io, int *n_names); void (*bfio_range)(int io, int modindex, int range[2]); int (*bfio_command)(int io, int modindex, const char params[], char **error); const char **(*bflogic_names)(int *n_names); int (*bflogic_command)(int modindex, const char params[], char **error); void (*convolver_coeffs2cbuf)(void *src, void *dest); void *(*convolver_fftplan)(int order, int invert, int inplace); int (*set_subdelay)(int io, int channel, int subdelay); int (*get_subdelay)(int io, int channel); }; struct bfevents { #define BF_FDEVENT_PEAK 0x1 #define BF_FDEVENT_INITIALISED 0x2 unsigned int fdevents; void (*peak)(void); void (*initialised)(void); void (*block_start)(struct bfaccess *bfaccess, unsigned int block_index, struct timeval *current_time); void (*input_timed)(void *buf, int channel); void (*input_freqd)(void *buf, int channel); void (*coeff_final)(int filter, int *coeff); void (*pre_convolve)(void *buf, int filter); void (*post_convolve)(void *buf, int filter); void (*output_freqd)(void *buf, int channel); void (*output_timed)(void *buf, int channel); }; #define BF_FUN_BFIO_ISCALLBACK "bfio_iscallback" #define BF_FUN_BFIO_PREINIT "bfio_preinit" #define BF_FUN_BFIO_COMMAND "bfio_command" #define BF_FUN_BFIO_INIT "bfio_init" #define BF_FUN_BFIO_READ "bfio_read" #define BF_FUN_BFIO_WRITE "bfio_write" #define BF_FUN_BFIO_SYNCH_START "bfio_synch_start" #define BF_FUN_BFIO_SYNCH_STOP "bfio_synch_stop" #define BF_FUN_BFIO_START "bfio_start" #define BF_FUN_BFIO_STOP "bfio_stop" #define BF_FUN_BFIO_MESSAGE "bfio_message" struct bfio_module { void *handle; int iscallback; void *(*preinit)(int *version_minor, int *version_major, int (*get_config_token)(union bflexval *lexval), int io, int *sample_format, int sample_rate, int open_channels, int *uses_sample_clock, int *callback_sched_policy, struct sched_param *callback_sched, int debug); int (*init)(void *params, int io, int sample_format, int sample_rate, int open_channels, int used_channels, const int channel_selection[], int period_size, int *device_period_size, int *isinterleaved, void *callback_state, int (*process_callback)(void **callback_states[2], int callback_state_count[2], void **buffers[2], int frame_count, int event)); int (*command)(int fd, const char params[]); /* see errno handling in dai_input in order to provide correct errnos */ int (*read)(int fd, void *buf, int offset, int count); int (*write)(int fd, const void *buf, int offset, int count); int (*synch_start)(void); void (*synch_stop)(void); int (*start)(int io); void (*stop)(int io); const char *(*message)(void); }; #define BF_FUN_BFLOGIC_PREINIT "bflogic_preinit" #define BF_FUN_BFLOGIC_INIT "bflogic_init" #define BF_FUN_BFLOGIC_COMMAND "bflogic_command" #define BF_FUN_BFLOGIC_MESSAGE "bflogic_message" struct bflogic_module { void *handle; #define BF_FORK_DONT_FORK 0 #define BF_FORK_PRIO_MAX 1 #define BF_FORK_PRIO_FILTER 2 #define BF_FORK_PRIO_OTHER 3 int fork_mode; int event_pipe[2]; struct bfevents bfevents; int (*preinit)(int *version_minor, int *version_major, int (*get_config_token)(union bflexval *lexval), int sample_rate, int block_length, int n_maxblocks, int n_coeffs, const struct bfcoeff coeffs[], const int n_channels[2], const struct bfchannel * const *channels, int n_filters, const struct bffilter filters[], struct bfevents *bfevents, int *fork_mode, int debug); int (*init)(struct bfaccess *bfaccess, int sample_rate, int block_length, int n_maxblocks, int n_coeffs, const struct bfcoeff coeffs[], const int n_channels[2], const struct bfchannel *channels[2], int n_filters, const struct bffilter filters[], int event_fd, int synch_fd); int (*command)(const char params[]); const char *(*message)(void); }; static inline int bf_sampleformat_size(int format) { switch (format) { case BF_SAMPLE_FORMAT_S8: return 1; case BF_SAMPLE_FORMAT_S16_LE: return 2; case BF_SAMPLE_FORMAT_S16_BE: return 2; case BF_SAMPLE_FORMAT_S16_4LE: return 4; case BF_SAMPLE_FORMAT_S16_4BE: return 4; case BF_SAMPLE_FORMAT_S24_LE: return 3; case BF_SAMPLE_FORMAT_S24_BE: return 3; case BF_SAMPLE_FORMAT_S24_4LE: return 4; case BF_SAMPLE_FORMAT_S24_4BE: return 4; case BF_SAMPLE_FORMAT_S32_LE: return 4; case BF_SAMPLE_FORMAT_S32_BE: return 4; case BF_SAMPLE_FORMAT_FLOAT_LE: return 4; case BF_SAMPLE_FORMAT_FLOAT_BE: return 4; case BF_SAMPLE_FORMAT_FLOAT64_LE: return 8; case BF_SAMPLE_FORMAT_FLOAT64_BE: return 8; default: return 0; } } static inline const char * bf_strsampleformat(int format) { switch (format) { case BF_SAMPLE_FORMAT_S8: return "S8"; case BF_SAMPLE_FORMAT_S16_LE: return "S16_LE"; case BF_SAMPLE_FORMAT_S16_BE: return "S16_BE"; case BF_SAMPLE_FORMAT_S16_NE: return "S16_NE"; case BF_SAMPLE_FORMAT_S16_4LE: return "S16_4LE"; case BF_SAMPLE_FORMAT_S16_4BE: return "S16_4BE"; case BF_SAMPLE_FORMAT_S16_4NE: return "S16_4NE"; case BF_SAMPLE_FORMAT_S24_LE: return "S24_LE"; case BF_SAMPLE_FORMAT_S24_BE: return "S24_BE"; case BF_SAMPLE_FORMAT_S24_NE: return "S24_NE"; case BF_SAMPLE_FORMAT_S24_4LE: return "S24_4LE"; case BF_SAMPLE_FORMAT_S24_4BE: return "S24_4BE"; case BF_SAMPLE_FORMAT_S24_4NE: return "S24_4NE"; case BF_SAMPLE_FORMAT_S32_LE: return "S32_LE"; case BF_SAMPLE_FORMAT_S32_BE: return "S32_BE"; case BF_SAMPLE_FORMAT_S32_NE: return "S32_NE"; case BF_SAMPLE_FORMAT_FLOAT_LE: return "FLOAT_LE"; case BF_SAMPLE_FORMAT_FLOAT_BE: return "FLOAT_BE"; case BF_SAMPLE_FORMAT_FLOAT_NE: return "FLOAT_NE"; case BF_SAMPLE_FORMAT_FLOAT64_LE: return "FLOAT64_LE"; case BF_SAMPLE_FORMAT_FLOAT64_BE: return "FLOAT64_BE"; case BF_SAMPLE_FORMAT_FLOAT64_NE: return "FLOAT64_NE"; case BF_SAMPLE_FORMAT_AUTO: return "AUTO"; default: return "##unknown sample format##"; } } #ifdef IS_BFIO_MODULE /* prototypes of functions the module implements */ int bfio_iscallback(void); void * bfio_preinit(int *version_minor, int *version_major, int (*get_config_token)(union bflexval *lexval), int io, int *sample_format, int sample_rate, int open_channels, int *uses_sample_clock, int *callback_sched_policy, struct sched_param *callback_sched, int debug); int bfio_init(void *params, int io, int sample_format, int sample_rate, int open_channels, int used_channels, const int channel_selection[], int period_size, int *device_period_size, int *isinterleaved, void *callback_state, int (*process_callback)(void **callback_states[2], int callback_state_count[2], void **buffers[2], int frame_count, int event)); int bfio_cb_init(void *params); int bfio_command(int fd, const char params[]); int bfio_read(int fd, void *buf, int offset, int count); int bfio_write(int fd, const void *buf, int offset, int count); int bfio_synch_start(void); void bfio_synch_stop(void); int bfio_start(int io); void bfio_stop(int io); const char * bfio_message(void); #endif #ifdef IS_BFLOGIC_MODULE /* prototypes of functions the module implements */ int bflogic_preinit(int *version_minor, int *version_major, int (*get_config_token)(union bflexval *lexval), int sample_rate, int block_length, int n_maxblocks, int n_coeffs, const struct bfcoeff coeffs[], const int n_channels[2], const struct bfchannel *channels[2], int n_filters, const struct bffilter filters[], struct bfevents *bfevents, int *fork_mode, int debug); int bflogic_init(struct bfaccess *bfaccess, int sample_rate, int block_length, int n_maxblocks, int n_coeffs, const struct bfcoeff coeffs[], const int n_channels[2], const struct bfchannel *channels[2], int n_filters, const struct bffilter filters[], int event_fd, int synch_fd); int bflogic_command(const char params[]); const char * bflogic_message(void); #endif #ifdef __cplusplus } #endif #endif brutefir-1.0o/bfrun.c0000664000175000017500000027246412752354353014177 0ustar andersanders/* * (c) Copyright 2001 - 2006, 2013, 2016 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include "defs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __OS_SUNOS__ #include #endif #include "dai.h" #include "convolver.h" #include "emalloc.h" #include "shmalloc.h" #include "bfrun.h" #include "fdrw.h" #include "bit.h" #include "bfconf.h" #include "inout.h" #include "log2.h" #include "dither.h" #include "timestamp.h" #include "delay.h" #include "pinfo.h" #include "timermacros.h" #define DEBUG_MAX_DAI_LOOPS 32 #define DEBUG_RING_BUFFER_SIZE 1024 /* debug structs */ struct debug_input_process { struct debug_input d[DEBUG_MAX_DAI_LOOPS]; int dai_loops; struct { uint64_t ts_call; uint64_t ts_ret; } r_output; struct { uint64_t ts_call; uint64_t ts_ret; } w_filter; }; struct debug_output_process { struct { uint64_t ts_call; uint64_t ts_ret; } r_filter; struct { uint64_t ts_call; uint64_t ts_ret; } w_input; struct debug_output d[DEBUG_MAX_DAI_LOOPS]; int dai_loops; }; struct debug_filter_process { struct { uint64_t ts_call; uint64_t ts_ret; } r_input; struct { uint64_t ts_call; uint64_t ts_ret; } mutex; struct { uint64_t ts_call; uint64_t ts_ret; } fsynch_fd; struct { uint64_t ts_call; uint64_t ts_ret; } fsynch_td; struct { uint64_t ts_call; uint64_t ts_ret; } w_output; }; struct intercomm_area { volatile bool_t doreset_overflow; int sync[BF_MAXPROCESSES]; volatile uint32_t period_us[BF_MAXPROCESSES]; volatile double realtime_index; struct bffilter_control fctrl[BF_MAXFILTERS]; struct bfoverflow overflow[BF_MAXCHANNELS]; uint32_t ismuted[2][BF_MAXCHANNELS/32]; volatile int delay[2][BF_MAXCHANNELS]; volatile int subdelay[2][BF_MAXCHANNELS]; volatile int n_pids; volatile pid_t pids[BF_MAXPROCESSES]; volatile int exit_status; volatile bool_t full_proc[BF_MAXPROCESSES]; volatile bool_t ignore_rtprio; struct { uint64_t ts_start; struct debug_input_process i[DEBUG_RING_BUFFER_SIZE]; struct debug_output_process o[DEBUG_RING_BUFFER_SIZE]; struct debug_filter_process f[DEBUG_RING_BUFFER_SIZE]; uint32_t periods; } debug; }; static volatile struct intercomm_area *icomm = NULL; static struct bfoverflow *reset_overflow; static int bl_output_2_bl_input[2]; static int bl_output_2_cb_input[2]; static int cb_output_2_bl_input[2]; static int cb_input_2_filter[2]; static int filter_2_cb_output[2]; static int mutex_pipe[2]; static int n_callback_devs[2]; static int n_blocking_devs[2]; struct { int n_fdpeak; int *fdpeak; int n_fdinitialised; int *fdinitialised; int n_peak; void (**peak)(void); int n_initialised; void (**initialised)(void); int n_block_start; void (**block_start)(struct bfaccess *bfaccess, unsigned int block_index, struct timeval *current_time); int n_input_timed; void (**input_timed)(void *buf, int channel); int n_input_freqd; void (**input_freqd)(void *buf, int channel); int n_coeff_final; void (**coeff_final)(int filter, int *coeff); int n_pre_convolve; void (**pre_convolve)(void *buf, int filter); int n_post_convolve; void (**post_convolve)(void *buf, int filter); int n_output_freqd; void (**output_freqd)(void *buf, int channel); int n_output_timed; void (**output_timed)(void *buf, int channel); } events; #define INIT_EVENTS_FD(id, fdname, counter) \ if (bfconf->logicmods[n].bfevents.fdevents & id) { \ events. fdname [events. counter ] = bfconf->logicmods[n].event_pipe[1];\ events. counter ++; \ } #define INIT_EVENTS_FUN(funname, counter) \ if (bfconf->logicmods[n].bfevents. funname != NULL) { \ events. funname [events. counter] = \ bfconf->logicmods[n].bfevents. funname; \ events. counter ++; \ } static void init_events(void) { int n; memset(&events, 0, sizeof(events)); events.fdpeak = emalloc(bfconf->n_logicmods * sizeof(int)); events.fdinitialised = emalloc(bfconf->n_logicmods * sizeof(int)); events.peak = emalloc(bfconf->n_logicmods * sizeof(void *)); events.initialised = emalloc(bfconf->n_logicmods * sizeof(void *)); events.block_start = emalloc(bfconf->n_logicmods * sizeof(void *)); events.input_timed = emalloc(bfconf->n_logicmods * sizeof(void *)); events.input_freqd = emalloc(bfconf->n_logicmods * sizeof(void *)); events.coeff_final = emalloc(bfconf->n_logicmods * sizeof(void *)); events.pre_convolve = emalloc(bfconf->n_logicmods * sizeof(void *)); events.post_convolve = emalloc(bfconf->n_logicmods * sizeof(void *)); events.output_freqd = emalloc(bfconf->n_logicmods * sizeof(void *)); events.output_timed = emalloc(bfconf->n_logicmods * sizeof(void *)); for (n = 0; n < bfconf->n_logicmods; n++) { INIT_EVENTS_FD(BF_FDEVENT_PEAK, fdpeak, n_fdpeak); INIT_EVENTS_FD(BF_FDEVENT_INITIALISED, fdinitialised, n_fdinitialised); INIT_EVENTS_FUN(peak, n_peak); INIT_EVENTS_FUN(initialised, n_initialised); INIT_EVENTS_FUN(block_start, n_block_start); INIT_EVENTS_FUN(input_timed, n_input_timed); INIT_EVENTS_FUN(input_freqd, n_input_freqd); INIT_EVENTS_FUN(coeff_final, n_coeff_final); INIT_EVENTS_FUN(pre_convolve, n_pre_convolve); INIT_EVENTS_FUN(post_convolve, n_post_convolve); INIT_EVENTS_FUN(output_freqd, n_output_freqd); INIT_EVENTS_FUN(output_timed, n_output_timed); } if (events.n_coeff_final > 1) { fprintf(stderr, "It makes no sense to have more than one module " "which wants final coefficient control\n"); bf_exit(BF_EXIT_INVALID_CONFIG); } } #undef INIT_EVENTS_FD #undef INIT_EVENTS_FUN #define D icomm->debug static void print_debug(void) { uint64_t tsdiv; uint32_t n, i, k; printf("\nWARNING: these timestamps only make sense if:\n"); printf(" - there is only one input device\n"); printf(" - there is only one output device\n"); printf(" - there is only one filter process\n"); printf(" - dai loop does not exceed %d\n\n", DEBUG_MAX_DAI_LOOPS); tsdiv = (uint64_t)bfconf->cpu_mhz; printf("%u periods\n\n", D.periods); D.periods += 2; if (bfconf->synched_write) { printf("output_process:\n"); for (n = 0; n < 2; n++) { printf(" period %i: (dai loop %d)\n", (int)n - 2, D.o[n].dai_loops); if (n == 1) { printf(" %" PRIu64 "\tcall synch input 0 (write)\n", (ull_t)((D.o[n].w_input.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.o[n].w_input.ts_ret - D.ts_start) / tsdiv)); } for (i = 0; i < D.o[n].dai_loops && i < DEBUG_MAX_DAI_LOOPS; i++) { printf(" %" PRIu64 "\tcall select fdmax %d\n", (ull_t)((D.o[n].d[i].select.ts_call - D.ts_start) / tsdiv), D.o[n].d[i].select.fdmax); printf(" %" PRIu64 "\tret %d\n", (ull_t)((D.o[n].d[i].select.ts_ret - D.ts_start) / tsdiv), D.o[n].d[i].select.retval); printf(" %" PRIu64 "\twrite(%d, %p, %d, %d)\n", (ull_t)((D.o[n].d[i].write.ts_call - D.ts_start) / tsdiv), D.o[n].d[i].write.fd, D.o[n].d[i].write.buf, D.o[n].d[i].write.offset, D.o[n].d[i].write.count); printf(" %" PRIu64 "\tret %d\n\n", (ull_t)((D.o[n].d[i].write.ts_ret - D.ts_start) / tsdiv), D.o[n].d[i].write.retval); } if (n == 0) { printf(" %" PRIu64 "\tcall synch input trigger start " "(write)\n", (ull_t)((D.o[n].d[0].init.ts_synchfd_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.o[n].d[0].init.ts_synchfd_ret - D.ts_start) / tsdiv)); } } } else { printf("output_process:\n"); printf(" period -2:\n"); printf(" %" PRIu64 "\tcall synch input trigger start (write)\n", (ull_t)((D.o[0].w_input.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.o[0].w_input.ts_ret - D.ts_start) / tsdiv)); printf("output_process:\n"); printf(" period -1:\n"); printf(" %" PRIu64 "\tcall synch input 0 (write)\n", (ull_t)((D.o[1].w_input.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.o[1].w_input.ts_ret - D.ts_start) / tsdiv)); } for (n = 0; n < D.periods && n < DEBUG_RING_BUFFER_SIZE - 2; n++) { printf("input_process:\n"); printf(" period %u: (dai loop %d)\n", n, D.i[n].dai_loops); if (n == 0) { printf(" %" PRIu64 "\tcall init start\n", (ull_t)((D.i[n].d[0].init.ts_start_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n", (ull_t)((D.i[n].d[0].init.ts_start_ret - D.ts_start) / tsdiv)); } for (i = 0; i < D.i[n].dai_loops && i < DEBUG_MAX_DAI_LOOPS; i++) { printf(" %" PRIu64 "\tcall select fdmax %d\n", (ull_t)((D.i[n].d[i].select.ts_call - D.ts_start) / tsdiv), D.i[n].d[i].select.fdmax); printf(" %" PRIu64 "\tret %d (%" PRIu64 ")\n", (ull_t)((D.i[n].d[i].select.ts_ret - D.ts_start) / tsdiv), D.i[n].d[i].select.retval, (ull_t)((D.i[n].d[i].select.ts_ret - D.ts_start) / tsdiv - (D.i[n].d[i].select.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tread(%d, %p, %d, %d)\n", (ull_t)((D.i[n].d[i].read.ts_call - D.ts_start) / tsdiv), D.i[n].d[i].read.fd, D.i[n].d[i].read.buf, D.i[n].d[i].read.offset, D.i[n].d[i].read.count); printf(" %" PRIu64 "\tret %d\n\n", (ull_t)((D.i[n].d[i].read.ts_ret - D.ts_start) / tsdiv), D.i[n].d[i].read.retval); } printf(" %" PRIu64 "\tcall synch output %d (read)\n", (ull_t)((D.i[n].r_output.ts_call - D.ts_start) / tsdiv), n - 1); printf(" %" PRIu64 "\tret\n", (ull_t)((D.i[n].r_output.ts_ret - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tcall synch filter %d (write)\n", (ull_t)((D.i[n].w_filter.ts_call - D.ts_start) / tsdiv), n); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.i[n].w_filter.ts_ret - D.ts_start) / tsdiv)); printf("filter_process:\n"); printf(" period %u:\n", n); printf(" %" PRIu64 "\tcall synch input %d (read)\n", (ull_t)((D.f[n].r_input.ts_call - D.ts_start) / tsdiv), n); printf(" %" PRIu64 "\tret\n", (ull_t)((D.f[n].r_input.ts_ret - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tcall mutex\n", (ull_t)((D.f[n].mutex.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.f[n].mutex.ts_ret - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tcall synch fd\n", (ull_t)((D.f[n].fsynch_fd.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.f[n].fsynch_fd.ts_ret - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tcall synch td\n", (ull_t)((D.f[n].fsynch_td.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n\n", (ull_t)((D.f[n].fsynch_td.ts_ret - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tcall synch output %d (write)\n", (ull_t)((D.f[n].w_output.ts_call - D.ts_start) / tsdiv), n); printf(" %" PRIu64 "\tret (%" PRIu64 " from synch input ret)\n\n", (ull_t)((D.f[n].w_output.ts_ret - D.ts_start) / tsdiv), (ull_t)((D.f[n].w_output.ts_ret - D.ts_start) / tsdiv - (D.f[n].r_input.ts_ret - D.ts_start) / tsdiv)); n += 2; printf("output_process:\n"); printf(" period %u: (dai loop %d)\n", n - 2, D.o[n].dai_loops); printf(" %" PRIu64 "\tcall synch filter %d (read)\n", (ull_t)((D.o[n].r_filter.ts_call - D.ts_start) / tsdiv), n - 2); printf(" %" PRIu64 "\tret (%" PRId64 ")\n", (ull_t)((D.o[n].r_filter.ts_ret - D.ts_start) / tsdiv), (ll_t)((D.o[n].r_filter.ts_ret - D.ts_start) / tsdiv - (D.o[n].r_filter.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tcall synch input %d (write)\n", (ull_t)((D.o[n].w_input.ts_call - D.ts_start) / tsdiv), n - 1); printf(" %" PRIu64 "\tret (%" PRId64 ")\n\n", (ull_t)((D.o[n].w_input.ts_ret - D.ts_start) / tsdiv), (ll_t)((D.o[n].w_input.ts_ret - D.ts_start) / tsdiv - (D.i[n-2].r_output.ts_ret - D.ts_start) / tsdiv)); if (!bfconf->synched_write && n == 2) { printf(" %" PRIu64 "\tcall init start\n", (ull_t)((D.o[n].d[0].init.ts_start_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret\n", (ull_t)((D.o[n].d[0].init.ts_start_ret - D.ts_start) / tsdiv)); } for (i = 0; i < D.o[n].dai_loops && i < DEBUG_MAX_DAI_LOOPS; i++) { printf(" %" PRIu64 "\tcall select fdmax %d\n", (ull_t)((D.o[n].d[i].select.ts_call - D.ts_start) / tsdiv), D.o[n].d[i].select.fdmax); printf(" %" PRIu64 "\tret %d\n", (ull_t)((D.o[n].d[i].select.ts_ret - D.ts_start) / tsdiv), D.o[n].d[i].select.retval); if (D.o[n-1].dai_loops > DEBUG_MAX_DAI_LOOPS) { k = DEBUG_MAX_DAI_LOOPS - 1; } else { k = D.o[n-1].dai_loops - 1; } printf(" %" PRIu64 "\twrite(%d, %p, %d, %d) (%" PRIu64 ")\n", (ull_t)((D.o[n].d[i].write.ts_call - D.ts_start) / tsdiv), D.o[n].d[i].write.fd, D.o[n].d[i].write.buf, D.o[n].d[i].write.offset, D.o[n].d[i].write.count, (ull_t)((D.o[n].d[i].write.ts_call - D.ts_start) / tsdiv - (D.o[n-1].d[k-1].write.ts_call - D.ts_start) / tsdiv)); printf(" %" PRIu64 "\tret %d\n\n", (ull_t)((D.o[n].d[i].write.ts_ret - D.ts_start) / tsdiv), D.o[n].d[i].write.retval); } n -= 2; } } #undef D static void sighandler(int sig) { bf_exit(icomm->exit_status); } static int ismuted(int io, int channel) { if ((io != IN && io != OUT) || channel < 0 || channel >= bfconf->n_channels[io]) { return (int)true; } return (int)bit_isset_volatile(icomm->ismuted[io], channel); } static void toggle_mute(int io, int channel) { int physch; if ((io != IN && io != OUT) || channel < 0 || channel >= bfconf->n_channels[io]) { return; } if (bit_isset_volatile(icomm->ismuted[io], channel)) { bit_clr_volatile(icomm->ismuted[io], channel); } else { bit_set_volatile(icomm->ismuted[io], channel); } physch = bfconf->virt2phys[io][channel]; if (bfconf->n_virtperphys[io][physch] == 1) { dai_toggle_mute(io, physch); return; } } static int set_delay(int io, int channel, int delay) { int physch; if ((io != IN && io != OUT) || channel < 0 || channel >= bfconf->n_channels[io]) { return -1; } if (delay == icomm->delay[io][channel]) { return 0; } if (delay < 0 || delay > bfconf->maxdelay[io][channel]) { return -1; } physch = bfconf->virt2phys[io][channel]; if (bfconf->n_virtperphys[io][physch] == 1) { if (dai_change_delay(io, physch, delay) == -1) { return -1; } } icomm->delay[io][channel] = delay; return 0; } static int get_delay(int io, int channel) { if ((io != IN && io != OUT) || channel < 0 || channel >= bfconf->n_channels[OUT]) { return 0; } return icomm->delay[io][channel]; } static int set_subdelay(int io, int channel, int subdelay) { if ((io != IN && io != OUT) || channel < 0 || channel >= bfconf->n_channels[io] || subdelay <= -BF_SAMPLE_SLOTS || subdelay >= BF_SAMPLE_SLOTS) { return -1; } if (subdelay == icomm->subdelay[io][channel]) { return 0; } if (!bfconf->use_subdelay[io] || bfconf->subdelay[io][channel] == BF_UNDEFINED_SUBDELAY) { return -1; } icomm->subdelay[io][channel] = subdelay; return 0; } static int get_subdelay(int io, int channel) { if ((io != IN && io != OUT) || channel < 0 || channel >= bfconf->n_channels[OUT]) { return 0; } return icomm->subdelay[io][channel]; } static void print_overflows(void) { bool_t is_overflow = false; double peak; int n; for (n = 0; n < bfconf->n_channels[OUT]; n++) { if (icomm->overflow[n].n_overflows > 0) { is_overflow = true; break; } } if (!is_overflow && !bfconf->show_progress) { return; } pinfo("peak: "); for (n = 0; n < bfconf->n_channels[OUT]; n++) { peak = icomm->overflow[n].largest; if (peak < (double)icomm->overflow[n].intlargest) { peak = (double)icomm->overflow[n].intlargest; } if (peak != 0.0) { if ((peak = 20.0 * log10(peak / icomm->overflow[n].max)) == 0.0) { peak = -0.0; /* we want to display -0.0 rather than +0.0 */ } pinfo("%d/%u/%+.2f ", n, icomm->overflow[n].n_overflows, peak); } else { pinfo("%d/%u/-Inf ", n, icomm->overflow[n].n_overflows); } } pinfo("\n"); } static void check_overflows(struct bfoverflow overflow[]) { struct bfoverflow of; uint32_t msg; int n; if (!bfconf->overflow_warnings) { return; } for (n = 0; n < bfconf->n_channels[OUT]; n++) { of = icomm->overflow[n]; if (memcmp(&of, &overflow[n], sizeof(struct bfoverflow)) != 0) { for (; n < bfconf->n_channels[OUT]; n++) { overflow[n] = icomm->overflow[n]; } msg = BF_FDEVENT_PEAK; for (n = 0; n < events.n_fdpeak; n++) { if (!writefd(events.fdpeak[n], &msg, 4)) { bf_exit(BF_EXIT_OTHER); } } for (n = 0; n < events.n_peak; n++) { events.peak[n](); } print_overflows(); return; } } } static void rti_and_overflow(void) { static struct bfoverflow overflow[BF_MAXCHANNELS]; static time_t lastprinttime = 0; static uint32_t max_period_us; static bool_t isinit = false; double rti, max_rti; uint32_t period_us; bool_t full_proc; time_t tt; int n; if (!isinit) { for (n = 0; n < bfconf->n_channels[OUT]; n++) { overflow[n] = icomm->overflow[n]; } max_period_us = (uint32_t)((uint64_t)bfconf->filter_length * 1000000 / bfconf->sampling_rate); isinit = true; } if (icomm->doreset_overflow) { icomm->doreset_overflow = false; memset(overflow, 0, sizeof(struct bfoverflow) * bfconf->n_channels[OUT]); } /* calculate realtime index */ if ((tt = time(NULL)) != lastprinttime) { max_rti = 0; full_proc = true; for (n = 0; n < bfconf->n_processes; n++) { period_us = icomm->period_us[n]; if (!icomm->full_proc[n]) { full_proc = false; } if (period_us == 0) { max_rti = 0; break; } rti = (float)period_us / (float)max_period_us; if (rti > max_rti) { max_rti = rti; } } if (bfconf->show_progress && max_rti != 0) { if (full_proc) { pinfo("rti: %.3f\n", max_rti); } else { pinfo("rti: not full processing - no rti update\n"); } } icomm->realtime_index = max_rti; check_overflows(overflow); lastprinttime = tt; } } static void icomm_mutex(int lock) { char dummydata[1]; dummydata[0] = '\0'; if (lock) { if (!readfd(mutex_pipe[0], dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } else { if (!writefd(mutex_pipe[1], dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } } static bool_t memiszero(void *buf, int size) { int n, count; uint32_t acc; /* we go through all memory always, to avoid variations in time it takes */ acc = 0; count = size >> 2; for (n = 0; n < count; n += 4) { acc |= ((uint32_t *)buf)[n+0]; acc |= ((uint32_t *)buf)[n+1]; acc |= ((uint32_t *)buf)[n+2]; acc |= ((uint32_t *)buf)[n+3]; } count = size - (count << 2); buf = &((uint32_t *)buf)[n]; for (n = 0; n < count; n++) { acc |= ((uint8_t *)buf)[n]; } return acc == 0; } static bool_t test_silent(void *buf, int size, int realsize, double analog_powersave, double scale) { int n, count; double dmax; float fmax; if (analog_powersave >= 1.0) { return memiszero(buf, size); } if (realsize == 4) { fmax = 0; count = size >> 2; for (n = 0; n < count; n++) { if (((float *)buf)[n] < 0) { if (-((float *)buf)[n] > fmax) { fmax = -((float *)buf)[n]; } } else { if (((float *)buf)[n] > fmax) { fmax = ((float *)buf)[n]; } } } dmax = fmax; } else { dmax = 0; count = size >> 3; for (n = 0; n < count; n++) { if (((double *)buf)[n] < 0) { if (-((double *)buf)[n] > dmax) { dmax = -((double *)buf)[n]; } } else { if (((double *)buf)[n] > dmax) { dmax = ((double *)buf)[n]; } } } } if (scale * dmax >= analog_powersave) { return false; } /* make it truly zero */ memset(buf, 0, size); return true; } static void input_process(void *buf[2], int filter_writefd, int output_readfd, int extra_output_readfd, int synch_writefd) { char dummydata[bfconf->n_processes]; bool_t do_yield; int n, curbuf; uint32_t msg; int dbg_pos; if (bfconf->realtime_priority) { bf_make_realtime(0, bfconf->realtime_midprio, "input"); } do_yield = false; if (dai_minblocksize() >= bfconf->filter_length || dai_input_poll_mode()) { do_yield = true; } msg = BF_FDEVENT_INITIALISED; for (n = 0; n < events.n_fdinitialised; n++) { if (!writefd(events.fdinitialised[n], &msg, 4)) { bf_exit(BF_EXIT_OTHER); } } for (n = 0; n < events.n_initialised; n++) { events.initialised[n](); } dbg_pos = 0; curbuf = 0; memset(dummydata, 0, bfconf->n_processes); if (synch_writefd != -1) { if (!writefd(synch_writefd, dummydata, 1) || !readfd(output_readfd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } while (true) { dai_input(icomm->debug.i[dbg_pos].d, DEBUG_MAX_DAI_LOOPS, &icomm->debug.i[dbg_pos].dai_loops); curbuf = !curbuf; timestamp(&icomm->debug.i[dbg_pos].r_output.ts_call); if (!readfd(output_readfd, dummydata, 1) || (extra_output_readfd != -1 && !readfd(extra_output_readfd, dummydata, 1))) { bf_exit(BF_EXIT_OTHER); } timestamp(&icomm->debug.i[dbg_pos].r_output.ts_ret); timestamp(&icomm->debug.i[dbg_pos].w_filter.ts_call); if (!writefd(filter_writefd, dummydata, bfconf->n_processes)) { bf_exit(BF_EXIT_OTHER); } if (bfconf->realtime_priority && do_yield) { sched_yield(); } timestamp(&icomm->debug.i[dbg_pos].w_filter.ts_ret); if (++dbg_pos == DEBUG_RING_BUFFER_SIZE) { dbg_pos = 0; } } } static void output_process(int filter_readfd, int synch_readfd, int input_writefd, int extra_input_writefd, bool_t trigger_callback_io, bool_t checkdrift) { char dummydata[bfconf->n_processes]; uint32_t bufindex = 0; int dbg_pos; if (bfconf->realtime_priority) { bf_make_realtime(0, bfconf->realtime_midprio, "output"); } dbg_pos = 0; memset(dummydata, 0, bfconf->n_processes); if (synch_readfd != -1) { if (!readfd(synch_readfd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } /* verify if we need to write iodelay output */ if (bfconf->synched_write) { pinfo("Fixed I/O-delay is %d samples\n" "Audio processing starts now\n", 2 * bfconf->filter_length + (bfconf->use_subdelay[IN] ? bfconf->sdf_length : 0) + (bfconf->use_subdelay[OUT] ? bfconf->sdf_length : 0)); if (trigger_callback_io) { dai_trigger_callback_io(); } if (extra_input_writefd != -1 && !writefd(extra_input_writefd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } dai_output(true, input_writefd, icomm->debug.o[dbg_pos].d, DEBUG_MAX_DAI_LOOPS, &icomm->debug.o[dbg_pos].dai_loops); dbg_pos++; timestamp(&icomm->debug.o[dbg_pos].w_input.ts_call); if (!writefd(input_writefd, dummydata, 1) || (extra_input_writefd != -1 && !writefd(extra_input_writefd, dummydata, 1))) { bf_exit(BF_EXIT_OTHER); } timestamp(&icomm->debug.o[dbg_pos].w_input.ts_ret); dai_output(true, -1, icomm->debug.o[dbg_pos].d, DEBUG_MAX_DAI_LOOPS, &icomm->debug.o[dbg_pos].dai_loops); dbg_pos++; } else { pinfo("Audio processing starts now\n"); if (trigger_callback_io) { dai_trigger_callback_io(); } timestamp(&icomm->debug.o[dbg_pos].w_input.ts_call); if (!writefd(input_writefd, dummydata, 1) || (extra_input_writefd != -1 && !writefd(extra_input_writefd, dummydata, 1))) { bf_exit(BF_EXIT_OTHER); } timestamp(&icomm->debug.o[dbg_pos].w_input.ts_ret); dbg_pos++; timestamp(&icomm->debug.o[dbg_pos].w_input.ts_call); if (!writefd(input_writefd, dummydata, 1) || (extra_input_writefd != -1 && !writefd(extra_input_writefd, dummydata, 1))) { bf_exit(BF_EXIT_OTHER); } timestamp(&icomm->debug.o[dbg_pos].w_input.ts_ret); dbg_pos++; } icomm->debug.periods = 0; while (true) { timestamp(&icomm->debug.o[dbg_pos].r_filter.ts_call); if (!readfd(filter_readfd, dummydata, bfconf->n_processes)) { bf_exit(BF_EXIT_OTHER); } timestamp(&icomm->debug.o[dbg_pos].r_filter.ts_ret); timestamp(&icomm->debug.o[dbg_pos].w_input.ts_call); if (!writefd(input_writefd, dummydata, 1) || (extra_input_writefd != -1 && !writefd(extra_input_writefd, dummydata, 1))) { bf_exit(BF_EXIT_OTHER); } timestamp(&icomm->debug.o[dbg_pos].w_input.ts_ret); /* write output */ dai_output(false, -1, icomm->debug.o[dbg_pos].d, DEBUG_MAX_DAI_LOOPS, &icomm->debug.o[dbg_pos].dai_loops); rti_and_overflow(); bufindex++; if (++dbg_pos == DEBUG_RING_BUFFER_SIZE) { dbg_pos = 0; } icomm->debug.periods++; if (bfconf->debug && icomm->debug.periods == DEBUG_RING_BUFFER_SIZE - 4) { fprintf(stderr, "Debug timestamp buffer is now full, exiting\n"); bf_exit(BF_EXIT_OTHER); } } } struct apply_subdelay_params { int subdelay; void *rest; }; static void apply_subdelay(void *realbuf, int n_samples, void *arg) { struct apply_subdelay_params *p; p = (struct apply_subdelay_params *)arg; if (p->rest == NULL) { return; } delay_subsample_update(realbuf, p->rest, p->subdelay); } static void synch_filter_processes(int filter_readfd, int filter_writefd[], int process_index) { int n; char dummydata[bfconf->n_processes - 1]; if (bfconf->n_processes > 1) { for (n = 0; n < bfconf->n_processes; n++) { if (n != process_index) { dummydata[0] = 0; if (!writefd(filter_writefd[n], dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } } if (!readfd(filter_readfd, dummydata, bfconf->n_processes - 1)) { bf_exit(BF_EXIT_OTHER); } } } static void filter_process(struct bfaccess *bfaccess, void *inbuf[2], void *outbuf[2], void *input_freqcbuf[], void *output_freqcbuf[], int filter_readfd, int filter_writefd[], int input_readfd, int cb_input_readfd, int output_writefd, int cb_output_writefd, int n_procinputs, int procinputs[], int n_procoutputs, int procoutputs[], int n_inputs, int inputs[], int n_outputs, int outputs[], int n_filters, struct bffilter filters[], int process_index, bool_t has_bl_input_devs, bool_t has_bl_output_devs, bool_t has_cb_input_devs, bool_t has_cb_output_devs) { int convbufsize = convolver_cbufsize(); int fragsize = bfconf->filter_length; int n_blocks = bfconf->n_blocks; int curblock = 0; int curbuf = 0; void *input_timecbuf[n_procinputs][2]; void **mixconvbuf_inputs[n_filters]; void **mixconvbuf_filters[n_filters]; void *cbuf[n_filters][n_blocks]; void *ocbuf[n_filters]; void *evalbuf[n_filters]; void *static_evalbuf = NULL; void *inbuf_copy = NULL; double *outscale[BF_MAXCHANNELS][n_filters]; double scales[n_filters + BF_MAXCHANNELS]; double virtscales[2][BF_MAXCHANNELS]; void *crossfadebuf[2]; void *mixbuf = NULL; void *outconvbuf[BF_MAXCHANNELS][n_filters]; int outconvbuf_n_filters[BF_MAXCHANNELS]; unsigned int blockcounter = 0; delaybuffer_t *output_db[BF_MAXCHANNELS]; delaybuffer_t *input_db[BF_MAXCHANNELS]; void *output_sd_rest[BF_MAXCHANNELS]; void *input_sd_rest[BF_MAXCHANNELS]; bool_t need_crossfadebuf = false; bool_t need_mixbuf = false; bool_t mixbuf_is_filled; int inbuf_copy_size; int n, i, j, coeff, delay, cblocks, prevcblocks, physch, virtch; struct buffer_format *bf, inbuf_copy_bf; uint8_t *memptr, *baseptr; struct bfoverflow of; uint32_t dummydata32; char dummydata[1]; int memsize, icomm_delay[2][BF_MAXCHANNELS]; struct bffilter_control icomm_fctrl[n_filters]; uint32_t icomm_ismuted[2][BF_MAXCHANNELS/32]; bool_t powersave, change_prio, first_print; int icomm_subdelay[2][BF_MAXCHANNELS]; struct apply_subdelay_params sd_params; int dbg_pos, subdelay_fb_size; int prevcoeff[n_filters]; int procblocks[n_filters]; uint32_t partial_proc[n_filters / 32 + 1]; int *mixconvbuf_filters_map[n_filters]; int outconvbuf_map[BF_MAXCHANNELS][n_filters]; bool_t input_freqcbuf_zero[bfconf->n_channels[IN]]; bool_t output_freqcbuf_zero[bfconf->n_channels[OUT]]; bool_t cbuf_zero[n_filters][n_blocks]; bool_t ocbuf_zero[n_filters]; bool_t evalbuf_zero[n_filters]; bool_t temp_buffer_zero; bool_t iszero; struct timeval period_start, period_end, tv; int32_t period_length; double clockmul; uint64_t t1, t2, t3, t4; uint64_t t[10]; uint32_t cc = 0; dummydata[0] = '\0'; dbg_pos = 0; first_print = true; change_prio = false; powersave = bfconf->powersave; if (dai_minblocksize() == 0 || dai_minblocksize() < bfconf->filter_length) { change_prio = true; } subdelay_fb_size = delay_subsample_filterblocksize(); temp_buffer_zero = false; memset(procblocks, 0, n_filters * sizeof(int)); memset(partial_proc, 0xFF, (n_filters / 32 + 1) * sizeof(uint32_t)); memset(evalbuf_zero, 0, n_filters * sizeof(bool_t)); memset(ocbuf_zero, 0, n_filters * sizeof(bool_t)); memset(cbuf_zero, 0, n_blocks * n_filters * sizeof(bool_t)); memset(output_freqcbuf_zero, 0, bfconf->n_channels[OUT] * sizeof(bool_t)); memset(input_freqcbuf_zero, 0, bfconf->n_channels[IN] * sizeof(bool_t)); memset(crossfadebuf, 0, sizeof(crossfadebuf)); memset(icomm_subdelay, 0, sizeof(icomm_subdelay)); if (!readfd(input_readfd, dummydata, 1)) { /* for init */ bf_exit(BF_EXIT_OTHER); } synch_filter_processes(filter_readfd, filter_writefd, process_index); /* allocate input delay buffers */ for (n = j = 0; n < n_procinputs; n++) { virtch = procinputs[n]; physch = bfconf->virt2phys[IN][virtch]; if (bfconf->use_subdelay[IN] && bfconf->subdelay[IN][virtch] != BF_UNDEFINED_SUBDELAY) { input_sd_rest[virtch] = emallocaligned(subdelay_fb_size * bfconf->realsize); memset(input_sd_rest[virtch], 0, subdelay_fb_size * bfconf->realsize); } else { input_sd_rest[virtch] = NULL; } if (bfconf->n_virtperphys[IN][physch] > 1) { for (i = 0; i < bfconf->n_subdevs[IN]; i++) { if (bfconf->subdevs[IN][i].channels.channel_name [bfconf->subdevs[IN][i].channels.used_channels-1] >= physch) { break; } } delay = 0; if (bfconf->use_subdelay[IN] && bfconf->subdelay[IN][virtch] == BF_UNDEFINED_SUBDELAY) { delay = bfconf->sdf_length; } input_db[virtch] = delay_allocate_buffer(fragsize, icomm->delay[IN][virtch] + delay, bfconf->maxdelay[IN][virtch] + delay, bfconf->subdevs[IN][i].channels.sf.bytes); if (bfconf->subdevs[IN][i].channels.sf.bytes > j) { j = bfconf->subdevs[IN][i].channels.sf.bytes; } } else { /* delays on channels with direct 1-1 virtual-physical mapping are taken care of in the dai module instead */ input_db[virtch] = NULL; } } inbuf_copy_size = j * fragsize; inbuf_copy_bf.sample_spacing = 1; inbuf_copy_bf.byte_offset = 0; /* allocate output delay buffers */ for (n = 0; n < n_procoutputs; n++) { virtch = procoutputs[n]; physch = bfconf->virt2phys[OUT][virtch]; if (bfconf->use_subdelay[OUT] && bfconf->subdelay[OUT][virtch] != BF_UNDEFINED_SUBDELAY) { output_sd_rest[virtch] = emallocaligned(subdelay_fb_size * bfconf->realsize); memset(output_sd_rest[virtch], 0, subdelay_fb_size * bfconf->realsize); } else { output_sd_rest[virtch] = NULL; } if (bfconf->n_virtperphys[OUT][physch] > 1) { delay = 0; if (bfconf->use_subdelay[OUT] && bfconf->subdelay[OUT][virtch] == BF_UNDEFINED_SUBDELAY) { delay = bfconf->sdf_length; } output_db[virtch] = delay_allocate_buffer(fragsize, icomm->delay[OUT][virtch] + delay, bfconf->maxdelay[OUT][virtch] + delay, bfconf->realsize); need_mixbuf = true; } else { output_db[virtch] = NULL; } } /* find out if there is a need of evaluation buffers, and how many, and if there is a need for a crossfade buffer */ for (n = i = j = 0; n < n_filters; n++) { if (filters[n].n_filters[IN] > 0) { i++; } if (filters[n].crossfade) { need_crossfadebuf = true; } } /* allocate input/output/evaluation convolve buffers */ if (inbuf_copy_size > convbufsize) { /* this should never happen, since convbufsize should be 2 * fragsize * realsize, sample sizes should never exceed 8 bytes, and realsize never be smaller than 4 bytes */ fprintf(stderr, "Unexpected buffer sizes.\n"); bf_exit(BF_EXIT_OTHER); } if (n_blocks > 1) { memsize = n_filters * n_blocks * convbufsize + n_filters * convbufsize + i * (convbufsize + convbufsize / 2) + 2 * n_procinputs * convbufsize; } else { memsize = n_filters * convbufsize + i * (convbufsize + convbufsize / 2) + 2 * n_procinputs * convbufsize; } if (i > 0) { memsize += convbufsize; if (need_crossfadebuf) { memsize += convbufsize; } } else { if (need_crossfadebuf) { memsize += 2 * convbufsize; } else if (need_mixbuf) { memsize += fragsize * bfconf->realsize; } } memptr = emallocaligned(memsize); baseptr = memptr; if (i > 0) { if (need_crossfadebuf) { crossfadebuf[0] = memptr + memsize - 2 * convbufsize; crossfadebuf[1] = memptr + memsize - convbufsize; static_evalbuf = crossfadebuf[0]; } else { static_evalbuf = memptr + memsize - convbufsize; } if (need_mixbuf) { mixbuf = static_evalbuf; } } else { if (need_crossfadebuf) { crossfadebuf[0] = memptr + memsize - 2 * convbufsize; crossfadebuf[1] = memptr + memsize - convbufsize; if (need_mixbuf) { mixbuf = crossfadebuf[0]; } } else if (need_mixbuf) { mixbuf = memptr + memsize - fragsize * bfconf->realsize; } } if (n_blocks > 1) { for (n = 0; n < n_filters; n++) { for (i = 0; i < n_blocks; i++) { cbuf[n][i] = memptr; memptr += convbufsize; } if (filters[n].n_filters[IN] > 0) { evalbuf[n] = memptr; memptr += (convbufsize + convbufsize / 2); } else { evalbuf[n] = NULL; } ocbuf[n] = memptr; memptr += convbufsize; } } else { for (n = 0; n < n_filters; n++) { cbuf[n][0] = ocbuf[n] = memptr; memptr += convbufsize; if (filters[n].n_filters[IN] > 0) { evalbuf[n] = memptr; memptr += (convbufsize + convbufsize / 2); } else { evalbuf[n] = NULL; } } } inbuf_copy = ocbuf[0]; for (n = 0; n < n_procinputs; n++, memptr += 2 * convbufsize) { input_timecbuf[n][0] = memptr; input_timecbuf[n][1] = memptr + convbufsize; } /* for each filter, find out which channel-inputs that are mixed */ for (n = 0; n < n_filters; n++) { if (filters[n].n_filters[IN] > 0) { /* allocate extra position for filter-input evaluation buffer */ mixconvbuf_inputs[n] = alloca((filters[n].n_channels[IN] + 1) * sizeof(void **)); mixconvbuf_inputs[n][filters[n].n_channels[IN]] = NULL; } else if (filters[n].n_channels[IN] == 0) { mixconvbuf_inputs[n] = NULL; continue; } else { mixconvbuf_inputs[n] = alloca(filters[n].n_channels[IN] * sizeof(void **)); } for (i = 0; i < filters[n].n_channels[IN]; i++) { mixconvbuf_inputs[n][i] = input_freqcbuf[filters[n].channels[IN][i]]; } } /* for each filter, find out which filter-inputs that are mixed */ for (n = 0; n < n_filters; n++) { prevcoeff[n] = icomm->fctrl[filters[n].intname].coeff; if (filters[n].n_filters[IN] == 0) { mixconvbuf_filters[n] = NULL; mixconvbuf_filters_map[n] = NULL; continue; } mixconvbuf_filters[n] = alloca(filters[n].n_filters[IN] * sizeof(void **)); mixconvbuf_filters_map[n] = alloca(filters[n].n_filters[IN] * sizeof(int)); for (i = 0; i < filters[n].n_filters[IN]; i++) { /* find out index of filter */ for (j = 0; j < n_filters; j++) { if (filters[n].filters[IN][i] == filters[j].intname) { break; } } mixconvbuf_filters_map[n][i] = j; mixconvbuf_filters[n][i] = ocbuf[j]; } } /* for each unique output channel, find out which filters that mixes its output to it */ memset(outconvbuf_n_filters, 0, sizeof(outconvbuf_n_filters)); for (n = 0; n < n_outputs; n++) { for (i = 0; i < n_filters; i++) { for (j = 0; j < filters[i].n_channels[OUT]; j++) { if (filters[i].channels[OUT][j] == outputs[n]) { outconvbuf_map[n][outconvbuf_n_filters[n]] = i; outconvbuf[n][outconvbuf_n_filters[n]] = ocbuf[i]; outscale[n][outconvbuf_n_filters[n]] = &icomm_fctrl[i].scale[OUT][j]; outconvbuf_n_filters[n]++; /* output exists only once per filter, we can break here */ break; } } } } /* calculate scales */ FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_channels[IO]; n++) { physch = bfconf->virt2phys[IO][n]; virtscales[IO][n] = dai_buffer_format[IO]->bf[physch].sf.scale; } } if (bfconf->debug) { fprintf(stderr, "(%d) got %d inputs, %d outputs\n", (int)getpid(), n_procinputs, n_procoutputs); for (n = 0; n < n_procinputs; n++) { fprintf(stderr, "(%d) input: %d\n", (int)getpid(), procinputs[n]); } for (n = 0; n < n_procoutputs; n++) { fprintf(stderr, "(%d) output: %d\n", (int)getpid(), procoutputs[n]); } } /* access all memory while being nobody, so we don't risk getting killed later if memory is scarce */ memset(baseptr, 0, memsize); for (n = 0; n < bfconf->n_coeffs; n++) { for (i = 0; i < bfconf->coeffs[n].n_blocks; i++) { memcpy(ocbuf[0], bfconf->coeffs_data[n][i], convbufsize); } } dummydata32 = 0; for (n = 0; n < sizeof(struct intercomm_area) / sizeof(uint32_t); n++) { dummydata32 += ((volatile uint32_t *)icomm)[n]; } memset(ocbuf[0], 0, convbufsize); memset(inbuf[0], 0, dai_buffer_format[IN]->n_bytes); memset(inbuf[1], 0, dai_buffer_format[IN]->n_bytes); memset(outbuf[0], 0, dai_buffer_format[OUT]->n_bytes); memset(outbuf[1], 0, dai_buffer_format[OUT]->n_bytes); for (n = 0; n < n_inputs; n++) { memset(input_freqcbuf[inputs[n]], 0, convbufsize); } for (n = 0; n < n_outputs; n++) { memset(output_freqcbuf[outputs[n]], 0, convbufsize); } if (bfconf->realtime_priority) { /* priority is lowered later if necessary */ bf_make_realtime(0, bfconf->realtime_maxprio, "filter"); } if (!writefd(output_writefd, dummydata, 1)) { /* for init */ bf_exit(BF_EXIT_OTHER); } /* main filter loop starts here */ memset(t, 0, sizeof(t)); while (true) { gettimeofday(&period_end, NULL); /* wait for next input buffer */ timestamp(&icomm->debug.f[dbg_pos].r_input.ts_call); if (has_bl_input_devs) { if (!readfd(input_readfd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } if (has_cb_input_devs) { if (!readfd(cb_input_readfd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } timestamp(&icomm->debug.f[dbg_pos].r_input.ts_ret); /* we only calculate period length if all filters are processing full length */ if (bit_find(partial_proc, 0, n_filters - 1) == -1) { timersub(&period_end, &period_start, &tv); period_length = tv.tv_sec * 1000000 + tv.tv_usec; icomm->period_us[process_index] = period_length; icomm->full_proc[process_index] = true; } else { icomm->full_proc[process_index] = false; } gettimeofday(&period_start, NULL); if (events.n_block_start > 0) { if (process_index == 0) { for (i = 0; i < events.n_block_start; i++) { tv = period_start; events.block_start[i](bfaccess, blockcounter, &tv); } } synch_filter_processes(filter_readfd, filter_writefd, process_index); } /* get all shared memory data we need where mutex is important */ timestamp(&icomm->debug.f[dbg_pos].mutex.ts_call); icomm_mutex(1); for (n = 0; n < n_filters; n++) { icomm_fctrl[n].coeff = icomm->fctrl[filters[n].intname].coeff; icomm_fctrl[n].delayblocks = icomm->fctrl[filters[n].intname].delayblocks; for (i = 0; i < filters[n].n_channels[IN]; i++) { icomm_fctrl[n].scale[IN][i] = icomm->fctrl[filters[n].intname].scale[IN][i]; } for (i = 0; i < filters[n].n_channels[OUT]; i++) { icomm_fctrl[n].scale[OUT][i] = icomm->fctrl[filters[n].intname].scale[OUT][i]; } for (i = 0; i < filters[n].n_filters[IN]; i++) { icomm_fctrl[n].fscale[i] = icomm->fctrl[filters[n].intname].fscale[i]; } } memcpy(icomm_ismuted, (void *)icomm->ismuted, sizeof(icomm_ismuted)); memcpy(icomm_delay, (void *)icomm->delay, sizeof(icomm_delay)); if (bfconf->use_subdelay[IN] || bfconf->use_subdelay[OUT]) { memcpy(icomm_subdelay, (void *)icomm->subdelay, sizeof(icomm_subdelay)); } icomm_mutex(0); /* change to lower priority so we can be pre-empted, but we only do so if required by the input (or output) process */ if (bfconf->realtime_priority && change_prio) { bf_make_realtime(0, bfconf->realtime_minprio, NULL); } timestamp(&icomm->debug.f[dbg_pos].mutex.ts_ret); timestamp(&t3); for (n = 0; n < n_procinputs; n++) { /* convert inputs */ timestamp(&t1); virtch = procinputs[n]; physch = bfconf->virt2phys[IN][virtch]; bf = &dai_buffer_format[IN]->bf[physch]; sd_params.subdelay = icomm_subdelay[IN][virtch]; sd_params.rest = input_sd_rest[virtch]; if (bfconf->n_virtperphys[IN][physch] == 1) { convolver_raw2cbuf(inbuf[curbuf], input_timecbuf[n][curbuf], input_timecbuf[n][!curbuf], bf, apply_subdelay, (void *)&sd_params); } else { if (!bit_isset(icomm_ismuted[IN], virtch)) { delay = icomm_delay[IN][virtch]; if (bfconf->use_subdelay[IN] && bfconf->subdelay[IN][virtch] == BF_UNDEFINED_SUBDELAY) { delay += bfconf->sdf_length; } delay_update(input_db[virtch], &((uint8_t *)inbuf[curbuf])[bf->byte_offset], bf->sf.bytes, bf->sample_spacing, delay, inbuf_copy); } else { memset(inbuf_copy, 0, fragsize * bf->sf.bytes); } inbuf_copy_bf.sf = bf->sf; convolver_raw2cbuf(inbuf_copy, input_timecbuf[n][curbuf], input_timecbuf[n][!curbuf], &inbuf_copy_bf, apply_subdelay, (void *)&sd_params); } for (i = 0; i < events.n_input_timed; i++) { events.input_timed[i](input_timecbuf[n][curbuf], procinputs[n]); } timestamp(&t2); t[0] += t2 - t1; /* transform to frequency domain */ timestamp(&t1); if (!powersave || !test_silent(input_timecbuf[n][curbuf], convbufsize, bfconf->realsize, bfconf->analog_powersave, bf->sf.scale)) { convolver_time2freq(input_timecbuf[n][curbuf], input_freqcbuf[procinputs[n]]); input_freqcbuf_zero[procinputs[n]] = false; } else if (!input_freqcbuf_zero[procinputs[n]]) { memset(input_freqcbuf[procinputs[n]], 0, convbufsize); input_freqcbuf_zero[procinputs[n]] = true; } for (i = 0; i < events.n_input_freqd; i++) { events.input_freqd[i](input_freqcbuf[procinputs[n]], procinputs[n]); } timestamp(&t2); t[1] += t2 - t1; } timestamp(&icomm->debug.f[dbg_pos].fsynch_fd.ts_call); synch_filter_processes(filter_readfd, filter_writefd, process_index); timestamp(&icomm->debug.f[dbg_pos].fsynch_fd.ts_ret); for (n = 0; n < n_filters; n++) { if (procblocks[n] < n_blocks) { procblocks[n]++; } else { bit_clr(partial_proc, n); } timestamp(&t1); coeff = icomm_fctrl[n].coeff; if (events.n_coeff_final == 1) { /* this module wants final control of the choice of coefficient */ events.coeff_final[0](filters[n].intname, &coeff); } delay = icomm_fctrl[n].delayblocks; if (delay < 0) { delay = 0; } else if (delay > n_blocks - 1) { delay = n_blocks - 1; } if (coeff < 0 || bfconf->coeffs[coeff].n_blocks > n_blocks - delay) { cblocks = n_blocks - delay; } else { cblocks = bfconf->coeffs[coeff].n_blocks; } if (prevcoeff[n] < 0 || bfconf->coeffs[prevcoeff[n]].n_blocks > n_blocks - delay) { prevcblocks = n_blocks - delay; } else { prevcblocks = bfconf->coeffs[prevcoeff[n]].n_blocks; } curblock = (int)((blockcounter + delay) % (unsigned int)(n_blocks)); /* mix and scale inputs prior to convolution */ if (filters[n].n_filters[IN] > 0) { /* mix, scale and reorder filter-inputs for evaluation in the time domain. */ iszero = true; for (i = 0; i < filters[n].n_filters[IN]; i++) { if (!ocbuf_zero[mixconvbuf_filters_map[n][i]]) { iszero = false; break; } } if (!iszero || !powersave) { convolver_mixnscale(mixconvbuf_filters[n], static_evalbuf, icomm_fctrl[n].fscale, filters[n].n_filters[IN], CONVOLVER_MIXMODE_OUTPUT); temp_buffer_zero = false; } else if (!temp_buffer_zero) { memset(static_evalbuf, 0, convbufsize); temp_buffer_zero = true; } /* evaluate convolution */ if (!temp_buffer_zero || !evalbuf_zero[n] || !powersave) { convolver_convolve_eval(static_evalbuf, evalbuf[n], static_evalbuf); evalbuf_zero[n] = false; if (temp_buffer_zero) { evalbuf_zero[n] = true; temp_buffer_zero = false; } } /* mix and scale channel-inputs and reorder prior to convolution */ iszero = temp_buffer_zero; for (i = 0; i < filters[n].n_channels[IN]; i++) { scales[i] = icomm_fctrl[n].scale[IN][i] * virtscales[IN][filters[n].channels[IN][i]]; if (!input_freqcbuf_zero[filters[n].channels[IN][i]]) { iszero = false; } } /* FIXME: unecessary scale multiply for filter-inputs */ scales[i] = 1.0; mixconvbuf_inputs[n][i] = static_evalbuf; if (!iszero || !powersave) { convolver_mixnscale(mixconvbuf_inputs[n], cbuf[n][curblock], scales, filters[n].n_channels[IN] + 1, CONVOLVER_MIXMODE_INPUT); cbuf_zero[n][curblock] = false; } else if (!cbuf_zero[n][curblock]) { memset(cbuf[n][curblock], 0, convbufsize); cbuf_zero[n][curblock] = true; } } else { iszero = true; for (i = 0; i < filters[n].n_channels[IN]; i++) { scales[i] = icomm_fctrl[n].scale[IN][i] * virtscales[IN][filters[n].channels[IN][i]]; if (!input_freqcbuf_zero[filters[n].channels[IN][i]]) { iszero = false; } } if (!iszero || !powersave) { convolver_mixnscale(mixconvbuf_inputs[n], cbuf[n][curblock], scales, filters[n].n_channels[IN], CONVOLVER_MIXMODE_INPUT); cbuf_zero[n][curblock] = false; } else if (!cbuf_zero[n][curblock]) { memset(cbuf[n][curblock], 0, convbufsize); cbuf_zero[n][curblock] = true; } } timestamp(&t2); t[2] += t2 - t1; /* convolve (or not) */ timestamp(&t1); curblock = (int)(blockcounter % (unsigned int)n_blocks); for (i = 0; i < events.n_pre_convolve; i++) { events.pre_convolve[i](cbuf[n][curblock], n); } if (coeff >= 0) { if (n_blocks == 1) { /* curblock is always zero when n_blocks == 1 */ if (!cbuf_zero[n][0] || !powersave) { if (filters[n].crossfade && prevcoeff[n] != coeff) { if (prevcoeff[n] < 0) { convolver_dirac_convolve(cbuf[n][0], crossfadebuf[0]); } else { convolver_convolve (cbuf[n][0], bfconf->coeffs_data[prevcoeff[n]][0], crossfadebuf[0]); } convolver_convolve_inplace (cbuf[n][0], bfconf->coeffs_data[coeff][0]); convolver_crossfade_inplace(cbuf[n][0], crossfadebuf[0], crossfadebuf[1]); temp_buffer_zero = false; } else { convolver_convolve_inplace (cbuf[n][0], bfconf->coeffs_data[coeff][0]); } /* cbuf points at ocbuf when n_blocks == 1 */ ocbuf_zero[n] = false; } else { ocbuf_zero[n] = true; procblocks[n] = 0; bit_set(partial_proc, n); } } else { if (!cbuf_zero[n][curblock] || !powersave) { if (filters[n].crossfade && prevcoeff[n] != coeff) { if (prevcoeff[n] < 0) { convolver_dirac_convolve(cbuf[n][curblock], crossfadebuf[0]); } else { convolver_convolve (cbuf[n][curblock], bfconf->coeffs_data[prevcoeff[n]][0], crossfadebuf[0]); } } convolver_convolve(cbuf[n][curblock], bfconf->coeffs_data[coeff][0], ocbuf[n]); ocbuf_zero[n] = false; } else if (!ocbuf_zero[n]) { memset(ocbuf[n], 0, convbufsize); ocbuf_zero[n] = true; } for (i = 1; i < cblocks && i < procblocks[n]; i++) { j = (int)((blockcounter - i) % (unsigned int)n_blocks); if (!cbuf_zero[n][j] || !powersave) { convolver_convolve_add (cbuf[n][j], bfconf->coeffs_data[coeff][i], ocbuf[n]); ocbuf_zero[n] = false; } } if (filters[n].crossfade && prevcoeff[n] != coeff && prevcoeff[n] >= 0) { for (i = 1; i < prevcblocks && i < procblocks[n]; i++) { j = (int)((blockcounter - i) % (unsigned int)n_blocks); if (!cbuf_zero[n][j] || !powersave) { convolver_convolve_add (cbuf[n][j], bfconf->coeffs_data[prevcoeff[n]][i], crossfadebuf[0]); } ocbuf_zero[n] = false; } } if (ocbuf_zero[n]) { procblocks[n] = 0; bit_set(partial_proc, n); } else if (filters[n].crossfade && prevcoeff[n] != coeff) { convolver_crossfade_inplace(ocbuf[n], crossfadebuf[0], crossfadebuf[1]); temp_buffer_zero = false; } } } else { if (n_blocks == 1) { if (!cbuf_zero[n][0] || !powersave) { if (filters[n].crossfade && prevcoeff[n] != coeff) { convolver_convolve (cbuf[n][0], bfconf->coeffs_data[prevcoeff[n]][0], crossfadebuf[0]); convolver_dirac_convolve_inplace(cbuf[n][0]); convolver_crossfade_inplace(cbuf[n][0], crossfadebuf[0], crossfadebuf[1]); temp_buffer_zero = false; } else { convolver_dirac_convolve_inplace(cbuf[n][0]); } ocbuf_zero[n] = false; } else { ocbuf_zero[n] = true; procblocks[n] = 0; bit_set(partial_proc, n); } } else { if (!cbuf_zero[n][curblock] || !powersave) { if (filters[n].crossfade && prevcoeff[n] != coeff) { convolver_convolve (cbuf[n][curblock], bfconf->coeffs_data[prevcoeff[n]][0], crossfadebuf[0]); } convolver_dirac_convolve(cbuf[n][curblock], ocbuf[n]); ocbuf_zero[n] = false; } else if (!ocbuf_zero[n]) { memset(ocbuf[n], 0, convbufsize); ocbuf_zero[n] = true; } if (filters[n].crossfade && prevcoeff[n] != coeff) { for (i = 1; i < prevcblocks && i < procblocks[n]; i++) { j = (int)((blockcounter - i) % (unsigned int)n_blocks); if (!cbuf_zero[n][j] || !powersave) { convolver_convolve_add (cbuf[n][j], bfconf->coeffs_data[prevcoeff[n]][i], crossfadebuf[0]); } ocbuf_zero[n] = false; } } if (ocbuf_zero[n]) { procblocks[n] = 0; bit_set(partial_proc, n); } else if (filters[n].crossfade && prevcoeff[n] != coeff) { convolver_crossfade_inplace(ocbuf[n], crossfadebuf[0], crossfadebuf[1]); temp_buffer_zero = false; } } } prevcoeff[n] = coeff; for (i = 0; i < events.n_post_convolve; i++) { events.post_convolve[i](cbuf[n][curblock], n); } timestamp(&t2); t[3] += t2 - t1; } timestamp(&t1); for (n = 0; n < n_outputs; n++) { iszero = true; for (i = 0; i < outconvbuf_n_filters[n]; i++) { scales[i] = *outscale[n][i] / virtscales[OUT][outputs[n]]; if (!ocbuf_zero[outconvbuf_map[n][i]]) { iszero = false; } } /* mix and scale convolve outputs prior to conversion to time domain */ if (!iszero || !powersave) { convolver_mixnscale(outconvbuf[n], output_freqcbuf[outputs[n]], scales, outconvbuf_n_filters[n], CONVOLVER_MIXMODE_OUTPUT); output_freqcbuf_zero[outputs[n]] = false; } else if (!output_freqcbuf_zero[outputs[n]]) { memset(output_freqcbuf[outputs[n]], 0, convbufsize); output_freqcbuf_zero[outputs[n]] = true; } } timestamp(&t2); t[4] += t2 - t1; timestamp(&icomm->debug.f[dbg_pos].fsynch_td.ts_call); synch_filter_processes(filter_readfd, filter_writefd, process_index); timestamp(&icomm->debug.f[dbg_pos].fsynch_td.ts_ret); mixbuf_is_filled = false; for (n = j = 0; n < n_procoutputs; n++) { /* transform back to time domain */ timestamp(&t1); virtch = procoutputs[n]; physch = bfconf->virt2phys[OUT][virtch]; for (i = 0; i < events.n_output_freqd; i++) { events.output_freqd[i](output_freqcbuf[virtch], virtch); } /* ocbuf[0] happens to be free, that's why we use it */ if (!output_freqcbuf_zero[virtch] || !powersave) { convolver_freq2time(output_freqcbuf[virtch], ocbuf[0]); ocbuf_zero[0] = false; if (n_blocks == 1) { cbuf_zero[0][0] = false; } } else if (!ocbuf_zero[0]) { memset(ocbuf[0], 0, convbufsize); ocbuf_zero[0] = true; if (n_blocks == 1) { cbuf_zero[0][0] = true; } } /* Check if there is NaN or Inf values, and abort if so. We cannot afford to check all values, but NaN/Inf tend to spread, so checking only one value usually catches the problem. */ if ((bfconf->realsize == sizeof(float) && !finite((double)((float *)ocbuf[0])[0])) || (bfconf->realsize == sizeof(double) && !finite(((double *)ocbuf[0])[0]))) { fprintf(stderr, "NaN or Inf values in the system! " "Invalid input? Aborting.\n"); bf_exit(BF_EXIT_OTHER); } timestamp(&t2); t[5] += t2 - t1; /* write to output buffer */ timestamp(&t1); for (i = 0; i < events.n_output_timed; i++) { events.output_timed[i](ocbuf[0], virtch); } if (output_sd_rest[virtch] != NULL) { delay_subsample_update(ocbuf[0], output_sd_rest[virtch], icomm_subdelay[OUT][virtch]); } if (bfconf->n_virtperphys[OUT][physch] == 1) { /* only one virtual channel allocated to this physical one, so we write to it directly */ of = icomm->overflow[virtch]; convolver_cbuf2raw(ocbuf[0], outbuf[curbuf], &dai_buffer_format[OUT]->bf[physch], bfconf->dither_state[physch] != NULL, bfconf->dither_state[physch], &of); icomm->overflow[virtch] = of; } else { /* Mute, delay and mix. This is done in the dai module normally, where we get lower I/O-delay on mute and delay operations. However, when mixing to a single physical channel we cannot do it there, so we must do it here instead. */ delay = icomm_delay[OUT][virtch]; if (bfconf->use_subdelay[OUT] && bfconf->subdelay[OUT][virtch] == BF_UNDEFINED_SUBDELAY) { delay += bfconf->sdf_length; } delay_update(output_db[virtch], ocbuf[0], bfconf->realsize, 1, delay, NULL); if (!bit_isset(icomm_ismuted[OUT], virtch)) { if (!mixbuf_is_filled) { memcpy(mixbuf, ocbuf[0], fragsize * bfconf->realsize); } else { if (bfconf->realsize == 4) { for (i = 0; i < fragsize; i += 4) { ((float *)mixbuf)[i+0] += ((float *)ocbuf[0])[i+0]; ((float *)mixbuf)[i+1] += ((float *)ocbuf[0])[i+1]; ((float *)mixbuf)[i+2] += ((float *)ocbuf[0])[i+2]; ((float *)mixbuf)[i+3] += ((float *)ocbuf[0])[i+3]; } } else { for (i = 0; i < fragsize; i += 4) { ((double *)mixbuf)[i+0] += ((double *)ocbuf[0])[i+0]; ((double *)mixbuf)[i+1] += ((double *)ocbuf[0])[i+1]; ((double *)mixbuf)[i+2] += ((double *)ocbuf[0])[i+2]; ((double *)mixbuf)[i+3] += ((double *)ocbuf[0])[i+3]; } } } temp_buffer_zero = false; mixbuf_is_filled = true; } if (++j == bfconf->n_virtperphys[OUT][physch]) { if (!mixbuf_is_filled) { /* we cannot set temp_buffer_zero here since fragsize * bfconf->realsize is smaller than convbufsize */ memset(mixbuf, 0, fragsize * bfconf->realsize); } j = 0; mixbuf_is_filled = false; /* overflow structs are same for all virtual channels assigned to a single physical one, so we copy them */ of = icomm->overflow[virtch]; convolver_cbuf2raw(mixbuf, outbuf[curbuf], &dai_buffer_format[OUT]->bf[physch], bfconf->dither_state[physch] != NULL, bfconf->dither_state[physch], &of); for (i = 0; i < bfconf->n_virtperphys[OUT][physch]; i++) { icomm->overflow[bfconf->phys2virt[OUT][physch][i]] = of; } } } timestamp(&t2); t[6] += t2 - t1; } timestamp(&t4); t[7] += t4 - t3; /* signal the output process */ timestamp(&icomm->debug.f[dbg_pos].w_output.ts_call); if (bfconf->realtime_priority && change_prio) { bf_make_realtime(0, bfconf->realtime_maxprio, NULL); } if (has_bl_output_devs) { if (!writefd(output_writefd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } if (has_cb_output_devs) { if (!writefd(cb_output_writefd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } } if (bfconf->realtime_priority) { sched_yield(); } timestamp(&icomm->debug.f[dbg_pos].w_output.ts_ret); /* swap convolve buffers */ curbuf = !curbuf; /* advance input block */ blockcounter++; if (bfconf->debug || bfconf->benchmark) { if (++cc % 10 == 0) { if (process_index == 0 && first_print) { first_print = false; fprintf(stderr, "\n\ pid ......... process id of filter process\n\ raw2real .... sample format conversion from input to internal format\n\ time2freq ... forward fast fourier transform of input buffers\n\ mixscale1 ... mixing and scaling (volume) of filter input buffers\n\ convolve .... convolution of filter buffers (and crossfade if used)\n\ mixscale2 ... mixing and scaling of filter output buffers\n\ freq2time ... inverse fast fouirer transform of input buffers\n\ real2raw .... sample format conversion from internal format to output\n\ total ....... total time required per period\n\ periods ..... number of periods processed so far\n\ rti ......... current realtime index\n\ \n\ all times are in milliseconds, mean value over 10 periods\n\ \n\ pid | raw2real | time2freq | mixscale1 | convolve | mixscale2 | \ freq2time | real2raw | total | periods | rti \n\ --------------------------------------------------------------------\ ----------------------------------------------------\n"); } clockmul = 1.0 / (bfconf->cpu_mhz * 1000.0); for (n = 0; n < 8; n++) { t[n] /= 10; } fprintf(stderr, "%5d | %9.3f | %9.3f | %9.3f | %9.3f |" " %9.3f | %9.3f | %9.3f | %9.3f | %7lu | %.3f\n", (int)getpid(), (double)t[0] * clockmul, (double)t[1] * clockmul, (double)t[2] * clockmul, (double)t[3] * clockmul, (double)t[4] * clockmul, (double)t[5] * clockmul, (double)t[6] * clockmul, (double)t[7] * clockmul, (unsigned long int)cc, icomm->realtime_index); memset(t, 0, sizeof(t)); } } if (++dbg_pos == DEBUG_RING_BUFFER_SIZE) { dbg_pos = 0; } } } void bf_callback_ready(int io) { static bool_t isinit = false; char data[bfconf->n_processes]; if (!isinit) { if (n_blocking_devs[IN] > 0) { /* trigger blocking I/O input, this is done in the first call which is for input if there is callback I/O input */ if (!writefd(cb_output_2_bl_input[1], data, 1)) { bf_exit(BF_EXIT_OTHER); } } } isinit = true; memset(data, 0, bfconf->n_processes); if (io == IN) { if (n_blocking_devs[OUT] > 0) { /* wait for blocking I/O output */ if (!readfd(bl_output_2_cb_input[0], data, 1)) { bf_exit(BF_EXIT_OTHER); } } /* trigger filter process(es). Other end will read for each dev */ if (!writefd(cb_input_2_filter[1], data, bfconf->n_processes)) { bf_exit(BF_EXIT_OTHER); } } else { /* wait for filter process(es) */ if (!readfd(filter_2_cb_output[0], data, bfconf->n_processes)) { bf_exit(BF_EXIT_OTHER); } if (n_blocking_devs[IN] > 0) { /* trigger input */ if (!writefd(cb_output_2_bl_input[1], data, 1)) { bf_exit(BF_EXIT_OTHER); } } if (n_blocking_devs[OUT] == 0) { rti_and_overflow(); } } } void bfrun(void) { int synch_pipe[2]; int bl_input_2_filter[2]; int filter_2_bl_output[2], filter2filter_pipes[bfconf->n_processes][2]; int filter_writefd[bfconf->n_processes]; char dummydata[bfconf->n_processes]; void *buffers[2][2]; void *input_freqcbuf[bfconf->n_channels[IN]], *input_freqcbuf_base; void *output_freqcbuf[bfconf->n_channels[OUT]], *output_freqcbuf_base; int nc[2], cpos[2], channels[2][BF_MAXCHANNELS]; int n, i, j, cbufsize, physch; bool_t checkdrift, trigger; struct bfaccess bfaccess; pid_t pid; /* FIXME: check so that unused pipe file descriptors are closed */ memset(dummydata, 0, bfconf->n_processes); n_callback_devs[IN] = n_callback_devs[OUT] = 0; n_blocking_devs[IN] = n_blocking_devs[OUT] = 0; FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_subdevs[IO]; n++) { if (bfconf->iomods[bfconf->subdevs[IO][n].module].iscallback) { n_callback_devs[IO]++; } else { n_blocking_devs[IO]++; } } } /* allocate shared memory for I/O buffers and interprocess communication */ cbufsize = convolver_cbufsize(); if ((input_freqcbuf_base = shmalloc(bfconf->n_channels[IN] * cbufsize)) == NULL || (output_freqcbuf_base = shmalloc(bfconf->n_channels[OUT] * cbufsize)) == NULL || (icomm = shmalloc(sizeof(struct intercomm_area))) == NULL) { fprintf(stderr, "Failed to allocate shared memory: %s.\n", strerror(errno)); bf_exit(BF_EXIT_NO_MEMORY); return; } for (n = 0; n < bfconf->n_channels[IN]; n++) { input_freqcbuf[n] = input_freqcbuf_base; input_freqcbuf_base = (uint8_t *)input_freqcbuf_base + cbufsize; } for (n = 0; n < bfconf->n_channels[OUT]; n++) { output_freqcbuf[n] = output_freqcbuf_base; output_freqcbuf_base = (uint8_t *)output_freqcbuf_base + cbufsize; } /* initialise process intercomm area */ for (n = 0; n < sizeof(struct intercomm_area); n++) { ((volatile uint8_t *)icomm)[n] = 0; } for (n = 0; n < sizeof(icomm->debug); n++) { ((volatile uint8_t *)&icomm->debug)[n] = ~0; } for (n = 0; n < bfconf->n_filters; n++) { icomm->fctrl[n] = bfconf->initfctrl[n]; } icomm->pids[0] = getpid(); icomm->n_pids = 1; icomm->exit_status = BF_EXIT_OK; FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_channels[IO]; n++) { icomm->delay[IO][n] = bfconf->delay[IO][n]; icomm->subdelay[IO][n] = bfconf->subdelay[IO][n]; if (bfconf->mute[IO][n]) { bit_set_volatile(icomm->ismuted[IO], n); } else { bit_clr_volatile(icomm->ismuted[IO], n); } } } /* install signal handlers */ if (signal(SIGINT, sighandler) == SIG_ERR || signal(SIGTERM, sighandler) == SIG_ERR) { fprintf(stderr, "Failed to install signal handlers.\n"); bf_exit(BF_EXIT_OTHER); return; } /* create synchronisation pipes */ for (n = 0; n < bfconf->n_processes; n++) { if (pipe(filter2filter_pipes[n]) == -1) { fprintf(stderr, "Failed to create pipe: %s.\n", strerror(errno)); bf_exit(BF_EXIT_OTHER); return; } } if (pipe(bl_output_2_bl_input) == -1 || pipe(bl_output_2_cb_input) == -1 || pipe(cb_output_2_bl_input) == -1 || pipe(bl_input_2_filter) == -1 || pipe(filter_2_bl_output) == -1 || pipe(cb_input_2_filter) == -1 || pipe(filter_2_cb_output) == -1 || pipe(mutex_pipe) == -1) { fprintf(stderr, "Failed to create pipe: %s.\n", strerror(errno)); bf_exit(BF_EXIT_OTHER); return; } if (!writefd(mutex_pipe[1], dummydata, 1)) { bf_exit(BF_EXIT_OTHER); return; } /* init digital audio interfaces for input and output */ if (!dai_init(bfconf->filter_length, bfconf->sampling_rate, bfconf->n_subdevs, bfconf->subdevs, buffers)) { fprintf(stderr, "Failed to initialise digital audio interfaces.\n"); bf_exit(BF_EXIT_OTHER); return; } if (dai_buffer_format[IN]->n_samples != bfconf->filter_length || dai_buffer_format[OUT]->n_samples != bfconf->filter_length) { /* a bug if it happens */ fprintf(stderr, "Fragment size mismatch.\n"); bf_exit(BF_EXIT_OTHER); return; } /* init overflow structure */ reset_overflow = emalloc(sizeof(struct bfoverflow) * bfconf->n_channels[OUT]); memset(reset_overflow, 0, sizeof(struct bfoverflow) * bfconf->n_channels[OUT]); for (n = 0; n < bfconf->n_channels[OUT]; n++) { physch = bfconf->virt2phys[OUT][n]; if (dai_buffer_format[OUT]->bf[physch].sf.isfloat) { reset_overflow[n].max = 1.0; } else { reset_overflow[n].max = (double) ((uint64_t)1 << ((dai_buffer_format[OUT]->bf[physch]. sf.sbytes << 3) - 1)) - 1; } icomm->overflow[n] = reset_overflow[n]; } /* initialise event listener structure */ init_events(); timestamp(&icomm->debug.ts_start); /* initialise bfaccess structure */ memset(&bfaccess, 0, sizeof(bfaccess)); bfaccess.fctrl = icomm->fctrl; bfaccess.overflow = icomm->overflow; bfaccess.realsize = bfconf->realsize; bfaccess.coeffs_data = bfconf->coeffs_data; bfaccess.control_mutex = icomm_mutex; bfaccess.reset_peak = bf_reset_peak; bfaccess.exit = bf_exit; bfaccess.toggle_mute = toggle_mute; bfaccess.ismuted = ismuted; bfaccess.set_delay = set_delay; bfaccess.get_delay = get_delay; bfaccess.realtime_index = bf_realtime_index; bfaccess.bfio_names = bfio_names; bfaccess.bfio_range = bfio_range; bfaccess.bfio_command = dai_subdev_command; bfaccess.bflogic_names = bflogic_names; bfaccess.bflogic_command = bflogic_command; bfaccess.convolver_coeffs2cbuf = convolver_runtime_coeffs2cbuf; bfaccess.convolver_fftplan = convolver_fftplan; bfaccess.set_subdelay = set_subdelay; bfaccess.get_subdelay = get_subdelay; /* create filter processes */ cpos[IN] = cpos[OUT] = 0; for (n = 0; n < bfconf->n_processes; n++) { /* calculate how many (and which) inputs/outputs the process should do FFTs for */ FOR_IN_AND_OUT { nc[IO] = bfconf->n_channels[IO] / bfconf->n_processes; j = 0; while ((j < nc[IO] || n == bfconf->n_processes - 1) && cpos[IO] < bfconf->n_physical_channels[IO]) { for (i = 0; i < bfconf->n_virtperphys[IO][cpos[IO]]; i++, j++) { channels[IO][j] = bfconf->phys2virt[IO][cpos[IO]][i]; } cpos[IO]++; } nc[IO] = j; } switch (pid = fork()) { case 0: close(bl_output_2_bl_input[0]); close(bl_output_2_bl_input[1]); close(bl_input_2_filter[1]); close(filter_2_bl_output[0]); close(cb_input_2_filter[1]); close(filter_2_cb_output[0]); for (i = 0; i < bfconf->n_processes; i++) { if (i == n) { filter_writefd[i] = -1; close(filter2filter_pipes[i][1]); } else { close(filter2filter_pipes[i][0]); filter_writefd[i] = filter2filter_pipes[i][1]; } } filter_process(&bfaccess, buffers[IN], buffers[OUT], input_freqcbuf, output_freqcbuf, filter2filter_pipes[n][0], filter_writefd, bl_input_2_filter[0], cb_input_2_filter[0], filter_2_bl_output[1], filter_2_cb_output[1], nc[IN], channels[IN], nc[OUT], channels[OUT], bfconf->fproc[n].n_unique_channels[IN], bfconf->fproc[n].unique_channels[IN], bfconf->fproc[n].n_unique_channels[OUT], bfconf->fproc[n].unique_channels[OUT], bfconf->fproc[n].n_filters, bfconf->fproc[n].filters, n, !!n_blocking_devs[IN], !!n_blocking_devs[OUT], !!n_callback_devs[IN], !!n_callback_devs[OUT]); /* never reached */ return; case -1: fprintf(stderr, "Fork failed: %s.\n", strerror(errno)); bf_exit(BF_EXIT_OTHER); return; default: icomm->pids[icomm->n_pids] = pid; icomm->n_pids += 1; break; } } close(bl_input_2_filter[0]); close(filter_2_bl_output[1]); close(cb_input_2_filter[0]); close(cb_input_2_filter[1]); close(filter_2_cb_output[0]); close(filter_2_cb_output[1]); for (n = 0; n < bfconf->n_processes; n++) { close(filter2filter_pipes[n][0]); close(filter2filter_pipes[n][1]); } for (n = 0; n < bfconf->n_logicmods; n++) { if (bfconf->logicmods[n].fork_mode != BF_FORK_DONT_FORK) { if (pipe(synch_pipe) == -1) { bf_exit(BF_EXIT_OTHER); return; } switch (pid = fork()) { case 0: if (bfconf->realtime_priority) { switch (bfconf->logicmods[n].fork_mode) { case BF_FORK_PRIO_MAX: bf_make_realtime(0, bfconf->realtime_usermaxprio, bfconf->logicnames[n]); break; case BF_FORK_PRIO_FILTER: bf_make_realtime(0, bfconf->realtime_minprio, bfconf->logicnames[n]); break; case BF_FORK_PRIO_OTHER: /* fall through */ default: break; } } close(synch_pipe[0]); close(bl_output_2_bl_input[0]); close(bl_output_2_bl_input[1]); close(filter_2_bl_output[0]); close(bl_input_2_filter[1]); bfconf->logicmods[n].init(&bfaccess, bfconf->sampling_rate, bfconf->filter_length, bfconf->n_blocks, bfconf->n_coeffs, bfconf->coeffs, bfconf->n_channels, (const struct bfchannel **) bfconf->channels, bfconf->n_filters, bfconf->filters, bfconf->logicmods[n].event_pipe[0], synch_pipe[1]); fprintf(stderr, "Forking logic module \"%s\": init function " "returned.\n", bfconf->logicnames[n]); bf_exit(BF_EXIT_OTHER); return; case -1: fprintf(stderr, "Fork failed: %s.\n", strerror(errno)); bf_exit(BF_EXIT_OTHER); return; default: icomm->pids[icomm->n_pids] = pid; icomm->n_pids += 1; close(synch_pipe[1]); if (!readfd(synch_pipe[0], dummydata, 1)) { bf_exit(BF_EXIT_OTHER); return; } close(synch_pipe[0]); } } else { if (bfconf->logicmods[n].init(&bfaccess, bfconf->sampling_rate, bfconf->filter_length, bfconf->n_blocks, bfconf->n_coeffs, bfconf->coeffs, bfconf->n_channels, (const struct bfchannel **) bfconf->channels, bfconf->n_filters, bfconf->filters, bfconf->logicmods[n].event_pipe[0], -1) == -1) { fprintf(stderr, "Failed to init logic module \"%s\".\n", bfconf->logicnames[n]); bf_exit(BF_EXIT_OTHER); return; } } } if (!bfconf->blocking_io) { /* no blocking I/O: finish startup, start callback I/O and exit */ if (!writefd(bl_input_2_filter[1], dummydata, bfconf->n_processes) || !readfd(filter_2_bl_output[0], dummydata, bfconf->n_processes)) { fprintf(stderr, "Error: ran probably out of memory, aborting.\n"); bf_exit(BF_EXIT_NO_MEMORY); return; } pinfo("Audio processing starts now\n"); dai_trigger_callback_io(); for (n = 0; n < icomm->n_pids; n++) { if (icomm->pids[n] == getpid()) { icomm->pids[n] = 0; break; } } exit(EXIT_SUCCESS); } if (n_blocking_devs[OUT] > 0) { /* create output process (if necessary) */ pid = 0; if (n_blocking_devs[IN] > 0) { if (pipe(synch_pipe) == -1) { bf_exit(BF_EXIT_OTHER); return; } if ((pid = fork()) == -1) { fprintf(stderr, "Fork failed: %s.\n", strerror(errno)); bf_exit(BF_EXIT_OTHER); return; } } if (pid == 0) { if (n_blocking_devs[IN] == 0) { if (!writefd(bl_input_2_filter[1], dummydata, bfconf->n_processes)) { fprintf(stderr, "Error: ran probably out of memory, " "aborting.\n"); bf_exit(BF_EXIT_NO_MEMORY); return; } } if ((n_blocking_devs[IN] == 0 && !writefd(bl_input_2_filter[1], dummydata, bfconf->n_processes)) || !readfd(filter_2_bl_output[0], dummydata, bfconf->n_processes)) { fprintf(stderr, "Error: ran probably out of memory, " "aborting.\n"); bf_exit(BF_EXIT_NO_MEMORY); return; } close(bl_input_2_filter[0]); close(bl_input_2_filter[1]); close(bl_output_2_bl_input[0]); close(synch_pipe[1]); checkdrift = true; FOR_IN_AND_OUT { for (n = 0; n < bfconf->n_subdevs[IO]; n++) { if (bfconf->subdevs[IO][n].uses_clock) { break; } } if (n == bfconf->n_subdevs[IO]) { checkdrift = false; } } if (n_callback_devs[IN] > 0 && n_blocking_devs[IN] > 0) { n = bl_output_2_bl_input[1]; i = bl_output_2_cb_input[1]; } else if (n_callback_devs[IN] > 0) { n = bl_output_2_cb_input[1]; i = -1; } else { n = bl_output_2_bl_input[1]; i = -1; } if (n_blocking_devs[IN] > 0) { j = synch_pipe[0]; trigger = false; } else { close(synch_pipe[0]); j = -1; trigger = true; } output_process(filter_2_bl_output[0], j, n, i, trigger, checkdrift); /* never reached */ return; } else { icomm->pids[icomm->n_pids] = pid; icomm->n_pids += 1; } } /* start the input process (this code is reached only if necessary) */ if (!writefd(bl_input_2_filter[1], dummydata, bfconf->n_processes) || (n_blocking_devs[OUT] == 0 && !readfd(filter_2_bl_output[0], dummydata, bfconf->n_processes))) { fprintf(stderr, "Error: ran probably out of memory, aborting.\n"); bf_exit(BF_EXIT_NO_MEMORY); return; } close(filter_2_bl_output[0]); close(filter_2_bl_output[1]); close(bl_output_2_bl_input[1]); close(synch_pipe[0]); if (n_callback_devs[OUT] > 0 && n_blocking_devs[OUT] > 0) { n = bl_output_2_bl_input[0]; i = cb_output_2_bl_input[0]; } else if (n_callback_devs[OUT] > 0) { n = cb_output_2_bl_input[0]; i = -1; } else { n = bl_output_2_bl_input[0]; i = -1; } if (n_blocking_devs[OUT] > 0) { j = synch_pipe[1]; } else { close(synch_pipe[1]); j = -1; } input_process(buffers[IN], bl_input_2_filter[1], n, i, j); /* never reached */ } double bf_realtime_index(void) { return icomm->realtime_index; } void bf_reset_peak(void) { int n; icomm->doreset_overflow = true; for (n = 0; n < bfconf->n_channels[OUT]; n++) { icomm->overflow[n] = reset_overflow[n]; } } int bflogic_command(int modindex, const char params[], char **message) { static char *msgstr = NULL; int ans; efree(msgstr); msgstr = NULL; if (modindex < 0 || modindex >= bfconf->n_logicmods) { if (message != NULL) { msgstr = estrdup("Invalid module index"); *message = msgstr; } return -1; } if (params == NULL) { if (message != NULL) { msgstr = estrdup("Missing parameters"); *message = msgstr; } return -1; } if (bfconf->logicmods[modindex].command == NULL) { if (message != NULL) { msgstr = estrdup("Module does not support any commands"); *message = msgstr; } return -1; } ans = bfconf->logicmods[modindex].command(params); msgstr = estrdup(bfconf->logicmods[modindex].message()); *message = msgstr; return ans; } const char ** bflogic_names(int *n_names) { if (n_names != NULL) { *n_names = bfconf->n_logicmods; } return (const char **)bfconf->logicnames; } const char ** bfio_names(int io, int *n_names) { static char **names[2]; static bool_t init = false; int n; if (io != IN && io != OUT) { return NULL; } if (!init) { init = true; FOR_IN_AND_OUT { names[IO] = emalloc(bfconf->n_subdevs[IO] * sizeof(char *)); for (n = 0; n < bfconf->n_subdevs[IO]; n++) { names[IO][n] = bfconf->ionames[bfconf->subdevs[IO][n].module]; } } } if (n_names != NULL) { *n_names = bfconf->n_subdevs[io]; } return (const char **)names[io]; } void bfio_range(int io, int modindex, int range[2]) { if (range != NULL) { range[0] = 0; range[1] = 0; } if ((io != IN && io != OUT) || modindex < 0 || modindex >= bfconf->n_subdevs[io] || range == NULL) { return; } range[0] = bfconf->subdevs[io][modindex].channels.channel_name[0]; range[1] = bfconf->subdevs[io][modindex].channels.channel_name [bfconf->subdevs[io][modindex].channels.open_channels - 1]; } void bf_register_process(pid_t pid) { icomm->pids[icomm->n_pids] = pid; icomm->n_pids += 1; } void bf_make_realtime(pid_t pid, int priority, const char name[]) { struct sched_param schp; if (icomm->ignore_rtprio) { return; } memset(&schp, 0, sizeof(schp)); schp.sched_priority = priority; if (sched_setscheduler(pid, SCHED_FIFO, &schp) != 0) { if (errno == EPERM) { pinfo("Warning: not allowed to set realtime priority. Will run " "with default priority\n instead, which is less " "reliable (underflow may occur).\n"); icomm->ignore_rtprio = true; return; } else { if (name != NULL) { fprintf(stderr, "Could not set realtime priority for %s " "process: %s.\n", name, strerror(errno)); } else { fprintf(stderr, "Could not set realtime priority: %s.\n", strerror(errno)); } bf_exit(BF_EXIT_OTHER); } } if (bfconf->lock_memory) { #ifdef __OS_FREEBSD__ pinfo("Warning: lock_memory not supported on this platform.\n"); #else if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) { if (name != NULL) { fprintf(stderr, "Could not lock memory for %s process: %s.\n", name, strerror(errno)); } else { fprintf(stderr, "Could not lock memory: %s.\n", strerror(errno)); } bf_exit(BF_EXIT_OTHER); } #endif } if (name != NULL) { pinfo("Realtime priority %d set for %s process (pid %d)\n", priority, name, (int)getpid()); } } void bf_exit(int status) { int n, self_pos = -1; pid_t self, other; self = getpid(); if (icomm != NULL) { icomm->exit_status = status; for (n = 0; n < icomm->n_pids; n++) { if (icomm->pids[n] == self) { icomm->pids[n] = 0; self_pos = n; } } for (n = 0; n < icomm->n_pids; n++) { if ((other = icomm->pids[n]) != 0) { kill(other, SIGTERM); } } } dai_die(); if (bfconf != NULL && bfconf->debug && self_pos == 0) { print_debug(); } if (icomm == NULL) { exit(status); } exit(icomm->exit_status); } brutefir-1.0o/bfrun.h0000644000175000017500000000175212752354353014170 0ustar andersanders/* * (c) Copyright 2001, 2003 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _BFRUN_H_ #define _BFRUN_H_ #include #include #include #include "defs.h" #include "bfmod.h" #include "dither.h" #include "convolver.h" struct filter_process { int n_unique_channels[2]; int *unique_channels[2]; int n_filters; struct bffilter *filters; }; void bfrun(void); void bf_callback_ready(int io); void bf_reset_peak(void); void bf_register_process(pid_t pid); void bf_exit(int status); double bf_realtime_index(void); void bf_make_realtime(pid_t pid, int priority, const char name[]); int bflogic_command(int modindex, const char params[], char **message); const char ** bflogic_names(int *n_names); const char ** bfio_names(int io, int *n_names); void bfio_range(int io, int modindex, int range[2]); #endif brutefir-1.0o/bit.h0000644000175000017500000000575612752354353013642 0ustar andersanders/* * (c) Copyright 2001, 2004 - 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _BIT_H_ #define _BIT_H_ #include #include "defs.h" static inline bool_t bit_isset(const uint32_t bitset[], int pos) { int n = pos >> 5; return bitset[n] & 1 << (pos - (n << 5)); } static inline void bit_set(uint32_t bitset[], int pos) { int n = pos >> 5; bitset[n] = bitset[n] | 1 << (pos - (n << 5)); } static inline void bit_clr(uint32_t bitset[], int pos) { int n = pos >> 5; bitset[n] = bitset[n] & ~(1 << (pos - (n << 5))); } static inline bool_t bit_isset_volatile(volatile const uint32_t bitset[], int pos) { int n = pos >> 5; return bitset[n] & 1 << (pos - (n << 5)); } static inline void bit_set_volatile(volatile uint32_t bitset[], int pos) { int n = pos >> 5; bitset[n] = bitset[n] | 1 << (pos - (n << 5)); } static inline void bit_clr_volatile(volatile uint32_t bitset[], int pos) { int n = pos >> 5; bitset[n] = bitset[n] & ~(1 << (pos - (n << 5))); } #if defined(__ARCH_IA32__) static inline int bit_bsf(uint32_t n) { int r; __asm__ volatile ("bsfl %1,%0\n\t" : "=r" (r) : "g" (n)); return r; } #else static inline int bit_bsf(uint32_t n) { static const int tab[256] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; if ((n & 0x0000FFFF) != 0) { if ((n & 0x000000FF) != 0) { return tab[n & 0x000000FF]; } else { return 8 + tab[(n & 0x0000FF00) >> 8]; } } else { if ((n & 0x00FF0000) != 0) { return 16 + tab[(n & 0x00FF0000) >> 16]; } else { return 24 + tab[(n & 0xFF000000) >> 24]; } } } #endif static inline int bit_find(const uint32_t bitset[], int start, int end) { uint32_t b; int n; if (end < start) { return -1; } if ((b = bitset[start >> 5] >> (start & 0x1F)) != 0) { if ((n = bit_bsf(b) + start) > end) { return -1; } return n; } for (n = (start >> 5) + 1; n <= end >> 5; n++) { if (bitset[n] != 0) { if ((n = bit_bsf(bitset[n]) + (n << 5)) > end) { return -1; } return n; } } return -1; } #endif brutefir-1.0o/brutefir.c0000644000175000017500000000414712752354353014672 0ustar andersanders/* * (c) Copyright 2001 - 2006, 2009, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include #include #include "emalloc.h" #include "bfconf.h" #include "pinfo.h" #include "bfmod.h" #define PRESENTATION_STRING \ "\n\ BruteFIR v1.0m (November 2013) \ (c) Anders Torger\n\ \n" #define USAGE_STRING \ "Usage: %s [-quiet] [-nodefault] [-daemon] [configuration file]\n" int main(int argc, char *argv[]) { char *config_filename = NULL; bool_t quiet = false; bool_t nodefault = false; bool_t run_as_daemon = false; int n; for (n = 1; n < argc; n++) { if (strcmp(argv[n], "-quiet") == 0) { quiet = true; } else if (strcmp(argv[n], "-nodefault") == 0) { nodefault = true; } else if (strcmp(argv[n], "-daemon") == 0) { run_as_daemon = true; } else { if (config_filename != NULL) { break; } config_filename = argv[n]; } } if (n != argc) { fprintf(stderr, PRESENTATION_STRING); fprintf(stderr, USAGE_STRING, argv[0]); return BF_EXIT_INVALID_CONFIG; } if (!quiet) { fprintf(stderr, PRESENTATION_STRING); } emalloc_set_exit_function(bf_exit, BF_EXIT_NO_MEMORY); bfconf_init(config_filename, quiet, nodefault); if (run_as_daemon) { switch (fork()) { case 0: break; case -1: fprintf(stderr, "fork failed: %s", strerror(errno)); exit(EXIT_FAILURE); default: exit(EXIT_SUCCESS); } if (setsid() == -1) { fprintf(stderr, "setsid failed: %s", strerror(errno)); exit(EXIT_FAILURE); } if (chdir("/") == -1) { fprintf(stderr, "chdir failed: %s", strerror(errno)); exit(EXIT_FAILURE); } umask(0); } /* start! */ bfrun(); fprintf(stderr, "Could not start filtering.\n"); bf_exit(BF_EXIT_OTHER); return BF_EXIT_OTHER; } brutefir-1.0o/brutefir.html0000644000175000017500000033326312752354353015420 0ustar andersanders BruteFIR

BruteFIR

last updated 2016-08-09

Table of contents

News

2016-08-09
Maintenance release 1.0o. Second this day, was a bit trigger happy on the first. Put in some minor bugfixes received from the Debian package maintainer.

2016-08-09
Maintenance release 1.0n, no functional change.

2013-11-29
There was still a typo in the last uploaded 1.0m affecting SSE2. Uploaded fix.

2013-11-28
There was a typo in the last uploaded 1.0m release causing the S24_LE/S24_3LE formats to break for input. So if you downloaded 1.0m yesterday please do it again.

2013-11-27
BruteFIR v1.0m. Fixed an SSE2 bug introduced in 1.0l. Added 'safety_limit' feature which can be used to protect your expensive speakers (and sensitive ears). Also fixed a rare race condition bug and further synchronized sample formats with ALSA, so now S24_4LE means low 24 bits of 32 bit word. Thus if you used S24_4LE before you should use S32_LE now to get the old behavior.

2013-10-06
BruteFIR v1.0l. Refreshed code to compile well on x86-64, dropped 3Dnow support and replaced the hand-coded SSE with SSE C code, and refreshed JACK and ALSA I/O modules to catch up with changes in the APIs. Also fixed a filter indexing bug in the cffa CLI command.

2009-03-31
BruteFIR v1.0k. Refreshed JACK and ALSA I/O modules to catch up with changes in the APIs.

2009-03-05
BruteFIR v1.0j. Fixed a memory leak in the CLI.

As you may have noted, I do not any longer actively develop BruteFIR further. From my point of view the software is "complete". I have had plans to start a next generation BruteFIR from scratch with a more modern design (using threads instead of forked processes etc), but priorities in life change, and I do not any longer have much time to write code so it is not likely to happen.

However, I'm happy to see that there are many BruteFIR users out there. Do continue to report bugs as my intention is to keep the code working and fix any bugs that arise.

2006-10-08
BruteFIR v1.0i. Minor fixes in CLI. Sub-sample delay now works also with negative delays.

There's also some interesting patent news, actually this is old news, but I did not know about it until now - the patent EP0649578 has been revoked after an opposition. It was argued that from the existing prior art (of which most is referenced here), the non-uniform partitioned part of the patent lacks inventive step. Additionally, there is a testimony that claims that the "invention" was actually exposed in advance - an academic person at a Danish university explained the idea to the people that then went home and filed the patent which has plagued the industry and open-source world for so long. A theft and lockdown of ideas which we so often before have seen in the world of patents. Anyway, based on this opposition, the EPO has revoked the patent.

For BruteFIR this does not mean anything since it employs uniform partitioned convolution. However, the non-uniform partitioned convolution algorithm is probably free to use in open-source software. There are still patents on this in other countries (such as the US), but with the corresponding patent revoked in Europe, they will be hard to defend. Note that I'm not a patent lawyer, so if you really are going to implement non-uniform partitioned convolution I recommend to consult a professional first, because there is no 100% clear prior art as in the uniform partitioned convolution case.

In the future, there might be a non-uniform version of BruteFIR, but not likely in the near future since it will require new design from the ground and up. The convolution principle is the same, but implementation is much different with non-uniform partitions, since you have to perform different size FFTs in parallel. The simplest idea would be to simply run the same convolution engine in several "layers" with different partition sizes and mixing together the result in the end. This way the implementation difference is small and could be realized quite easily in BruteFIR, but efficiency will suffer. I probably will rather spend time to do it from the ground and up. But not this year.

2006-07-12
BruteFIR v1.0h. Added a sub-sample delay function (that is delays smaller than one sample can be specified), support for text format in the file I/O module, and support for naming ports in the JACK I/O module.

2006-03-30
BruteFIR v1.0g. Fixed input mixer and delay setting bugs.

2005-08-11
BruteFIR v1.0f. Fixed a filter parse bug.

2005-06-28
BruteFIR v1.0e. Fix to work with GCC 4.0.

2005-06-12
Released a minor maintenance release, 1.0d. Contains some minor adjustments to the JACK I/O module, and a fatal bug fix concerning multiple inputs/outputs, which was introduced in 1.0b.

2005-01-04
BruteFIR v1.0c. Mistake in 1.0b caused the CLI module not accepting return characters, causing problems for telnet operation. Fixed that.

2004-11-21
BruteFIR v1.0b. Updated the JACK I/O module, it is now possible to run several BruteFIR instances using JACK at the same time, and it is not necessary to connect to external ports at startup. The CLI can now take commands from a serial line. Additionally, a couple of remaining bugs in the equalizer module have been fixed.

2004-08-07
BruteFIR v1.0a. Minor update, removed the coefficient set limit and updated the example configuration files in the package.

2004-04-21
BruteFIR v1.0. I felt it was time to release 1.0 now. I have fixed up the code so it compiles on FreeBSD and Solaris again, and I added an OSS module, which makes BruteFIR truly usable on FreeBSD platforms.

2004-02-22
BruteFIR v0.99n. As suspected, the merge function was not good enough, and has now been removed. However, instead, a cross-fade algorithm has been added, which indeed is a bit costly in terms of CPU time, but makes coefficient changes truly seamless.

2004-01-17
BruteFIR v0.99m. Fixed a few bugs, and updated the ALSA code to support the 1.0 version of the API. Now it is also possible to make more time-precise CLI scripting. I'm suspecting that the merge function is not good enough to be very useful, I may remove it or replace it with a better sounding (but inefficient) cross-fade algorithm.

2003-10-26
BruteFIR v0.99l. Added a function to hide discontinuities that may occur when filter coefficients are changed in runtime (function is called "merge"). Also added a skip option to coefficient loaded, to skip a given number of bytes in the beginning of a file.

2003-08-10
BruteFIR v0.99k. This is a maintenance release, which fixes a few bugs, including a severe powersave bug which could cause unexpected and very loud noise come out. It also adds an option to run in daemon mode.

2003-07-11
BruteFIR v0.99j. Now we are getting near a 1.0 release. This release contains quite many new features, and bug fixes. Some feature highlights: BruteFIR now employs FFTW3, there is support for 32 and 64 bits in the same binary and buffer over/underflows can be ignored. Among important bug fixes are that FFTW wisdom is now stored properly, so it can be re-used more often, and the equalizer module now sets the magnitude properly at the edges.

2003-02-11
BruteFIR v0.99i. I released the h-version a bit too early, lots of small but significant mistakes followed. This version fixes those (hopefully).

2003-02-09
BruteFIR v0.99h. A couple of bug fixes associated to the new callback I/O. It also adds support for native endian and auto sample formats, and a simple automatic load balancer for multi-processor machines.

2003-02-02
BruteFIR v0.99g. This release adds support for callback I/O. One callback I/O module is available, supporting JACK. This support means that the program has went through quite radical reorganizations, so something might be broke. If you discover any problems, please let me know.

2003-01-05
BruteFIR v0.99f. Minor peak meter adjustment and bug fix.

2002-12-25
BruteFIR v0.99e. Lots of tuning have been made to work better with sound card I/O. It should now be more reliable in low latency configurations. The release also includes some various minor improvements and bug fixes.

For those that find the default configuration file unnecessary and just in the way, there is now the -nodefault command line option, which will cause BruteFIR to skip the default configuration file.

2002-11-28
BruteFIR v0.99d. Fixes yet another bug in the ALSA code, which caused the software not to work with hardware with odd period sizes, such as some (all?) ice1712-based cards. The real-time index has also been much simplified and improved in terms of reliability, and a power-save feature was added.

Sometime soon, there will be 1.0...

2002-10-10
BruteFIR v0.99c, is an important bug fix release. Among other fixes, it fixes the slightly embarrassing bug of incorrect reading of 3 byte 24 bit formats. Apart from many bug fixes, it adds double buffer support to the equalizer module, and a simple script function to the CLI. The risk of buffer underflow at startup has also been strongly reduced.

2002-09-12
BruteFIR v0.99b, fixed a serious bug in the ALSA code, which caused buffer underflow when the software buffer size was larger than the hardware buffer size.

2002-08-25
BruteFIR v0.99a, a couple of minor bug fixes, discovered during the development of AlmusVCU.

2002-08-04
This new release (v0.99) contains a first version of an equalizer module, which allows equalization to be changed in runtime. Now the I/O delay is fixed, always exactly twice the filter block length (if the sound card hardware is properly designed). Good for synchronization with other audio processors, or clustering. There is also a slight change in configuration file format, so you know why it will complain when run with an old configuration file.

2002-07-26
Added a minor feature that proved necessary for some applications, such as Ambisonics. This feature makes it is possible to multiply inputs/outputs in mixing with negative values, not just positive. The new version is BruteFIR 0.98e. An invalid version was available a few hours during this day (forgot to include some CLI patches), so if you downloaded your v0.98e at this date, download it again.

2002-07-21
Two bug fixes in this new release, BruteFIR 0.98d. The first concerns scaling of coefficient parameters, where PCM coefficients where incorrectly scaled. The other fix is in the ALSA I/O module, which could at some occasions fail to set the sample rate.

2002-06-14
BruteFIR 0.98c, another small step towards 1.0. This contains an important bugfix. Earlier versions could mix up the mix buffers which caused looping sound with some filter configurations, this is now fixed. The common mistake (at least for me) to link a 32 bit BruteFIR with a 64 bit FFTW or the other way around is now taken care of.

2002-05-05
BruteFIR 0.98b. The sample rate monitoring added in 0.98a is now optional, through the option monitor_rate. Also support for SSE2 for Pentium 4 processors is implemented (only used when compiled with double precision). It is also possible to compile and run on Solaris with Sparc processors.

2002-04-16
Yet another of the usual minor updates: BruteFIR 0.98a. This fixes a minor bug which could cause stray processes to be left after exit. It also improves the real-time index calculation so it works properly on SMP, and the program now exits with an error when sample rate is changed in runtime. There are now interpretable exit codes from the program as well, so one can now why it exited.

2002-03-25
BruteFIR 0.98: This new release supports virtual inputs and outputs, which can be used to control delay of individual outputs even if they are mixed to the same physical output.

2001-12-20
Another bugfix release, 0.97d. Also added a -quiet command line parameter to suppress title, warnings and informational messages at startup.

2001-12-17
Due to popular demand, the ALSA I/O module has got support for accessing the software modes of the ALSA library. The new release is 0.97c.

2001-12-16
Ooops. The new sample format handling was not as good as I initially thought. Now that has been fixed. Oh, clipping for 32 bit formats works again. I hope I did not burst anyone's ears (other than mine). The release version is 0.97b.

2001-12-15
Some major bugs was introduced in 0.97, hopefully most of them has been squashed in this new release, 0.97a.

2001-12-09
BruteFIR 0.97: a new release with lots of major changes. The software is now much more modular. It uses modules for input and output, ALSA and file I/O being the first modules available. It also supports logic modules, the old BruteFIR CLI being the first example. The logic modules can be used to achieve adaptive filtering. The new module architecture will probably need some time to stabilize, and due to the large amount of changes to the code, there is a great risk that this new version is less stable than the last. A few details in the configuration file format has changed as well, for which the documentation has been updated. The documentation for how to program a BruteFIR module is not yet available though.

2001-11-04
Added a todo list. Any suggestions are welcome of course.

2001-10-27
Added some quick and dirty benchmarks, and added some new documentation. I made a low latency benchmark due to popular demand, and the interesting result is that it is possible to get as low as three milliseconds I/O delay, which is much lower than what I expected.

2001-09-27
New release, BruteFIR 0.96a. Some minor bugfixes, and at last processor capability detection code has been included, so BruteFIR will detect SSE or 3DNow, and use the optimized code accordingly.

2001-08-26
Updated documentation to cover all the new features of BruteFIR 0.96.

2001-08-20
BruteFIR 0.96 has been released, with a few important bugfixes, but also much new features, which not yet has been documented here. It is now possible to make filter networks, and have different length on different filters.

2001-07-18
A new release, BruteFIR 0.95b, which contains an important bugfix is available for download. It fixes a block bounds violation error when converting from 32 bit integers to floating point. It also contains some tuning of realtime priorities.

2001-06-10
Some minor updates to the documentation.

2001-06-03
A bugfix release, BruteFIR 0.95a, is available for download. It fixes a bug which caused the program to crash when long filters in raw format was read.

The documentation is now up to date again.

2001-05-26
New release, BruteFIR 0.95. This includes some new features, for example support for changing delay in runtime and support for non-interleaved sound cards. An important bug fix has also been applied, when mixing files and sound cards for inputs/outputs trouble could occur, but that should be fixed now.

Again, the documentation on this page is not entirely up to date with the software itself.

2001-04-11
BruteFIR 0.94a released, which is a bugfix release. A severe bug in the ALSA support code caused the error "Hardware does not support enough fragments." with common sound cards. Now it is gone. Still there is some work to do on the ALSA support code, like adding support for cards with non-interleaved buffer layout (like the RME9652).

2001-04-08
Major changes and cleanups of this page has been done, and the source code has been re-released. The new version is 0.94, and contains a new improved convolution algorithm with hand-coded assembler optimizations for Intel's SSE and AMD's 3Dnow. With this, BruteFIR is now capable of even higher throughput.

Note on the documentation's age

Note that the core of this documentation was written 1999 — 2001 and is thus old. It's up to date regarding how to configure BruteFIR, but there's many references to old kernel versions and old CPUs embedded in here.

What is it?

BruteFIR is a software convolution engine, a program for applying long FIR filters to multi-channel digital audio, either offline or in realtime. Its basic operation is specified through a configuration file, and filters, attenuation and delay can be changed in runtime through a simple command line interface. The FIR filter algorithm used is an optimized frequency domain algorithm, partly implemented in hand-coded assembler, thus throughput is extremely high. In realtime, a standard computer can typically run more than 10 channels with more than 60000 filter taps each.

Through its highly modular design, things like adaptive filtering, signal generators and sample I/O are easily added, extended and modified, without the need to alter the program itself.

BruteFIR is free and open-source. It is licensed through the GNU General Public License [6].

The preferred operating system platform for the program is Linux [11], but it is easily ported to other Unixes as well, and supports for example FreeBSD out of the box. BruteFIR uses the high-performance FFTW library [7] for the Fast Fourier Transform (FFT, [5]) calculations, and ALSA, the Advanced Linux Sound Architecture [2], is the preferred way of interfacing sound cards, although OSS, Open Sound System [25], is supported as well. The main features are:

  • Designed for realtime filtering of HiFi quality digital audio
  • Up to 256 inputs and 256 outputs
  • Input/output provided by external modules for maximum flexibility
    • Default I/O modules provide support for sound cards and files
    • Access multiple I/O modules (= several sound cards / files) at the same time
    • 8 - 24 bit audio at any rate supported by sound cards
    • Easy-to-use C language API to create your own I/O modules, for example to support more file formats, other sound card APIs, or generate test signals
  • Mix/copy channels before and/or after filtering
  • Cascade filters or build complex filter networks
  • Simple C language API to create logic modules, to add new functionality
    • Create your own logic module, for example to do adaptive filtering
    • Provided is a logic module which implements a CLI accessible through telnet to manage runtime settings, and a dynamic equalizer.
    • Toggle/change filter in runtime
    • Alter attenuation for each individual input and output in runtime
    • Alter delay for each individual input and output in runtime
    • Sub-sample delays are possible
  • Filter length limited only by processor power and memory
  • Typical filter lengths are in the range 2048 - 262144 taps
  • Reasonable low I/O-delay (typically 200 ms)
  • Fixed I/O-delay, thus possible to sample-align with other processors
  • Cross-fade for seamless filter coefficient changes.
  • Re-dithering of outputs (HP TPDF)
  • Overflow protection and monitoring
  • 32 or 64 bit floating point internal resolution.
  • Supports multiple processors

What is it good for?

A few examples of applications where BruteFIR could be a central component:

  • Digital crossover filters
  • Room equalization
  • Cross-talk cancellation
  • Wavefield synthesis
  • Auralization
  • Ambiophonics
  • Ambisonics
Among these, room equalization and auralization needs the longest FIR filters in the common case. Many applications can do with quite short filters actually, but the thing is that you will probably not need to compromise on the filter lengths when you use BruteFIR, even when sample rates go up. However, BruteFIR is pretty useless by itself, since it is only a FIR filter engine. It does not provide any filter coefficients, thus it is not a filter design program. Also, due to its relatively high I/O-delay, BruteFIR is most suited for applications when the input signal is not live.

If you are interested in room equalization, my old NWFIIR project [18] might be of interest. It's a bit dated though. A better program for room equalization is Denis Sbragion's DRC [22].

BruteFIR convolution

The main design goal of BruteFIR is to achieve as high throughput as possible when filters are long (longer than 10000 taps). This means that the filter algorithm must be very fast, since it will be consuming almost all processor time of the whole program. BruteFIR's convolution algorithm is an example of a situation where a theoretically less efficient algorithm is faster in practice, because it is easily optimized and hides performance problems of more complex components.

Frequency domain algorithms for convolution is much faster than the straight-forward time domain one when filters are long. The well known overlap-save algorithm is used as the base in BruteFIR's convolution. However, there are practical problems with this algorithm as we will see.

The problem of complexity

Efficient convolution is done in the frequency domain and therefore an FFT algorithm is needed. The FFT calculations occupy typically more than 90% of all processing time when plain overlap-save is employed. Unfortunately, FFT it is not easy to implement. There exist numerous implementations which vary greatly in performance, which is one proof of the complexity. Since it takes up almost all processing time, we must optimize it in order to make the convolution faster. This leaves us with a quite hard optimization problem.

One way to optimize is to code assembler by hand and try to be better than the compiler. Modern processors for personal computers like Intel's Pentium III [10] or AMD's Athlon [1] has custom SIMD instructions (Single Instruction Multiple Data), which allows for a single instruction to operate on more than one data element at a time. For example, a single instruction may add together four or eight floating point numbers. Typically, one can improve the performance of an algorithm four times when using these instructions. They are not used by common compilers like GCC (GNU Compiler Collection [9]), meaning that we have a good opportunity to write assembler code that will with a wide margin outperform code generated by the compiler. Most FFT libraries are written in C, and thus does not use these efficient SIMD instructions. So, theoretically, we could implement an FFT algorithm using SIMD instructions and beat the ones already available. However, we are going for a simpler approach as we shall see. Since one of the design goals of BruteFIR is to be fairly portable, we want to make any assembler implementation small and simple, so it easily can be ported to other processor architectures. Maybe 'small', but certainly not 'simple' would be applicable on an assembler implementation of FFT. In conclusion, we find optimization with assembler as an attractive method to increase performance of existing algorithms. However, the algorithm we need to optimize, FFT, is quite complex and thus not an attractive target for optimization.

One of the fastest FFT libraries available is FFTW [7], [8], which is used by BruteFIR. There are more efficient FFT libraries out there (?), but they are often limited to short lengths (typically less than 8192), or are not free software nor open-source, which is a requirement of the BruteFIR project.

Problems with long FFTs

Many of the fastest FFT implementations support only shorter filter lengths (djbfft [3] being one example), and those that support long lengths may behave poorly on some architectures. One example is FFTW which on my 900 MHz AMD Athlon test system gets a large performance dip when FFT lengths become larger than 32768 (real-valued transforms). On the test system, a 262144 point FFT is 30 times slower than a 32768 point, which theoretically should be only 10 times. Although the behavior is more stable on my 550 MHz Pentium III test system, performance drops more than O(n * log2(n)) which is the complexity of the FFT algorithm. Note that these tests were performed using FFTW2.

These performance problems is of course due to memory accesses, and poor cooperation between the hardware caching architecture and the software. When the data of the algorithm exceeds the cache size, the problem becomes obvious.

Both Pentium and Athlon architectures allows for giving the cache hints from the software to reduce problems in these situations, but this must be done in assembler, and is therefore seldom used.

Apart from performance problems, long FFTs include more multiplications and scalings which induces a larger quantization error. This is however a minor problem (?).

Partitioned convolution

We have seen that the central algorithm of fast convolution, the Fast Fourier Transform, is complex to implement and optimize. We have also seen that the need of long FFTs reduces the choices of available implementations and that the existing can behave poorly on some hardware architectures. A modified fast convolution algorithm that uses shorter FFTs, and where most time is spent in code which is small and easily optimized, would be ideal.

Many have worked on improving the standard frequency domain convolution algorithms for different purposes. The central idea found in many of these improvements, is that the impulse response, that is the filter, is partitioned into several smaller parts. When each part is filtered with the input, the results delayed suitably and finally added together, one gets the same result as when processing the whole filter at once. As far as I know, the earliest user of this simple but powerful concept is T.G. Stockham [16], who published his results only one year after the famous Cooley and Tukey FFT paper [5]. The concept can be used to solve several problems. Stockham used it for saving memory, but in later work made in the eighties and early nineties, at the time when realtime DSP became feasible for the first time, it was stated that it can also be used to reduce quantization errors, reduce I/O-delay, and adapt to optimal FFT lengths of a specific implementation. All these improvements are described by J.S. Soo and K.K. Pang [14], [15]. Other realtime partitioned convolution pioneers are B.D. Kulp [17], P.C.W. Sommen [12], [13] and J.M.P. Borrallo and M. G. Otero [4]. Their work is a good place to start reading for the one interested in getting a more detailed description of partitioned convolution. The convolution algorithm in BruteFIR is conceptually exactly the same as the one found in these papers.

When partitioned convolution is used, something interesting happens in the processing time distribution of the algorithm. The major part of processing is moved from the FFT algorithm, to the trivial operation of convolution in the frequency domain which is simply multiplication. The more parts we split the impulse response into, the more convolution and less FFT is done. Naturally the FFTs get shorter, and thus we get rid of the problems associated to long FFTs. We now realize that partitioned convolution is the answer to our wishes, we do not need long FFTs and it becomes less important to optimize the FFT algorithm.

Optimizing where it counts

We notice that we will earn most from optimizing the operation where a segment of input converted to the frequency domain is multiplied with the corresponding part of the filter also in the frequency domain. The result is then added to the output. When the data format is half-complex, a format used by most real-valued FFTs, The straight-forward implementation look like this when programmed in C:

    d[0] += b[0] * c[0];
    for (n = 1; n < n_fft / 2; n++) {
	d[n] += b[n] * c[n] - b[n_fft - n] * c[n_fft - n];
	d[n_fft - n] += b[n] * c[n_fft - n] + b[n_fft - n] * c[n];
    }
    d[n] += b[n] * c[n];

b is the input, c is the filter coefficients, and d is the output. As we see, this is a very short and simple algorithm, which is easy to implement in assembler. There are a couple of problems though. The data in each array is accessed from the tail and the front at the same time. It would be better for the cache to localize the accesses, and move from front to end only. It is also a problem that the data is accessed both in forward and reverse order (both 0,1,2,3 and 3,2,1,0), since we want to used SIMD instructions. To solve the problem, we need to reorder the data. This will only be necessary to do once with the filter coefficients, so it is free. For the input however, we need to do this once after each forward transform, and for the output we need to restore the half-complex order prior to each inverse transform. In BruteFIR the input reordering is put into the mixing and scaling step, and the output reordering in the quantization step, so the cost is next to nothing. Below is a C implementation of the previous algorithm, when data has been reordered to better fit SIMD instructions and to improve the memory access pattern:

    d1s = d[0] + b[0] * c[0];
    d2s = d[4] + b[4] * c[4];
    for (n = 0; n < n_fft; n += 8) {
	d[n+0] += b[n+0] * c[n+0] - b[n+4] * c[n+4];
	d[n+1] += b[n+1] * c[n+1] - b[n+5] * c[n+5];
	d[n+2] += b[n+2] * c[n+2] - b[n+6] * c[n+6];
	d[n+3] += b[n+3] * c[n+3] - b[n+7] * c[n+7];

    	d[n+4] += b[n+0] * c[n+4] + b[n+4] * c[n+0];
    	d[n+5] += b[n+1] * c[n+5] + b[n+5] * c[n+1];
    	d[n+6] += b[n+2] * c[n+6] + b[n+6] * c[n+2];
    	d[n+7] += b[n+3] * c[n+7] + b[n+7] * c[n+3];
    }
    d[0] = d1s;
    d[4] = d2s;

The above function is easily converted into assembler using Intel's SSE instructions, or AMD's 3Dnow instructions, with cache hint instructions. The key loop (which is unrolled to further improve performance) becomes less than 50 lines long.

It is interesting that partitioned convolution makes much more memory references than ordinary overlap-save. In the most simple algorithm analysis, only the number of mathematical operations (like multiplications and additions) are considered when evaluating performance. Better analysis also counts the number of memory references, but unfortunately that is not enough considering the modern computer architecture; it is also of profound importance to take how the accesses are done into consideration. One bad reference can be worse in terms of performance than ten good ones on a modern computer.

Conclusion

By implementing partitioned convolution we have avoided the need of using long FFTs, and moved the major part of the processing time from the FFT to a simple multiplication loop. By reordering data after the forward transform and restoring it prior to inverse transform, the multiplication loop can be easily realized with SIMD instructions, and thus become very efficient. On the 900 MHz AMD Athlon test system, filtering of a 131072 tap long filter is twice as fast when 16 partitions of 8192 taps each are used instead of a single partition (note: this test case is exceptional, the performance improvement is less in the common case). This despite the new algorithm uses more memory references and more mathematical operations.

Apart from the improvement in throughput, we also get lower I/O-delay (equals about twice the partition length), lower memory consumption, and more flexible filter length options. A 140000 tap filter would require a 262144 tap filter if ordinary overlap-save was used, but with partitioned convolution we can use 18 partitions of 8192 taps, and then get a gross performance improvement, coupled with delay reduction.

Still, one must not over-estimate partitioned convolution. If there really is an optimal FFT algorithm available, ordinary overlap-save will certainly outperform the partitioned algorithm. An example of an assembler-optimized FFT algorithm can be found in the non-free and non-portable Intel Native signaling processing library [19].

Where can I get it?

You are free to download version 1.0n.

The package contains the source-code, you will need a supported platform to run it on (Linux is recommended, but FreeBSD or Solaris should work out of the box too, it is not as closely maintained though). Apart from the basic stuff you must also have FFTW3 installed (note that FFTW2, as used by old versions of BruteFIR, won't work). FFTW3 must be compiled for both double and single precision.

If you want sound card support, it is recommended to use ALSA on Linux platforms, and when that is not available, OSS can be used.

If you want to use the JACK support, you need an up to date version of JACK installed.

Be sure that you use an official GCC compiler when compiling BruteFIR. One user reported bad sound quality (noise artifacts in the BruteFIR output), and it was shown that he had used GCC 2.96 (not an official version), that caused errors in the floating point calculations of BruteFIR.

The package does not yet contain configure scripts or other nice things to make compiling easier. However, with some luck it should work simply by typing 'make'. You can also view the Makefile to see what compile options there are. If you have any questions, just mail me, torger@ludd.ltu.se.

How fast is it?

BruteFIR's main feature is that is fast. It's brutally fast. The key component making BruteFIR fast is the convolution algorithm described above.

Note: the test descriptions here are a bit dated, made using an old version of BruteFIR. However, the results should provide a rough idea of what BruteFIR can do in terms of throughput. The example configuration files have been updated to work with the current version.

How high throughput can I get?

With a massive convolution configuration file setting up BruteFIR to run 26 filters, each 131072 taps long, each connected to its own input and output (that is 26 inputs and outputs), meaning a total of 3407872 filter taps, a 1 GHz AMD Athlon with 266 MHz DDR RAM gets about 90% processor load, and can successfully run it in real time. The sample rate was 44.1 kHz, BruteFIR was compiled with 32 bit floating point precision, and the I/O delay was set to 375 ms. The sound card used was an RME Audio Hammerfall.

How low I/O delay can I get?

BruteFIR is mainly designed for high throughput, not low delay. However, there is an interest of using BruteFIR for low delay convolution anyway, so here are some benchmarks so you know what to expect. Partitioned convolution can indeed allow for quite low delay, very low if the processing power is available, and the filters are not too long.

Below is an example of a simple cross-talk cancellation application running on a 1 GHz AMD Athlon with 266 MHz DDR RAM and an RME Audio Hammerfall sound card. You can download the cross-talk cancellation configuration file that was used if you want to test yourself. There are only four filters and their length are no more than 8192 taps (note: the example files included in the package are only 4096 taps long, as seen in the updated example configuration file), so it is indeed a very light application, which is a requirement if you want very low delay, since partitioned convolution does not scale very well with low delays (meaning a large number of partitions). The sample rate in these tests is 44.1 kHz, and BruteFIR was running with 32 bit floating point precision.

delay in ms processor load partition size number of partitions
3 ms 60% 64 samples 128
6 ms 30% 128 samples 64
12 ms 16% 256 samples 32
24 ms 11% 512 samples 16
47 ms 8% 1024 samples 8

As seen in the table, BruteFIR allows for as low delay as 3 milliseconds, which is the limit of the sound card used, which cannot have shorter than 64 sample partitions.

If you want to run BruteFIR to achieve high throughput, you should expect to have a delay of at least 100 ms though (and using no more than 16 partitions or so).

If you try to run BruteFIR with shorter delay than the computer can handle, or with too long filters, the program will exit with a broken pipe signal. If you get broken pipe only after a while, this is probably due to that you have not applied a good low latency patch to the kernel (there are bad ones as well), or you have cron jobs running or other software that competes for using the processor. For reasonable low latency, a low latency kernel can handle other processes running, but for as low as 3 milliseconds like in this example, you should have a dedicated clean system for running BruteFIR.

Hardware considerations

Note: the hardware referenced here is a bit dated (a long time ago the text was written), but apart from that, the text is up to date.

What is important for BruteFIR is that the machine has fast memory and fast processor. A Pentium 4 with its RDRAM is probably the best choice today. However, an Athlon with DDR RAM is not bad either, and significantly cheaper. A fast processor on a computer with slow memory is what most often causes disappointment. For example, a dual Pentium III at 1 GHz with good use of both processors was found to be slower than a single processor 1 GHz AMD Athlon with DDR RAM. The problem was that the Pentium III had poor memory performance. The stream benchmark [20] is a good program to use to verify the memory bandwidth if you think you get poor BruteFIR performance.

If you use SDRAM you will never get exceptional memory bandwidth, however, some tuning of timer settings in the BIOS, or overclocking of the memory bus can give you quite decent performance.

When it comes to sound hardware, you should be able to use any card that is compatible with ALSA [2]. However, it is not very likely that the sound card code of BruteFIR will work for all sound cards supported by ALSA, although that is the goal. If you get problems with your sound card, please send me a mail, and I will do my best to get it to work, or even better, try to get it to work yourself and send me a patch.

The best sound cards are those which support partition sizes which are a powers of two. If that is not the case, BruteFIR must run in input poll mode, which is not necessarily less reliable, but will consume a part of the spare processor time.

The worst possible sound card is one which does not support partition sizes with a power of two, and can only transfer large sample blocks at a time. Then BruteFIR will run unreliably or not at all.

If you want to avoid problems I recommend RME Audio [21] Hammerfall (Light) (RME9652 and RME9636) and also cards from the RME Audio Digi96 series (RME96), since those are the cards I use myself. The Hammerfall cards support up to 26 inputs and 26 outputs, the Digi96 cards support up to 8 channels. They are not the cheapest cards out there, but these are clean professional cards, fully digital with ADAT and S/PDIF inputs and outputs, which means you can have high-quality DACs and ADCs outside the computer to get the best sonic performance possible.

The Hammerfall cards allow for shorter delay (minimum partition size is 64 samples) than the Digi96 series (minimum size 1024 samples).

Configuring and running

When BruteFIR is run for the first time (without parameters), it will generate a default configuration file (~/.brutefir_defaults) (if not the -nodefault option is used), and then complain that it cannot find .brutefir_config in the home directory, which is the default location. The default configuration file contains default settings, which is extended and/or overridden in the main configuration file. A setting that is specified in the default configuration file, is not necessary to be listed in the main configuration file.

BruteFIR takes only four parameters, namely the filename of the main configuration file, and optionally -quiet to suppress title, warnings and informational messages at startup, and -nodefault if BruteFIR should read all settings from the main configuration file, and finally -daemon if it should run as a daemon.

If no parameters are given, the filename given in the default configuration file is used. If the filename is "stdin", BruteFIR will expect the configuration file to be available on the standard input.

The (default) default configuration file looks like this:

## DEFAULT GENERAL SETTINGS ##
 
float_bits: 32;             # internal floating point precision
sampling_rate: 44100;       # sampling rate in Hz of audio interfaces
filter_length: 65536;       # length of filters
config_file: "~/.brutefir_config"; # standard location of main config file
overflow_warnings: true;    # echo warnings to stderr if overflow occurs
show_progress: true;        # echo filtering progress to stderr
max_dither_table_size: 0;   # maximum size in bytes of precalculated dither
allow_poll_mode: false;     # allow use of input poll mode
modules_path: ".";          # extra path where to find BruteFIR modules
powersave: false;           # pause filtering when input is zero
monitor_rate: false;        # monitor sample rate
lock_memory: true;          # try to lock memory if realtime prio is set
sdf_length: -1;             # subsample filter half length in samples
convolver_config: "~/.brutefir_convolver"; # location of convolver config file
 
## COEFF DEFAULTS ##
 
coeff {
        format: "text";     # file format
        attenuation: 0.0;   # attenuation in dB
	blocks: -1;         # how long in blocks
	skip: 0;            # how many bytes to skip
	shared_mem: false;  # allocate in shared memory
};
 
## INPUT DEFAULTS ##
 
input {
        device: "file" {};  # module and parameters to get audio
        sample: "S16_LE";   # sample format
        channels: 2/0,1;    # number of open channels / which to use
        delay: 0,0;         # delay in samples for each channel
	maxdelay: -1;	    # max delay for variable delays
	mute: false, false; # mute active on startup for each channel
};
 
## OUTPUT DEFAULTS ##
 
output {
        device: "file" {};  # module and parameters to put audio
        sample: "S16_LE";   # sample format
        channels: 2/0,1;    # number of open channels / which to use
        delay: 0,0;         # delay in samples for each channel
	maxdelay: -1;	    # max delay for variable delays
	mute: false, false; # mute active on startup for each channel
        dither: false;      # apply dither
	merge: false;       # merge discontinuities at coeff change
};
 
## FILTER DEFAULTS ##
 
filter {
        process: -1;        # process index to run in (-1 means auto)
	delay: 0;           # predelay, in blocks
	crossfade: false;   # crossfade when coefficient is changed
};

The syntax of the main configuration file is very similar as we will see. As we can see, there are five sections in the configuration:

  • General settings. Here the general parameters for BruteFIR is set up.
  • Coefficient settings. Parameters for files where-from filter coefficients are loaded.
  • Input settings. Settings for digital audio inputs.
  • Output settings. Settings for digital audio outputs.
  • Filter settings. Parameters for the FIR filters.

The general syntax rules for the configuration files is easily grasped from the default configuration file. The semicolons are important, they note the end of a setting, not line breaks, so you may have several settings on one line if you like. All characters on a line after a # is found are ignored. There are three data types: strings, numbers and booleans. Strings are text between quotes, a number is either with or without a decimal dot, and a boolean is either 'true' or 'false'.

Note that everything is case sensitive, so setting names must be written with small letters. Although the configuration file examples shown here is nicely ordered in sections, it is perfectly alright to mix settings in any order you like.

The general settings section in the main configuration file has the same syntax as in the default configuration file. The difference is that coeff, input, output and filter structures can exist in multiples, and are given names and more parameters.

General settings

Default values of all general settings (except logic) must be given in the default configuration file. Any of these settings may be overridden in the main configuration file (except config_file). These settings are:

float_bits: <NUMBER: internal floating point resolution, either 32 or 64>;
sampling_rate: <NUMBER: sampling rate in Hz>;
filter_length: <NUMBER: length in samples of the (sub)filters>[,<NUMBER: number of subfilters per filter>];;
config_file: <STRING: default location of main configuration file>;
overflow_warnings: <BOOLEAN: echo overflow warnings to stderr>;
show_progress: <BOOLEAN: echo progress to stderr>;
max_dither_table_size: <NUMBER: maximum size in bytes of pre-calculated dither>;
allow_poll_mode: <BOOLEAN: allow input poll mode>;
modules_path: <STRING: extra path where to find BruteFIR modules>;
logic: <STRING: logic module name> { <logic module parameters> }[, ...];
powersave: <BOOLEAN or NUMBER: pause filtering when input is zero>;
monitor_rate: <BOOLEAN: monitor sample rate, and abort if it changes>;
lock_memory: <BOOLEAN: try to lock memory if realtime prio is set>;
sdf_length: <NUMBER: sub-sample delay filter half length in samples>[, <NUMBER: kaiser window beta>];
convolver_config: <STRING: file to store FFTW wisdom in>;
benchmark: <BOOLEAN: start in benchmark mode (can only be used in main config file)>;
safety_limit: <NUMBER: if non-zero max dB in output before aborting>;

The filter_length setting specifies how long the filters should be. This can be done in two ways. Either by specifying the length in one number, which must be a power of two. If so, the convolution will be done on the whole filter length. To partition a 65536 tap filter in 16 parts, you write filter_length: 4096,16. Partitioned filters can be used to improve performance and reduce I/O-delay.

The convolver_config setting specifies where FFTW wisdom should be stored, that is optimization information for the FFT calculations.

If overflow_warnings is set to true, information about overflows will be printed to the screen when they occur. Note that overflowed samples are always set to the maximum output value of the output device, so there is no actual overflow on the output (unless the actual floating point value is overflowed). If overflow occurs, it means that the filter is amplifying too much, either through its coefficients or through input and output attenuation. Overflow is not checked for if the output values are floating point.

If dither is applied to any output, a dither table will be calculated when the program is started. It contains uncorrelated random values that is used to generate the dither. The more channels that applies dither, the larger table is needed, if to keep the dither uncorrelated between channels. This table can get quite large memory-wise. If you want to limit its size, set max_dither_table_size to a value. It should rather not be less than one megabyte though. If it is set to zero or negative, the program will itself choose a size.

BruteFIR uses external modules to provide sample I/O, and optionally add new logic. It will search a few default directories to find any modules that should be loaded, as specified in the configuration. The setting modules_path will add an extra directory, which is searched first. The value in the created default configuration file will be ".", that is the current working directory.

If any logic modules should be loaded, these are listed in the logic field, in pairs of module name / module parameters, separated with commas. Which logic modules that are available and what functionality they provide can be found in the Logic modules section.

If there is any sound card used for input or output (or any other sample-clock dependent device), BruteFIR will automatically set its delay-sensitive processes to realtime priority, thus you will typically need to run the program as root. To maintain realtime performance, it is important that there is no memory belonging to the program in the swapfile, thus all memory must be locked to RAM. This is done if lock_memory is set to true. Note that the memory is never locked when realtime priority is not set (that is when there are only files used for input and output). Warning: there seems to be a bug in the Linux kernel which makes the shared memory to be locked one time for each process, meaning that when lock_memory is set to true, BruteFIR will seem to consume a lot more memory than it should. Also, it makes of course no sense to lock memory if your system does not have a swap activated. Due to this issue, the best thing to do is to have a system with no swap and avoid locking the memory.

The powersave feature if activated, will monitor the inputs, and if an input channel provides zero samples, the associated filters will not do any processing, since with zero on the input, BruteFIR knows in advance that there will be zero on the output. BruteFIR will continue run as normal, and filters with non-zero inputs will continue to to process normally. As soon as there is non-zero input on a suspended filter, it starts processing again. This powersave feature is transparent, there will be no convolution errors if it is activated. The reason for having it optional is that one may want to make performance tests, without the need to feed a meaningful signal to BruteFIR.

If analog inputs are used, the input will never be exactly zero, and thus the powersave feature will not be triggered. However, if a value is specified instead of the boolean (for example powersave: -80;), that value is interpreted as the lowest level in dB the input signal can be, before BruteFIR will consider the input as zero, and trigger powersave. Thus, a noise floor can be specified, and then powersave can work together with analog inputs.

If benchmark mode is activated (can only be done in the main configuration file), performance statistics will be printed on screen. Note that due to complex caching effects of modern computers, the displayed processing times can look strange, a step that requires much more arithmetic operations than another may in certain circumstances still be considerably faster, if it has better luck with the cache. Since benchmarking measures elapsed time, the computer must not be loaded with any other tasks in order to get reliable results.

If a sound card which is used for input cannot be configured to have a period size (interrupt interval) equal to or smaller than the configured filter (partition) length, or if it is cannot be a power of two, BruteFIR must be run in input poll mode. This means that the sound card is polled for data, and sound card interrupts are not used. BruteFIR will run just as reliably (as long as the sound card allows for small transfers) but will consume more of the spare processor time. Thus it will look like BruteFIR uses more processor than it actually needs to. If more processor time is used for filtering, less will be used for polling, thus input poll mode does not mean that it is not possible to have as long filters as running in normal mode. However, for some applications (for example when the spare processor time is used by another vital program), input poll mode is not suitable, and by setting the allow_poll_mode to false, BruteFIR will exit with an error if input poll mode is required.

If subsample delays should be possible to set, the sdf_length setting must be larger than zero. It specifies the half length of a sub-sample delay filter. A sub-sample delay filter is simply a sinc sampled with a sub-sample offset. Thus, when a signal is convolved with the filter it is delayed with the corresponding offset. Since a sinc signal is infinitely long, it must be windowed. A kaiser window is used, default beta is 9.0, but an own value can be specified by adding it after a comma (example: sdf_length: 31, 8.5;), there is little reason to use other than the default though. The distortion caused by the windowing is a soft rolloff at higher frequencies, the shape depends on the beta value. There is no phase distortion. Since the sub-sample filters are linear phase, they will add a pre-response (in practice I/O-delay), which is their half filter length, that is the value given after the sdf_length setting. If sub-sample delay are used only on inputs or outputs, the added pre-response is the same as the sdf_length, if used on both (usually not necessary), it will be twice the length. To activate sub-sample delay, also a valid subdelay must be specified in at least one of the input/output structures. The valid range is -99 to 99.

The advantage of a long sub-sample filter length is that the rolloff in the high frequencies starts later and gets sharper, that is less high frequency information is lost. The disadvantage of long sub-sample filters is that the required CPU time increases, and the added I/O-delay increases. Sub-sample filters are processed separately in the frequency domain using FFT, and therefore it is recommended to keep sdf_length at a power of two minus one (the actual filter length is twice sdf_length plus one), which means that as much as possible of the FFT block is used (an sdf_length of 16 requires as much CPU time as an sdf_length of 31, since the same block length is required). With an sdf_length of 31 and the default beta of 9.0, and a sample rate of 44100 Hz, the response is flat up to 19 kHz, and then a soft rolloff begins which reaches -0.20 dB at 20 kHz, which is good enough for most needs. The next natural step, 63, keeps a flat response up to about 20500 Hz, with -0.20 dB at 21 kHz.

The purpose of the safety_limit setting is to protect your ears and expensive speakers, it's active if set to a non-zero value. Every output sample is checked and if it exceeds this value (in dB) BruteFIR will immediately exit with an error message, before any sound is sent to the output.

General structure syntax

<structure type name> <STRING: name (list for some) | NUMBER: index> {
	<field name 1>: <setting 1>;
	[...]
};

Names of structures (given after the type name) is not given in the default configuration file, but must be provided in the main configuration file. The name is either a custom string, or an index number, which must then be the same as the order of the structure in the file, that is the first structure must be indexed 0, the second 1 and so on. If a string name is given, the index number is given automatically (the opposite also applies), and when referring to the structure, either the string name or the index number can be used. Some structures, namely input and output, may have a comma-separated list of names, since the names applies to the channels defined in the structure.

After the name, or the structure type name if in the default configuration file, There is a left brace ({), and then structure fields and their settings, each field/setting pair ending with semicolon (;). As for the general settings, field names always end with a colon (:). The order of the fields is not important. The structure is closed with a right brace (}) and ended with a semicolon.

Coeff structure

coeff <STRING: name | NUMBER: index> {
	filename: <STRING: filename>; | <NUMBER: shmid>/<NUMBER: offset>/<NUMBER: blocks>[,...];
	format: <STRING: sample format string | "text" | "processed">;
	attenuation: <NUMBER: attenuation in dB>;
	blocks: <NUMBER: length in blocks>;
	skip: <NUMBER: bytes to skip in beginning of file>;
	shared_mem: <BOOLEAN: allocate in shared mem>
};

In the default configuration file, the filename field is not set, so it must be present in the main configuration file.

The coeff structure defines a set of filter coefficients, which becomes a FIR filter. There are several different file formats:

  • "text" coefficients are listed in a text file, one coefficient per line. They are parsed with the standard C library strtod function.
  • A sample format string describing a raw format, for example 16 bit little endian integer. The format of this string is described in the Input and output structure section.
  • "processed" coefficients are stored in the format BruteFIR uses internally. Attenuation or adapted length cannot be applied if this format is used.

Note that BruteFIR currently does not provide any way to convert other formats to the "processed" format (well actually it does, but only through its module API).

The coefficients can be scaled, by setting the attenuation to non-zero.

Instead of a filename, comma-separated number groups can be given. The first number will be a shared memory ID (man shmat) where the data is found, the second number is the offset in bytes into the shared memory area where the program starts to read, and the third is how many blocks that should be read. A block is a filter segment, that is if filter_length is 4096, 16 one block is 4096 coefficients, and there can be no more than 16 blocks per coefficient set. If not all blocks covered in the first group, there must be following number groups to provide the full length. When a shared memory segment is given, it is required that the format is "processed".

In some cases, when one wants to test the performance of a certain BruteFIR configuration, but don't feel like generating coefficients, one can set the filename to "dirac pulse". Then BruteFIR will generate a dirac pulse filter internally and use it as any other filter, and thus will cost as much in processing as any other filter of the same length. However, if you need a dirac pulse in the real case, it makes no sense using this feature, since simply setting the coeff field in the filter structure to -1 gives the same effect and uses very little processor power (and memory).

The blocks field says how long in filter blocks the coefficient set should be. If it is set to -1, the full length is assumed. Note that custom lengths are only possible if partitioned convolution is employed (quite naturally, since else there will only be one filter block covering the full length).

The skip field if given specifies how many bytes in the beginning of the file that should be skipped. This can be used to skip headers in a file or similar. The field will be ignored if the coefficients are not read from file.

The shared_mem field indicates if the coefficient should be stored in shared memory. Some modules may require that, such as the equalization module.

Input and output structure

input <STRING: name | NUMBER: index>[, ...] {
        device: <STRING: I/O module name> { <I/O module settings> };
        sample: <STRING: sample format>;
        channels: <NUMBER: open channels>[/<NUMBER: channel index>[, ...]];
	delay: <NUMBER: delay in samples>[, ...];
	subdelay: <NUMBER: additional delay in 1/100th samples (valid range -99 - 99)>[, ...];
	maxdelay: <NUMBER: maximum delay for dynamic changes>;
	individual_maxdelay: <NUMBER: maximum delay for dynamic changes>[, ...];;
	mute: <BOOLEAN: mute channel>[, ...];
	mapping: <NUMBER: channel index>[, ...];
};

output <STRING: name | NUMBER: index>[, ...] {
        device: <same syntax as for the input structure>;
        sample: <same syntax as for the input structure>;
        channels: <same syntax as for the input structure>;
	delay: <same syntax as for the input structure>;
	subdelay: <NUMBER: additional delay in 1/100th samples (valid range -99 - 99)>[, ...];
	maxdelay: <same syntax as for the input structure>;
	individual_maxdelay: <same syntax as for the input structure>;
	mute: <same syntax as for the input structure>;
	mapping: <same syntax as for the input structure>;
	dither: <BOOLEAN: apply dither>;
	merge: <BOOLEAN: merge discontinuities at coeff change>;
};

All fields for the input and output structures except mapping, delay and mute must be set in the default configuration file.

The device field specifies the source/destination of the digital audio. This is always an I/O module. First the name of the module is stated, followed by a its configuration within {}. If the audio is read/written from/to a module which does not continue forever (for example reading from a file), BruteFIR will finish when the first I/O module comes to an end (hopefully an input module, write failure of an output module is considered an error).

The sample format should be one of the following strings:

  • "S8", signed 8 bit integer.
  • "S16_LE", signed 16 bit little endian integer.
  • "S16_BE", signed 16 bit big endian integer.
  • "S16_4LE", signed 16 bit little endian integer, stored in the high bits of 4 bytes.
  • "S16_4BE", signed 16 bit big endian integer, stored in the high bits of 4 bytes.
  • "S24_LE", signed 24 bit little endian integer.
  • "S24_BE", signed 24 bit big endian integer.
  • "S24_4LE", signed 24 bit little endian integer, stored in the high bits of 4 bytes.
  • "S24_4BE", signed 24 bit big endian integer, stored in the high bits of 4 bytes.
  • "S32_LE", signed 32 bit little endian integer.
  • "S32_BE", signed 32 bit big endian integer.
  • "FLOAT_LE", 32 bit little endian floating point.
  • "FLOAT_BE", 32 bit big endian floating point.
  • "FLOAT64_LE", 64 bit little endian floating point.
  • "FLOAT64_BE", 64 bit big endian floating point.
  • "<X>_NE", native endian, <X> is replaced with S16, S16_4 etc, and the format will be converted to the LE or BE counterpart depending on if the machine is little endian or big endian.
  • "AUTO", will be converted to one of the LE or BE formats (or S8), as decided by the associated I/O module.

The common format 16 bit signed little endian found in for example 16 bit wav-files is thus "S16_LE". The floating point formats can be in any range, however all integer formats will be scaled to -1.0 to +1.0 internally, so if to match an integer format, the range should be -1.0 to +1.0. There is no overflow checking for floating point formats (that is values larger than +1.0 or lesser than -1.0 is not truncated).

The channels field specifies the number of open and used channels of the device. If the number of open channels exceed the number of used channels, a slash (/) followed by a comma-separated list of channel indexes of used channels must be appended. If we for example have a eight channel ADAT sound card, but we only want to use the first two, we write 8/0,1 as the channels setting. As you see, the lowest channel index is zero, not one.

The length of the list of names (given after the structure type name) must match or exceed the number of used channels. If there are more channels in the head (the logical, or virtual channels) than there are available through the device, the specified channels must be mapped onto the physical device channels. This is done with the mapping field, which simply is a list of indexes, which index in the head to map to which physical device channel. Here a simplified example:

output 14,15,16 {
        ...
        channels: 8/5,4;
	mapping: 0,1,0;
};

In this example, two channels from the eight channel device are used, channels with index 5 and 4. The order of the channel indexes matter, physical channel 5 will now be considered the first (index 0) of the available physical channels, and 4 the second (index 1). The mapping fields tells how to map the channels called 14, 15 and 16 in the header to those two physical channels. The mapping is in the same order as the channels in the header, that is 14 is mapped to physical channel index 0 (which is channel 5 on the eight channel device), 15 to index 1 (channel 4 on the device), and 16 to index 0, that is the logical channels 14 and 16 will mix into the same output on the device. In the standard case, where logical channels are the same as the amount of channels made available through the channels field, a mapping specification is not needed. Then the first logical channel is mapped to the first listed device channel and so on.

The list of delays specifies how many samples a channel should be delayed. This could be used to compensate for speaker positions that is either to close or too far away. It could also be used to compensate for acasual filters. Delay can be changed in runtime, if maxdelay is not set to a negative value. It defines the upper bound of delay in samples. When the program is started, delay buffers for all channels to match maxdelay is allocated. If it is negative, only the precise amount specified by the delay array is allocated.

The setting individual_maxdelay was added later, and works the same as maxdelay with the difference that it is specified per channel. It is useful to save memory when there are many channels, and only some of them need dynamic delay (or considerably larger buffer than the others).

If the general setting sdf_length is larger than zero, the subdelay setting will take effect. It specifies the sub-sample delay per channel in 1/100th of samples (valid range is -99 to 99). This delay can be changed in runtime. To disable sub-sample delay on a channel, set its sub-delay to a negative value outside the valid range. Since sub-sample delay consumes CPU time, it is recommended to only activate it where necessary. Sub-delay filters adds pre-response, and therefore all channels with sub-delay disabled will be automatically compensated with an I/O delay to make them aligned.

The mute list of booleans, specifies, in order, which channels that should be muted from the beginning. The muted channels can later be unmuted from the CLI.

If the dither flag is set to true, dither is applied on all used channels. Dither is a method to add carefully devised noise to improve the resolution. Although most modern recordings contain dither, they need to be re-dithered after they have been filtered for best resolution. Dither should be applied when the resolution is reduced, for example from 24 bits on the input to 16 bits on the output. However, one can claim that dither should always be applied, since the internal resolution is always higher than the output. When BruteFIR is compiled with single precision, it is not possible to apply dither to 24 bit output, since the internal resolution is not high enough. BruteFIR's dither algorithm is the highly efficient HP TPDF dither algorithm (High Pass Triangular Probability Distribution Function).

If the merge flag is set to true, discontinuities that may occur when coefficients are changed in runtime, is smoothed out with a simple merge algorithm. This avoids "clicks" that may occur in the sound when coefficients are changed. Note that discontinuities occurs also when volume is changed, but that is not merged, since those discontinuities are generally not audible or masked by the volume change itself. If someone does not agree with that, let me know, and I will make it apply the merger at volume changes too.

Filter structure

filter <STRING: name | NUMBER: index> {
        from_inputs: <STRING: name | NUMBER: index>[/<NUMBER:attenuation in dB>][/<NUMBER:multiplier>][, ...];
        from_filters: <same syntax as from_inputs field>;
        to_outputs: <same syntax as from_inputs field>;
        to_filters: <STRING: name | NUMBER: index>[, ...];
        process: <NUMBER: process index>;
	coeff: <STRING: name | NUMBER: index>;
	delay: <NUMBER: pre-delay in blocks>;
	crossfade: <BOOLEAN: cross-fade when coefficient is changed>;
};

Only the process field should be given in the default configuration file.

The filter structure defines where a filter is placed and what its parameters are. This is done in a filter:

  1. Possible attenuation is applied to the inputs, where-after they are mixed together.
  2. The mixed-together inputs are filtered.
  3. The filter output is copied to the output channels, possibly with individual attenuation. Attenuation is however not applicable to outputs going to other filters.

If an output channel exists in several filter structures, the filter outputs will be mixed into that channel. Thus, a set of filter structures defines how inputs and outputs should be copied, mixed and filtered.

With help of the from_filters and to_filters fields, filters can be connected to each-other. The only real constraint is that there must be no loops. BruteFIR will detect and point out errors if such exist in a given filter network. Note that if possible coefficients should be pre-convolved rather than put as filters in series, since a 2N length filter computes much faster than two cascaded N length filters.

The from_inputs, from_filters and to_outputs fields have the same syntax. One channel/filter is given as the string name or index number, and if attenuation should be applied, it is followed by a slash (/) and attenuation in dB. Instead of, or combined with, attenuation in dB, a multiplier can be given, a number which all samples will be multiplied with. The writing "channel 1"/6/-1 means that channel 1 is attenuated 6 dB and the polarity is changed (multiplication with -1). It is also possible to write "channel 1"//-0.5 which is equivalent to the first example.

If more than one channel should be included, they are separated with commas. The to_filters field has the same syntax with the exception that attenuation is not allowed.

The process field specifies in which Unix process the filter should be run. All filters with the same process index will run in the same process. Process index 0 must exist, and if there are more processes they should be in series, 0, 1, 2, 3 and so on. This field is important if BruteFIR runs on a multi-processor machine. The optimal situation is that there is one process per processor, and that each process requires the same processor time. Then you will get most out of your multi-processor computer. There is one limitation of how filters can be distributed between processes: mixing to an output channel or a filter input must be done within the same process.

If the process field is set to -1, an automatic but naive load balancing will take place, which may or may not be as good as a hand-made load balancing.

The coeff field defines which coefficient set that should be used for the filter. It could be given as the string name of the set, or as its index number. If the index number is set to minus one (-1), there will be no filtering in the filter, it will just mix and copy inputs/outputs as specified. Note that the length of the coefficient set specifies how processor intensive the filter will be.

The delay field specifies how many filter blocks pre-delay there should be. Zero or negative means no delay. The maximum allowed delay is one block less than full length. Thus, with unpartitioned filtering there can be no delay at all. The delay cost is zero both in terms of memory and processing.

If the crossfade setting is set to true, there will be a cross-fade when the coefficient is changed in runtime, making the coefficient change totally seamless. This means that when changing coefficient (using the CLI for example), the filter will convolve one block with the old coefficient, fade out that and mix it with a fade in block with the new coefficient. This means that at the time of coefficient change, there will be roughly twice the amount of processing for that filter. This processing spike can of course cause buffer underflow if running with a sound card and heavy CPU load in the normal case. If there for example are 10 filters in a configuration (all with crossfade active), and all coefficients are changed at the same time, the normal CPU load should not exceed 50%, since the spike will roughly require twice the load. However, if the coefficients are changed only one filter at a time, only 10% extra processing is required compared to the normal case in the example.

Configuration file example

Here follows an example of a main configuration file, showing some of the aspects of BruteFIR's possibilities. It implements a cross talk cancellation filter for a stereo dipole. The two filters are placed in two processes get the max out of a dual processor machine. A computer with a single processor should if possible keep all filters within the same process for best performance. Note that the configuration uses the default settings extensively. For example, no general settings have been specified apart from the addition of the CLI logic module, and in the coeff structures, only the filename field is used.

logic: "cli" { port: 3000; };

coeff "direct path" {
        filename: "direct_path.txt";
};

coeff "cross path" {
        filename: "cross_path.txt";
};

input "left", "right" {
        device: "file" { path: "/disk0/tmp/music.raw"; };
        sample: "S16_LE";
        channels: 2;
};

output "stereo dipole left", "stereo dipole right" {
        device: "file" { path: "output01.raw"; };
        sample: "S16_LE";
        channels: 2;
};

filter "left speaker direct path" {
        inputs: 0/6.0;
        outputs: 0;
        process: 0;
	coeff: "direct path";
};

filter "left speaker cross path" {
        inputs: "right"/6.0;
        outputs: "stereo dipole left";
        process: 0;
	coeff: "cross path";
};

filter "right speaker direct path" {
        inputs: "right"/6.0;
        outputs: "stereo dipole right";
        process: 1;
	coeff: "direct path";
};

filter "right speaker cross path" {
        inputs: "left"/6.0;
        outputs: "stereo dipole right";
        process: 1;
	coeff: 1;
};

I/O modules

I/O modules are used to provide sample input and output for the BruteFIR convolution engine. It is entirely up to the I/O module of how to produce input samples or store output samples. It could for example read input from a sound card, a file, or simply generate noise from a formula.

In the BruteFIR configuration file, an I/O module is specified in each input and output structure.

The purpose of having I/O modules instead of building all functionality directly into BruteFIR is that it should be easy to extend with new functionality, without compromising the core convolution engine.

All I/O modules has the extension ".bfio".

ALSA sound card I/O (alsa)

The ALSA I/O module (named "alsa") is used to read and write samples from/to sound cards. It supports all BruteFIR sample formats also supported by the referenced sound device. The basic configuration is simple, only one field, called device need to be set, where the associated value is a string which is passed without modification to ALSA's device open function. Examples: "alsa" { device: "hw"; } or "alsa" { device: "hw:1"; }.

In the above examples, the hardware is accessed directly (the "hw" prefix), but you can also use ALSA's software modes. That is however not recommended, since some functions of BruteFIR, for example overflow protection, expects to be at the very last output stage, and not before another software layer which may perform for example mixing or volume control.

In theory it should also be possible to access files (for example wav-files) through ALSA, "alsa" { device: "file:test.wav"; } but this does not seem to work currently, and is not recommended, since the module assumes that all devices are driven by a sample clock (thus is a sound card).

If the ALSA I/O module is used in several input/output structures, all referenced sound cards will be linked together using the ALSA API. This makes starting and stopping sound cards synchronized, if the hardware and driver supports it, if not, the ALSA subsystem tries to make starting and stopping is synchronized as it can. However, when there are many alsa devices used, this linking can cause the computer to lock up, at least it has happened in the past. This is probably due to a problem in ALSA, and may have been resolved when you read this. However, should you bump into problems, you can disable linking by setting link to false (example: "alsa" { device: "hw:1"; link: false; }).

Per default, when reading fails due to an overflow, or writing fails due to and underflow, BruteFIR will abort. If your computer is heavily loaded, and/or partitions are short, and/or other services are running on the computer, over/underflow can occur occasionally. In those cases, one might rather get occasional clicks in the sound rather than a total stop. The ALSA I/O module can hide over/underflow from BruteFIR, and thus it will not abort when that occurs. Just set the ignore_xrun parameter to true (example: "alsa" { device: "hw:1"; ignore_xrun: true; }).

OSS sound card I/O (oss)

The OSS I/O module (named "oss") provides sound card I/O through the OSS API. It has only one parameter, device, which points out the device to open. Example: "oss" { device: "/dev/dsp"; }.

The I/O module supports OSS multi-channel and full duplex modes.

JACK audio server I/O (jack)

The JACK I/O module (named "jack") provides BruteFIR with support for the low-latency JACK audio server [23]. JACK is an audio server under development, and the goal for the JACK I/O module is that it should be compatible with the current CVS version.

To avoid putting I/O-delay into the JACK graph, the JACK buffer size should be set to the same as the BruteFIR partition size. It is however possible to set the JACK buffer size to a smaller value. The I/O-delay in number of JACK buffers as seen by following JACK clients will be:

2 * <BruteFIR partition size> / <JACK buffer size> - 2

Note that both the JACK buffer size and BruteFIR period size is always a power of two.

Currently, the JACK I/O module assumes that jackd is run with the -R parameter, at its default client realtime priority which is 9.

The names of the BruteFIR ports will be "brutefir:input-X" for the inputs, and "brutefir:output-X", where X is the channel index. The JACK client name which is per default "brutefir" can be changed, by setting "clientname" (example: clientname: "brutefir-A";). It is a global setting, and if used it must be set in the first JACK device clause (the first from the top in the configuration file). The clientname will change the port name prefix as well (the prefix is the client name). If multiple BruteFIR instances should be run, they must have different client names, or else the port names will collide.

If the local ports should be connected to other JACK ports at startup, the setting ports is used, where the associated string values are the names of the ports to connect to. Examples: "jack" { ports: "alsa_pcm:capture_1", "alsa_pcm:capture_2"; } for input, and "jack" { ports: "alsa_pcm:playback_1", "alsa_pcm:playback_2"; } for output. The port listing must be set to the same amount as the number of channels for the device. However, empty strings could be used if a specific channel index should not be connected, for example: "jack" { ports: "", "alsa_pcm:capture_2"; } will only connect the second port.

It is also possible to optionally specify the port names to other than the default naming, like this: "jack" { ports: "alsa_pcm:capture_1"/"in-A"; }, that is adding a slash and specifying a name after that, this will replace the default "input-X" for inputs and "output-X" for outputs. If a port should not be connected but still be named, the first string is empty, like this: "jack" { ports: ""/"in-A"; }.

If no ports should be connected, and the client name is left to the default, the JACK device clause is empty ("jack" { };).

The sample format for the JACK device should be set to AUTO, which will be the JACK sample format (floating point).

Raw PCM file I/O (file)

The raw PCM file I/O module (named "file") is used to read and write samples from/to files. It supports all BruteFIR sample formats and reads/writes them directly in raw form, interleaved format. The parameter string is in the simplest case the filename. Example: "file" { path: "test.pcm"; }. One can also specify how many bytes to skip in the beginning for input files, and if to append output files. Examples: "file" { path: "test.pcm"; skip: 44; } and "file" { path: "test.pcm"; append: true; }.

It is also possible to read from and write to text files (X floating point ASCII values per line separated with whitespace, where X is the number of channels). Just add the option text: true;. The module will convert to/from 64 bit floating point, and thus requires that sample format (or use AUTO).

If the file I/O module is used for input, the input file can be looped, by setting loop to true.

By using /dev/stdin like this "file" { path: "/dev/stdin"; }, BruteFIR will read data from standard input, so it is then possible to do things like mpg123 -s test.mp3 | brutefir.

Writing your own I/O module

This will probably never be documented. The best way is to look at the source code to see how it is done.

Logic modules

Command line interface (cli)

The CLI logic module (named "cli") provides a command line interface available through telnet, a local socket, a pipe, or a serial line. The CLI is used for changing settings in runtime, which is of course only suitable when BruteFIR is used in realtime. It can be used interactively by hand, for example by connecting to it through telnet. It is also suitable for scripting BruteFIR, or using it as a means of inter-process communication if BruteFIR is used as the convolution engine for another program.

The context sensitive port field specifies which interface will be used as follows:

  • port: <INTEGER: TCP port number>; the CLI will listen on the given port number for incoming telnet clients.
  • port: <STRING: "/dev/" ...>; when the string starts with "/dev/" the CLI assumes a serial device (such as "/dev/ttyS0" on Linux) is pointed out, and opens it as a serial port, with the default line speed 9600 baud, if not the line_speed field is used specifying another speed.
  • port: <STRING: name of local socket>; any other string not starting with "/dev/" is handled as the file name for a local socket, and the CLI will create and listen for incoming connections on the given path. If the path exists, it will be replaced.
  • port: <INTEGER: read end file descriptor>, <INTEGER: write end file descriptor>; the CLI will assume that the given file descriptors are already opened and ready for use, and will attach the read end to CLI input, and the write end to CLI output. This interface is suitable as inter-process communication when BruteFIR is integrated into another program, and is started through fork() and exec().

The CLI does not have much terminal functionality to speak of, and is thus a bit cumbersome to use interactively. It reads a whole line at a time, and can interpret backspace, but that is about it. There is no echo functionality so the connecting client needs to handle that (telnet does, and terminal software for serial lines usually have a function to enable local echo).

Instead of specifying a port, one can specify a string of commands, which will be run in a loop as a script. Example: "cli" { script: "cfc 0 0;; sleep 10;; cfc 0 1;; sleep 10"; }. The script may span several lines. Each line is carried out atomically (this is also true for command line mode), so if there are several commands on a single line, separated with semicolon, they will be performed atomically (an atomic set of statements). The exception is when an empty statement is put in the line (just a semicolon), like in the script example, this will work as a line break, and thus separate atomic sets of statements.

A typical use for atomic set of statements is to change filter coefficients and volume at the same time.

The sleep function in the CLI allows for sleeping in seconds, milliseconds or blocks. One block is exactly the filter length in samples, and if partitioned, it is the length of the partition. Block sleep can only be used in script mode.

When in script mode, the first atomic statements will be executed just before the first block is processed, then the block is processed (and sent to the output), and then the next set of atomic statements is run. That is, each set of atomic statements is performed before the corresponding block is processed. The next atomic statement set is not performed until the next block is about to be processed.

The block sleep command (only works in script mode) works such that the sleep is commenced at the next block. The statement sleep b1; will thus cause the next block to be skipped. Note that since one block passes for each atomic statement set, a single line with only sleep b1; will skip two blocks, not one, since one block is consumed when parsing the sleep command, and the other is skipped by the sleep duration. That is to skip only one block, either use sleep b0; alone, or use sleep b1 as the last statement together with other statements in an atomic statement set (recommended).

Sleep in seconds and milliseconds will start the timer when the command is issued (at the start of the block if in a script), and continue with the next command after at least the given time has passed. If run in a script, the timer is polled at the start of each block, and the next command is then executed at the start of the first block where the timer has expired.

If several sleep commands are executed in the same atomic statement set in a script, only the last will take effect, and will be executed only when all other commands in the set have been processed. To avoid confusion, it is thus recommended to employ sleep commands either alone, or as the last in the atomic statement set.

If the field echo is set to true, the CLI commands will be echoed back to the user (the whole line at a time). This is off per default.

When connected and you type "help" at the prompt, you will get the following output:

Commands:

lf -- list filters.
lc -- list coefficient sets.
li -- list inputs.
lo -- list outputs.
lm -- list modules.

cfoa -- change filter output attenuation.
        cfoa <filter> <output> <attenuation|Mmultiplier>
cfia -- change filter input attenuation.
        cfia <filter> <input> <attenuation|Mmultiplier>
cffa -- change filter filter-input attenuation.
        cffa <filter> <filter-input> <attenuation|Mmultiplier>
cfc  -- change filter coefficients.
        cfc <filter> <coeff>
cfd  -- change filter delay. (may truncate coeffs!)
        cfd <filter> <delay blocks>
cod  -- change output delay.
        cod <output> <delay> [<subdelay>]
cid  -- change input delay.
        cid <input> <delay> [<subdelay>]
tmo  -- toggle mute output.
        tmo <output>
tmi  -- toggle mute input.
        tmi <input>
imc  -- issue input module command.
        imc <index> <command>
omc  -- issue output module command.
        omc <index> <command>
lmc  -- issue logic module command.
        lmc <module> <command>

sleep -- sleep for the given number of seconds [and ms], or blocks.
         sleep 10 (sleep 10 seconds).
	 sleep b10 (sleep 10 blocks).
	 sleep 0 300 (sleep 300 milliseconds).
abort -- terminate immediately.
tp    -- toggle prompt.
ppk   -- print peak info, channels/samples/max dB.
rpk   -- reset peak meters.
upk   -- toggle print peak info on changes.
rti   -- print current realtime index.
quit  -- close connection.
help  -- print this text.

Notes:

- When entering several commands on a single line,
  separate them with semicolons (;).
- Inputs/outputs/filters can be given as index
  numbers or as strings between quotes ("").

Most commands are simple and don't need to be further explained. Naturally, any changes will lag behind as long as the I/O delay is. The exception is the mute and change delay commands, they will lag behind as long as the period size of the sound card is, which most often is smaller than the program's total I/O delay. However, when there is a virtual channel mapping, the mute and delay will be lagged as well.

The imc, omc and lmc commands are used to give commands to I/O modules and logic modules in run-time. To find out which modules that are loaded and which indexes they have, use the command lm. Not all modules support run-time commands though.

Changing attenuations with cffa, cfia and cfoa can be done with dB numbers or simply by giving a multiplier, which then is prefixed with m, like this cfoa 0 0 m-0.5. Changing the attenuation with dB will not change the sign of the current multiplier.

Run-time equalizer

The equalizer logic module takes control over one or more coefficient sets, and renders equalizer filters to them, as specified by the user. This can be done in the initial configuration, and also updated in runtime, through the CLI.

The startup configuration can look like this:

  "eq"  {
		debug_dump_filter: "/tmp/rendered-%d";
		{
			coeff: 0, 1;
			#bands: "ISO octave";
			#bands: "ISO 1/3 octave";
			bands: 100, 200, 500;
			magnitude: 20/-3.2, 100/8.5;
			phase: 20/0, 100/180;
		};
		{
			coeff: "eq-1";
			bands: "ISO octave";
			magnitude: 31.5/-3.2, 125/8.5;
			phase: 31.5/3.2;
		};
	};

If you want to analyze the rendered filters, the debug_dump_filter setting specifies a file name where the rendered coefficients will be written. It must contain %d, which will be replaced by the coefficient index. Then follows equalizers. Each specify which coefficient index (or name) it should render the equalizer filter to. These must be allocated and must be stored in shared memory, for example like this:

coeff 0 {
        filename: "dirac pulse";
	shared_mem: true;
	blocks: 4;
};

The dirac pulse will be replaced by the rendered filter. Each equalizer has a set of frequency bands (max 128), they can be manually specified, or use the ISO octave band presets. Optionally, magnitude (in dB) and phase (in degrees) settings can be specified. The frequency value must then match one of the given bands.

If you specify two filters, the rendering will be double-buffered, meaning that the eq module will keep one coefficient active in the filter(s), and render to the other, and switch when ready. This means that there is no risk of playing an incomplete equalizer, which can cause some noise (usually in the form of a beep), thus it is recommended to use double-buffered mode if the equalizer will be altered in runtime. In the filter configuration and when referring to the equalizer in the CLI, the first of the two coefficients should then be used.

In run-time, equalizers can be modified through the CLI. An example: lmc eq 0 mag 20/-10, 4000/10 will set the magnitude to -10 dB at 20 Hz and +10 dB at 4000 Hz for equalizer for coefficient 0. Instead of mag, phase can be given. The command lmc eq "eq-1" info will list the current settings for the equalizer stored in the coefficient called "eq-1".

The more heavily loaded the computer is by convolution, the longer time it will take to render the new equalizer. If the coefficient set it renders to is very short, and the magnitude and phase response is very detailed (sharp edges etc) it will not be able to adapt to it fully.

Writing your own logic module

This will probably never be documented. Just look at the source code and see how it is done.

Tuning

Realtime index

The program calculates a realtime index which can be shown through the CLI, or will be printed periodically to the screen if the show_progress flag is set. The realtime index is a floating point value. When it is 1.0, 100% of the available processing power must be used at all times to be able to achieve realtime performance. If it is larger than 1.0, it means that with the current configuration, BruteFIR will not manage realtime performance.

If your configuration is too demanding for realtime, you should shorten the filters (or remove channels) until the realtime index is very close below 1.0, perhaps 0.95. This way you make full use of your computer. However, if you have multiple processors, it is not as simple. The realtime index will show how much is needed from the most loaded processor, but leaves a proper load balancing to you. So, devise your configuration carefully if you have multiple processors. The number of input and output channels and the filter length is what steals processor time. The number of filters, dither, delay, mixing and attenuation is very cheap in comparison.

When testing with realtime indexes above 1.0, inputs and outputs must of course be files. For performance testing, you could use "/dev/zero" for input and "/dev/null" for output. Also note that it takes some time for the index to stabilize.

The realtime index typically matches the processor load, if running with a sound card. However, if input poll mode is employed, real time index can be considerably lower than the processor load, since input polling is performed in the spare processor time.

FFTW wisdom

When BruteFIR runs for the first time, it will generate FFTW wisdom, which takes some time. FFTW wisdom is benchmarking information which tells the FFTW library how to run FFT the most efficient way on the given computer. Since the information is hardware and binary dependent, the file should be removed when hardware is changed/upgraded or BruteFIR is recompiled. A wisdom file that was not generated on the hardware BruteFIR is running on, or not by the binary that is run, may yield sub-optimal performance. When BruteFIR is calculating FFTW wisdom, the computer should not be running other processor-demanding software.

Naturally, it is very important that FFTW was compiled with the correct optimization flags to achieve optimal performance.

The wisdom is loaded used and updated each time BruteFIR is run. Each time BruteFIR uses a partition length it has not used before (and thus there is no wisdom available), it will need to generate new wisdom, which will take some time.

Low latency patch

If you are going to use BruteFIR in realtime, it is strongly recommended that you patch your kernel to reduce latency, or else the program may fail to keep up when a cron-job or a screen saver starts. The Linux kernel's latency problems has been reduced in the 2.4 kernel, but it is still not satisfactory without the patch applied.

For the 2.4 kernel, Andrew Morton's low latency patches are recommended [24].

The new 2.6 kernel does have a low-latency setting in the kernel configuration, which should be activated. Although no extra patches should be required for a 2.6 kernel in the normal case, there still are low-latency patches out there for really demanding situations.

Sample clock problems

If you use digital input and output, as I would recommend, you may get problems if the sound card is not configured properly. It is very important that the input and output sample clock use the same clock as reference. Or else, micro-differences between the input and output sample clock will make BruteFIR's IO buffers to slide apart, and eventually make the program stop. Usually there is an option to set the digital sound card's sample clock to 'slave'.

If you have analog input or output or both, you cannot get this problem (unless you use several different sound cards, then it will fail due to differences in clocking).

Digital sound cards that work in slave mode allows that the sample clock is changed in runtime. Usually, this is not what one want for BruteFIR, since the filters are designed for only one sample rate. Therefore BruteFIR can be configured to exit if it detects a sample clock different from the one mentioned in the configuration file.

Double precision or not

BruteFIR can run with 32 or 64 bit floating point internal resolution. Traditionally, 32 bit is called "single precision", and 64 bit "double precision". The float_bits setting is used to change resolution. Per default, BruteFIR runs in 32 bit.

Depending on processor used, you may lose assembler optimizations when running in 64 bit. Also, memory bandwidth used by BruteFIR will naturally double, which reduces performance. Thus, although 64 bit and 32 bit operations are generally equally fast, due to increased memory usage, BruteFIR needs 30 - 50% extra processor time, not counting additional effects if assembler optimizations are lost.

When do you need double precision? If you are picky enough on sound quality that you would require dither on 24 bit output, then you need double precision. For most audio work however, 32 bit precision is enough.

Choosing number of partitions

There is no formula for calculating the optimal number of partitions to get maximum throughput. It varies between hardware platforms, so trial and error is the only working method. More than about 16 partitions are generally not recommended though.

If you are using partitioned filters to reduce the I/O-delay for realtime filtering, make sure that it does not get too low. If I/O-delay is too low, the sound card can get overflowed/underflowed causing the program to exit with a broken pipe signal.

Realtime issues

Extreme low latencies, such as 64 sample partitions, will probably not work for long periods of time, even with a low latency patched kernel.

The processor cannot be loaded more than typically 85% for safe realtime operation. For very low latencies, this number could go down to 70%. The reason for this is that computing time will vary somewhat, that is how modern computers work, and to be able to cope with the maximum computing times, some spare processor time must be left.

Request features

Which new features that get into BruteFIR are decided by its users. If you need a feature, let me know, and I'll see what I can do (and want to do).

References

  1. Advanced Micro Devices, Inc. website. http://www.amd.com.
    Makers of the Athlon processor.
  2. A. Bagnara, J. Kysela et al ALSA, Advanced Linux Sound Architecture. http://www.alsa-project.org.
    A powerful and flexible audio applications API developed primarily for Linux.
  3. D.J. Bernstein djbfft. http://cr.yp.to/djbfft.html.
    A compact FFT library implemented in C, faster than most, including FFTW.
  4. J. M. P. Borallo, M. G. Otero On the implementation of a partitioned block frequency domain adaptive filter (PBFDAF) for long acoustic echo cancellation. Elsevier Signal Processing, vol 27 No 3 June 1992, page 301-315.
  5. J. W. Cooley, J. W. Tukey An Algorithm for the Machine Computation of the Complex Fourier Series. Mathematics of Computation, Vol. 19, April 1965, pp. 297-301.
  6. Free Software Foundation GNU General Public License. http://www.gnu.org/copyleft.
    One of the most common free software licenses. Its main purpose is to make sure that the software is kept free and open source.
  7. M. Frigo, S. G. Johnson FFTW. http://www.fftw.org.
    A fast and full-featured FFT library implemented in C. Called "Fastest Fourier Transform in the West".
  8. M. Frigo, S. G. Johnson FFTW: An Adaptive Software Architecture for the FFT. Proceedings of the International Conference on Acoustics, Speech, and Signal Processing, Vol. 3, 1998, pp. 1381-1384.
  9. GNU Compiler Collection. http://gcc.gnu.org.
    A free software multi-platform compiler supporting the programming languages C, C++, Objective C and Fortran.
  10. Intel Corporation website. http://www.intel.com.
    Makers of the Pentium processor.
  11. Linux Online website. http://www.linux.org.
    Linux is a free Unix-type operating system originally created by Linus Torvalds with the assistance of developers around the world.
  12. P. C. W. Sommen Adaptive Filtering Methods. Ph. D. dissertation, Tech. Univ. Eindhoven, Eindhoven, The Netherlands, 1992.
  13. P. C. W. Sommen Partitioned frequency domain adaptive filters. Proc Asilomar Conf. Signals, Systems and Computers, 1989, pp. 676 - 681.
  14. J. S. Soo, K. K. Pang A new structure for block FIR adaptive digital filters. Proc. IREECON, vol 38, pp. 364 - 367, 1987.
  15. J. S. Soo, K. K. Pang Multidelay block frequency adaptive filter, IEEE Trans. Acoust. Speech Signal Process., Vol. ASSP-38, No. 2, February 1990.
  16. T. G. Stockham Jr. High-speed convolution and correlation. AFIPS Proc. 1966 Spring Joint Computer Conf., Vol 28, Spartan Books, 1966, pp. 229 - 233.
  17. B. D. Kulp Digital Equalization using Fouring Transform Techniques. AES preprint 2694, 1988.
  18. A. Torger NWFIIR Audio Tools. http://www.ludd.ltu.se/~torger/filter.html.
    A set of tools for measuring and processing impulse responses, room equalisation being the target application.
  19. Intel Signal Processing Library. http://developer.intel.com/software/products/perflib/spl/index.htm.
  20. STREAM: Sustainable Memory Bandwidth in High Performance Computers. http://www.cs.virginia.edu/stream/.
    A portable and simple memory benchmark program.
  21. RME Audio. http://www.rme-audio.com.
  22. D. Sbragion Digital Room Correction. http://freshmeat.net/projects/drc.
    A program which generates room correction FIR filters to be used in HiFi systems.
  23. P. Davis et al JACK audio server. http://jackit.sourceforge.net/.
    A low-latency audio server, written primarily for the GNU/Linux operating system.
  24. A. Morton Linux Scheduling Latency. http://www.zip.com.au/~akpm/linux/schedlat.html.
    A collection of notes and tools related to an effort to decrease the typical scheduling latency of the 2.4.x kernel.
  25. Open Sound System. http://www.opensound.com.
    A highly portable sound card API available on a large variation of (Unix) platforms.





(c) Copyright 2001 - 2006, 2009 - 2016 Anders Torger brutefir-1.0o/convolver.h0000644000175000017500000001011512752354353015062 0ustar andersanders/* * (c) Copyright 2001, 2002, 2004, 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _CONVOLVER_H_ #define _CONVOLVER_H_ #include #include "defs.h" #include "bfmod.h" #include "dai.h" /* Convert from raw sample format to the convolver's own time-domain format. */ void convolver_raw2cbuf(void *rawbuf, void *cbuf, void *next_cbuf, struct buffer_format *bf, void (*postprocess)(void *realbuf, int n_samples, void *arg), void *pp_arg); /* Transform from time-domain to frequency-domain. */ void convolver_time2freq(void *input_cbuf, void *output_cbuf); /* Scale and mix in the frequency-domain. The 'mixmode' parameter may be used internally for possible reordering of data prior to or after convolution. */ void convolver_mixnscale(void *input_cbufs[], void *output_cbuf, double scales[], int n_bufs, #define CONVOLVER_MIXMODE_INPUT 1 #define CONVOLVER_MIXMODE_INPUT_ADD 2 #define CONVOLVER_MIXMODE_OUTPUT 3 int mixmode); /* Convolution in the frequency-domain, done in-place. */ void convolver_convolve_inplace(void *cbuf, void *coeffs); /* Convolution in the frequency-domain. */ void convolver_convolve(void *input_cbuf, void *coeffs, void *output_cbuf); void convolver_crossfade_inplace(void *input_cbuf, void *crossfade_cbuf, void *buffer_cbuf); /* Convolution in the frequency-domain, with the result added to the output. */ void convolver_convolve_add(void *input_cbuf, void *coeffs, void *output_cbuf); /* Convolve with dirac pulse. */ void convolver_dirac_convolve(void *input_cbuf, void *output_cbuf); void convolver_dirac_convolve_inplace(void *cbuf); /* Transform from frequency-domain to time-domain. */ void convolver_freq2time(void *input_cbuf, void *output_cbuf); /* Evaluate convolution output by transforming it back to time-domain, do overlap-save and transform back to frequency-domain again. Used when filters are put in series. The 'buffer_cbuf' must be 1.5 times the cbufsize and must be cleared the first call, and be kept the following calls. Input and output buffers may be the same. */ void convolver_convolve_eval(void *input_cbuf, void *buffer_cbuf, void *output_cbuf); /* Convert from the convolver's own time-domain format to raw sample format. */ void convolver_cbuf2raw(void *cbuf, void *outbuf, struct buffer_format *bf, bool_t apply_dither, void *dither_state, struct bfoverflow *overflow); /* Return the size of the convolver's internal format corresponding to the given number of samples. */ int convolver_cbufsize(void); /* Convert a set of coefficients to the convolver's internal frequency domain format. */ void * convolver_coeffs2cbuf(void *coeffs, int n_coeffs, double scale, void *optional_dest); /* Fast version of the above to be used in runtime */ void convolver_runtime_coeffs2cbuf(void *src, void *dest); /* Make a quick sanity check */ bool_t convolver_verify_cbuf(void *cbufs[], int n_cbufs); /* Dump the contents of a cbuf to a text-file (after conversion back to coefficient list) */ void convolver_debug_dump_cbuf(const char filename[], void *cbufs[], int n_cbufs); /* Get a plan for the FFT lib used. Do not free it. */ void * convolver_fftplan(int order, int invert, int inplace); typedef struct _td_conv_t_ td_conv_t; int convolver_td_block_length(int n_coeffs); td_conv_t * convolver_td_new(void *coeffs, int n_coeffs); void convolver_td_convolve(td_conv_t *tdc, void *overlap_block); /* Initialise convolver. Some convolvers may ignore 'config_filename' */ bool_t convolver_init(const char config_filename[], int length, int realsize); #endif brutefir-1.0o/convolver_xmm.c0000644000175000017500000000363212752354353015744 0ustar andersanders/* * (c) Copyright 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include "asmprot.h" #include void convolver_sse_convolve_add(void *input_cbuf, void *coeffs, void *output_cbuf, int loop_counter) { __m128 *b = (__m128 *)input_cbuf; __m128 *c = (__m128 *)coeffs; __m128 *d = (__m128 *)output_cbuf; float d1s, d2s; int i; d1s = ((float *)d)[0] + ((float *)b)[0] * ((float *)c)[0]; d2s = ((float *)d)[4] + ((float *)b)[4] * ((float *)c)[4]; for (i = 0; i < loop_counter; i++) { int n = i << 1; d[n+0] = _mm_add_ps(d[n+0], _mm_sub_ps(_mm_mul_ps(b[n+0], c[n+0]), _mm_mul_ps(b[n+1], c[n+1]))); d[n+1] = _mm_add_ps(d[n+1], _mm_add_ps(_mm_mul_ps(b[n+0], c[n+1]), _mm_mul_ps(b[n+1], c[n+0]))); } ((float *)d)[0] = d1s; ((float *)d)[4] = d2s; } #ifdef __SSE2__ void convolver_sse2_convolve_add(void *input_cbuf, void *coeffs, void *output_cbuf, int loop_counter) { __m128d *b = (__m128d *)input_cbuf; __m128d *c = (__m128d *)coeffs; __m128d *d = (__m128d *)output_cbuf; double d1s, d2s; int i; d1s = ((double *)d)[0] + ((double *)b)[0] * ((double *)c)[0]; d2s = ((double *)d)[4] + ((double *)b)[4] * ((double *)c)[4]; for (i = 0; i < loop_counter; i++) { int n = i << 2; d[n+0] = _mm_add_pd(d[n+0], _mm_sub_pd(_mm_mul_pd(b[n+0], c[n+0]), _mm_mul_pd(b[n+2], c[n+2]))); d[n+1] = _mm_add_pd(d[n+1], _mm_sub_pd(_mm_mul_pd(b[n+1], c[n+1]), _mm_mul_pd(b[n+3], c[n+3]))); d[n+2] = _mm_add_pd(d[n+2], _mm_add_pd(_mm_mul_pd(b[n+0], c[n+2]), _mm_mul_pd(b[n+2], c[n+0]))); d[n+3] = _mm_add_pd(d[n+3], _mm_add_pd(_mm_mul_pd(b[n+1], c[n+3]), _mm_mul_pd(b[n+3], c[n+1]))); } ((double *)d)[0] = d1s; ((double *)d)[4] = d2s; } #endif brutefir-1.0o/crosspath.txt0000644000175000017500000027411312752354353015455 0ustar andersanders0 -3.0517578125000000e-05 -3.0516377591993660e-05 -6.1027098126942292e-05 -3.0514212994603440e-05 -6.1011862271698192e-05 -6.0994014347670600e-05 -6.0982514696661383e-05 -3.0497039915644564e-05 -6.0901304095750675e-05 -3.0418867027037777e-05 -6.0756043239962310e-05 -3.4040155139081207e-09 -3.0375431379070505e-05 -6.0632995882770047e-05 -6.0688489611493424e-05 -3.0424469514400698e-05 -9.0661895228549838e-05 -3.0213253921829164e-05 -1.4076213972202822e-07 -5.9958350902888924e-05 -6.0039412346668541e-05 -3.0144368793116882e-05 -6.0148737247800454e-05 -3.0253859222284518e-05 -8.9539229520596564e-05 -2.9902137612225488e-05 -5.9346697526052594e-05 -2.9571361665148288e-05 -4.6377996909541253e-07 -8.8086795585695654e-05 -5.9032910940004513e-05 -5.8654008171288297e-05 -2.9778533644275740e-05 -5.7592086477598059e-07 -8.7229054770432413e-05 -5.8089895901503041e-05 -8.6879175796639174e-05 -9.5887321549525950e-07 -5.7960318372352049e-05 -2.8991125873290002e-05 -8.5163177573122084e-05 -2.7959382350672968e-05 -2.7767315259552561e-05 -2.6832949515664950e-05 -2.7183961719856597e-05 -2.6895242626778781e-05 -5.5063534091459587e-05 -2.7163961931364611e-05 -2.7137000870425254e-05 -2.5499084586044773e-05 -5.1935152441728860e-05 2.3144004899222637e-06 -2.5991806978709064e-05 -2.7215597583563067e-05 -8.2184516941197217e-05 -5.5873777455417439e-05 -8.1484839029144496e-05 -2.6786377929965965e-05 -2.5238923626602627e-05 -2.3041346139507368e-05 -2.3920629246276803e-05 -2.4177994419005699e-05 -7.6134463597554713e-05 -2.5932342396117747e-05 -2.6504336346988566e-05 -4.9185608077095821e-05 -2.5180241209454834e-05 -4.8279067414114252e-05 -4.9525639042258263e-05 -2.4842236598487943e-05 -4.8908987082540989e-05 -2.3012868041405454e-05 -2.2451293261838146e-05 -4.3045922211604193e-05 -2.0995992599637248e-05 -2.0760200641234405e-05 -2.3568038159282878e-05 -4.6151784772519022e-05 -4.8077039537020028e-05 -4.6677261707372963e-05 -4.7173063649097458e-05 -2.3568598408019170e-05 -4.5395932829705998e-05 -2.2101146896602586e-05 -4.3028368963859975e-05 -3.9696860767435282e-05 -1.8312979591428302e-05 5.3434127949003596e-06 -1.4469780580839142e-05 8.2794822446885519e-06 -5.0434999138815328e-05 3.0561805033357814e-05 1.0856922017410398e-05 -6.5669792093103752e-06 9.5237828645622358e-06 -9.9522185337264091e-06 -1.3842964108334854e-05 -1.3832994227414019e-05 -3.3321895898552611e-05 4.8600281843391713e-06 -1.2737139513774309e-05 -9.8163836810272187e-06 -1.0671953532437328e-05 -9.1741267169709317e-06 -1.1576057659112848e-05 -1.1479984095785767e-05 -1.4935163562768139e-05 -1.4989986084401608e-05 -3.2934887713054195e-05 -1.4721541447215714e-05 -2.9383305445662700e-05 -1.0062195542559493e-05 4.3541358536458574e-06 -9.9500348369474523e-06 -2.9162825740058906e-05 -3.1352275982499123e-05 -3.5731212847167626e-05 -6.8394056143006310e-06 -4.6653585741296411e-05 -1.3093132110952865e-05 -9.3974222181714140e-06 -4.2907140596071258e-06 -1.8332242689211853e-05 1.7756459783413447e-05 -2.5762928999029100e-05 -2.7860818590852432e-05 -3.0078223062446341e-05 -1.4133038348518312e-05 -1.1208559044462163e-05 -5.8484924920776393e-06 -1.7208594726980664e-05 4.1929260987672023e-06 -2.2062718926463276e-05 -1.2767504813382402e-05 -2.5111556169576943e-05 -1.2576919289131183e-05 -2.3179301933851093e-05 -1.3749056051892694e-05 -2.9057715437375009e-05 -3.2929638109635562e-05 -3.7266352592268959e-05 -3.3781863749027252e-05 -2.7703194064088166e-05 -1.6195224816328846e-05 7.5209618444205262e-06 4.4200646698300261e-06 9.2804075393360108e-06 -9.5440445875283331e-06 -1.4922859918442555e-05 -7.7261665865080431e-06 -3.7900841221016890e-07 -1.0492937690287363e-05 -3.5484770251059672e-06 -1.0223732715530787e-05 -8.3594695752253756e-06 -1.1283266758255195e-05 -2.1251664293231443e-05 -1.5460338545381092e-05 -2.8048607418895699e-05 -2.1914138415013440e-05 -1.9156488633598201e-05 -2.0086006770725362e-05 -3.1043698982102796e-05 -2.1512316379812546e-05 -1.5338322555180639e-05 2.7607331958279246e-06 -8.1060005641120370e-07 1.0807640137500130e-05 4.7149201236607041e-06 -3.3226783102691115e-07 2.6227569378534099e-06 5.6389189921901561e-06 1.1373604138498195e-05 9.2239188234088942e-06 1.4618902923757560e-06 -2.7718770070350729e-06 -1.5535373677266762e-05 -1.7896634744829498e-05 -2.0292414774303325e-05 -1.1455596904852428e-05 5.6754839761197218e-07 1.7315764125669375e-05 2.2330550564220175e-05 1.8372114936937578e-05 9.6360363386338577e-06 -1.9910976334358566e-06 -8.1756097642937675e-06 -1.2267447345948312e-05 -9.0392013589735143e-06 -5.7101224228972569e-06 -2.3238428070726513e-08 1.6849268149599084e-06 2.9846903544239467e-06 1.2727307421300793e-06 -2.7255168788542505e-06 -8.5556321209878661e-06 -1.5588178939651698e-05 -1.9533033992047422e-05 -2.4741853849263862e-05 -2.4512346499250270e-05 -1.8989559976034798e-05 -5.0753506002365611e-06 1.0239997209282592e-05 2.3169593987404369e-05 2.8019341698382050e-05 2.1293852114467882e-05 5.9743920246546622e-06 -1.2046821211697534e-05 -2.5050278054550290e-05 -2.8467989977798425e-05 -2.1904890672885813e-05 -1.1353447916917503e-05 -2.1846440176886972e-06 3.8201324059627950e-07 -3.5116959224978928e-06 -9.3289427240961231e-06 -1.2293406143726315e-05 -9.0957610154873691e-06 -1.7195367263411754e-06 5.3443441174749751e-06 6.9050929596414790e-06 1.5629991594323656e-06 -7.9744204413145781e-06 -1.5923156752251089e-05 -1.6943553418968804e-05 -8.9927652879850939e-06 4.7573466872563586e-06 1.8242864825879224e-05 2.5671954063000157e-05 2.5139826902886853e-05 1.8918899513664655e-05 1.1948768587899394e-05 8.7041771621443331e-06 1.0802690667333081e-05 1.6001002222765237e-05 2.0789168047485873e-05 2.2154823454911821e-05 1.9692612113431096e-05 1.4854939763608854e-05 1.0426774679217488e-05 7.5857119554711971e-06 6.2094077293295413e-06 5.2472778406809084e-06 4.3207242015341762e-06 3.9904257391754072e-06 5.3419817049871199e-06 8.4391786003834568e-06 1.2154832802480087e-05 1.4181998267304152e-05 1.3541402040573303e-05 1.0761191333585884e-05 8.1038888311013579e-06 7.3544538281566929e-06 8.1678535934770480e-06 8.3466647993191145e-06 5.3265321184881032e-06 -1.6840233456605347e-06 -1.0333008503948804e-05 -1.6434461940662004e-05 -1.6235011571552604e-05 -9.3868038675282151e-06 7.4156571372441249e-07 8.8321221483056433e-06 1.0873640349018387e-05 6.0574975577765144e-06 -2.5454028218518943e-06 -1.0603003829601221e-05 -1.4556323549186345e-05 -1.3710891835216898e-05 -9.3475573521573097e-06 -3.5246303013991565e-06 2.0823752038268140e-06 6.8836029640806373e-06 1.0675083103706129e-05 1.2981390682398342e-05 1.3246061826066580e-05 1.1323645594529808e-05 7.9882765930960886e-06 4.4932166929356754e-06 1.8886210000346182e-06 3.7509195749407809e-07 -3.9212852698256029e-07 -8.5353849499369971e-07 -7.4400014682396431e-07 -1.8852888672427071e-07 8.7947705651458818e-07 1.5132504813664127e-06 9.6898565971059725e-07 -1.8281407392350957e-06 -6.3178413256537169e-06 -1.1416028428357095e-05 -1.5564117347821593e-05 -1.7806307369028218e-05 -1.7853395547717810e-05 -1.6167417925316840e-05 -1.3244005458545871e-05 -9.6448120530112647e-06 -5.8297582654631697e-06 -2.8879658202640712e-06 -1.9496383174555376e-06 -4.3776353777502663e-06 -1.0060233762487769e-05 -1.7267790099140257e-05 -2.2607266146223992e-05 -2.2531390641233884e-05 -1.5325320418924093e-05 -3.0976527796156006e-06 9.5837058324832469e-06 1.7157195543404669e-05 1.6467769455630332e-05 8.4117200458422303e-06 -2.5705098778416868e-06 -1.1097440619778354e-05 -1.3378615221881773e-05 -9.3829585239291191e-06 -2.2484948658529902e-06 3.5153564112988533e-06 4.4579019231605344e-06 -6.1094726788724074e-07 -9.9297285487409681e-06 -2.0193961972836405e-05 -2.7225365556660108e-05 -2.8342117730062455e-05 -2.2956328393775038e-05 -1.3452188795781694e-05 -4.0669792724656872e-06 9.2305197085806867e-07 -1.2256663239895715e-07 -6.2252620409708470e-06 -1.4172234841680620e-05 -2.1027168259024620e-05 -2.4902125005610287e-05 -2.5883173293550499e-05 -2.4790468160063028e-05 -2.2574524336960167e-05 -1.9270737539045513e-05 -1.4959251529944595e-05 -9.9153230621595867e-06 -5.1900515245506540e-06 -1.7230264575118781e-06 -2.1176654740884260e-07 -6.2553613133786712e-07 -2.7316568775859196e-06 -5.9433768910821527e-06 -9.9211683846078813e-06 -1.3807251889375038e-05 -1.6742435036576353e-05 -1.7959599063033238e-05 -1.7811755242291838e-05 -1.7271511751459911e-05 -1.7497484805062413e-05 -1.8166621885029599e-05 -1.8002050637733191e-05 -1.5067369531607255e-05 -9.0558369265636429e-06 -1.7631400623940863e-06 3.1143240448727738e-06 2.9370969514275203e-06 -2.2865438040753361e-06 -9.1992314992239699e-06 -1.2788139429176226e-05 -9.2611198851955123e-06 6.3230754676624201e-07 1.1994803571724333e-05 1.7701315300655551e-05 1.3121447409503162e-05 -7.7040300539010786e-07 -1.7442522221244872e-05 -2.8298160032136366e-05 -2.7352274628356099e-05 -1.5045757209009025e-05 2.6029497348645236e-06 1.6877884263521992e-05 2.2062789867050014e-05 1.7595826648175716e-05 8.5693936853203923e-06 2.5425110834476072e-06 4.7713156163808890e-06 1.5273599274223670e-05 2.8901780751766637e-05 3.8346344808815047e-05 3.8410631532315165e-05 2.9183258448028937e-05 1.6266822058241814e-05 6.8222734626033343e-06 5.4919705689826515e-06 1.2106330359529238e-05 2.2251553673413582e-05 3.0446242817561142e-05 3.3552467357367277e-05 3.1656054488848895e-05 2.7680660423357040e-05 2.3911878088256344e-05 2.1442256183945574e-05 1.9121214791084640e-05 1.5824884030735120e-05 1.1532090866239741e-05 8.2565220509422943e-06 8.0028039519675076e-06 1.2142352716182359e-05 1.9685639927047305e-05 2.8001670216326602e-05 3.4208766010124236e-05 3.7110632547410205e-05 3.7313784559955820e-05 3.7166733818594366e-05 3.8538775697816163e-05 4.2043273424496874e-05 4.5816632336936891e-05 4.7550580347888172e-05 4.6081280743237585e-05 4.2904517613351345e-05 4.0957154851639643e-05 4.2966028559021652e-05 4.8263871576637030e-05 5.3526291594607756e-05 5.4278058087220415e-05 4.9196387408301234e-05 4.1086979763349518e-05 3.5762284824158996e-05 3.7839276046724990e-05 4.7088517021620646e-05 5.7494489738019183e-05 6.0880011005792767e-05 5.1582734158728272e-05 3.0395405701710843e-05 4.0397653719992377e-06 -1.8069826182909310e-05 -2.9676433769054711e-05 -2.9967357477289625e-05 -2.3457811039406806e-05 -1.6262360077234916e-05 -1.3078073607175611e-05 -1.5593321222695522e-05 -2.2185802663443610e-05 -2.9871585866203532e-05 -3.6074365198146552e-05 -3.8771591789554805e-05 -3.7076308217365295e-05 -3.1039904570207000e-05 -2.2394589905161411e-05 -1.4102151908446103e-05 -9.3497565103461966e-06 -9.9379676612443291e-06 -1.5087008250702638e-05 -2.1760964955319650e-05 -2.6278543373337016e-05 -2.6445413823239505e-05 -2.2770662326365709e-05 -1.7900249076774344e-05 -1.5500072549912147e-05 -1.7637572454987094e-05 -2.3768105165800080e-05 -3.1231553293764591e-05 -3.6216813896317035e-05 -3.6413122870726511e-05 -3.1787465559318662e-05 -2.4459008272970095e-05 -1.7399102944182232e-05 -1.2994698408874683e-05 -1.2255626643309370e-05 -1.4224420738173649e-05 -1.6908139514271170e-05 -1.8507149434299208e-05 -1.8228973203804344e-05 -1.6833182598929852e-05 -1.5890938811935484e-05 -1.6731955838622525e-05 -1.9671635527629405e-05 -2.3220520233735442e-05 -2.5443969207117334e-05 -2.5111990908044390e-05 -2.2513602743856609e-05 -1.9646455257316120e-05 -1.8616508896229789e-05 -2.0103765564272180e-05 -2.2727597752236761e-05 -2.4148615921149030e-05 -2.2824013285571709e-05 -1.9453978893579915e-05 -1.6062918803072535e-05 -1.5001631254563108e-05 -1.6679176042089239e-05 -1.9326758774695918e-05 -2.0199682694510557e-05 -1.7315906006842852e-05 -1.0918140105786733e-05 -3.5674738683155738e-06 1.5371783774753567e-06 2.4142000256688334e-06 -1.0696302297219518e-06 -7.1434760684496723e-06 -1.3187571312300861e-05 -1.7174501408590004e-05 -1.8684917449718341e-05 -1.8689715943764895e-05 -1.8770402675727382e-05 -1.9946404790971428e-05 -2.1793668565806001e-05 -2.2381866074283607e-05 -2.0010393200209364e-05 -1.4155257304082625e-05 -6.4620380726410076e-06 -1.3187167269279598e-07 2.0154996036580997e-06 -6.4887274220382096e-07 -5.9548642639128957e-06 -1.0345576811232604e-05 -1.1139760317746550e-05 -8.3515787991927937e-06 -4.5854376367060468e-06 -3.2282230222335784e-06 -6.4638488765922375e-06 -1.3566621419158764e-05 -2.1774540073238313e-05 -2.7471156499814242e-05 -2.8312646463746205e-05 -2.4554252377129160e-05 -1.8417869796394370e-05 -1.2643738955375738e-05 -8.8336291810264811e-06 -7.0826854425831698e-06 -5.9927288020844571e-06 -4.3606851249933243e-06 -1.1764088867494138e-06 3.2975544854707550e-06 7.9426154115935788e-06 1.1548654583748430e-05 1.3002116247662343e-05 1.2181626516394317e-05 9.8912569228559732e-06 7.4061472332687117e-06 5.6067142395477276e-06 4.4632606659433804e-06 3.9096394175430760e-06 2.9564132546511246e-06 1.0158908025914570e-06 -2.1273558559187222e-06 -5.9487920225365087e-06 -9.6024959930218756e-06 -1.2415481251082383e-05 -1.4115139492787421e-05 -1.4302433555712923e-05 -1.2779420103470329e-05 -8.7443095253547654e-06 -1.4861245745123597e-06 8.8299766503041610e-06 1.9912558855139650e-05 2.7894204322365113e-05 2.9479224394890480e-05 2.3941654944792390e-05 1.4389486750587821e-05 6.4005716922110878e-06 4.9013210627890658e-06 1.0924346497631632e-05 2.0525934814941138e-05 2.7014699298888445e-05 2.4273915187222883e-05 1.1645985068753362e-05 -6.2241906562121585e-06 -2.0596831745933741e-05 -2.4716799089219421e-05 -1.6884416254470125e-05 -1.8946335558212013e-06 1.2278542271815240e-05 1.9088482076767832e-05 1.7304802895523608e-05 1.0881276466534473e-05 5.9769681683974341e-06 6.7739401856670156e-06 1.3402033800957724e-05 2.2016311049810611e-05 2.8063725039828569e-05 2.8423539333743975e-05 2.3609585696249269e-05 1.6419671737821773e-05 1.0749100511020515e-05 8.9496488726581447e-06 1.1259044185862876e-05 1.5838933904888108e-05 2.0341725758044049e-05 2.2980346329859458e-05 2.3576390958623961e-05 2.2937445464776829e-05 2.2549837012775242e-05 2.2920663468539715e-05 2.3489468730986118e-05 2.2625958081334829e-05 1.8879754861700349e-05 1.1480304237920791e-05 1.5344796793215210e-06 -8.9420855147182010e-06 -1.7023468899424188e-05 -2.0003017198177986e-05 -1.5838135368539952e-05 -4.8750148380349856e-06 1.0665838999557309e-05 2.6062138203997165e-05 3.6201985494699329e-05 3.8084403058746830e-05 3.1734860385768116e-05 2.1142197510926053e-05 1.1385821380827110e-05 6.8977083174104337e-06 8.9135992311639711e-06 1.5591549527016468e-05 2.3797194444341585e-05 3.0518323910655454e-05 3.4467855584807694e-05 3.5554661735659465e-05 3.4922253689728677e-05 3.3502947189845145e-05 3.1967254471965134e-05 2.9804077712469734e-05 2.6320554752601311e-05 2.0947554730810225e-05 1.4741729501110967e-05 1.0047099749499466e-05 9.2446116468636319e-06 1.3073811715003103e-05 1.9624745618784800e-05 2.5052102500922047e-05 2.6195888494839892e-05 2.2807165805716068e-05 1.8107941286871210e-05 1.6246296581812203e-05 1.9624118067440577e-05 2.6024896214948967e-05 3.0836075893603265e-05 2.9362641726038419e-05 2.1060910512460396e-05 9.3696053227176890e-06 1.6190017504413845e-07 -2.6774991965794470e-06 1.2021704378639697e-06 8.4043604147154838e-06 1.4311417544377036e-05 1.5696425180067308e-05 1.2178958058939315e-05 5.6156604841817170e-06 -4.1757937196962303e-07 -3.2190184811042855e-06 -8.9641548584040720e-07 6.3076922742766328e-06 1.6544607206014916e-05 2.6574241928756237e-05 3.3108561183325946e-05 3.3790100133046508e-05 2.8579035642906092e-05 1.9062244973611087e-05 8.2676433521555737e-06 -1.1800864285760326e-06 -7.2050197559292428e-06 -9.4446959337801673e-06 -8.7438384070992470e-06 -6.8954805101384409e-06 -5.9611843425955158e-06 -7.1751514951756690e-06 -1.0284056770615280e-05 -1.4154265045362990e-05 -1.6261839846265502e-05 -1.4983332221163437e-05 -9.9652334029087797e-06 -3.2034552077675471e-06 1.9700175926118391e-06 2.2604745026910678e-06 -3.4145737117796671e-06 -1.3028971807216294e-05 -2.2262569473241456e-05 -2.7095635232399218e-05 -2.5741044737515040e-05 -2.0268054868211038e-05 -1.4492134141619317e-05 -1.2113714547012933e-05 -1.3900089470553212e-05 -1.7694068446871825e-05 -2.0135197701165453e-05 -1.9797971617663279e-05 -1.7751517589204013e-05 -1.7057362128980458e-05 -1.9940078345825896e-05 -2.5970488422899507e-05 -3.2107840524986386e-05 -3.4381391742499545e-05 -3.0852726922603324e-05 -2.3228236386785284e-05 -1.5167772289714776e-05 -1.0451110938447528e-05 -1.0095434845425189e-05 -1.2521823919087183e-05 -1.4950382137612905e-05 -1.6506182873854414e-05 -1.8646558601176366e-05 -2.3701259124209173e-05 -3.2578500395175070e-05 -4.3736472434829921e-05 -5.3089395805727690e-05 -5.7569483033148572e-05 -5.5768752645235509e-05 -4.9466652853880078e-05 -4.2209168896079063e-05 -3.7334386433940381e-05 -3.6022032873006538e-05 -3.7589670682791620e-05 -3.9919075788930058e-05 -4.1477323975414038e-05 -4.1896477341651917e-05 -4.1396357119083405e-05 -3.9855982322478667e-05 -3.7266967410687357e-05 -3.3314478059764951e-05 -2.9173652364988811e-05 -2.7099100407212973e-05 -2.9715487471548840e-05 -3.7663190596504137e-05 -4.8973484808811918e-05 -5.9093315940117463e-05 -6.3824176322668791e-05 -6.1599057517014444e-05 -5.4710209951736033e-05 -4.6945322537794709e-05 -4.2159306758549064e-05 -4.1572362533770502e-05 -4.4539498048834503e-05 -4.8529123887419701e-05 -5.1617855206131935e-05 -5.1736315072048455e-05 -4.8388763389084488e-05 -4.1160441469401121e-05 -3.1706069421488792e-05 -2.2759755665902048e-05 -1.8083548638969660e-05 -2.0147528630332090e-05 -2.9166003514546901e-05 -4.2096631659660488e-05 -5.4463300330098718e-05 -6.1779326642863452e-05 -6.2105274992063642e-05 -5.6116707128239796e-05 -4.6967667003627867e-05 -3.7958667235216126e-05 -3.1797451811144128e-05 -2.9281251045176759e-05 -2.9582399292849004e-05 -3.0627896194346249e-05 -3.0404053177335300e-05 -2.7670725103234872e-05 -2.2297717805486172e-05 -1.5070821973495185e-05 -7.3470173447276466e-06 -2.7380411893318524e-07 5.0533149078546558e-06 8.2581846072571352e-06 9.1912252173642628e-06 8.7459757196484134e-06 7.8584125731140375e-06 7.7352387961582281e-06 8.7088119471445680e-06 1.0915311577264220e-05 1.3335565199668054e-05 1.5381188859464601e-05 1.6761718143243343e-05 1.8660601199371740e-05 2.2100000933278352e-05 2.7997060897178017e-05 3.5561828553909436e-05 4.3158106564078480e-05 4.7761976020410657e-05 4.7385117795784026e-05 4.1617582610342652e-05 3.2464871765114367e-05 2.3209158825920895e-05 1.8133288904209621e-05 1.9466890080366284e-05 2.7733480237657204e-05 4.0461036405758932e-05 5.4088355682324618e-05 6.4325773564632982e-05 6.9078902015462518e-05 6.7770815803669393e-05 6.2765160691924393e-05 5.6792996474541724e-05 5.2230105211492628e-05 4.9580416089156643e-05 4.8365076509071514e-05 4.7354726120829582e-05 4.6682514948770404e-05 4.7105979319894686e-05 5.0175141950603575e-05 5.6077544286381453e-05 6.3446837884839624e-05 6.9650777732022107e-05 7.2416441980749369e-05 7.0952570240478963e-05 6.7170381953474134e-05 6.3679617596790195e-05 6.2785526097286493e-05 6.4809588366188109e-05 6.8500172346830368e-05 7.1372225647792220e-05 7.1803398896008730e-05 6.9714267738163471e-05 6.7023873270954937e-05 6.5551263105589896e-05 6.6739448811858892e-05 6.9648907810915262e-05 7.2635870310477912e-05 7.3843308200594038e-05 7.2766932134982198e-05 7.0027730544097722e-05 6.7666893301066011e-05 6.6870998125523329e-05 6.8063731305301189e-05 6.9281944888643920e-05 6.8406159698497504e-05 6.3428618886973709e-05 5.4903830459807068e-05 4.4957705540582538e-05 3.7307523598428816e-05 3.4807548217941076e-05 3.8194568332983181e-05 4.4962507672607899e-05 5.1580376748461276e-05 5.4203719628276303e-05 5.2361254347488284e-05 4.7658468247391284e-05 4.3780451960628852e-05 4.2787563870660961e-05 4.4245876779314131e-05 4.5093300286680460e-05 4.2370713345007971e-05 3.4734861401375383e-05 2.4623721401439980e-05 1.6069656339823268e-05 1.3273893273435533e-05 1.6863588825799525e-05 2.4341094103874639e-05 3.0943148885853589e-05 3.3139178412966430e-05 2.9839868147973903e-05 2.3733407942927442e-05 1.8165534129366279e-05 1.5691508451709524e-05 1.6205673091462813e-05 1.7973281501326710e-05 1.8703971363720484e-05 1.7686703358776867e-05 1.4859921975585166e-05 1.1643063771771267e-05 8.7637199612800032e-06 6.8152776293572970e-06 5.1318461373739410e-06 3.5839607335219625e-06 1.7293000382778700e-06 4.6691380362062773e-07 3.4782465263560880e-07 1.8981961602548836e-06 4.0669742702448275e-06 5.1442307267279830e-06 3.2987272788886912e-06 -1.5802504549355945e-06 -8.4602652350440621e-06 -1.4812070730840787e-05 -1.9292727301944979e-05 -2.1876378013985232e-05 -2.4848799512255937e-05 -3.0270004572230391e-05 -3.9004662539809942e-05 -4.8357069317717105e-05 -5.4365445976145566e-05 -5.3450297855306417e-05 -4.6296907385112718e-05 -3.6732431908603758e-05 -3.0953327950555831e-05 -3.2766256481409073e-05 -4.2551546357572079e-05 -5.6040320487227291e-05 -6.8015164288226515e-05 -7.3988900112453848e-05 -7.3348775913473219e-05 -6.8027606175746769e-05 -6.2389415688812733e-05 -5.9768284700112417e-05 -6.2059414631221443e-05 -6.8347420892678201e-05 -7.7017422881908715e-05 -8.4998318925499916e-05 -9.0365836513228714e-05 -9.2170914285816252e-05 -9.1514608357101679e-05 -9.0161804109811783e-05 -9.0548535808920860e-05 -9.3186259618960321e-05 -9.7147567430511117e-05 -1.0006310913013294e-04 -1.0020725312642753e-04 -9.6985997515730560e-05 -9.2611800937447697e-05 -9.0078363427892327e-05 -9.1763671662192792e-05 -9.6911644504871219e-05 -1.0293152445228770e-04 -1.0614439088385552e-04 -1.0563414980424568e-04 -1.0243196447845548e-04 -1.0026858217315748e-04 -1.0185160499531776e-04 -1.0792621469590813e-04 -1.1543359141796827e-04 -1.2028520723106340e-04 -1.1872922186739743e-04 -1.1095709487562999e-04 -1.0001545160776004e-04 -9.0887420810759068e-05 -8.6468964582309127e-05 -8.7083288235589862e-05 -8.8974782556761056e-05 -8.8476786913815886e-05 -8.2592436228878796e-05 -7.2264338086824864e-05 -6.0458391089923680e-05 -5.1759914640570059e-05 -4.8270077968481928e-05 -5.0569065933814272e-05 -5.6626446166774258e-05 -6.4228726841975003e-05 -7.1377842687070370e-05 -7.7443692134693265e-05 -8.1147714809048921e-05 -8.1213700468651950e-05 -7.5369549449533224e-05 -6.3280363974627107e-05 -4.6661225496791303e-05 -3.0677460017614067e-05 -2.0737019440275617e-05 -2.1348898371797986e-05 -3.2023875974118710e-05 -4.8075031372718513e-05 -6.1543119954876602e-05 -6.5960382926277816e-05 -5.8624878874979913e-05 -4.2903957364615053e-05 -2.5086159439524636e-05 -1.2105050700483844e-05 -6.6115949266531970e-06 -7.5045695666631218e-06 -1.0069059499073774e-05 -1.0704765372793190e-05 -8.1413118095952086e-06 -4.4852367864223197e-06 -1.8078897028317442e-06 -1.4253163271860103e-06 -1.4877794001222355e-06 3.1220301366374770e-07 5.5964019338716753e-06 1.2769080967700575e-05 1.8976988940266892e-05 2.1381272745202295e-05 2.0030080122523941e-05 1.7106962332036346e-05 1.6179037629626691e-05 1.8679689674172550e-05 2.4356442736461759e-05 3.0507289920933545e-05 3.5714660043595359e-05 3.9534417737741023e-05 4.3988911784254014e-05 4.9844013119582087e-05 5.7219120208173990e-05 6.3724954088684171e-05 6.8054905568715185e-05 6.9481568061746657e-05 7.0042107836343348e-05 7.1199196099769324e-05 7.4088929977733642e-05 7.7402306487783790e-05 7.9746008850634098e-05 8.0089150287676603e-05 7.9870944318827242e-05 8.1249847426079214e-05 8.6510241089854389e-05 9.4493865617550910e-05 1.0239226685371250e-04 1.0615606151986867e-04 1.0470213601365685e-04 1.0039071639766917e-04 9.8686687124427408e-05 1.0319674765923992e-04 1.1418607027735561e-04 1.2674111349042505e-04 1.3537354243453592e-04 1.3590545859187841e-04 1.2976539437659085e-04 1.2106305803172290e-04 1.1497160448925570e-04 1.1315755546092987e-04 1.1448584700701758e-04 1.1575848475331441e-04 1.1600777361309156e-04 1.1575707321753725e-04 1.1773472215281799e-04 1.2243328092154115e-04 1.2946697825100273e-04 1.3566354755312204e-04 1.3917079195380211e-04 1.3900674821343273e-04 1.3682838471140712e-04 1.3404447236098349e-04 1.3228850730229169e-04 1.3074297748971730e-04 1.2878295092377812e-04 1.2555674766190350e-04 1.2209797569084913e-04 1.1944051220780239e-04 1.1879947851411998e-04 1.1931296467082575e-04 1.2013150262646377e-04 1.1945802543777972e-04 1.1744052608264610e-04 1.1431782331783324e-04 1.1176870611961931e-04 1.1026341962860897e-04 1.1042112600989640e-04 1.1154917592648417e-04 1.1377167538739741e-04 1.1590217036427930e-04 1.1702941992552951e-04 1.1449572775745764e-04 1.0681018466129899e-04 9.3219925474841148e-05 7.6809672464150935e-05 6.2091552536003292e-05 5.4511696362169459e-05 5.5754837376298383e-05 6.4014740928541869e-05 7.2761220508255064e-05 7.6392680057324469e-05 7.1011214458849281e-05 5.8604975492926314e-05 4.3658605136442930e-05 3.2395779271610081e-05 2.7585077987168916e-05 2.9221042495919392e-05 3.3122647437267005e-05 3.5475110053084791e-05 3.3242820791201666e-05 2.7327852876624092e-05 1.9443217752268538e-05 1.2269735634617973e-05 6.4627256506355479e-06 2.4493019736837596e-06 -8.3833532471544459e-07 -3.5727846352529014e-06 -6.7004402808379382e-06 -1.0146484783035703e-05 -1.5346769941970706e-05 -2.2487316527985968e-05 -3.1889110687188804e-05 -4.1845640225801617e-05 -5.0737355195451528e-05 -5.6433091231156141e-05 -5.9353820688556880e-05 -6.0553851653821766e-05 -6.2799976149108261e-05 -6.6848777350969613e-05 -7.2695969720371068e-05 -7.7558725024573505e-05 -7.9762859968468547e-05 -7.8635268437210470e-05 -7.6530261139851063e-05 -7.6286909461487085e-05 -8.0908415839076042e-05 -9.0307068603578955e-05 -1.0270052734995261e-04 -1.1428377911215648e-04 -1.2278663052711636e-04 -1.2738995428662747e-04 -1.3029144611209631e-04 -1.3302752631716430e-04 -1.3707957987207919e-04 -1.4059316890779883e-04 -1.4157121768221259e-04 -1.3755718828178942e-04 -1.2986015644855797e-04 -1.2120792234782130e-04 -1.1677924339892343e-04 -1.1900540266651660e-04 -1.2875669926870614e-04 -1.4246458886191249e-04 -1.5566276852041483e-04 -1.6312001389451325e-04 -1.6286103345919400e-04 -1.5495374100282788e-04 -1.4337210450321436e-04 -1.3235614460427314e-04 -1.2696829799097031e-04 -1.2871831131633371e-04 -1.3744374155066907e-04 -1.4934137288946658e-04 -1.6060000052675605e-04 -1.6691902419552207e-04 -1.6750300710555166e-04 -1.6327694174833596e-04 -1.5849911142140627e-04 -1.5637339674867690e-04 -1.5953616821207106e-04 -1.6637730004731566e-04 -1.7393004964105785e-04 -1.7686258070170879e-04 -1.7316687444690615e-04 -1.6328267520293593e-04 -1.5194664592854679e-04 -1.4349764387588948e-04 -1.4163952437229455e-04 -1.4570094936061651e-04 -1.5267575508914888e-04 -1.5773522318340838e-04 -1.5868287300691009e-04 -1.5504902694374323e-04 -1.4984305016696453e-04 -1.4548054605256766e-04 -1.4417619968298823e-04 -1.4482875121757388e-04 -1.4585601456928998e-04 -1.4439361984841526e-04 -1.3983972894493490e-04 -1.3243155262898654e-04 -1.2472060916479677e-04 -1.1834551696665585e-04 -1.1528728646226227e-04 -1.1508191528264433e-04 -1.1697232548613101e-04 -1.1851846647914499e-04 -1.1784967500716448e-04 -1.1287595407338813e-04 -1.0401703184470534e-04 -9.2293412308208644e-05 -8.1026766565628350e-05 -7.2459530201740563e-05 -6.9074711063876748e-05 -6.9986905145924538e-05 -7.3420953413005918e-05 -7.5383082730695605e-05 -7.3951792728621513e-05 -6.8127585109323263e-05 -6.0218109865672886e-05 -5.2253166359150782e-05 -4.6473047405015677e-05 -4.2478935938561335e-05 -3.9389109588228166e-05 -3.5428587580099702e-05 -3.1229865271598101e-05 -2.7482283257995732e-05 -2.5697694582049735e-05 -2.4078704882413149e-05 -1.9890609110007063e-05 -9.2687914730049670e-06 7.8768116509309039e-06 2.9066171919112094e-05 4.8180649173446000e-05 6.1127982917241752e-05 6.6216016421094537e-05 6.6858483478426933e-05 6.6746157244779170e-05 6.9350295234471560e-05 7.3910356150008738e-05 7.8694545663893223e-05 8.0998383054975420e-05 8.1765370850916952e-05 8.2955470134038478e-05 8.8498331024311483e-05 9.8999844340141863e-05 1.1306213855277747e-04 1.2568362581077963e-04 1.3346767809707671e-04 1.3469094119500369e-04 1.3222063716966659e-04 1.2996557052247226e-04 1.3280690473038703e-04 1.4143074804451317e-04 1.5472427185159177e-04 1.6832786786835641e-04 1.7957738600671291e-04 1.8622491916175932e-04 1.8978887237608433e-04 1.9183542462997139e-04 1.9534978491719812e-04 2.0093844796065241e-04 2.0835496252402663e-04 2.1480936266016215e-04 2.1818708046339452e-04 2.1640567865688354e-04 2.1096668206155300e-04 2.0429381402209401e-04 2.0108396711293608e-04 2.0370798301883042e-04 2.1301720698829740e-04 2.2506201639771461e-04 2.3537545348517597e-04 2.3907917784526944e-04 2.3591841454617679e-04 2.2818474099040031e-04 2.2166018607094884e-04 2.1966286294627935e-04 2.2313492081593722e-04 2.2823303879704326e-04 2.3179991694632918e-04 2.3150931519921869e-04 2.2997286578174680e-04 2.3037564824335277e-04 2.3580851848237216e-04 2.4413110804744065e-04 2.5107653345912695e-04 2.5100511265918612e-04 2.4302605015691370e-04 2.2979991626925766e-04 2.1760098752565682e-04 2.1068137721158564e-04 2.1074082178529352e-04 2.1440355340018868e-04 2.1791517792735249e-04 2.1750663290731609e-04 2.1357918740250170e-04 2.0679908629972488e-04 2.0015146583318710e-04 1.9427041115704924e-04 1.9021725165657699e-04 1.8736260244622827e-04 1.8679194909054786e-04 1.8784299027174711e-04 1.9029289251193404e-04 1.9115443865302950e-04 1.8833730428013951e-04 1.7987002502195537e-04 1.6764842439442873e-04 1.5449470083694905e-04 1.4411666779778898e-04 1.3700593262910843e-04 1.3247158494777977e-04 1.2724823318421841e-04 1.1941794946324080e-04 1.0791225940920413e-04 9.5398172561544925e-05 8.4083294495940208e-05 7.7116979809943587e-05 7.4424453487154096e-05 7.4966497777495533e-05 7.4743024015333503e-05 7.0925372710917145e-05 6.0638139984803274e-05 4.4937722122995183e-05 2.5503177312202752e-05 6.6928882915817667e-06 -9.3951675808057189e-06 -2.0777111785719171e-05 -2.8520526029751636e-05 -3.3499462006147951e-05 -3.7612378946505487e-05 -4.0600440115667880e-05 -4.3618500058073550e-05 -4.6299057430587709e-05 -5.0340506277279928e-05 -5.6248863984365016e-05 -6.5900880144909024e-05 -7.8863755334168673e-05 -9.5452967798337340e-05 -1.1309674300719053e-04 -1.3027462409809232e-04 -1.4359691704157740e-04 -1.5251971490215510e-04 -1.5752529725432396e-04 -1.6220819088630378e-04 -1.6853703709784895e-04 -1.7820652283262461e-04 -1.8831239140126854e-04 -1.9594897457864136e-04 -1.9822407921310514e-04 -1.9757496193051338e-04 -1.9773453823290765e-04 -2.0390376448631287e-04 -2.1665463282261044e-04 -2.3381729261018336e-04 -2.4968004436232150e-04 -2.6097992667928338e-04 -2.6627682382240891e-04 -2.6831179275177419e-04 -2.6919302763417363e-04 -2.7122144820168614e-04 -2.7345967828296125e-04 -2.7546129422262311e-04 -2.7642791974358261e-04 -2.7896012761630118e-04 -2.8517024475149810e-04 -2.9766175430268049e-04 -3.1398574355989695e-04 -3.3032320789061487e-04 -3.4049234818667173e-04 -3.4225816489197314e-04 -3.3637738670222461e-04 -3.2847648253664374e-04 -3.2307385117746890e-04 -3.2333887065760791e-04 -3.2711349194869399e-04 -3.3185424399562180e-04 -3.3355283085256815e-04 -3.3199580502696335e-04 -3.2726902281865478e-04 -3.2199796987697482e-04 -3.1638407381251454e-04 -3.1106936512514949e-04 -3.0501943547278643e-04 -2.9981334228068590e-04 -2.9653109959326684e-04 -2.9778247699141502e-04 -3.0303423409350216e-04 -3.1125085661187768e-04 -3.1830708030611277e-04 -3.2208353513851762e-04 -3.2017746707424521e-04 -3.1376362312585115e-04 -3.0382245313376188e-04 -2.9343753703869879e-04 -2.8311897767707705e-04 -2.7428776957094669e-04 -2.6609888300299644e-04 -2.5918195024132729e-04 -2.5319767883047462e-04 -2.4942529853433371e-04 -2.4696340551599860e-04 -2.4526377092115581e-04 -2.4159555323421955e-04 -2.3530106409452856e-04 -2.2521204664371908e-04 -2.1333665063139051e-04 -2.0086746371816844e-04 -1.9022301421500742e-04 -1.8033778178505599e-04 -1.7036305507645011e-04 -1.5751586761325598e-04 -1.4259954332374036e-04 -1.2714383774437010e-04 -1.1549174814717844e-04 -1.0960607323795557e-04 -1.1006551358150318e-04 -1.1220113083254546e-04 -1.1157177505083382e-04 -1.0416541044833139e-04 -9.1073983639944345e-05 -7.5381889473646879e-05 -6.2401137256529182e-05 -5.3160096285864711e-05 -4.6433237002929673e-05 -3.6715257010655478e-05 -2.0894602130283602e-05 1.9734459328901721e-06 2.6992385755875148e-05 4.9132795538753271e-05 6.3032348407432437e-05 6.9587993493769318e-05 7.1464208303950727e-05 7.4679970566648990e-05 8.1727695942390710e-05 9.3652444775216281e-05 1.0737747652456164e-04 1.2087778304703534e-04 1.3237042003311217e-04 1.4347689284477383e-04 1.5502750466112047e-04 1.6894417058210820e-04 1.8358138913754374e-04 1.9805895863100886e-04 2.0986827439628541e-04 2.1944496256764978e-04 2.2683083079755306e-04 2.3412289738189429e-04 2.4127455253619701e-04 2.4920201394706964e-04 2.5637075304985046e-04 2.6325054932385683e-04 2.6910373708233237e-04 2.7583984774537385e-04 2.8326938627287745e-04 2.9204072779975832e-04 3.0065595638006926e-04 3.0909950146451592e-04 3.1623194809071720e-04 3.2402624492533505e-04 3.3296900801360607e-04 3.4490245161578059e-04 3.5811532870866358e-04 3.7104578223079443e-04 3.8015222526155412e-04 3.8510802551172674e-04 3.8634613156318665e-04 3.8786671939305961e-04 3.9212714182212949e-04 4.0157232433557510e-04 4.1384511860087514e-04 4.2620531166903675e-04 4.3430144432932138e-04 4.3767510214820504e-04 4.3730618199333549e-04 4.3740804539993405e-04 4.3996167369186878e-04 4.4627080205827951e-04 4.5297844917513430e-04 4.5717862667515874e-04 4.5521592255681753e-04 4.4828551472164690e-04 4.3892278335988522e-04 4.3273597839288414e-04 4.3195881880819798e-04 4.3690338497981429e-04 4.4249830534681678e-04 4.4479288044385612e-04 4.4007375254295766e-04 4.3050537351518869e-04 4.1985453572124243e-04 4.1367716039530933e-04 4.1284930193796754e-04 4.1597461677156389e-04 4.1753263212740421e-04 4.1387716191820800e-04 4.0274654747918248e-04 3.8668233901262283e-04 3.6856322549283504e-04 3.5263106110505760e-04 3.3985119080170989e-04 3.3076835097745061e-04 3.2333240960724652e-04 3.1726702582091093e-04 3.1115836463868618e-04 3.0565640190616250e-04 2.9930160962976515e-04 2.9189273482188582e-04 2.8156902408227324e-04 2.6947879814542830e-04 2.5580060901120305e-04 2.4284387473016977e-04 2.3024556867312640e-04 2.1832626953255385e-04 2.0491944451350719e-04 1.9028081442229450e-04 1.7405769904144108e-04 1.5872108633629978e-04 1.4452336472459137e-04 1.3224198482930660e-04 1.1901484685949981e-04 1.0369540541432798e-04 8.4609848272521049e-05 6.4157749875448644e-05 4.4380147301126271e-05 2.8757740437868051e-05 1.6783369574113749e-05 7.7900122050778009e-06 -2.1579003259830642e-06 -1.4183278835844249e-05 -2.9814260415150784e-05 -4.6669018047396094e-05 -6.3842482632026076e-05 -7.9594996350351721e-05 -9.5370189228560776e-05 -1.1153798550367355e-04 -1.3014157593715936e-04 -1.5033465751912445e-04 -1.7202933668158948e-04 -1.9274037913419306e-04 -2.1213083527982235e-04 -2.2847860236652195e-04 -2.4317341740243137e-04 -2.5684424326755106e-04 -2.7215291629545391e-04 -2.8949230909347534e-04 -3.0934211099520326e-04 -3.2858992926776409e-04 -3.4527727984823287e-04 -3.5691977245733142e-04 -3.6568194627761841e-04 -3.7394129321910441e-04 -3.8591629709117115e-04 -4.0137115865945816e-04 -4.1924670222215354e-04 -4.3521798215806484e-04 -4.4794025598093867e-04 -4.5702524948865175e-04 -4.6611516154371202e-04 -4.7700706636533141e-04 -4.9140700139105320e-04 -5.0618412205949426e-04 -5.1898154197260737e-04 -5.2558875177055597e-04 -5.2669836441054940e-04 -5.2345549920573831e-04 -5.2048877114430070e-04 -5.2033847896382213e-04 -5.2591186249628663e-04 -5.3615693468600512e-04 -5.5043178144842386e-04 -5.6540477089583874e-04 -5.7924573775380850e-04 -5.8910326333716512e-04 -5.9577333740890026e-04 -5.9932394651696086e-04 -6.0273997951298952e-04 -6.0633488465100527e-04 -6.1135733267292380e-04 -6.1558058951050043e-04 -6.1843311414122581e-04 -6.1796722002327442e-04 -6.1546161305159330e-04 -6.1122974148020148e-04 -6.0798937920480967e-04 -6.0561054851859808e-04 -6.0549244517460465e-04 -6.0596514958888292e-04 -6.0705788200721145e-04 -6.0703163035213947e-04 -6.0640193987637758e-04 -6.0345930978655815e-04 -5.9896626044064760e-04 -5.9148087166249752e-04 -5.8178161270916462e-04 -5.6890083942562342e-04 -5.5490300292149186e-04 -5.4071762133389711e-04 -5.2954303100705147e-04 -5.2124779904261231e-04 -5.1597115816548467e-04 -5.0990370800718665e-04 -5.0109066069126129e-04 -4.8708179383538663e-04 -4.6977688907645643e-04 -4.5103087904863060e-04 -4.3463378096930683e-04 -4.2031338671222329e-04 -4.0742947021499276e-04 -3.9216896402649581e-04 -3.7336419336497784e-04 -3.4961922210641205e-04 -3.2413148437626660e-04 -2.9882349190302193e-04 -2.7688549016602337e-04 -2.5669430033303797e-04 -2.3726686777081341e-04 -2.1501975425053388e-04 -1.8999460735358298e-04 -1.6241874254774302e-04 -1.3631924230139703e-04 -1.1318583710817620e-04 -9.4625036581419408e-05 -7.7659286034759134e-05 -6.0467617004178464e-05 -3.9886315789772198e-05 -1.6918262190301903e-05 8.2350925367791206e-06 3.2750784157542512e-05 5.6916844187071547e-05 7.9696183092892170e-05 1.0220386320725083e-04 1.2300323578529060e-04 1.4313183783087879e-04 1.6179450904019177e-04 1.8127646762877703e-04 2.0167794718872756e-04 2.2460009495262057e-04 2.4780843523330986e-04 2.7021972346119583e-04 2.8929373365826905e-04 3.0626909574493766e-04 3.2204933813773096e-04 3.4051534021273255e-04 3.6259592161513865e-04 3.8892956217750907e-04 4.1598171810619533e-04 4.4154800707474351e-04 4.6262898831628263e-04 4.8052181955426931e-04 4.9583101645112038e-04 5.1164103206247091e-04 5.2781595150008798e-04 5.4510828340426087e-04 5.6173687335103750e-04 5.7845312403514981e-04 5.9456325834617019e-04 6.1166461091488600e-04 6.2840577447786927e-04 6.4505846239626408e-04 6.5963750239461660e-04 6.7336269421502948e-04 6.8669475149363279e-04 7.0252985460683703e-04 7.2058866498991847e-04 7.4099178891628981e-04 7.5991940684616566e-04 7.7561289072036743e-04 7.8577856766059995e-04 7.9270533751696348e-04 7.9846248263493180e-04 8.0731592606753111e-04 8.1978406524285674e-04 8.3573325537145138e-04 8.5105531616136432e-04 8.6379516869783401e-04 8.7131909094750881e-04 8.7521394016221166e-04 8.7658496340736747e-04 8.7862659711390734e-04 8.8112137746065855e-04 8.8412838522344828e-04 8.8464334839954972e-04 8.8245637016370893e-04 8.7666691979393363e-04 8.6983456276357174e-04 8.6253840709105134e-04 8.5683912038803101e-04 8.5180776659399271e-04 8.4795831935480237e-04 8.4363535279408097e-04 8.3898921729996800e-04 8.3184050163254142e-04 8.2227663369849324e-04 8.0889766104519367e-04 7.9380953684449196e-04 7.7741889981552958e-04 7.6213729334995151e-04 7.4641453102231026e-04 7.2976847877725959e-04 7.0919806603342295e-04 6.8582169478759170e-04 6.6046224674209952e-04 6.3697231234982610e-04 6.1641150387004018e-04 5.9928110567852855e-04 5.8205577079206705e-04 5.6272087385877967e-04 5.3856254089623690e-04 5.1127537153661251e-04 4.8164228792302310e-04 4.5241665793582797e-04 4.2348823626525700e-04 3.9664306677877903e-04 3.7151944707147777e-04 3.5036110784858465e-04 3.3146736677736044e-04 3.1338154803961515e-04 2.9048204305581748e-04 2.6042826357297599e-04 2.2156770864967257e-04 1.7829654098022729e-04 1.3495364692062140e-04 9.7579635621514171e-05 6.6809610871132463e-05 4.2064799345098436e-05 1.8282396922586486e-05 -7.3727496783249080e-06 -3.7839781725779176e-05 -7.1559312345925719e-05 -1.0757814015960321e-04 -1.4286680379882455e-04 -1.7707012011669576e-04 -2.0873871108051389e-04 -2.3910829622764140e-04 -2.6752904523164034e-04 -2.9587975586764514e-04 -3.2378098694607615e-04 -3.5304439370520413e-04 -3.8214921369217336e-04 -4.1195208905264735e-04 -4.4117792276665568e-04 -4.7125291894190013e-04 -5.0161016406491399e-04 -5.3364370251074433e-04 -5.6558544747531414e-04 -5.9734738897532225e-04 -6.2660349067300558e-04 -6.5386161440983415e-04 -6.7834241781383753e-04 -7.0234650047495961e-04 -7.2607630863785744e-04 -7.5201631989330053e-04 -7.7965582022443414e-04 -8.1009266432374716e-04 -8.4108114242553711e-04 -8.7224727030843496e-04 -9.0095010818913579e-04 -9.2790351482108235e-04 -9.5286004943773150e-04 -9.7887858282774687e-04 -1.0064106900244951e-03 -1.0368165094405413e-03 -1.0673985816538334e-03 -1.0970837902277708e-03 -1.1226028436794877e-03 -1.1443990515545011e-03 -1.1618491262197495e-03 -1.1778443586081266e-03 -1.1929358588531613e-03 -1.2090959353372455e-03 -1.2248108396306634e-03 -1.2400177074596286e-03 -1.2523452751338482e-03 -1.2621777132153511e-03 -1.2684810208156705e-03 -1.2735715135931969e-03 -1.2774106580764055e-03 -1.2814801884815097e-03 -1.2837378308176994e-03 -1.2840575072914362e-03 -1.2807247694581747e-03 -1.2756104115396738e-03 -1.2693564640358090e-03 -1.2650396674871445e-03 -1.2617566389963031e-03 -1.2597405584529042e-03 -1.2554352870211005e-03 -1.2475538533180952e-03 -1.2335727224126458e-03 -1.2149200774729252e-03 -1.1926458682864904e-03 -1.1708564125001431e-03 -1.1514065554365516e-03 -1.1366426479071379e-03 -1.1239505838602781e-03 -1.1109320912510157e-03 -1.0917725740000606e-03 -1.0650088079273701e-03 -1.0301548754796386e-03 -9.9296786356717348e-04 -9.5821573631837964e-04 -9.3110738089308143e-04 -9.1046054149046540e-04 -8.9327781461179256e-04 -8.7224296294152737e-04 -8.4321864414960146e-04 -8.0362556036561728e-04 -7.5742712942883372e-04 -7.0921768201515079e-04 -6.6631386289373040e-04 -6.3058873638510704e-04 -6.0201971791684628e-04 -5.7442282559350133e-04 -5.4309458937495947e-04 -5.0325650954619050e-04 -4.5682088239118457e-04 -4.0675955824553967e-04 -3.5935972118750215e-04 -3.1606366974301636e-04 -2.7719387435354292e-04 -2.3782587959431112e-04 -1.9613695621956140e-04 -1.5065782645251602e-04 -1.0554276377661154e-04 -6.3182611484080553e-05 -2.5254148567910306e-05 1.2648132724280003e-05 5.4666845244355500e-05 1.0569869482424110e-04 1.6302384028676897e-04 2.2165918198879808e-04 2.7323191170580685e-04 3.1580415088683367e-04 3.5201010177843273e-04 3.9152090903371572e-04 4.4038853957317770e-04 5.0118291983380914e-04 5.6634785141795874e-04 6.2705314485356212e-04 6.7480030702427030e-04 7.1094080340117216e-04 7.4186432175338268e-04 7.7849789522588253e-04 8.2533137174323201e-04 8.8194018462672830e-04 9.3989068409428000e-04 9.9281035363674164e-04 1.0369111550971866e-03 1.0764412581920624e-03 1.1161980219185352e-03 1.1615693802013993e-03 1.2107903603464365e-03 1.2608363758772612e-03 1.3059200718998909e-03 1.3458018656820059e-03 1.3817647704854608e-03 1.4191432856023312e-03 1.4593749074265361e-03 1.5031689545139670e-03 1.5467675402760506e-03 1.5893141971901059e-03 1.6292261425405741e-03 1.6690182965248823e-03 1.7078995006158948e-03 1.7454741755500436e-03 1.7775567248463631e-03 1.8036232795566320e-03 1.8239127239212394e-03 1.8442911095917225e-03 1.8685262184590101e-03 1.8994632409885526e-03 1.9328726921230555e-03 1.9637318328022957e-03 1.9858824089169502e-03 1.9999551586806774e-03 2.0087852608412504e-03 2.0189951173961163e-03 2.0315924193710089e-03 2.0447755232453346e-03 2.0508095622062683e-03 2.0460628438740969e-03 2.0296967122703791e-03 2.0089657045900822e-03 1.9900563638657331e-03 1.9781722221523523e-03 1.9699386321008205e-03 1.9602011889219284e-03 1.9423105986788869e-03 1.9159461371600628e-03 1.8833732465282083e-03 1.8507643835619092e-03 1.8193852156400681e-03 1.7892218893393874e-03 1.7550594639033079e-03 1.7156216781586409e-03 1.6708248294889927e-03 1.6260683769360185e-03 1.5838555991649628e-03 1.5453433152288198e-03 1.5056488336995244e-03 1.4608270721510053e-03 1.4071097830310464e-03 1.3483273796737194e-03 1.2886998010799289e-03 1.2343119597062469e-03 1.1843143729493022e-03 1.1360197095200419e-03 1.0829862440004945e-03 1.0243451688438654e-03 9.6157990628853440e-04 9.0174761135131121e-04 8.4832205902785063e-04 8.0222310498356819e-04 7.5727695366367698e-04 7.0832291385158896e-04 6.5053085563704371e-04 5.8648391859605908e-04 5.1967374747619033e-04 4.5595841947942972e-04 3.9573971298523247e-04 3.3829861786216497e-04 2.7922668959945440e-04 2.1789515449199826e-04 1.5394823276437819e-04 9.0051406004931778e-05 2.4868333639460616e-05 -4.3207513954257593e-05 -1.1976977111771703e-04 -2.0580019918270409e-04 -3.0032859649509192e-04 -3.9611241663806140e-04 -4.8781523946672678e-04 -5.7164230383932590e-04 -6.5209780586883426e-04 -7.3490256909281015e-04 -8.2709215348586440e-04 -9.2801987193524837e-04 -1.0335898259654641e-03 -1.1346623068675399e-03 -1.2269318103790283e-03 -1.3097475748509169e-03 -1.3904370134696364e-03 -1.4755134470760822e-03 -1.5709361759945750e-03 -1.6751377843320370e-03 -1.7843086970970035e-03 -1.8912247614935040e-03 -1.9936112221330404e-03 -2.0902722608298063e-03 -2.1846604067832232e-03 -2.2771032527089119e-03 -2.3687896318733692e-03 -2.4564815685153008e-03 -2.5398214347660542e-03 -2.6178930420428514e-03 -2.6941329706460238e-03 -2.7697063051164150e-03 -2.8461934998631477e-03 -2.9198182746767998e-03 -2.9884595423936844e-03 -3.0484129674732685e-03 -3.1020939350128174e-03 -3.1519045587629080e-03 -3.2036481425166130e-03 -3.2585244625806808e-03 -3.3158296719193459e-03 -3.3694515004754066e-03 -3.4156723413616419e-03 -3.4516246523708105e-03 -3.4819063730537891e-03 -3.5111678298562765e-03 -3.5451960284262896e-03 -3.5828596446663141e-03 -3.6205283831804991e-03 -3.6499788984656334e-03 -3.6681266501545906e-03 -3.6746705882251263e-03 -3.6762235686182976e-03 -3.6768577992916107e-03 -3.6793593317270279e-03 -3.6782929673790932e-03 -3.6684977822005749e-03 -3.6443816497921944e-03 -3.6089257337152958e-03 -3.5676080733537674e-03 -3.5290033556520939e-03 -3.4946771338582039e-03 -3.4619951620697975e-03 -3.4222865942865610e-03 -3.3710985444486141e-03 -3.3078296110033989e-03 -3.2394332811236382e-03 -3.1717985402792692e-03 -3.1092998106032610e-03 -3.0480690766125917e-03 -2.9829670675098896e-03 -2.9073085170239210e-03 -2.8214368503540754e-03 -2.7272985316812992e-03 -2.6303702034056187e-03 -2.5307312607765198e-03 -2.4272503796964884e-03 -2.3148094769567251e-03 -2.1939175203442574e-03 -2.0676709245890379e-03 -1.9449704559519887e-03 -1.8302022945135832e-03 -1.7233071848750114e-03 -1.6152465250343084e-03 -1.4983374858275056e-03 -1.3683364959433675e-03 -1.2328005395829678e-03 -1.1024603154510260e-03 -9.8770286422222853e-04 -8.8646938093006611e-04 -7.8906636917963624e-04 -6.8118085619062185e-04 -5.5858411360532045e-04 -4.2704155202955008e-04 -3.0182275804691017e-04 -1.9118106865789741e-04 -9.1655703727155924e-05 1.6143774701049551e-05 1.5136571892071515e-04 3.2262038439512253e-04 5.1704834913834929e-04 7.1222963742911816e-04 8.8777276687324047e-04 1.0445450898259878e-03 1.2002994772046804e-03 1.3817639555782080e-03 1.6011644620448351e-03 1.8515738192945719e-03 2.1076011471450329e-03 2.3486763238906860e-03 2.5702326092869043e-03 2.7915006503462791e-03 3.0371043831110001e-03 3.3217312302440405e-03 3.6338453646749258e-03 3.9480570703744888e-03 4.2409533634781837e-03 4.5147081837058067e-03 4.7938968054950237e-03 5.1121762953698635e-03 5.4813656024634838e-03 5.8827712200582027e-03 6.2736137770116329e-03 6.6211838275194168e-03 6.9242296740412712e-03 7.2206445038318634e-03 7.5559434480965137e-03 7.9504363238811493e-03 8.3732968196272850e-03 8.7621295824646950e-03 9.0598631650209427e-03 9.2612309381365776e-03 9.4147622585296631e-03 9.5930509269237518e-03 9.8333628848195076e-03 1.0109330527484417e-02 1.0341272689402103e-02 1.0457686148583889e-02 1.0448015294969082e-02 1.0381435044109821e-02 1.0355802252888680e-02 1.0427319444715977e-02 1.0557902045547962e-02 1.0640499182045460e-02 1.0573931969702244e-02 1.0350376367568970e-02 1.0069085285067558e-02 9.8747676238417625e-03 9.8422840237617493e-03 9.9111311137676239e-03 9.9105928093194962e-03 9.6852118149399757e-03 9.2168413102626801e-03 8.6614908650517464e-03 8.2433428615331650e-03 8.0863498151302338e-03 8.0945342779159546e-03 8.0014877021312714e-03 7.5584864243865013e-03 6.7492672242224216e-03 5.8435900136828423e-03 5.2379495464265347e-03 5.1571084186434746e-03 5.4576219990849495e-03 5.6818374432623386e-03 5.3895777091383934e-03 4.5160027220845222e-03 3.4977551549673080e-03 2.9963506385684013e-03 3.4009714145213366e-03 4.4501232914626598e-03 5.3321179002523422e-03 5.2436287514865398e-03 4.0598246268928051e-03 2.5518087204545736e-03 1.9073238363489509e-03 2.7810891624540091e-03 4.6053947880864143e-03 5.7763615623116493e-03 4.7906949184834957e-03 1.5637268079444766e-03 -2.1312271710485220e-03 -3.7353616207838058e-03 -1.9322470761835575e-03 1.8343898700550199e-03 3.8455966860055923e-03 7.1308412589132786e-04 -7.5013735331594944e-03 -1.6236677765846252e-02 -1.8961217254400253e-02 -1.2281900271773338e-02 -1.3005707296542823e-04 6.6296709701418877e-03 -3.7997614126652479e-03 -3.5734660923480988e-02 -8.0868199467658997e-02 -1.2082117795944214e-01 -1.3715468347072601e-01 -1.2080327421426773e-01 -8.0842636525630951e-02 -3.5714663565158844e-02 -3.7923438940197229e-03 6.6292127594351768e-03 -1.2441715807653964e-04 -1.2255863286554813e-02 -1.8909741193056107e-02 -1.6167974099516869e-02 -7.4340696446597576e-03 7.6008983887732029e-04 3.8635022938251495e-03 1.8294845940545201e-03 -1.9420338794589043e-03 -3.7295066285878420e-03 -2.0965223666280508e-03 1.6289348714053631e-03 4.8779728822410107e-03 5.8736316859722137e-03 4.7016879543662071e-03 2.8685780707746744e-03 1.9814083352684975e-03 2.6102906558662653e-03 4.1037541814148426e-03 5.2795037627220154e-03 5.3722104057669640e-03 4.5085353776812553e-03 3.4850933589041233e-03 3.0992135871201754e-03 3.5974220372736454e-03 4.5838723890483379e-03 5.4056597873568535e-03 5.6467936374247074e-03 5.3938366472721100e-03 5.0965351983904839e-03 5.2038375288248062e-03 5.8363745920360088e-03 6.7482772283256054e-03 7.5353947468101978e-03 7.9391039907932281e-03 7.9947272315621376e-03 7.9682171344757080e-03 8.1290220841765404e-03 8.5646063089370728e-03 9.1352062299847603e-03 9.6075516194105148e-03 9.8248133435845375e-03 9.8148630931973457e-03 9.7422618418931961e-03 9.7832391038537025e-03 9.9942227825522423e-03 1.0292160324752331e-02 1.0522678494453430e-02 1.0583008639514446e-02 1.0482504963874817e-02 1.0330157354474068e-02 1.0241152718663216e-02 1.0260857641696930e-02 1.0334050282835960e-02 1.0360026732087135e-02 1.0261846706271172e-02 1.0043645277619362e-02 9.7722765058279037e-03 9.5287393778562546e-03 9.3426834791898727e-03 9.1828890144824982e-03 8.9806625619530678e-03 8.6900750175118446e-03 8.3147883415222168e-03 7.9085268080234528e-03 7.5285779312252998e-03 7.2020618245005608e-03 6.9058695808053017e-03 6.5959659405052662e-03 6.2375711277127266e-03 5.8377836830914021e-03 5.4334485903382301e-03 5.0704325549304485e-03 4.7652102075517178e-03 4.5018829405307770e-03 4.2402939870953560e-03 3.9524785242974758e-03 3.6353971809148788e-03 3.3166806679219007e-03 3.0257489997893572e-03 2.7784635312855244e-03 2.5608458090573549e-03 2.3481985554099083e-03 2.1183770149946213e-03 1.8742778338491917e-03 1.6337212873622775e-03 1.4207439962774515e-03 1.2402617139741778e-03 1.0812925174832344e-03 9.1932422947138548e-04 7.4147724080830812e-04 5.4931396152824163e-04 3.6362497485242784e-04 2.0242457685526460e-04 7.4551644502207637e-05 -3.2996576919686049e-05 -1.3856240548193455e-04 -2.5914463913068175e-04 -3.9264938095584512e-04 -5.2697717910632491e-04 -6.4403517171740532e-04 -7.3996570426970720e-04 -8.2256871974095702e-04 -9.1219734167680144e-04 -1.0215766960754991e-03 -1.1537417303770781e-03 -1.2948479270562530e-03 -1.4311436098068953e-03 -1.5524821355938911e-03 -1.6646839212626219e-03 -1.7771463608369231e-03 -1.9009875832125545e-03 -2.0339395850896835e-03 -2.1685026586055756e-03 -2.2911250125616789e-03 -2.3988215252757072e-03 -2.4936436675488949e-03 -2.5865768548101187e-03 -2.6822770014405251e-03 -2.7811538893729448e-03 -2.8736668173223734e-03 -2.9541342519223690e-03 -3.0199103057384491e-03 -3.0805040150880814e-03 -3.1440660823136568e-03 -3.2184110023081303e-03 -3.2974728383123875e-03 -3.3718678168952465e-03 -3.4286794252693653e-03 -3.4667714498937130e-03 -3.4906119108200073e-03 -3.5138134844601154e-03 -3.5429550334811211e-03 -3.5803024657070637e-03 -3.6164422053843737e-03 -3.6432230845093727e-03 -3.6536694969981909e-03 -3.6520562134683132e-03 -3.6440608091652393e-03 -3.6384612321853638e-03 -3.6347045097500086e-03 -3.6299931816756725e-03 -3.6162990145385265e-03 -3.5923831164836884e-03 -3.5593733191490173e-03 -3.5260189324617386e-03 -3.4960831981152296e-03 -3.4719430841505527e-03 -3.4460769966244698e-03 -3.4127710387110710e-03 -3.3654144499450922e-03 -3.3078566193580627e-03 -3.2459557987749577e-03 -3.1899395398795605e-03 -3.1416050624102354e-03 -3.0989954248070717e-03 -3.0516260303556919e-03 -2.9929466545581818e-03 -2.9190054628998041e-03 -2.8364651370793581e-03 -2.7519552968442440e-03 -2.6741055771708488e-03 -2.6024193502962589e-03 -2.5340837892144918e-03 -2.4606091901659966e-03 -2.3793783038854599e-03 -2.2888358216732740e-03 -2.1948511712253094e-03 -2.1001030690968037e-03 -2.0078546367585659e-03 -1.9140166696161032e-03 -1.8169746035709977e-03 -1.7132131615653634e-03 -1.6069975681602955e-03 -1.5023098094388843e-03 -1.4063047710806131e-03 -1.3191233156248927e-03 -1.2390832416713238e-03 -1.1577489785850048e-03 -1.0710194474086165e-03 -9.7480241674929857e-04 -8.7364460341632366e-04 -7.7031948603689671e-04 -6.7122670589014888e-04 -5.7646486675366759e-04 -4.8741936916485429e-04 -4.0075799915939569e-04 -3.1765751191414893e-04 -2.3603539739269763e-04 -1.5768759476486593e-04 -8.0289872130379081e-05 -4.7798612285987474e-06 7.0738329668529332e-05 1.4317245222628117e-04 2.1243921946734190e-04 2.7570634847506881e-04 3.3620011527091265e-04 3.9537134580314159e-04 4.5836617937311530e-04 5.2433146629482508e-04 5.9254968073219061e-04 6.5657519735395908e-04 7.1456911973655224e-04 7.6447351602837443e-04 8.1151252379640937e-04 8.5854274220764637e-04 9.1029255418106914e-04 9.6405809745192528e-04 1.0184478014707565e-03 1.0687445756047964e-03 1.1164806783199310e-03 1.1617757845669985e-03 1.2090513482689857e-03 1.2578938622027636e-03 1.3094365131109953e-03 1.3599391095340252e-03 1.4093705685809255e-03 1.4552344800904393e-03 1.5002537984400988e-03 1.5434648375958204e-03 1.5867361798882484e-03 1.6279023839160800e-03 1.6686011804267764e-03 1.7075382638722658e-03 1.7473432235419750e-03 1.7863272223621607e-03 1.8252887530252337e-03 1.8600714392960072e-03 1.8909322097897530e-03 1.9163839751854539e-03 1.9409023225307465e-03 1.9656454678624868e-03 1.9935523159801960e-03 2.0199725404381752e-03 2.0414516329765320e-03 2.0509823225438595e-03 2.0490980241447687e-03 2.0373982843011618e-03 2.0242750179022551e-03 2.0139149855822325e-03 2.0102919079363346e-03 2.0085170399397612e-03 2.0043537952005863e-03 1.9903182983398438e-03 1.9664354622364044e-03 1.9335385877639055e-03 1.8991502001881599e-03 1.8668088596314192e-03 1.8410037737339735e-03 1.8187242094427347e-03 1.7975480295717716e-03 1.7711177933961153e-03 1.7385832034051418e-03 1.6985276015475392e-03 1.6555938636884093e-03 1.6118604689836502e-03 1.5716300113126636e-03 1.5339109813794494e-03 1.4993399381637573e-03 1.4636933337897062e-03 1.4266156358644366e-03 1.3852195115759969e-03 1.3416057918220758e-03 1.2952347751706839e-03 1.2493492104113102e-03 1.2030305806547403e-03 1.1582539882510900e-03 1.1125835590064526e-03 1.0671599302440882e-03 1.0197067167609930e-03 9.7178737632930279e-04 9.2149776173755527e-04 8.7088684085756540e-04 8.1884069368243217e-04 7.6868600444868207e-04 7.2096940129995346e-04 6.7894067615270615e-04 6.4066913910210133e-04 6.0552434297278523e-04 5.6809117086231709e-04 5.2684481488540769e-04 4.7890021232888103e-04 4.2763428064063191e-04 3.7463719490915537e-04 3.2475078478455544e-04 2.7738348580896854e-04 2.3278451408259571e-04 1.8608573009259999e-04 1.3684462464880198e-04 8.3814826211892068e-05 3.1816067348700017e-05 -1.6605263226665556e-05 -5.7102421124000102e-05 -9.2648959252983332e-05 -1.2586111552082002e-04 -1.6359450819436461e-04 -2.0712328841909766e-04 -2.5733083020895720e-04 -3.0875002266839147e-04 -3.5863296943716705e-04 -4.0272725163958967e-04 -4.4337450526654720e-04 -4.8177398275583982e-04 -5.2266888087615371e-04 -5.6565011618658900e-04 -6.1089207883924246e-04 -6.5302837174385786e-04 -6.8987742997705936e-04 -7.1758427657186985e-04 -7.3898636037483811e-04 -7.5655314140021801e-04 -7.7808101195842028e-04 -8.0677698133513331e-04 -8.4546930156648159e-04 -8.8921445421874523e-04 -9.3380978796631098e-04 -9.7151647787541151e-04 -1.0011664126068354e-03 -1.0220407275483012e-03 -1.0393531993031502e-03 -1.0552647290751338e-03 -1.0740814032033086e-03 -1.0942413937300444e-03 -1.1157578555867076e-03 -1.1350893182680011e-03 -1.1528696632012725e-03 -1.1680076131597161e-03 -1.1832292657345533e-03 -1.1974436929449439e-03 -1.2119824532419443e-03 -1.2240861542522907e-03 -1.2344270944595337e-03 -1.2415216770023108e-03 -1.2482510646805167e-03 -1.2541281757876277e-03 -1.2613150756806135e-03 -1.2673618039116263e-03 -1.2727149296551943e-03 -1.2750785099342465e-03 -1.2764984276145697e-03 -1.2764376588165760e-03 -1.2772391783073545e-03 -1.2765431310981512e-03 -1.2741221580654383e-03 -1.2659595813602209e-03 -1.2527332874014974e-03 -1.2337707448750734e-03 -1.2136463774368167e-03 -1.1937146773561835e-03 -1.1770259588956833e-03 -1.1605966137722135e-03 -1.1430928716436028e-03 -1.1200309963896871e-03 -1.0921896900981665e-03 -1.0601868852972984e-03 -1.0292882798239589e-03 -1.0010047117248178e-03 -9.7763398662209511e-04 -9.5554476138204336e-04 -9.3280686996877193e-04 -9.0497120982035995e-04 -8.7356002768501639e-04 -8.3942525088787079e-04 -8.0776074901223183e-04 -7.7955884626135230e-04 -7.5644301250576973e-04 -7.3440314736217260e-04 -7.1189180016517639e-04 -6.8519101478159428e-04 -6.5548712154850364e-04 -6.2251591589301825e-04 -5.8974383864551783e-04 -5.5701559176668525e-04 -5.2645278628915548e-04 -4.9629074055701494e-04 -4.6871911035850644e-04 -4.4257740955799818e-04 -4.2005695286206901e-04 -3.9917387766763568e-04 -3.7982893991284072e-04 -3.5776232834905386e-04 -3.3237290335819125e-04 -3.0141620663926005e-04 -2.6757811428979039e-04 -2.3157786927185953e-04 -1.9683982827700675e-04 -1.6233640781138092e-04 -1.2914453691337258e-04 -9.5119277830235660e-05 -6.2381222960539162e-05 -3.0456752938334830e-05 -2.1500184175238246e-06 2.4457889594486915e-05 4.9912378017324954e-05 7.8624652815051377e-05 1.1026163701899350e-04 1.4525174628943205e-04 1.7924689745996147e-04 2.1160181495361030e-04 2.4037560797296464e-04 2.6879669167101383e-04 2.9787136008962989e-04 3.3061389694921672e-04 3.6480778362601995e-04 4.0003913454711437e-04 4.3256106437183917e-04 4.6320020919665694e-04 4.9102166667580605e-04 5.1895465003326535e-04 5.4639088921248913e-04 5.7485455181449652e-04 6.0139200650155544e-04 6.2590802554041147e-04 6.4579874742776155e-04 6.6360365599393845e-04 6.7956105340272188e-04 6.9703237386420369e-04 7.1486213710159063e-04 7.3298043571412563e-04 7.4740691343322396e-04 7.5806543463841081e-04 7.6400768011808395e-04 7.6936528785154223e-04 7.7596807386726141e-04 7.8771176049485803e-04 8.0251524923369288e-04 8.1914523616433144e-04 8.3263462875038385e-04 8.4252445958554745e-04 8.4741297177970409e-04 8.5117027629166842e-04 8.5502304136753082e-04 8.6219608783721924e-04 8.7024958338588476e-04 8.7828468531370163e-04 8.8189798407256603e-04 8.8083912851288915e-04 8.7422097567468882e-04 8.6617399938404560e-04 8.5796415805816650e-04 8.5291941650211811e-04 8.4931339370086789e-04 8.4743311163038015e-04 8.4422866348177195e-04 8.4014923777431250e-04 8.3286740118637681e-04 8.2333863247185946e-04 8.0927542876452208e-04 7.9255003947764635e-04 7.7328551560640335e-04 7.5566326268017292e-04 7.4121437501162291e-04 7.3260709177702665e-04 7.2719156742095947e-04 7.2292663389816880e-04 7.1390892844647169e-04 6.9892243482172489e-04 6.7665055394172668e-04 6.5178389195352793e-04 6.2677206005901098e-04 6.0592725640162826e-04 5.8803654974326491e-04 5.7244504569098353e-04 5.5534753482788801e-04 5.3759012371301651e-04 5.1935610827058554e-04 5.0429732073098421e-04 4.9162161303684115e-04 4.8075511585921049e-04 4.6642977395094931e-04 4.4728213106282055e-04 4.2231386760249734e-04 3.9616972208023071e-04 3.7101653288118541e-04 3.4996977774426341e-04 3.2986610312946141e-04 3.0901000718586147e-04 2.8349435888230801e-04 2.5536402245052159e-04 2.2675564105156809e-04 2.0335579756647348e-04 1.8551960238255560e-04 1.7279235180467367e-04 1.5873889788053930e-04 1.4013846521265805e-04 1.1377445480320603e-04 8.3584927779156715e-05 5.3157389629632235e-05 2.8713380743283778e-05 1.0522080629016273e-05 -2.0831471374549437e-06 -1.4805684259044938e-05 -2.9940423701191321e-05 -5.0252601795364171e-05 -7.2821152571123093e-05 -9.7024938440881670e-05 -1.1957132664974779e-04 -1.4211054076440632e-04 -1.6416207654401660e-04 -1.8796289805322886e-04 -2.1156246657483280e-04 -2.3490005696658045e-04 -2.5519265909679234e-04 -2.7365959249436855e-04 -2.8976536123082042e-04 -3.0678606708534062e-04 -3.2445412944070995e-04 -3.4432896063663065e-04 -3.6414104397408664e-04 -3.8382940692827106e-04 -4.0114621515385807e-04 -4.1701487498357892e-04 -4.2998889693990350e-04 -4.4181427801959217e-04 -4.5232451520860195e-04 -4.6470033703371882e-04 -4.7872209688648582e-04 -4.9562758067622781e-04 -5.1142560550943017e-04 -5.2419240819290280e-04 -5.3016748279333115e-04 -5.3156551439315081e-04 -5.3093454334884882e-04 -5.3468020632863045e-04 -5.4462376283481717e-04 -5.6109449360519648e-04 -5.7787669356912374e-04 -5.9062329819425941e-04 -5.9479556512087584e-04 -5.9294368838891387e-04 -5.8827665634453297e-04 -5.8708316646516323e-04 -5.8984325733035803e-04 -5.9618899831548333e-04 -6.0059753013774753e-04 -6.0128117911517620e-04 -5.9702602447941899e-04 -5.9246015734970570e-04 -5.8969663223251700e-04 -5.9163797413930297e-04 -5.9518951456993818e-04 -5.9829512611031532e-04 -5.9659901307895780e-04 -5.9089163551107049e-04 -5.8122858172282577e-04 -5.7094782823696733e-04 -5.6046724785119295e-04 -5.5142526980489492e-04 -5.4213288240134716e-04 -5.3388217929750681e-04 -5.2614510059356689e-04 -5.2164937369525433e-04 -5.1948212785646319e-04 -5.2031280938535929e-04 -5.2038772264495492e-04 -5.1832536701112986e-04 -5.1121797878295183e-04 -5.0073524471372366e-04 -4.8715036245994270e-04 -4.7384164645336568e-04 -4.6091730473563075e-04 -4.5000092359259725e-04 -4.3925928184762597e-04 -4.2953135562129319e-04 -4.1869864799082279e-04 -4.0718517266213894e-04 -3.9214856224134564e-04 -3.7391774822026491e-04 -3.5162380663678050e-04 -3.2938588992692530e-04 -3.1023999326862395e-04 -2.9898204957135022e-04 -2.9418064514175057e-04 -2.9285674099810421e-04 -2.8686108998954296e-04 -2.7273991145193577e-04 -2.5002832990139723e-04 -2.2599149087909609e-04 -2.0643345487769693e-04 -1.9629797316156328e-04 -1.9172317115589976e-04 -1.8720465595833957e-04 -1.7497400403954089e-04 -1.5420516137965024e-04 -1.2688388233073056e-04 -1.0031655256170779e-04 -7.7905293437652290e-05 -6.2080835050437599e-05 -4.8749836423667148e-05 -3.5141885746270418e-05 -1.7370450223097578e-05 2.9545303732447792e-06 2.5373945391038433e-05 4.6807337639620528e-05 6.7818720708601177e-05 8.6557178292423487e-05 1.0371669486630708e-04 1.1685136996675283e-04 1.2665361282415688e-04 1.3341841986402869e-04 1.4198342978488654e-04 1.5491829253733158e-04 1.7469389422331005e-04 1.9750586943700910e-04 2.1850460325367749e-04 2.3132095520850271e-04 2.3656521807424724e-04 2.3768714163452387e-04 2.4350042804144323e-04 2.5739325792528689e-04 2.7975239208899438e-04 3.0231699929572642e-04 3.1833237153477967e-04 3.2257265411317348e-04 3.1929861870594323e-04 3.1531866989098489e-04 3.1995202880352736e-04 3.3471855567768216e-04 3.5671022487804294e-04 3.7652350147254765e-04 3.8909749127924442e-04 3.9228881360031664e-04 3.9233171264640987e-04 3.9518540143035352e-04 4.0672364411875606e-04 4.2455387301743031e-04 4.4345061178319156e-04 4.5417956425808370e-04 4.5307257096283138e-04 4.3989505502395332e-04 4.2245531221851707e-04 4.0824874304234982e-04 4.0476993308402598e-04 4.1183701250702143e-04 4.2602064786478877e-04 4.3920858297497034e-04 4.4717322452925146e-04 4.4757410068996251e-04 4.4451651046983898e-04 4.4165505096316338e-04 4.4359499588608742e-04 4.4908354175277054e-04 4.5585641055367887e-04 4.5764434617012739e-04 4.5207899529486895e-04 4.3771247146651149e-04 4.1953384061343968e-04 4.0171074215322733e-04 3.8992066401988268e-04 3.8396535092033446e-04 3.8294788100756705e-04 3.8175092777237296e-04 3.7903094198554754e-04 3.7346576573327184e-04 3.6803015973418951e-04 3.6321519291959703e-04 3.6054759402759373e-04 3.5742812906391919e-04 3.5282736644148827e-04 3.4463306656107306e-04 3.3499032724648714e-04 3.2497406937181950e-04 3.1777916592545807e-04 3.1261076219379902e-04 3.0896492535248399e-04 3.0279054772108793e-04 2.9339405591599643e-04 2.7969450457021594e-04 2.6505344430916011e-04 2.5114347226917744e-04 2.4088297504931688e-04 2.3295516439247876e-04 2.2686562442686409e-04 2.1938121062703431e-04 2.1012595971114933e-04 1.9779901776928455e-04 1.8404830188956112e-04 1.6826557111926377e-04 1.5194425941444933e-04 1.3391896209213883e-04 1.1661650933092460e-04 1.0086761903949082e-04 9.0207679022569209e-05 8.4227227489463985e-05 8.2816142821684480e-05 8.1521255197003484e-05 7.7812335803173482e-05 6.8218869273550808e-05 5.3809144446859136e-05 3.5408815165283158e-05 1.6542553566978313e-05 -2.0842601315962384e-06 -1.8328151782043278e-05 -3.3031534258043393e-05 -4.4973399781156331e-05 -5.5599462939426303e-05 -6.4338302763644606e-05 -7.3458795668557286e-05 -8.3730374171864241e-05 -9.7246273071505129e-05 -1.1263669875916094e-04 -1.2927103671245277e-04 -1.4345340605359524e-04 -1.5485098992940038e-04 -1.6270286869257689e-04 -1.7004921392071992e-04 -1.7779442714527249e-04 -1.8801663827616721e-04 -1.9793420506175607e-04 -2.0615449466276914e-04 -2.0975107327103615e-04 -2.1000378183089197e-04 -2.0857396884821355e-04 -2.1052264492027462e-04 -2.1734464098699391e-04 -2.3025878181215376e-04 -2.4525017943233252e-04 -2.5962261133827269e-04 -2.6891988818533719e-04 -2.7360190870240331e-04 -2.7463777223601937e-04 -2.7685138047672808e-04 -2.8269621543586254e-04 -2.9457666096277535e-04 -3.0962820164859295e-04 -3.2456134795211256e-04 -3.3337791683152318e-04 -3.3440240076743066e-04 -3.2779277535155416e-04 -3.1930173281580210e-04 -3.1357846455648541e-04 -3.1521441997028887e-04 -3.2216683030128479e-04 -3.2998880487866700e-04 -3.3158424776047468e-04 -3.2547613955102861e-04 -3.1327968463301659e-04 -3.0301345395855606e-04 -2.9995155637152493e-04 -3.0667579267174006e-04 -3.1800783472135663e-04 -3.2763823401182890e-04 -3.2944983104243875e-04 -3.2413942972198129e-04 -3.1509014661423862e-04 -3.0919356504455209e-04 -3.0872094794176519e-04 -3.1342313741333783e-04 -3.1743964063934982e-04 -3.1679432140663266e-04 -3.0865683220326900e-04 -2.9656398692168295e-04 -2.8507481329143047e-04 -2.7980143204331398e-04 -2.8092580032534897e-04 -2.8624053811654449e-04 -2.8961151838302612e-04 -2.8780192951671779e-04 -2.7936202241107821e-04 -2.6822555810213089e-04 -2.5748700136318803e-04 -2.5086346431635320e-04 -2.4689824203960598e-04 -2.4407054297626019e-04 -2.3881821834947914e-04 -2.3119192337617278e-04 -2.2138381609693170e-04 -2.1255703177303076e-04 -2.0465740817598999e-04 -1.9807480566669255e-04 -1.8997630104422569e-04 -1.8009467748925090e-04 -1.6820628661662340e-04 -1.5758078370708972e-04 -1.4932414342183620e-04 -1.4459837984759361e-04 -1.4008599100634456e-04 -1.3364027836360037e-04 -1.2212029832880944e-04 -1.0759352153399959e-04 -9.2230977315921336e-05 -8.0354278907179832e-05 -7.1938011387828737e-05 -6.6372973378747702e-05 -5.9913116274401546e-05 -5.2350238547660410e-05 -4.3526153604034334e-05 -3.6702353099826723e-05 -3.1909854442346841e-05 -2.8681110052275471e-05 -2.2622791220783256e-05 -1.2088417861377820e-05 3.5010784813493956e-06 1.9738392438739538e-05 3.2838172046467662e-05 3.9510006899945438e-05 4.2272207792848349e-05 4.5359112846199423e-05 5.5002048611640930e-05 7.2516340878792107e-05 9.6119620138779283e-05 1.1869423178723082e-04 1.3432968989945948e-04 1.3845617650076747e-04 1.3385273632593453e-04 1.2554528075270355e-04 1.2138068996137008e-04 1.2417417019605637e-04 1.3392520486377180e-04 1.4529409236274660e-04 1.5490368241444230e-04 1.6054349543992430e-04 1.6470265109091997e-04 1.6953391605056822e-04 1.7735733126755804e-04 1.8579880998004228e-04 1.9282540597487241e-04 1.9490084378048778e-04 1.9291952776256949e-04 1.8882146105170250e-04 1.8748032744042575e-04 1.9068198162131011e-04 1.9900944607798010e-04 2.0903366385027766e-04 2.1804907009936869e-04 2.2298992553260177e-04 2.2470533440355211e-04 2.2411055397242308e-04 2.2416675346903503e-04 2.2510252892971039e-04 2.2733522928319871e-04 2.2865143546368927e-04 2.2873801935929805e-04 2.2671867918688804e-04 2.2469263058155775e-04 2.2367732890415937e-04 2.2588427236769348e-04 2.3064862762112170e-04 2.3721560137346387e-04 2.4248809495475143e-04 2.4530044174753129e-04 2.4392185150645673e-04 2.3997374228201807e-04 2.3479544324800372e-04 2.3193148081190884e-04 2.3177986440714449e-04 2.3423250240739435e-04 2.3599268752150238e-04 2.3544454597868025e-04 2.3105146829038858e-04 2.2531495778821409e-04 2.2048407117836177e-04 2.1968153305351734e-04 2.2227641602512449e-04 2.2639660164713860e-04 2.2756963153369725e-04 2.2441720648203045e-04 2.1659869526047260e-04 2.0751320698764175e-04 1.9902415806427598e-04 1.9264874572400004e-04 1.8588142120279372e-04 1.7704963102005422e-04 1.6407453222200274e-04 1.4884413394611329e-04 1.3369787484407425e-04 1.2322918337304145e-04 1.1876957432832569e-04 1.2099259765818715e-04 1.2626984971575439e-04 1.3147077697794884e-04 1.3201852561905980e-04 1.2693571625277400e-04 1.1586208711378276e-04 1.0243659198749810e-04 8.9883615146391094e-05 8.2217709859833121e-05 7.9788827861193568e-05 8.1572943599894643e-05 8.2789971202146262e-05 8.0381309089716524e-05 7.1861206379253417e-05 5.9443540521897376e-05 4.6467815991491079e-05 3.7801353755639866e-05 3.4748511097859591e-05 3.6987898056395352e-05 3.9989365177461877e-05 4.0179820643970743e-05 3.3726242691045627e-05 2.0926847355440259e-05 3.0464627798210131e-06 -1.6111713193822652e-05 -3.4266289731021971e-05 -4.7880435886327177e-05 -5.5940883612493053e-05 -5.7019708037842065e-05 -5.3304065659176558e-05 -4.7993988118832931e-05 -4.6759199904045090e-05 -5.2420706197153777e-05 -6.5226166043430567e-05 -7.9242265201173723e-05 -8.8570464868098497e-05 -8.8606429926585406e-05 -8.1529629824217409e-05 -7.2973372880369425e-05 -7.1348826168105006e-05 -7.9309778811875731e-05 -9.4982984592206776e-05 -1.1000163794960827e-04 -1.1765424278564751e-04 -1.1468345473986119e-04 -1.0576812928775325e-04 -9.7979645943269134e-05 -9.9060271168127656e-05 -1.1010139132849872e-04 -1.2757003423757851e-04 -1.4347210526466370e-04 -1.5255658945534378e-04 -1.5232432633638382e-04 -1.4634299441240728e-04 -1.3893029245082289e-04 -1.3485846284311265e-04 -1.3456591113936156e-04 -1.3751150981988758e-04 -1.4054631174076349e-04 -1.4313837164081633e-04 -1.4568321057595313e-04 -1.5125211211852729e-04 -1.6054227307904512e-04 -1.7347364337183535e-04 -1.8578677554614842e-04 -1.9430406973697245e-04 -1.9631467876024544e-04 -1.9309234630782157e-04 -1.8719457148108631e-04 -1.8292528693564236e-04 -1.8194870790466666e-04 -1.8483647727407515e-04 -1.8855083908420056e-04 -1.9074206647928804e-04 -1.8819964316207916e-04 -1.8129416275769472e-04 -1.7167674377560616e-04 -1.6394902195315808e-04 -1.6116905317176133e-04 -1.6529219283256680e-04 -1.7340134945698082e-04 -1.8171174451708794e-04 -1.8526190251577646e-04 -1.8262767116539180e-04 -1.7477676738053560e-04 -1.6649521421641111e-04 -1.6151329327840358e-04 -1.6217969823628664e-04 -1.6658096865285188e-04 -1.7149979248642921e-04 -1.7296388978138566e-04 -1.7044879496097565e-04 -1.6454310389235616e-04 -1.5816034283488989e-04 -1.5191431157290936e-04 -1.4642537280451506e-04 -1.4026545977685601e-04 -1.3381071039475501e-04 -1.2754226918332279e-04 -1.2334417260717601e-04 -1.2071933451807126e-04 -1.1819738574558869e-04 -1.1206029739696532e-04 -1.0081786604132503e-04 -8.4471481386572123e-05 -6.7411856434773654e-05 -5.3689898777520284e-05 -4.7608089516870677e-05 -4.9134610890178010e-05 -5.6271121138706803e-05 -6.4265273977071047e-05 -6.9635920226573944e-05 -6.9555520894937217e-05 -6.4477571868337691e-05 -5.5430846259696409e-05 -4.5179182052379474e-05 -3.5053431929554790e-05 -2.7153435439686291e-05 -2.1419496988528408e-05 -1.8235165043734014e-05 -1.5784238712512888e-05 -1.2572409104905091e-05 -6.0535835473274346e-06 3.6884639484924264e-06 1.5435201930813491e-05 2.4892635337891988e-05 2.9460126825142652e-05 2.7792208129540086e-05 2.2984624592936598e-05 1.9231792975915596e-05 2.1758836737717502e-05 3.1780913559487090e-05 4.7706562327221036e-05 6.3705228967592120e-05 7.4751573265530169e-05 7.7401768066920340e-05 7.3435832746326923e-05 6.6855427576228976e-05 6.3426603446714580e-05 6.5466003434266895e-05 7.2998096584342420e-05 8.2293168816249818e-05 9.0167224698234349e-05 9.3539892986882478e-05 9.3003807705827057e-05 8.9760549599304795e-05 8.7541564425919205e-05 8.8212836999446154e-05 9.3240894784685224e-05 1.0056274913949892e-04 1.0803227633005008e-04 1.1224088666494936e-04 1.1271958646830171e-04 1.0999756341334432e-04 1.0769836080726236e-04 1.0846301302080974e-04 1.1466506111901253e-04 1.2438451813068241e-04 1.3401347678154707e-04 1.3822893379256129e-04 1.3459072215482593e-04 1.2370874173939228e-04 1.1097108654212207e-04 1.0152277536690235e-04 9.9839242466259748e-05 1.0527649283176288e-04 1.1478862870717421e-04 1.2346019502729177e-04 1.2893888924736530e-04 1.3099962961860001e-04 1.3179093366488814e-04 1.3189298624638468e-04 1.3164176198188215e-04 1.2960375170223415e-04 1.2587671517394483e-04 1.2133379641454667e-04 1.1896005889866501e-04 1.2056266859872267e-04 1.2718065408989787e-04 1.3645662693306804e-04 1.4526600716635585e-04 1.4978522085584700e-04 1.4906965952832252e-04 1.4388437557499856e-04 1.3755449617747217e-04 1.3185544230509549e-04 1.2729683658108115e-04 1.2152931594755501e-04 1.1307703243801370e-04 1.0111717710969970e-04 8.8011584011837840e-05 7.5873918831348419e-05 6.7350512836128473e-05 6.2396415160037577e-05 6.0826627304777503e-05 6.1227678088471293e-05 6.3645173213444650e-05 6.7945642513222992e-05 7.4852869147434831e-05 8.3024446212220937e-05 9.0873618319164962e-05 9.5300158136524260e-05 9.4675109721720219e-05 8.7611078924965113e-05 7.5395357271190733e-05 6.0264312196522951e-05 4.6448509237961844e-05 3.7556554161710665e-05 3.6219520552549511e-05 4.1491992305964231e-05 5.0582035328261554e-05 5.8705285482574254e-05 6.2519051425624639e-05 5.9932113799732178e-05 5.2094059356022626e-05 4.0521852497477084e-05 2.8008458684780635e-05 1.6018359019653872e-05 6.0618403949774802e-06 -1.7351303540635854e-06 -6.4526802816544659e-06 -8.6085983639350161e-06 -7.9164765338646248e-06 -6.0473280427686404e-06 -4.1682274058985058e-06 -4.2947112888214178e-06 -6.4142873270611744e-06 -1.0441156518936623e-05 -1.4514414942823350e-05 -1.8192118659499101e-05 -2.1220597773208283e-05 -2.5267789169447497e-05 -3.1575036700814962e-05 -4.0865139453671873e-05 -5.1260278269182891e-05 -6.0186655900906771e-05 -6.4226856920868158e-05 -6.3193409005180001e-05 -5.8686207921709865e-05 -5.5111111578298733e-05 -5.5614193115616217e-05 -6.1930004449095577e-05 -7.1781068982090801e-05 -8.1632955698296428e-05 -8.7003587395884097e-05 -8.6210544395726174e-05 -7.9970370279625058e-05 -7.2838411142583936e-05 -6.9285299105104059e-05 -7.2442489909008145e-05 -8.0393314419779927e-05 -8.8805150880943984e-05 -9.2054280685260892e-05 -8.8445107394363731e-05 -8.0260477261617780e-05 -7.4005525675602257e-05 -7.4918665632139891e-05 -8.4493360191117972e-05 -9.7769778221845627e-05 -1.0733851377153769e-04 -1.0724015737650916e-04 -9.7816875495482236e-05 -8.4735715063288808e-05 -7.6556738349609077e-05 -7.7781616710126400e-05 -8.6748463218100369e-05 -9.5411531219724566e-05 -9.6262192528229207e-05 -8.6471976828761399e-05 -7.1050068072509021e-05 -5.8927485952153802e-05 -5.7865538110490888e-05 -6.8797606218140572e-05 -8.6254047346301377e-05 -1.0091243893839419e-04 -1.0626742005115375e-04 -1.0064848174806684e-04 -8.8885164586827159e-05 -7.7721495472360402e-05 -7.3014103691093624e-05 -7.5802083301823586e-05 -8.3300845290068537e-05 -9.0138739324174821e-05 -9.2380774731282145e-05 -8.9034409029409289e-05 -8.3012622781097889e-05 -7.8163342550396919e-05 -7.7252407209016383e-05 -7.8889686847105622e-05 -7.9895246017258614e-05 -7.6501368312165141e-05 -6.8592773459386081e-05 -5.9028086980106309e-05 -5.2799085096921772e-05 -5.2536506700562313e-05 -5.7398043281864375e-05 -6.2976701883599162e-05 -6.5048632677644491e-05 -6.1475504480767995e-05 -5.4345917305909097e-05 -4.6705448767170310e-05 -4.2081068386323750e-05 -4.0819199057295918e-05 -4.1921171941794455e-05 -4.2543982999632135e-05 -4.1770708776311949e-05 -3.9230122638400644e-05 -3.6460125556914136e-05 -3.4723449061857536e-05 -3.5712924727704376e-05 -3.9101625588955358e-05 -4.3544252548599616e-05 -4.5781598601024598e-05 -4.3608954001683742e-05 -3.6186291254125535e-05 -2.6075831556227058e-05 -1.6874822904355824e-05 -1.1861586244776845e-05 -1.1386395271983929e-05 -1.3361648598220199e-05 -1.3977168237033766e-05 -1.1051140063500497e-05 -4.5583669816551264e-06 2.8652648325078189e-06 9.0318226284580305e-06 1.3056682291789912e-05 1.6263729776255786e-05 1.9845678252750076e-05 2.4119070076267235e-05 2.7325158953317441e-05 2.7427890017861500e-05 2.3273671104107052e-05 1.6825157217681408e-05 1.1420685041230172e-05 1.0840030881809071e-05 1.6529118511243723e-05 2.7121130187879317e-05 3.8587528251809999e-05 4.7404835640918463e-05 5.1306931709405035e-05 5.1037019147770479e-05 4.8529585910728201e-05 4.6494427806464955e-05 4.5859898818889633e-05 4.6817531256237999e-05 4.7680419811513275e-05 4.7377274313475937e-05 4.5747292460873723e-05 4.4280746806180105e-05 4.4920889195054770e-05 4.9497088184580207e-05 5.7894591009244323e-05 6.8045206717215478e-05 7.6113086834084243e-05 7.9190111136995256e-05 7.6280484790913761e-05 6.9283501943573356e-05 6.1570928664878011e-05 5.7021676184376702e-05 5.7295648730359972e-05 6.1841841670684516e-05 6.7505141487345099e-05 7.1451227995567024e-05 7.1753929660189897e-05 6.9154768425505608e-05 6.5583735704421997e-05 6.3214320107363164e-05 6.2552586314268410e-05 6.2550767324864864e-05 6.0199017752893269e-05 5.3653027862310410e-05 4.2824081901926547e-05 3.1092782592168078e-05 2.2918366084923036e-05 2.2638665541308001e-05 3.1204690458253026e-05 4.6341614506673068e-05 6.2648490711580962e-05 7.4823350587394089e-05 7.9520832514390349e-05 7.7281249104999006e-05 7.0924375904724002e-05 6.4895502873696387e-05 6.2058010371401906e-05 6.3870422309264541e-05 6.9034300395287573e-05 7.5084113632328808e-05 7.8472774475812912e-05 7.6994285336695611e-05 6.9933346821926534e-05 5.9661673731170595e-05 4.9927348300116137e-05 4.4223423174116760e-05 4.3672742322087288e-05 4.6394998207688332e-05 4.8251138650812209e-05 4.5427128497976810e-05 3.6780595110030845e-05 2.4363109332625754e-05 1.1774225640692748e-05 2.8480862965807319e-06 -1.2388065897539491e-06 -1.0856049357244046e-06 1.1577467375900596e-06 4.1830353438854218e-06 7.5710127021011431e-06 1.1744211406039540e-05 1.6308700651279651e-05 1.9769155187532306e-05 1.9746010366361588e-05 1.4765279047423974e-05 5.1256442930025514e-06 -6.1378627833619248e-06 -1.5109680134628434e-05 -1.8007953258347698e-05 -1.3806449715048075e-05 -4.4060898289899342e-06 5.8972623264708091e-06 1.2924347174703144e-05 1.3761731679551303e-05 8.8015340224956162e-06 2.3514286340287072e-07 -8.5445281001739204e-06 -1.5832836652407423e-05 -2.0870280422968790e-05 -2.3839236746425740e-05 -2.4164353817468509e-05 -2.1401086996775120e-05 -1.5586176232318394e-05 -8.7518310465384275e-06 -3.7550844353972934e-06 -3.7151398828427773e-06 -9.0031062427442521e-06 -1.7708513041725382e-05 -2.5754387024790049e-05 -3.0278712074505165e-05 -3.0428707759710960e-05 -2.7807478545582853e-05 -2.4859295081114396e-05 -2.2841821191832423e-05 -2.1091249436722137e-05 -1.8348644516663626e-05 -1.3633840353577398e-05 -8.2010301412083209e-06 -4.3188601921428926e-06 -4.4868106670037378e-06 -9.1522615548456088e-06 -1.7104895960073918e-05 -2.5954715965781361e-05 -3.3589349186513573e-05 -3.9246635424206033e-05 -4.3362240830902010e-05 -4.6255943743744865e-05 -4.8209512897301465e-05 -4.8616711865179241e-05 -4.7807759983697906e-05 -4.6217017370508984e-05 -4.5596068957820535e-05 -4.6501980250468478e-05 -4.8091136704897508e-05 -4.7294939577113837e-05 -4.1564351704437286e-05 -3.0079845600994304e-05 -1.5606028682668693e-05 -2.9729858397331554e-06 2.6170846467721276e-06 -9.6184407993860077e-07 -1.2538161172415130e-05 -2.7227692044107243e-05 -3.9921680581755936e-05 -4.6886969357728958e-05 -4.7779125452507287e-05 -4.4438056647777557e-05 -3.9806458516977727e-05 -3.6210949474480003e-05 -3.4582357329782099e-05 -3.4145843528676778e-05 -3.3477143006166443e-05 -3.0939554562792182e-05 -2.6066167265526019e-05 -1.9249711840529926e-05 -1.2588581739692017e-05 -8.2567703429958783e-06 -8.3980721683474258e-06 -1.3004163520236034e-05 -2.0554105503833853e-05 -2.8222206310601905e-05 -3.3593583793845028e-05 -3.5756122088059783e-05 -3.5552096960600466e-05 -3.4679775126278400e-05 -3.4797765692928806e-05 -3.6045690649189055e-05 -3.7320380215533078e-05 -3.7280962715158239e-05 -3.5117584047839046e-05 -3.0794126359978691e-05 -2.5440314857405610e-05 -2.0287738152546808e-05 -1.6453028365503997e-05 -1.4165631910145748e-05 -1.3487258911482058e-05 -1.4304430806078017e-05 -1.5982215700205415e-05 -1.7314432625425979e-05 -1.7361209756927565e-05 -1.5703641111031175e-05 -1.3739528185396921e-05 -1.4232527973945253e-05 -1.9438066374277696e-05 -2.9290131351444870e-05 -4.0331357013201341e-05 -4.6989560360088944e-05 -4.4720749428961426e-05 -3.2252617529593408e-05 -1.3689664228877518e-05 3.9322421798715368e-06 1.3129308172210585e-05 1.0932309123745654e-05 -5.8375644584884867e-07 -1.5251512195391115e-05 -2.6655847250367515e-05 -3.1226780265569687e-05 -2.9481790988938883e-05 -2.4453895093756728e-05 -1.9230414181947708e-05 -1.4541467862727586e-05 -9.4502847787225619e-06 -2.8960139388800599e-06 4.7726989578222856e-06 1.1637501302175224e-05 1.5060462828841992e-05 1.3908922483096831e-05 9.2747959570260718e-06 4.2413707888044883e-06 1.9019520323126926e-06 3.5973885132989381e-06 7.6507385529112071e-06 1.1256712241447531e-05 1.1962936696363613e-05 9.9800154202966951e-06 7.3535552473913413e-06 6.9178759076748975e-06 9.9903600130346604e-06 1.5903557141427882e-05 2.2045709556550719e-05 2.5927805836545303e-05 2.6212463126285002e-05 2.3349641196546145e-05 1.9248771423008293e-05 1.6010737454053015e-05 1.5177683962974697e-05 1.6804602637421340e-05 1.9364706531632692e-05 2.1181836928008124e-05 2.1219510017544962e-05 1.9908346075681038e-05 1.8885690224124119e-05 2.0185110770398751e-05 2.4471310098306276e-05 3.0820156098343432e-05 3.6538800486596301e-05 3.9082700823200867e-05 3.7615569453919306e-05 3.3201060432475060e-05 2.7950516596320085e-05 2.3711461835773662e-05 2.1455434762174264e-05 2.0650386431952938e-05 2.0338840840850025e-05 2.0214356482028961e-05 2.0864490579697303e-05 2.3151620553107932e-05 2.6964511562255211e-05 3.1097424653125927e-05 3.3737724152160808e-05 3.4171556762885302e-05 3.3009680919349194e-05 3.2398260373156518e-05 3.4422417229507118e-05 3.9153845136752352e-05 4.4163236452732235e-05 4.5781733206240460e-05 4.1366423829458654e-05 3.1145158573053777e-05 1.8562699551694095e-05 8.6479813035111874e-06 5.5093596529331990e-06 9.8986965895164758e-06 1.9594295736169443e-05 3.0020073609193787e-05 3.7032674299553037e-05 3.8313632103381678e-05 3.4442120522726327e-05 2.8234961064299569e-05 2.2775897377869114e-05 1.9735167370527051e-05 1.8808761524269357e-05 1.8609662220114842e-05 1.7876987840281799e-05 1.7285488866036758e-05 1.8310774976271205e-05 2.2319538402371109e-05 2.8505015507107601e-05 3.3698066545184702e-05 3.3996620913967490e-05 2.7510737709235400e-05 1.6077701729955152e-05 4.5720371417701244e-06 -1.4266677226260072e-06 8.4991870608064346e-07 9.9589933597599156e-06 2.1340019884519279e-05 2.9575716325780377e-05 3.0972601962275803e-05 2.4784752895357087e-05 1.3185452189645730e-05 1.5602955727445078e-07 -1.0685429515433498e-05 -1.7502366972621530e-05 -2.0592662622220814e-05 -2.1579817257588729e-05 -2.2059926777728833e-05 -2.3143287762650289e-05 -2.4306456907652318e-05 -2.4768785806372762e-05 -2.4134660634445027e-05 -2.2178406652528793e-05 -1.9527944459696300e-05 -1.6712590877432376e-05 -1.4797352378081996e-05 -1.4351266145240515e-05 -1.5629058907506987e-05 -1.8132330296793953e-05 -2.1039904822828248e-05 -2.2804577383794822e-05 -2.2357897250913084e-05 -1.8864047888200730e-05 -1.3064543054497335e-05 -6.5206590988964308e-06 -1.0312483027519193e-06 2.3254319785337429e-06 3.6087553780816961e-06 3.4172740015492309e-06 1.8224948235001648e-06 -1.7356846910843160e-06 -8.0581321526551619e-06 -1.6338091882062145e-05 -2.3779837647452950e-05 -2.6484372938284650e-05 -2.2267395252129063e-05 -1.2549744496936910e-05 -2.5791514417505823e-06 1.4896413631504402e-06 -4.0725426515564322e-06 -1.7720776668284088e-05 -3.3436983358114958e-05 -4.4055686885258183e-05 -4.5011067413724959e-05 -3.6831290344707668e-05 -2.4929420760599896e-05 -1.5807614545337856e-05 -1.3637382835440803e-05 -1.8036251276498660e-05 -2.4705874238861725e-05 -2.8662818294833414e-05 -2.7043166483053938e-05 -2.1274596292641945e-05 -1.6006953956093639e-05 -1.6220355973928235e-05 -2.3666807464906015e-05 -3.5303739423397928e-05 -4.4946209527552128e-05 -4.7285237087635323e-05 -4.1171264456352219e-05 -2.9805587473674677e-05 -1.8886190446210094e-05 -1.2701173545792699e-05 -1.1898289812961593e-05 -1.3745250726060476e-05 -1.5026068467705045e-05 -1.3946038052381482e-05 -1.1362529221514706e-05 -8.9955146904685535e-06 -7.6899441410205327e-06 -6.3895022321958095e-06 -3.1170609418040840e-06 3.2736784305598121e-06 1.1748774340958335e-05 1.9409008018556051e-05 2.3299400709220208e-05 2.1989109882269986e-05 1.6600095477770083e-05 9.5596342362114228e-06 3.5842226679960731e-06 6.4841788116609678e-07 1.1926472325285431e-06 4.6412351366598159e-06 9.5703344413777813e-06 1.4001320778334048e-05 1.5698906281613745e-05 1.3563741958932951e-05 7.8099565143929794e-06 5.5351506489387248e-07 -5.2876930567435920e-06 -6.9902202994853724e-06 -3.6528106193145504e-06 3.0600067475461401e-06 9.5833929663058370e-06 1.2641796274692751e-05 1.0923706213361584e-05 5.7897696024156176e-06 6.2449217352877895e-08 -3.9429469325114042e-06 -5.8891423577733804e-06 -7.3051778599619865e-06 -9.9264179880265146e-06 -1.3542936358135194e-05 -1.5692356100771576e-05 -1.3344731996767223e-05 -5.3415569709613919e-06 6.0754146034014411e-06 1.6103651432786137e-05 1.9995819457108155e-05 1.5680543583584949e-05 5.3442117859958671e-06 -6.2506519498128910e-06 -1.4433784599532373e-05 -1.7201087757712230e-05 -1.5933350368868560e-05 -1.3907075299357530e-05 -1.3305506399774458e-05 -1.4098781321081333e-05 -1.3826655049342662e-05 -1.0183837730437517e-05 -2.5487765924481209e-06 6.8199842644389719e-06 1.4515684597427025e-05 1.7864520486909896e-05 1.6782094462541863e-05 1.3505949027603492e-05 1.0871453923755325e-05 1.0299888344889041e-05 1.1317448297631927e-05 1.1557807738427073e-05 8.9061231847153977e-06 2.3222048639581772e-06 -6.2463072936225217e-06 -1.3633865819429047e-05 -1.6021362171159126e-05 -1.1058862583013251e-05 3.7510312722588424e-07 1.3622462574858218e-05 2.2311276552500203e-05 2.1659940102836117e-05 1.1514226571307518e-05 -2.9636294129886664e-06 -1.3438388123176992e-05 -1.3113089153193869e-05 -5.3719861625722842e-07 1.9196420907974243e-05 3.7041681935079396e-05 4.4875712774228305e-05 3.9907790778670460e-05 2.5587185518816113e-05 9.3110857051215135e-06 -2.2324538804241456e-06 -6.1385744629660621e-06 -4.2953806769219227e-06 -6.6289737787883496e-07 1.2916427749587456e-06 8.5138930216999142e-07 -4.1394523009330442e-07 -7.6665861570290872e-07 1.8193296114077384e-07 1.3695980669581331e-06 1.0703303132686415e-06 -6.1658124650421087e-07 -2.1390783331298735e-06 -1.1505778729770100e-06 3.1496920200879686e-06 8.7789385361247696e-06 1.1567377441679128e-05 7.8287694122991525e-06 -3.0018870802450692e-06 -1.6920881535043009e-05 -2.7566442440729588e-05 -2.9365757654886693e-05 -2.1471749278134666e-05 -7.5338857641327195e-06 5.9343415159673896e-06 1.3538479834096506e-05 1.3631737601826899e-05 8.6186028056545183e-06 2.4994251361931674e-06 -1.2776206403941615e-06 -1.9447877548373071e-06 -6.7293365191289922e-07 2.7109973643746343e-07 -4.8552914222455001e-07 -3.3404003261239268e-06 -7.4068707363039721e-06 -1.1703479685820639e-05 -1.4869665392325260e-05 -1.6321884686476551e-05 -1.4982582797529176e-05 -1.0850208127521910e-05 -4.2644196582841687e-06 3.3649512261035852e-06 1.0624358765198849e-05 1.6182591934921220e-05 1.9861943655996583e-05 2.2068752514314838e-05 2.3559276087325998e-05 2.4105809643515386e-05 2.3798662368790247e-05 2.2315991373034194e-05 2.0121711713727564e-05 1.7080717952921987e-05 1.3055903764325194e-05 7.1832910180091858e-06 -2.9580999694189813e-07 -8.0299869296140969e-06 -1.2551159670692869e-05 -1.0983733773173299e-05 -3.1886113447399111e-06 7.0121868702699430e-06 1.3627630323753692e-05 1.2001350114587694e-05 2.7656001293507870e-06 -8.4151370174367912e-06 -1.3977884918858763e-05 -9.7467354862601496e-06 2.2138699478091439e-06 1.4637165804742835e-05 1.9914707081625238e-05 1.5290945157175884e-05 4.4764265112462454e-06 -5.0567878133733757e-06 -6.9297620939323679e-06 -6.9384975631692214e-07 7.8981574915815145e-06 1.1224517947994173e-05 4.8477759264642373e-06 -9.3173584900796413e-06 -2.3689892259426415e-05 -3.0524712201440707e-05 -2.6234849428874440e-05 -1.4461458704317920e-05 -2.3265113213710720e-06 3.3086246276070597e-06 1.4285631095845019e-06 -3.4309598504478345e-06 -4.3619425014185254e-06 2.1810974430991337e-06 1.3496479368768632e-05 2.1975261915940791e-05 2.0568606487358920e-05 7.7112454164307564e-06 -1.0606339856167324e-05 -2.4959790607681498e-05 -2.7620240871328861e-05 -1.8416858438285999e-05 -3.6068013287149370e-06 7.3369928941247053e-06 8.3164941315772012e-06 -2.0073383666385780e-07 -1.1847978385048918e-05 -1.9206807337468490e-05 -1.8050081052933820e-05 -1.0337585081288125e-05 -1.6615238109807251e-06 2.3180505195341539e-06 9.2827292519359617e-09 -5.2167515605106018e-06 -7.6601781984209083e-06 -3.0450137273874134e-06 8.4749308371101506e-06 2.1720485165133141e-05 3.0102404707577080e-05 2.9162689315853640e-05 1.9917020836146548e-05 7.7571367000928149e-06 -2.6913420470009441e-07 -6.2093693031783914e-07 4.7693879423604812e-06 9.5069444796536118e-06 7.3299261202919297e-06 -3.4876684367191046e-06 -1.8513386748963967e-05 -3.0196273655747063e-05 -3.1807649065740407e-05 -2.2121323127066717e-05 -5.2916402637492865e-06 1.1117863323306665e-05 2.0635481632780284e-05 2.0499370293691754e-05 1.2883855561085511e-05 2.2479493964056019e-06 -6.5234380599576980e-06 -1.0820151146617718e-05 -9.9791323009412736e-06 -5.3318681239034049e-06 8.7862281361594796e-07 5.6963062888826244e-06 6.8708177423104644e-06 3.0947467166697606e-06 -4.7927114792400971e-06 -1.4063175513001624e-05 -2.0786508684977889e-05 -2.1512440071091987e-05 -1.4789773558732122e-05 -2.3130330646381481e-06 1.2236005204613321e-05 2.3994540242711082e-05 3.0059065466048196e-05 2.9004970201640390e-05 2.2057836758904159e-05 1.0922449291683733e-05 -1.6287714288409916e-06 -1.3248683899291791e-05 -2.2106880351202562e-05 -2.7142803446622565e-05 -2.7650054107652977e-05 -2.3519702153862454e-05 -1.5207055184873752e-05 -4.7292210183513816e-06 5.5423120102204848e-06 1.2863329175161198e-05 1.5593222997267731e-05 1.3262482752907090e-05 7.4197623689542525e-06 1.3619660421682056e-07 -6.4385808400402311e-06 -1.1659351912385318e-05 -1.5476372936973348e-05 -1.9190758393961005e-05 -2.2886042643222027e-05 -2.5764205929590389e-05 -2.5530287530273199e-05 -2.0644474716391414e-05 -1.1242571417824365e-05 -2.5202544406965899e-07 8.3839959188480861e-06 1.1308491593808867e-05 8.0366316979052499e-06 1.2588664048962528e-06 -4.4044827518519014e-06 -5.1996967158629559e-06 8.4044721404552547e-08 9.0616831585066393e-06 1.7466427379986271e-05 2.0865027181571350e-05 1.7420745280105621e-05 8.0708177847554907e-06 -3.4151071304222569e-06 -1.3190656318329275e-05 -1.7812932128435932e-05 -1.6184549167519435e-05 -9.3545340860146098e-06 -6.1183993693703087e-07 6.5742674451030325e-06 8.9571549324318767e-06 5.3498602028412279e-06 -3.3104111025750171e-06 -1.3449335710902233e-05 -2.1052775991847739e-05 -2.2910042389412411e-05 -1.8936520064016804e-05 -1.1303520295768976e-05 -3.8368689274648204e-06 9.9346493698249105e-07 2.7665391826303676e-06 3.6166557038086466e-06 5.7386782827961724e-06 9.9477692856453359e-06 1.4900844689691439e-05 1.8125225324183702e-05 1.7773323634173721e-05 1.4217397620086558e-05 9.3696535259368829e-06 5.7710303735802881e-06 4.2577257772791199e-06 4.4619828258873895e-06 4.8012525439844467e-06 4.7781250032130629e-06 4.6940385800553486e-06 5.7393999668420292e-06 8.1410053098807111e-06 1.0619343811413273e-05 1.0602428119454999e-05 6.4257283156621270e-06 -1.2973320053788484e-06 -9.7238207672489807e-06 -1.5475550753762946e-05 -1.6465328371850774e-05 -1.3086949365970213e-05 -7.9710098361829296e-06 -4.3165491661056876e-06 -3.3836122383945622e-06 -4.4271223487157840e-06 -5.0679900596151128e-06 -3.1250897336576600e-06 1.9311853520775912e-06 8.8348315330222249e-06 1.5538660591118969e-05 2.0500427126535214e-05 2.3598506231792271e-05 2.4781100364634767e-05 2.4113765903166495e-05 1.9796938431682065e-05 1.0764824764919467e-05 -2.7428145585872699e-06 -1.6917427274165675e-05 -2.6429304853081703e-05 -2.7262847652309574e-05 -1.9839701053570025e-05 -9.1823603725060821e-06 -2.1671912691090256e-06 -2.5198469302267767e-06 -9.1809088189620525e-06 -1.6201858670683578e-05 -1.7916841898113489e-05 -1.2376989616313949e-05 -2.9483057915058453e-06 4.5945480451337062e-06 5.9636936384777073e-06 1.8957065321956179e-06 -3.7124373193364590e-06 -5.8655687098507769e-06 -2.9316213385754963e-06 3.0391238396987319e-06 7.8878147178329527e-06 9.0119483502348885e-06 6.3959828366932925e-06 2.5076351448660716e-06 -3.0606219070250518e-07 -1.2309271824051393e-06 -1.5099085430847481e-06 -2.5908943825925235e-06 -4.8772026275401004e-06 -6.8809931690338999e-06 -6.7458877310855314e-06 -3.4122076613130048e-06 1.8180721781391185e-06 6.5116846599266864e-06 8.5078245319891721e-06 7.3720061664062086e-06 4.1091425373451784e-06 8.1739494817156810e-07 -1.3388112165557686e-06 -2.2104936761024874e-06 -2.6203106244793162e-06 -3.1783279155206401e-06 -3.8325833884300664e-06 -3.8785565266152844e-06 -3.3186261134687811e-06 -2.2469409941550111e-06 -1.4986138694439433e-06 -1.5372934285551310e-06 -2.6493128189031268e-06 -4.3947966332780197e-06 -6.7570726969279349e-06 -9.7589690994936973e-06 -1.3369531188800465e-05 -1.6393398254876956e-05 -1.6784211766207591e-05 -1.2380918633425608e-05 -3.1007964480522787e-06 8.3400263974908739e-06 1.6711434000171721e-05 1.7729791579768062e-05 1.0157524229725823e-05 -1.9526014511939138e-06 -1.1966117199335713e-05 -1.3798005966236815e-05 -6.3090287767408881e-06 6.9188481575110927e-06 1.9360562873771414e-05 2.6398896807222627e-05 2.7174133720109239e-05 2.4764980480540544e-05 2.3502841941080987e-05 2.5977971745305695e-05 3.1189680157694966e-05 3.5920435038860887e-05 3.6564328183885664e-05 3.1885741918813437e-05 2.3178243282018229e-05 1.4072325029701460e-05 8.2144160842290148e-06 7.5814650699612685e-06 1.1420046575949527e-05 1.7133190340246074e-05 2.1427060346468352e-05 2.1886902686674148e-05 1.8027447367785498e-05 1.1083372555731330e-05 2.8785655104002217e-06 -4.1079324546444695e-06 -8.0004247138276696e-06 -7.6323867688188329e-06 -3.6678552532976028e-06 2.6111479201063048e-06 8.9842842498910613e-06 1.3467069038597401e-05 1.4089464457356371e-05 1.0032614227384329e-05 1.7944639694178477e-06 -7.9402962001040578e-06 -1.5747158613521606e-05 -1.8628559701028280e-05 -1.5843183064134791e-05 -9.1326319306972437e-06 -1.8489047306502471e-06 3.4472625429771142e-06 6.1751675275445450e-06 7.4853901423921343e-06 8.3292216004338115e-06 8.6815189206390642e-06 6.6779757617041469e-06 1.0861483588087140e-06 -8.1579955804045312e-06 -1.7956308511202224e-05 -2.4649780243635178e-05 -2.5512750653433613e-05 -2.1338142687454820e-05 -1.4958488463889807e-05 -9.7800675575854257e-06 -7.3445389716653153e-06 -7.2696411734796129e-06 -7.5410794124763925e-06 -7.0695964495826047e-06 -5.8558939599606674e-06 -4.6952109187259339e-06 -4.2583242247928865e-06 -4.3718773667933419e-06 -4.3017998905270360e-06 -3.8477460293506738e-06 -3.2133309559867484e-06 -3.3216185784112895e-06 -4.8507954488741234e-06 -7.3491678449499886e-06 -9.7056490631075576e-06 -1.0915257007582113e-05 -1.0712636139942333e-05 -9.8027048807125539e-06 -9.6687363111414015e-06 -1.1712858395185322e-05 -1.6511105059180409e-05 -2.3620039428351447e-05 -3.0980685551185161e-05 -3.5979901440441608e-05 -3.6357487260829657e-05 -3.1365059840027243e-05 -2.2346930563799106e-05 -1.2846965546486899e-05 -6.9359557528514415e-06 -7.5856614785152487e-06 -1.4339797417051159e-05 -2.4160535758710466e-05 -3.2238112908089533e-05 -3.5579476389102638e-05 -3.4514712751843035e-05 -3.2195795938605443e-05 -3.1944211514201015e-05 -3.4845113987103105e-05 -3.8727925129933283e-05 -3.9567228668602183e-05 -3.4482582123018801e-05 -2.4078392016235739e-05 brutefir-1.0o/dai.c0000664000175000017500000015513412752354353013612 0ustar andersanders/* * (c) Copyright 2001 - 2006, 2013, 2016 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif #include "defs.h" #include "emalloc.h" #include "shmalloc.h" #include "dai.h" #include "bfrun.h" #include "bit.h" #include "inout.h" #include "bfconf.h" #include "timestamp.h" #include "fdrw.h" #include "pinfo.h" #include "delay.h" #include "timermacros.h" #include "numunion.h" /* FIXME: use bfconf directly in more situations? */ #define CB_MSG_START 1 #define CB_MSG_STOP 2 struct subdev { volatile bool_t finished; bool_t uses_callback; bool_t uses_clock; bool_t isinterleaved; bool_t bad_alignment; int index; int fd; int buf_size; int buf_offset; int buf_left; int block_size; int block_size_frames; struct dai_channels channels; delaybuffer_t **db; struct bfio_module *module; struct { int iodelay_fill; int curbuf; volatile int frames_left; } cb; }; struct comarea { volatile bool_t blocking_stopped; volatile int lastbuf_index; volatile int frames_left; volatile int cb_lastbuf_index; volatile int cb_frames_left; volatile bool_t is_muted[2][BF_MAXCHANNELS]; volatile int delay[2][BF_MAXCHANNELS]; volatile pid_t pid[2]; volatile pid_t callback_pid; struct subdev dev[2][BF_MAXCHANNELS]; struct dai_buffer_format buffer_format[2]; int buffer_id; volatile int cb_buf_index[2]; }; struct dai_buffer_format *dai_buffer_format[2] = { NULL, NULL }; static void *iobuffers[2][2]; static struct comarea *ca = NULL; static int n_devs[2] = { 0, 0 }; static int n_fd_devs[2] = { 0, 0 }; static fd_set dev_fds[2]; static fd_set clocked_wfds; static int n_clocked_devs = 0; static int dev_fdn[2] = { 0, 0 }; static int min_block_size[2] = { 0, 0 }; static int cb_min_block_size[2] = { 0, 0 }; static bool_t input_poll_mode = false; static struct subdev *dev[2][BF_MAXCHANNELS]; static struct subdev *fd2dev[2][FD_SETSIZE]; static struct subdev *ch2dev[2][BF_MAXCHANNELS]; static int period_size; static int sample_rate; static int monitor_rate_fd = -1; static int synchpipe[2][2], paramspipe_s[2][2], paramspipe_r[2][2]; static int cbpipe_s[2], cbpipe_r[2]; static int cbmutex_pipe[2][2]; static int cbreadywait_pipe[2][2]; static volatile int callback_ready_waiting[2] = { 0, 0 }; static int process_callback(void **state[2], int state_count[2], void **buffers[2], int frame_count, int event); static void cbmutex(int io, bool_t lock) { char dummy = 0; if (lock) { if (!readfd(cbmutex_pipe[io][0], &dummy, 1)) { bf_exit(BF_EXIT_OTHER); } } else { if (!writefd(cbmutex_pipe[io][1], &dummy, 1)) { bf_exit(BF_EXIT_OTHER); } } } static bool_t output_finish(void) { bool_t finished; int n; cbmutex(OUT, true); finished = true; for (n = 0; n < n_devs[OUT]; n++) { if (!dev[OUT][n]->finished) { finished = false; break; } } if (finished) { pinfo("\nFinished!\n"); return true; } cbmutex(OUT, false); return false; } static void update_devmap(int idx, int io) { int n; if (dev[io][idx]->fd >= 0) { FD_SET(dev[io][idx]->fd, &dev_fds[io]); if (dev[io][idx]->fd > dev_fdn[io]) { dev_fdn[io] = dev[io][idx]->fd; } fd2dev[io][dev[io][idx]->fd] = dev[io][idx]; } for (n = 0; n < dev[io][idx]->channels.used_channels; n++) { ch2dev[io][dev[io][idx]->channels.channel_name[n]] = dev[io][idx]; } } /* if noninterleaved, update channel layout to fit the noninterleaved access mode (it is setup for interleaved layout per default). */ static void noninterleave_modify(int idx, int io) { int n; if (!dev[io][idx]->isinterleaved) { dev[io][idx]->channels.open_channels = dev[io][idx]->channels.used_channels; for (n = 0; n < dev[io][idx]->channels.used_channels; n++) { dev[io][idx]->channels.channel_selection[n] = n; } } } static void update_delay(struct subdev *sd, int io, uint8_t *buf) { struct buffer_format *bf; int n, newdelay, virtch; if (sd->db == NULL) { return; } for (n = 0; n < sd->channels.used_channels; n++) { if (sd->db[n] == NULL) { continue; } bf = &dai_buffer_format[io]->bf[sd->channels.channel_name[n]]; virtch = bfconf->phys2virt[io][sd->channels.channel_name[n]][0]; newdelay = ca->delay[io][sd->channels.channel_name[n]]; if (bfconf->use_subdelay[io] && bfconf->subdelay[io][virtch] == BF_UNDEFINED_SUBDELAY) { newdelay += bfconf->sdf_length; } delay_update(sd->db[n], (void *)&buf[bf->byte_offset], bf->sf.bytes, bf->sample_spacing, newdelay, NULL); } } static void allocate_delay_buffers(int io, struct subdev *sd) { int n, virtch, extra_delay; sd->db = emalloc(sd->channels.used_channels * sizeof(delaybuffer_t *)); for (n = 0; n < sd->channels.used_channels; n++) { /* check if we need a delay buffer here, that is if at least one channel has a direct virtual to physical mapping */ if (bfconf->n_virtperphys[io][sd->channels.channel_name[n]] == 1) { virtch = bfconf->phys2virt[io][sd->channels.channel_name[n]][0]; extra_delay = 0; if (bfconf->use_subdelay[io] && bfconf->subdelay[io][virtch] == BF_UNDEFINED_SUBDELAY) { extra_delay = bfconf->sdf_length; } sd->db[n] = delay_allocate_buffer(period_size, bfconf->delay[io][virtch] + extra_delay, bfconf->maxdelay[io][virtch] + extra_delay, sd->channels.sf.bytes); } else { /* this delay is taken care of previous to feeding the channel output to this module */ sd->db[n] = NULL; } } } static void do_mute(struct subdev *sd, int io, int wsize, void *_buf, int offset) { int n, i, k, n_mute = 0, ch[sd->channels.used_channels], framesize; int bsch[sd->channels.used_channels], mid_offset; uint8_t *endp, *p, *startp; numunion_t *buf; uint16_t *p16; uint32_t *p32; uint64_t *p64; buf = (numunion_t *)_buf; /* Calculate which channels that should be cleared */ for (n = 0; n < sd->channels.used_channels; n++) { if (ca->is_muted[io][sd->channels.channel_name[n]]) { ch[n_mute] = sd->channels.channel_selection[n]; bsch[n_mute] = ch[n_mute] * sd->channels.sf.bytes; n_mute++; } } if (n_mute == 0) { return; } if (!sd->isinterleaved) { offset /= sd->channels.open_channels; wsize /= sd->channels.open_channels; p = &buf->u8[offset]; i = period_size * sd->channels.sf.bytes; for (n = 0; n < n_mute; n++) { memset(p + ch[n] * i, 0, wsize); } return; } startp = &buf->u8[offset]; endp = &buf->u8[offset] + wsize; framesize = sd->channels.open_channels * sd->channels.sf.bytes; mid_offset = offset; if ((i = offset % framesize) != 0) { for (k = 0; k < n_mute && bsch[k] + sd->channels.sf.bytes <= i; k++); for (n = i, p = startp; p < startp + framesize - i && p < endp; p++, n++) { if (n >= bsch[k] && n < bsch[k] + sd->channels.sf.bytes) { *p = 0; if (n == bsch[k] + sd->channels.sf.bytes) { if (++k == n_mute) { break; } } } } if (p == endp) { return; } mid_offset += framesize - i; } switch (sd->channels.sf.bytes) { case 1: for (p = &buf->u8[mid_offset]; p < endp; p += sd->channels.open_channels) { for (n = 0; n < n_mute; n++) { p[ch[n]] = 0; } } break; case 2: for (p16 = &buf->u16[mid_offset>>1]; (uint8_t *)p16 < endp; p16 += sd->channels.open_channels) { for (n = 0; n < n_mute; n++) { p16[ch[n]] = 0; } } break; case 3: for (p = &buf->u8[mid_offset]; p < endp; p += sd->channels.open_channels) { for (n = 0; n < n_mute; n++) { p[bsch[n]+0] = 0; p[bsch[n]+1] = 0; p[bsch[n]+2] = 0; } } case 4: { for (p32 = &buf->u32[mid_offset>>2]; (uint8_t *)p32 < endp; p32 += sd->channels.open_channels) { for (n = 0; n < n_mute; n++) { p32[ch[n]] = 0; } } break; } case 8: { for (p64 = &buf->u64[mid_offset>>3]; (uint8_t *)p64 < endp; p64 += sd->channels.open_channels) { for (n = 0; n < n_mute; n++) { p64[ch[n]] = 0; } } break; } default: fprintf(stderr, "Sample byte size %d not supported.\n", sd->channels.sf.bytes); bf_exit(BF_EXIT_OTHER); break; } if ((i = (offset + wsize) % framesize) != 0) { if ((p = endp - i) < startp) { return; } for (n = k = 0; p < endp; p++, n++) { if (n >= bsch[k] && n < bsch[k] + sd->channels.sf.bytes) { *p = 0; if (n == bsch[k] + sd->channels.sf.bytes) { if (++k == n_mute) { break; } } } } } } static bool_t init_input(struct dai_subdevice *subdev, int idx) { int fd; dev[IN][idx]->uses_callback = bfconf->iomods[subdev->module].iscallback; dev[IN][idx]->channels = subdev->channels; dev[IN][idx]->uses_clock = subdev->uses_clock; dev[IN][idx]->module = &bfconf->iomods[subdev->module]; dev[IN][idx]->index = idx; if ((fd = dev[IN][idx]->module->init(subdev->params, IN, subdev->channels.sf.format, sample_rate, subdev->channels.open_channels, subdev->channels.used_channels, subdev->channels.channel_selection, period_size, &dev[IN][idx]->block_size_frames, &dev[IN][idx]->isinterleaved, dev[IN][idx]->uses_callback ? dev[IN][idx] : NULL, dev[IN][idx]->uses_callback ? process_callback : NULL)) == -1) { fprintf(stderr, "Failed to init input device.\n"); return false; } if (dev[IN][idx]->uses_callback) { dev[IN][idx]->fd = -1; if (dev[IN][idx]->block_size_frames == 0 || period_size % dev[IN][idx]->block_size_frames != 0) { fprintf(stderr, "Invalid block size for callback input.\n"); return false; } if (dev[IN][idx]->uses_clock && (dev[IN][idx]->block_size_frames < cb_min_block_size[IN] || cb_min_block_size[IN] == 0)) { cb_min_block_size[IN] = dev[IN][idx]->block_size_frames; } } else { n_fd_devs[IN]++; dev[IN][idx]->fd = fd; if (bfconf->monitor_rate && monitor_rate_fd == -1 && subdev->uses_clock) { monitor_rate_fd = dev[IN][idx]->fd; } if (dev[IN][idx]->uses_clock && dev[IN][idx]->block_size_frames != 0 && (dev[IN][idx]->block_size_frames < min_block_size[IN] || min_block_size[IN] == 0)) { min_block_size[IN] = dev[IN][idx]->block_size_frames; } } dev[IN][idx]->isinterleaved = !!dev[IN][idx]->isinterleaved; if (dev[IN][idx]->uses_clock && period_size % dev[IN][idx]->block_size_frames != 0) { dev[IN][idx]->bad_alignment = true; } noninterleave_modify(idx, IN); dev[IN][idx]->block_size = dev[IN][idx]->block_size_frames * dev[IN][idx]->channels.open_channels * dev[IN][idx]->channels.sf.bytes; allocate_delay_buffers(IN, dev[IN][idx]); update_devmap(idx, IN); return true; } static bool_t init_output(struct dai_subdevice *subdev, int idx) { int fd; dev[OUT][idx]->uses_callback = bfconf->iomods[subdev->module].iscallback; dev[OUT][idx]->channels = subdev->channels; dev[OUT][idx]->uses_clock = subdev->uses_clock; dev[OUT][idx]->module = &bfconf->iomods[subdev->module]; dev[OUT][idx]->index = idx; if ((fd = dev[OUT][idx]->module->init(subdev->params, OUT, subdev->channels.sf.format, sample_rate, subdev->channels.open_channels, subdev->channels.used_channels, subdev->channels.channel_selection, period_size, &dev[OUT][idx]->block_size_frames, &dev[OUT][idx]->isinterleaved, dev[OUT][idx]->uses_callback ? dev[OUT][idx] : NULL, dev[OUT][idx]->uses_callback ? process_callback : NULL)) == -1) { fprintf(stderr, "Failed to init output device.\n"); return false; } if (dev[OUT][idx]->uses_callback) { dev[OUT][idx]->fd = -1; if (dev[OUT][idx]->block_size_frames == 0 || period_size % dev[OUT][idx]->block_size_frames != 0) { fprintf(stderr, "Invalid block size for callback output.\n"); return false; } if (dev[OUT][idx]->uses_clock && (dev[OUT][idx]->block_size_frames < cb_min_block_size[OUT] || cb_min_block_size[OUT] == 0)) { cb_min_block_size[OUT] = dev[OUT][idx]->block_size_frames; } } else { n_fd_devs[OUT]++; dev[OUT][idx]->fd = fd; if (dev[OUT][idx]->uses_clock) { FD_SET(dev[OUT][idx]->fd, &clocked_wfds); n_clocked_devs++; } if (dev[OUT][idx]->uses_clock && dev[OUT][idx]->block_size_frames != 0 && (dev[OUT][idx]->block_size_frames < min_block_size[OUT] || min_block_size[OUT] == 0)) { min_block_size[OUT] = dev[OUT][idx]->block_size_frames; } } dev[OUT][idx]->isinterleaved = !!dev[OUT][idx]->isinterleaved; noninterleave_modify(idx, OUT); dev[OUT][idx]->block_size = dev[OUT][idx]->block_size_frames * dev[OUT][idx]->channels.open_channels * dev[OUT][idx]->channels.sf.bytes; allocate_delay_buffers(OUT, dev[OUT][idx]); update_devmap(idx, OUT); return true; } static void calc_buffer_format(int fragsize, int io, struct dai_buffer_format *format) { int n, i, ch; format->n_samples = fragsize; format->n_channels = 0; format->n_bytes = 0; for (n = 0; n < n_devs[io]; n++) { dev[io][n]->buf_offset = format->n_bytes; format->n_channels += dev[io][n]->channels.used_channels; for (i = 0; i < dev[io][n]->channels.used_channels; i++) { ch = dev[io][n]->channels.channel_name[i]; format->bf[ch].sf = dev[io][n]->channels.sf; if (dev[io][n]->isinterleaved) { format->bf[ch].byte_offset = format->n_bytes + dev[io][n]->channels.channel_selection[i] * dev[io][n]->channels.sf.bytes; format->bf[ch].sample_spacing = dev[io][n]->channels.open_channels; } else { format->bf[ch].byte_offset = format->n_bytes; format->bf[ch].sample_spacing = 1; format->n_bytes += dev[io][n]->channels.sf.bytes * fragsize; } } dev[io][n]->buf_size = dev[io][n]->buf_left = dev[io][n]->channels.open_channels * dev[io][n]->channels.sf.bytes * fragsize; if (dev[io][n]->isinterleaved) { format->n_bytes += dev[io][n]->buf_size; } if (format->n_bytes % ALIGNMENT != 0) { format->n_bytes += ALIGNMENT - format->n_bytes % ALIGNMENT; } } } static void handle_params(int io) { char *params, *msgstr = NULL; int size, ans, subdev_index; if (!readfd(paramspipe_s[io][0], &subdev_index, sizeof(int))) { fprintf(stderr, "Failed to read from pipe.\n"); bf_exit(BF_EXIT_OTHER); } if (!readfd(paramspipe_s[io][0], &size, sizeof(int))) { fprintf(stderr, "Failed to read from pipe.\n"); bf_exit(BF_EXIT_OTHER); } if ((params = alloca(size)) == NULL) { fprintf(stderr, "Could not allocate %d bytes on stack.\n", size); bf_exit(BF_EXIT_OTHER); } if (!readfd(paramspipe_s[io][0], params, size)) { fprintf(stderr, "Failed to read from pipe.\n"); bf_exit(BF_EXIT_OTHER); } if (dev[io][subdev_index]->module->command == NULL) { ans = -1; msgstr = estrdup("Module does not support any commands"); } else { ans = dev[io][subdev_index]->module-> command(dev[io][subdev_index]->fd, params); msgstr = estrdup(dev[io][subdev_index]->module->message()); } if (!writefd(paramspipe_r[io][1], &ans, sizeof(int))) { fprintf(stderr, "Failed to write to pipe.\n"); bf_exit(BF_EXIT_OTHER); } size = strlen(msgstr) + 1; if (!writefd(paramspipe_r[io][1], &size, sizeof(int)) || !writefd(paramspipe_r[io][1], msgstr, size)) { fprintf(stderr, "Failed to write to pipe.\n"); bf_exit(BF_EXIT_OTHER); } efree(msgstr); } static bool_t callback_init(int n_subdevs[2], struct dai_subdevice *subdevs[2]) { int n; if (pipe(cbreadywait_pipe[IN]) == -1 || pipe(cbreadywait_pipe[OUT]) == -1) { fprintf(stderr, "Failed to create pipe: %s.\n", strerror(errno)); return false; } /* initialise inputs */ for (n = 0; n < n_subdevs[IN]; n++) { if (!bfconf->iomods[subdevs[IN][n].module].iscallback) { continue; } if (!init_input(&subdevs[IN][n], n)) { return false; } } /* initialise outputs */ for (n = 0; n < n_subdevs[OUT]; n++) { if (!bfconf->iomods[subdevs[OUT][n].module].iscallback) { continue; } if (!init_output(&subdevs[OUT][n], n)) { return false; } } FOR_IN_AND_OUT { for (n = 0; n < n_subdevs[IO]; n++) { if (!bfconf->iomods[subdevs[IO][n].module].iscallback) { continue; } if (dev[IO][n]->bad_alignment) { fprintf(stderr, "\ Error: currently no support for bad callback I/O block alignment.\n\ BruteFIR's partition length must be divisable with the sound server's\n\ buffer size.\n"); return false; } } } return true; } static void callback_process(int n_subdevs[2], struct dai_subdevice *subdevs[2], pid_t wpid) { uint8_t *buffer; char msg; int n; close(cbpipe_r[0]); close(cbpipe_s[1]); msg = (char)callback_init(n_subdevs, subdevs); writefd(cbpipe_r[1], &msg, 1); if (msg == 0) { while (true) sleep(1000); } readfd(cbpipe_s[0], &msg, 1); /* attach I/O buffers */ if ((buffer = shmat(ca->buffer_id, NULL, 0)) == (void *)-1) { fprintf(stderr, "Failed to attach to shared memory with id %d: " "%s.\n", ca->buffer_id, strerror(errno)); msg = 0; } else { FOR_IN_AND_OUT { iobuffers[IO][0] = buffer; buffer += ca->buffer_format[IO].n_bytes; iobuffers[IO][1] = buffer; buffer += ca->buffer_format[IO].n_bytes; } msg = 1; } writefd(cbpipe_r[1], &msg, 1); if (msg == 0) { while (true) sleep(1000); } if (bfconf->realtime_priority) { bf_make_realtime(getpid(), bfconf->realtime_midprio, "callback"); } while (true) { if (!readfd(cbpipe_s[0], &msg, 1)) { break; } switch ((int)msg) { case CB_MSG_START: for (n = 0; n < bfconf->n_iomods; n++) { if (!bfconf->iomods[n].iscallback) { continue; } if (bfconf->iomods[n].synch_start() != 0) { fprintf(stderr, "Failed to start I/O module, aborting.\n"); bf_exit(BF_EXIT_OTHER); } } if (wpid > 0) { waitpid(wpid, &n, 0); } break; case CB_MSG_STOP: for (n = 0; n < bfconf->n_iomods; n++) { if (!bfconf->iomods[n].iscallback) { continue; } bfconf->iomods[n].synch_stop(); } msg = 1; writefd(cbpipe_r[1], &msg, 1); break; default: fprintf(stderr, "Bug: invalid msg %d, aborting.\n", (int)msg); bf_exit(BF_EXIT_OTHER); break; } } while (true) sleep(1000); } bool_t dai_init(int _period_size, int rate, int n_subdevs[2], struct dai_subdevice *subdevs[2], void *buffers[2][2]) { bool_t all_bad_alignment, none_clocked; uint8_t *buffer; char dummy = 0; int n; pid_t pid; memset(dev_fds, 0, sizeof(dev_fds)); memset(&clocked_wfds, 0, sizeof(clocked_wfds)); memset(fd2dev, 0, sizeof(fd2dev)); memset(ch2dev, 0, sizeof(ch2dev)); period_size = _period_size; sample_rate = rate; /* allocate shared memory for interprocess communication */ if ((ca = shmalloc(sizeof(struct comarea))) == NULL) { fprintf(stderr, "Failed to allocate shared memory.\n"); return false; } memset(ca, 0, sizeof(struct comarea)); ca->frames_left = -1; ca->cb_frames_left = -1; FOR_IN_AND_OUT { dai_buffer_format[IO] = &ca->buffer_format[IO]; n_devs[IO] = n_subdevs[IO]; for (n = 0; n < bfconf->n_physical_channels[IO]; n++) { if (bfconf->n_virtperphys[IO][n] == 1) { ca->delay[IO][n] = bfconf->delay[IO][bfconf->phys2virt[IO][n][0]]; ca->is_muted[IO][n] = bfconf->mute[IO][bfconf->phys2virt[IO][n][0]]; } else { ca->delay[IO][n] = 0; ca->is_muted[IO][n] = false; } } for (n = 0; n < n_devs[IO]; n++) { dev[IO][n] = &ca->dev[IO][n]; } } if (pipe(cbpipe_s) == -1 || pipe(cbpipe_r) == -1 || pipe(cbmutex_pipe[IN]) == -1 || pipe(cbmutex_pipe[OUT]) == -1) { fprintf(stderr, "Failed to create pipe: %s.\n", strerror(errno)); return false; } if (!writefd(cbmutex_pipe[IN][1], &dummy, 1) || !writefd(cbmutex_pipe[OUT][1], &dummy, 1)) { return false; } /* initialise callback io, if any */ if (bfconf->callback_io) { if ((pid = fork()) == -1) { fprintf(stderr, "Fork failed: %s.\n", strerror(errno)); return false; } if (pid != 0) { bf_register_process(pid); ca->callback_pid = getpid(); callback_process(n_subdevs, subdevs, bfconf->blocking_io ? 0 : pid); } close(cbpipe_r[1]); close(cbpipe_s[0]); if (!readfd(cbpipe_r[0], &dummy, 1)) { return false; } if (dummy == 0) { return false; } } FOR_IN_AND_OUT { if (pipe(synchpipe[IO]) == -1 || pipe(paramspipe_s[IO]) == -1 || pipe(paramspipe_r[IO]) == -1) { fprintf(stderr, "Failed to create pipe: %s.\n", strerror(errno)); return false; } if (!writefd(synchpipe[IO][1], &dummy, 1)) { return false; } } /* initialise inputs */ for (n = 0; n < n_subdevs[IN]; n++) { if (bfconf->iomods[subdevs[IN][n].module].iscallback) { continue; } if (!init_input(&subdevs[IN][n], n)) { return false; } } /* initialise outputs */ for (n = 0; n < n_subdevs[OUT]; n++) { if (bfconf->iomods[subdevs[OUT][n].module].iscallback) { continue; } if (!init_output(&subdevs[OUT][n], n)) { return false; } } /* calculate buffer format, and allocate buffers */ FOR_IN_AND_OUT { calc_buffer_format(period_size, IO, &ca->buffer_format[IO]); } if ((buffer = shmalloc_id(&ca->buffer_id, 2 * dai_buffer_format[IN]->n_bytes + 2 * dai_buffer_format[OUT]->n_bytes)) == NULL) { fprintf(stderr, "Failed to allocate shared memory.\n"); return false; } memset(buffer, 0, 2 * dai_buffer_format[IN]->n_bytes + 2 * dai_buffer_format[OUT]->n_bytes); FOR_IN_AND_OUT { iobuffers[IO][0] = buffer; buffer += dai_buffer_format[IO]->n_bytes; iobuffers[IO][1] = buffer; buffer += dai_buffer_format[IO]->n_bytes; buffers[IO][0] = iobuffers[IO][0]; buffers[IO][1] = iobuffers[IO][1]; } if (bfconf->callback_io) { /* some magic callback I/O init values */ for (n = 0; n < n_devs[OUT]; n++) { if (dev[OUT][n]->uses_callback) { dev[OUT][n]->buf_left = 0; dev[OUT][n]->cb.frames_left = -1; dev[OUT][n]->cb.iodelay_fill = 2 * period_size / dev[OUT][n]->block_size_frames - 2; } } /* let callback I/O attach shared mem buffers */ if (!writefd(cbpipe_s[1], &dummy, 1) || !readfd(cbpipe_r[0], &dummy, 1)) { return false; } if (dummy == 0) { return false; } } /* decide if to use input poll mode */ input_poll_mode = false; all_bad_alignment = true; none_clocked = true; for (n = 0; n < n_subdevs[IN]; n++) { if (dev[IN][n]->uses_clock && !dev[IN][n]->uses_callback) { none_clocked = false; if (!dev[IN][n]->bad_alignment) { all_bad_alignment = false; } } } if (bfconf->blocking_io && all_bad_alignment && !none_clocked) { if (!bfconf->allow_poll_mode) { fprintf(stderr, "\ Error: sound input hardware requires poll mode to be activated but current\n\ configuration does not allow it (allow_poll_mode: false;).\n"); return false; } input_poll_mode = true; pinfo("Input poll mode activated\n"); } return true; } void dai_trigger_callback_io(void) { char msg; msg = CB_MSG_START; if (!writefd(cbpipe_s[1], &msg, 1)) { bf_exit(BF_EXIT_OTHER); } } int dai_minblocksize(void) { int size = 0; if (bfconf->blocking_io) { FOR_IN_AND_OUT { if (min_block_size[IO] != 0 && (min_block_size[IO] < size || size == 0)) { size = min_block_size[IO]; } } } if (bfconf->callback_io) { FOR_IN_AND_OUT { if (cb_min_block_size[IO] != 0 && (cb_min_block_size[IO] < size || size == 0)) { size = cb_min_block_size[IO]; } } } return size; } bool_t dai_input_poll_mode(void) { return input_poll_mode; } bool_t dai_isinit(void) { return (dai_buffer_format[IN] != NULL); } void dai_toggle_mute(int io, int channel) { if ((io != IN && io != OUT) || channel < 0 || channel >= BF_MAXCHANNELS) { return; } ca->is_muted[io][channel] = !ca->is_muted[io][channel]; } int dai_change_delay(int io, int channel, int delay) { if (delay < 0 || channel < 0 || channel >= BF_MAXCHANNELS || (io != IN && io != OUT) || bfconf->n_virtperphys[io][channel] != 1) { return -1; } ca->delay[io][channel] = delay; return 0; } int dai_subdev_command(int io, int subdev_index, const char params[], char **message) { static char *msgstr = NULL; char dummy; int n, ans; efree(msgstr); msgstr = NULL; if (io != IN && io != OUT) { if (message != NULL) { msgstr = estrdup("Invalid io selection"); *message = msgstr; } return -1; } if (subdev_index < 0 || subdev_index >= n_devs[io]) { if (message != NULL) { msgstr = estrdup("Invalid device index"); *message = msgstr; } return -1; } if (params == NULL) { if (message != NULL) { msgstr = estrdup("Missing parameters"); *message = msgstr; } return -1; } if (!readfd(synchpipe[io][0], &dummy, 1)) { fprintf(stderr, "Failed to read from pipe.\n"); bf_exit(BF_EXIT_OTHER); } n = strlen(params) + 1; if (!writefd(paramspipe_s[io][1], &subdev_index, sizeof(int)) || !writefd(paramspipe_s[io][1], &n, sizeof(int)) || !writefd(paramspipe_s[io][1], params, n)) { fprintf(stderr, "Failed to write to pipe.\n"); bf_exit(BF_EXIT_OTHER); } if (!readfd(paramspipe_r[io][0], &ans, sizeof(int))) { fprintf(stderr, "Failed to read from pipe.\n"); bf_exit(BF_EXIT_OTHER); } if (!readfd(paramspipe_r[io][0], &n, sizeof(int))) { fprintf(stderr, "Failed to read from pipe.\n"); bf_exit(BF_EXIT_OTHER); } msgstr = emalloc(n); if (!readfd(paramspipe_r[io][0], msgstr, n)) { fprintf(stderr, "Failed to read from pipe.\n"); bf_exit(BF_EXIT_OTHER); } if (message != NULL) { *message = msgstr; } if (!writefd(synchpipe[io][1], &dummy, 1)) { fprintf(stderr, "Failed to write to pipe.\n"); bf_exit(BF_EXIT_OTHER); } return ans; } void dai_die(void) { bool_t iscallback; int n; if (ca == NULL) { return; } iscallback = (getpid() == ca->callback_pid); if (iscallback) { for (n = 0; n < bfconf->n_iomods; n++) { if (bfconf->iomods[n].iscallback) { bfconf->iomods[n].synch_stop(); } } return; } else if (ca->blocking_stopped) { return; } if (getpid() == ca->pid[OUT]) { for (n = 0; n < bfconf->n_iomods; n++) { if (!bfconf->iomods[n].iscallback && bfconf->iomods[n].synch_stop != NULL) { bfconf->iomods[n].synch_stop(); } } } FOR_IN_AND_OUT { if (getpid() == ca->pid[IO]) { for (n = 0; n < bfconf->n_iomods; n++) { if (!bfconf->iomods[n].iscallback && bfconf->iomods[n].stop != NULL) { bfconf->iomods[n].stop(IO); } } } } } void dai_input(volatile struct debug_input dbg[], int dbg_len, volatile int *dbg_loops) { static bool_t isfirst = true, startmeasure = true; static int buf_index = 0, frames = 0, curbuf = 0; static struct timeval starttv; int fdn, fdmax, n, i, k, fd, devsleft, frames_left = 0, dbg_pos = 0; struct timeval tv, zerotv, *ptv; struct subdev *sd = NULL; double measured_rate; fd_set rfds, readfds; int64_t usec, usec2; struct timespec ts; bool_t firstloop; uint8_t *buf; int minleft; buf = (uint8_t *)iobuffers[IN][curbuf]; curbuf = !curbuf; *dbg_loops = 0; zerotv.tv_sec = 0; zerotv.tv_usec = 0; firstloop = true; minleft = period_size; if ((ca->frames_left != -1 && buf_index == ca->lastbuf_index + 1) || (ca->cb_frames_left != -1 && buf_index == ca->cb_lastbuf_index + 1)) { for (n = 0; n < bfconf->n_iomods; n++) { if (bfconf->iomods[n].stop != NULL) { bfconf->iomods[n].stop(IN); } } /* There is no more data to read, just sleep and let the output process end all processes. */ while (true) sleep(100000); } devsleft = n_fd_devs[IN]; memcpy(&rfds, &dev_fds[IN], sizeof(fd_set)); if (isfirst) { ca->pid[IN] = getpid(); timestamp(&dbg[0].init.ts_start_call); dai_trigger_callback_io(); for (n = 0; n < bfconf->n_iomods; n++) { if (bfconf->iomods[n].iscallback) { continue; } if ((bfconf->iomods[n].start != NULL && bfconf->iomods[n].start(IN) != 0) || (bfconf->iomods[n].synch_start != NULL && bfconf->iomods[n].synch_start() != 0)) { fprintf(stderr, "Failed to start I/O module, aborting.\n"); bf_exit(BF_EXIT_OTHER); } } timestamp(&dbg[0].init.ts_start_ret); } while (devsleft != 0) { memcpy(&readfds, &rfds, sizeof(fd_set)); FD_SET(paramspipe_s[IN][0], &readfds); fdmax = (dev_fdn[IN] > paramspipe_s[IN][0]) ? dev_fdn[IN] : paramspipe_s[IN][0]; dbg[dbg_pos].select.fdmax = fdmax; timestamp(&dbg[dbg_pos].select.ts_call); if (input_poll_mode) { if (!firstloop) { usec = (int64_t)minleft * 1000000 / (int64_t)sample_rate; if (min_block_size[IN] > 0) { usec2 = (int64_t)min_block_size[IN] * 1000000 / (int64_t)sample_rate; if (usec2 < usec) { usec = usec2; } } /* nanosleep sleeps precise in maximum 2 ms */ if (usec > 40000) { ts.tv_sec = 0; ts.tv_nsec = usec * 1000; nanosleep(&ts, NULL); } else if (usec > 20000) { ts.tv_sec = 0; ts.tv_nsec = 10000000; nanosleep(&ts, NULL); } else if (usec > 2050) { ts.tv_sec = 0; ts.tv_nsec = 2000000; nanosleep(&ts, NULL); } else if (usec > 50) { ts.tv_sec = 0; ts.tv_nsec = (usec - 50) * 1000; nanosleep(&ts, NULL); } } ptv = &zerotv; } else { ptv = NULL; } while ((fdn = select(fdmax + 1, &readfds, NULL, NULL, ptv)) == -1 && errno == EINTR); timestamp(&dbg[dbg_pos].select.ts_ret); dbg[dbg_pos].select.retval = fdn; if (fdn == -1) { fprintf(stderr, "Select failed: %s.\n", strerror(errno)); bf_exit(BF_EXIT_OTHER); } for (n = 0; n < n_devs[IN]; n++) { if (dev[IN][n]->uses_clock && !dev[IN][n]->uses_callback && !FD_ISSET(dev[IN][n]->fd, &readfds)) { if (input_poll_mode || dev[IN][n]->bad_alignment) { FD_SET(dev[IN][n]->fd, &readfds); fdn++; } } } fd = -1; while (fdn--) { for (fd++; !FD_ISSET(fd, &readfds) && fd <= fdmax; fd++); if (fd == paramspipe_s[IN][0]) { handle_params(IN); continue; } sd = fd2dev[IN][fd]; dbg[dbg_pos].read.fd = fd; dbg[dbg_pos].read.buf = buf + sd->buf_offset; dbg[dbg_pos].read.offset = sd->buf_size - sd->buf_left; dbg[dbg_pos].read.count = sd->buf_left; timestamp(&dbg[dbg_pos].read.ts_call); n = sd->module->read(fd, buf + sd->buf_offset, sd->buf_size - sd->buf_left, sd->buf_left); timestamp(&dbg[dbg_pos].read.ts_ret); dbg[dbg_pos].read.retval = n; if (++dbg_pos == dbg_len) { dbg_pos = 0; } (*dbg_loops)++; switch (n) { case -1: switch (errno) { case EINTR: case EAGAIN: /* try again later */ break; case EIO: /* invalid input signal */ fprintf(stderr, "I/O module failed to read due to invalid " "input signal, aborting.\n"); bf_exit(BF_EXIT_INVALID_INPUT); break; case EPIPE: /* buffer underflow */ /* Actually, this should be overflow, but since we have linked the devices, broken pipe on output will be noted on the input as well, and it is more likely that it is an underflow on the output than an overflow on the input */ fprintf(stderr, "I/O module failed to read (probably) due " "to buffer underflow on output, aborting.\n"); bf_exit(BF_EXIT_BUFFER_UNDERFLOW); break; default: /* general error */ fprintf(stderr, "I/O module failed to read, aborting.\n"); bf_exit(BF_EXIT_OTHER); break; } break; case 0: if (sd->isinterleaved) { memset(buf + sd->buf_offset + sd->buf_size - sd->buf_left, 0, sd->buf_left); } else { i = sd->buf_size / sd->channels.open_channels; k = sd->buf_left / sd->channels.open_channels; for (n = 1; n <= sd->channels.open_channels; n++) { memset(buf + sd->buf_offset + n * i - k, 0, k); } } devsleft--; FD_CLR(fd, &rfds); frames_left = (sd->buf_size - sd->buf_left) / sd->channels.sf.bytes / sd->channels.open_channels; if (ca->frames_left == -1 || frames_left < ca->frames_left) { ca->frames_left = frames_left; } ca->lastbuf_index = buf_index; break; default: sd->buf_left -= n; if (monitor_rate_fd == fd) { if (startmeasure) { if (sd->buf_left == 0) { startmeasure = false; gettimeofday(&starttv, NULL); } } else { frames += n / (sd->buf_size / period_size); if (frames >= sample_rate && sd->buf_left == 0) { gettimeofday(&tv, NULL); timersub(&tv, &starttv, &tv); measured_rate = 1000.0 * (double)frames / (double)(tv.tv_sec * 1000 + tv.tv_usec / 1000); if (bfconf->debug) { fprintf(stderr, "measured rate: %.3f kHz " "(%d frames / %ld usecs)\n", measured_rate / 1000.0, frames, tv.tv_sec * 1000000 + tv.tv_usec); } if (measured_rate < (double)sample_rate * 0.98 || measured_rate > (double)sample_rate / 0.98) { fprintf(stderr, "Configured sample rate is " "%.1f kHz, but measured is %.1f kHz, " "aborting.\n", (double)sample_rate / 1000.0, measured_rate / 1000.0); bf_exit(BF_EXIT_INVALID_INPUT); } startmeasure = true; frames = 0; } } } n = sd->buf_left / (sd->buf_size / period_size); if (sd->uses_clock && (n < minleft || minleft == -1)) { minleft = n; } if (sd->buf_left == 0) { sd->buf_left = sd->buf_size; devsleft--; FD_CLR(fd, &rfds); } break; } } firstloop = false; } for (n = 0; n < n_devs[IN]; n++) { sd = dev[IN][n]; if (!sd->uses_callback) { do_mute(sd, IN, sd->buf_size, (void *)(buf + sd->buf_offset), 0); update_delay(sd, IN, buf); } } isfirst = false; buf_index++; } void dai_output(bool_t iodelay_fill, int synch_fd, volatile struct debug_output dbg[], int dbg_len, volatile int *dbg_loops) { static bool_t isfirst = true; static bool_t islast = false; static int buf_index = 0; static int curbuf = 0; static fd_set readfds; int devsleft, fdn, fd, n, frames_left; uint8_t *buf, dummydata[1] = { '\0' }; fd_set wfds, writefds; struct subdev *sd; int dbg_pos = 0; buf = (uint8_t *)iobuffers[OUT][curbuf]; curbuf = !curbuf; *dbg_loops = 0; if ((ca->frames_left != -1 && buf_index == ca->lastbuf_index) || (ca->cb_frames_left != -1 && buf_index == ca->cb_lastbuf_index)) { frames_left = ca->frames_left; n = ca->cb_frames_left; if (frames_left == -1 || (n != -1 && n < frames_left)) { frames_left = n; } for (n = 0; n < n_devs[OUT]; n++) { if (!dev[OUT][n]->uses_callback) { dev[OUT][n]->buf_left = dev[OUT][n]->buf_size = frames_left * dev[OUT][n]->channels.sf.bytes * dev[OUT][n]->channels.open_channels; } } islast = true; } if (isfirst) { FD_ZERO(&readfds); } for (n = 0; n < n_devs[OUT]; n++) { if (dev[OUT][n]->uses_callback) { continue; } update_delay(dev[OUT][n], OUT, buf); } if (iodelay_fill) { memcpy(&wfds, &clocked_wfds, sizeof(fd_set)); devsleft = n_clocked_devs; } else { devsleft = n_fd_devs[OUT]; memcpy(&wfds, &dev_fds[OUT], sizeof(fd_set)); } while (devsleft != 0) { memcpy(&writefds, &wfds, sizeof(fd_set)); FD_SET(paramspipe_s[OUT][0], &readfds); fdn = (dev_fdn[OUT] > paramspipe_s[OUT][0]) ? dev_fdn[OUT] : paramspipe_s[OUT][0]; dbg[dbg_pos].select.fdmax = fdn + 1; timestamp(&dbg[dbg_pos].select.ts_call); while ((fdn = select(fdn + 1, &readfds, &writefds, NULL, NULL)) == -1 && errno == EINTR); timestamp(&dbg[dbg_pos].select.ts_ret); dbg[dbg_pos].select.retval = fdn; if (fdn == -1) { fprintf(stderr, "Select failed: %s.\n", strerror(errno)); bf_exit(BF_EXIT_OTHER); } if (FD_ISSET(paramspipe_s[OUT][0], &readfds)) { handle_params(OUT); fdn--; } fd = -1; while (fdn--) { for (fd++; !FD_ISSET(fd, &writefds) && fd <= dev_fdn[OUT]; fd++); sd = fd2dev[OUT][fd]; if (sd->block_size > 0 && sd->buf_left > sd->block_size) { n = sd->block_size + sd->buf_left % sd->block_size; } else { n = sd->buf_left; } do_mute(sd, OUT, n, (void *)(buf + sd->buf_offset), sd->buf_size - sd->buf_left); dbg[dbg_pos].write.fd = fd; dbg[dbg_pos].write.buf = buf + sd->buf_offset; dbg[dbg_pos].write.offset = sd->buf_size - sd->buf_left; dbg[dbg_pos].write.count = n; timestamp(&dbg[dbg_pos].write.ts_call); n = sd->module->write(fd, buf + sd->buf_offset, sd->buf_size - sd->buf_left, n); timestamp(&dbg[dbg_pos].write.ts_ret); dbg[dbg_pos].write.retval = n; if (++dbg_pos == dbg_len) { dbg_pos = 0; } (*dbg_loops)++; switch (n) { case -1: switch (errno) { case EINTR: case EAGAIN: /* try again later */ break; case EPIPE: /* buffer underflow */ fprintf(stderr, "I/O module failed to write due to buffer " "underflow, aborting.\n"); bf_exit(BF_EXIT_BUFFER_UNDERFLOW); break; default: /* general error */ fprintf(stderr, "I/O module failed to write, aborting.\n"); bf_exit(BF_EXIT_OTHER); break; } break; default: sd->buf_left -= n; break; } if (sd->buf_left == 0) { sd->buf_left = sd->buf_size; devsleft--; FD_CLR(fd, &wfds); } } if (synch_fd != -1) { timestamp(&dbg[0].init.ts_synchfd_call); if (!writefd(synch_fd, dummydata, 1)) { bf_exit(BF_EXIT_OTHER); } sched_yield(); /* let input process start now */ timestamp(&dbg[0].init.ts_synchfd_ret); synch_fd = -1; } if (!iodelay_fill && isfirst) { isfirst = false; timestamp(&dbg[0].init.ts_start_call); for (n = 0; n < bfconf->n_iomods; n++) { if (bfconf->iomods[n].iscallback) { continue; } if (bfconf->iomods[n].start != NULL && bfconf->iomods[n].start(OUT) != 0) { fprintf(stderr, "I/O module failed to start, aborting.\n"); bf_exit(BF_EXIT_OTHER); } } timestamp(&dbg[0].init.ts_start_ret); ca->pid[OUT] = getpid(); } } if (iodelay_fill) { return; } if (islast) { for (n = 0; n < bfconf->n_iomods; n++) { if (bfconf->iomods[n].iscallback) { /* callback I/O is stopped elsewhere */ continue; } if (bfconf->iomods[n].synch_stop != NULL) { bfconf->iomods[n].synch_stop(); } if (bfconf->iomods[n].stop != NULL) { bfconf->iomods[n].stop(OUT); } } ca->blocking_stopped = true; for (n = 0; n < n_devs[OUT]; n++) { sd = dev[OUT][n]; if (!sd->uses_callback) { sd->finished = true; } } if (output_finish()) { bf_exit(BF_EXIT_OK); } else { while (true) sleep(1000); } } buf_index++; } static void process_callback_input(struct subdev *sd, void *cbbufs[], int frame_count) { struct buffer_format *bf; uint8_t *buf, *copybuf; int n, cnt, count; buf = (uint8_t *)iobuffers[IN][sd->cb.curbuf]; count = frame_count * sd->channels.used_channels * sd->channels.sf.bytes; if (sd->isinterleaved) { memcpy(buf + sd->buf_offset + sd->buf_size - sd->buf_left, cbbufs[0], count); } else { bf = &dai_buffer_format[IN]->bf[sd->channels.channel_name[0]]; cnt = count / sd->channels.used_channels; copybuf = buf + sd->buf_offset + (sd->buf_size - sd->buf_left) / sd->channels.used_channels; for (n = 0; n < sd->channels.used_channels; n++) { memcpy(copybuf, cbbufs[n], cnt); copybuf += period_size * bf->sf.sbytes; } } sd->buf_left -= count; if (sd->buf_left == 0) { sd->cb.curbuf = !sd->cb.curbuf; do_mute(sd, IN, sd->buf_size, (void *)(buf + sd->buf_offset), 0); update_delay(sd, IN, buf); } } static void process_callback_output(struct subdev *sd, void *cbbufs[], int frame_count, bool_t iodelay_fill) { struct buffer_format *bf; uint8_t *buf, *copybuf; int n, cnt, count; buf = (uint8_t *)iobuffers[OUT][sd->cb.curbuf]; count = frame_count * sd->channels.used_channels * sd->channels.sf.bytes; if (iodelay_fill) { if (sd->isinterleaved) { memset(cbbufs[0], 0, count); } else { cnt = count / sd->channels.used_channels; for (n = 0; n < sd->channels.used_channels; n++) { memset(cbbufs[n], 0, cnt); } } return; } if (sd->buf_left == sd->buf_size) { update_delay(sd, OUT, buf); } do_mute(sd, OUT, count, (void *)(buf + sd->buf_offset), sd->buf_size - sd->buf_left); if (sd->isinterleaved) { memcpy(cbbufs[0], buf + sd->buf_offset + sd->buf_size - sd->buf_left, count); } else { bf = &dai_buffer_format[OUT]->bf[sd->channels.channel_name[0]]; cnt = count / sd->channels.used_channels; copybuf = buf + sd->buf_offset + (sd->buf_size - sd->buf_left) / sd->channels.used_channels; for (n = 0; n < sd->channels.used_channels; n++) { memcpy(cbbufs[n], copybuf, cnt); copybuf += period_size * bf->sf.sbytes; } } sd->buf_left -= count; if (sd->buf_left == 0) { sd->cb.curbuf = !sd->cb.curbuf; } } static void trigger_callback_ready(int io) { char dummy[BF_MAXCHANNELS]; if (callback_ready_waiting[io] > 0) { memset(dummy, 0, sizeof(dummy)); if (!writefd(cbreadywait_pipe[io][1], dummy, callback_ready_waiting[io])) { bf_exit(BF_EXIT_OTHER); } callback_ready_waiting[io] = 0; } cbmutex(io, false); } static void wait_callback_ready(int io) { char dummy; callback_ready_waiting[io]++; cbmutex(io, false); if (!readfd(cbreadywait_pipe[io][0], &dummy, 1)) { bf_exit(BF_EXIT_OTHER); } } static int process_callback(void **states[2], int state_count[2], void **buffers[2], int frame_count, int event) { bool_t finished, unlock_output; int n, i, buf_index; struct subdev *sd; switch (event) { case BF_CALLBACK_EVENT_LAST_INPUT: if (ca->cb_frames_left == -1 || frame_count < ca->cb_frames_left) { ca->cb_frames_left = frame_count; } ca->cb_lastbuf_index = ca->cb_buf_index[IN]; return 0; case BF_CALLBACK_EVENT_FINISHED: for (n = 0; n < state_count[OUT]; n++) { sd = (struct subdev *)states[OUT][n]; sd->finished = true; } cbmutex(IN, true); trigger_callback_ready(IN); cbmutex(OUT, true); trigger_callback_ready(OUT); if (output_finish()) { bf_exit(BF_EXIT_OK); } return -1; case BF_CALLBACK_EVENT_ERROR: fprintf(stderr, "An error occurred in a callback I/O module.\n"); bf_exit(BF_EXIT_OTHER); break; case BF_CALLBACK_EVENT_NORMAL: break; default: fprintf(stderr, "Invalid event: %d\n", event); bf_exit(BF_EXIT_OTHER); break; } if (states == NULL || state_count == NULL || buffers == NULL || frame_count <= 0) { fprintf(stderr, "Invalid parameters: states %p; state_count %p; " "buffers: %p; frame_count: %d\n", states, state_count, buffers, frame_count); bf_exit(BF_EXIT_OTHER); } if (state_count[IN] > 0) { cbmutex(IN, true); for (n = 0, i = 0; n < state_count[IN]; n++) { sd = (struct subdev *)states[IN][n]; if (frame_count != sd->block_size_frames) { fprintf(stderr, "Error: unexpected callback I/O block " "alignment (%d != %d)\n", frame_count, sd->block_size_frames); bf_exit(BF_EXIT_OTHER); } process_callback_input(sd, &buffers[IN][i], frame_count); if (sd->isinterleaved) { i++; } else { i += sd->channels.used_channels; } } sd = (struct subdev *)states[IN][0]; if (sd->buf_left == 0) { finished = true; for (n = 0; n < n_devs[IN]; n++) { sd = dev[IN][n]; if (sd->uses_callback && sd->buf_left != 0) { finished = false; break; } } if (finished) { for (n = 0; n < n_devs[IN]; n++) { sd = dev[IN][n]; if (sd->uses_callback) { sd->buf_left = sd->buf_size; } } bf_callback_ready(IN); ca->cb_buf_index[IN]++; trigger_callback_ready(IN); } else { wait_callback_ready(IN); } } else { cbmutex(IN, false); } } if (state_count[OUT] > 0) { cbmutex(OUT, true); unlock_output = false; sd = (struct subdev *)states[OUT][0]; if (sd->buf_left == 0 && sd->cb.iodelay_fill == 0) { finished = true; for (n = 0; n < n_devs[OUT]; n++) { sd = dev[OUT][n]; if (sd->uses_callback && (sd->buf_left != 0 || sd->cb.iodelay_fill != 0)) { finished = false; break; } } if (finished) { for (n = 0; n < n_devs[OUT]; n++) { sd = dev[OUT][n]; if (sd->uses_callback) { sd->buf_left = sd->buf_size; } } bf_callback_ready(OUT); ca->cb_buf_index[OUT]++; trigger_callback_ready(OUT); } else { wait_callback_ready(OUT); } } else { unlock_output = true; } for (n = 0, i = 0; n < state_count[OUT]; n++) { sd = (struct subdev *)states[OUT][n]; if (frame_count != sd->block_size_frames) { fprintf(stderr, "Error: unexpected callback I/O block " "alignment (%d != %d)\n", frame_count, sd->block_size_frames); bf_exit(BF_EXIT_OTHER); } process_callback_output(sd, &buffers[OUT][i], frame_count, sd->cb.iodelay_fill != 0); if (sd->cb.iodelay_fill != 0) { sd->cb.iodelay_fill--; } if (sd->isinterleaved) { i++; } else { i += sd->channels.used_channels; } } if (unlock_output) { cbmutex(OUT, false); } /* last buffer? */ n = ca->cb_buf_index[IN]; i = ca->cb_buf_index[OUT]; buf_index = n < i ? i : n; sd = (struct subdev *)states[OUT][0]; if (sd->cb.frames_left == -1 && ((ca->frames_left != -1 && buf_index == ca->lastbuf_index + 1) || (ca->cb_frames_left != -1 && buf_index == ca->cb_lastbuf_index + 1))) { n = ca->frames_left; i = ca->cb_frames_left; if (n == -1 || (n > i && i != -1)) { n = i; } sd->cb.frames_left = n; } if (sd->cb.frames_left != -1) { if (sd->cb.frames_left > sd->block_size_frames) { sd->cb.frames_left -= sd->block_size_frames; return 0; } if (sd->cb.frames_left == 0) { return -1; } return sd->cb.frames_left; } } return 0; } brutefir-1.0o/dai.h0000664000175000017500000000741612752354353013616 0ustar andersanders/* * (c) Copyright 2001 - 2003, 2005 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _DAI_H_ #define _DAI_H_ #include #include #include #include #include "defs.h" #include "inout.h" #include "bfmod.h" /* digital audio interface */ struct sample_format { bool_t isfloat; bool_t swap; int bytes; int sbytes; double scale; int format; }; struct buffer_format { struct sample_format sf; int sample_spacing; /* in samples */ int byte_offset; /* in bytes */ }; struct dai_channels { struct sample_format sf; /* how many channels to open (on device level) */ int open_channels; /* how many channels of the opened channels that are used (on device level) used_channels <= open_channels */ int used_channels; /* array (used_channels elements long) which contains the channel indexes on device level (0 <= index < open_channels) of the used channels. */ int *channel_selection; /* array (used_channels elements long) which contains the channel indexes on a logical level. These indexes are those used in the dai_* function calls. */ int *channel_name; }; struct dai_subdevice { struct dai_channels channels; void *params; bool_t uses_clock; int sched_policy; struct sched_param sched_param; int module; }; struct dai_buffer_format { int n_bytes; int n_samples; int n_channels; struct buffer_format bf[BF_MAXCHANNELS]; }; extern struct dai_buffer_format *dai_buffer_format[2]; struct debug_input { struct { uint64_t ts_start_call; uint64_t ts_start_ret; } init; struct { int fdmax; int retval; uint64_t ts_call; uint64_t ts_ret; } select; struct { int fd; void *buf; int offset; int count; int retval; uint64_t ts_call; uint64_t ts_ret; } read; }; struct debug_output { struct { uint64_t ts_synchfd_call; uint64_t ts_synchfd_ret; uint64_t ts_start_call; uint64_t ts_start_ret; } init; struct { int fdmax; int retval; uint64_t ts_call; uint64_t ts_ret; } select; struct { int fd; void *buf; int offset; int count; int retval; uint64_t ts_call; uint64_t ts_ret; } write; }; /* * The subdevs structures are used internally, so they must not be deallocated * nor modified. */ bool_t dai_init(int period_size, /* in samples, must be a power of two */ int rate, int n_subdevs[2], struct dai_subdevice *subdevs[2], void *buffers[2][2]); /* * Always deliver full fragment. If less than full (for files), it is handled * internally. (State is kept so not a full fragment is written on output). */ void dai_input(volatile struct debug_input dbg[], int dbg_len, volatile int *dbg_loops); /* * Always full fragment size. Buffer area not filled with samples must be zero. */ void dai_output(bool_t iodelay_fill, int synch_fd, volatile struct debug_output dbg[], int dbg_len, volatile int *dbg_loops); void dai_trigger_callback_io(void); int dai_minblocksize(void); bool_t dai_input_poll_mode(void); bool_t dai_isinit(void); void dai_toggle_mute(int io, int channel); int dai_change_delay(int io, int channel, int delay); int dai_subdev_command(int io, int subdev_index, const char params[], char **message); /* * Shut down prematurely. Must be called by the input and output process * separately. For any other process it has no effect. */ void dai_die(void); #endif brutefir-1.0o/defs.h0000644000175000017500000000053612752354353013774 0ustar andersanders/* * (c) Copyright 1999, 2002 - 2004, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _DEFS_H_ #define _DEFS_H_ #include #include "sysarch.h" #define true ((bool_t)1) #define false ((bool_t)0) typedef int bool_t; typedef uint64_t ull_t; typedef int64_t ll_t; #endif brutefir-1.0o/delay.c0000664000175000017500000003341512752354353014150 0ustar andersanders/* * (c) Copyright 2002, 2004, 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include #include "defs.h" #include "pinfo.h" #include "delay.h" #include "bfrun.h" #include "emalloc.h" #include "firwindow.h" #include "convolver.h" #include "timestamp.h" static int realsize; static int subdelay_filter_length; static td_conv_t **subdelay_filter = NULL; static int subdelay_step_count; static int subdelay_filterblock_size; static int subdelay_fragment_size; struct _delaybuffer_t_ { int fragsize; /* fragment size */ int maxdelay; /* maximum allowable delay, or negative if delay cannot be changed in runtime */ int curdelay; /* current delay */ int curbuf; /* index of current full-sized buffer */ int n_fbufs; /* number of full-sized buffers currently used */ int n_fbufs_cap; /* number of full-sized buffers allocated */ void **fbufs; /* full-sized buffers */ int n_rest; /* samples in rest buffer */ void *rbuf; /* rest buffer */ void *shortbuf[2]; /* pointers to buffers which fit the whole delay, only used when delay is <= fragment size */ }; static double sinc(double x) { if (x == 0.0) { return 1.0; } return sin(x) / x; } static void * sample_sinc(int half_length, double offset, double kaiser_beta) { int n, filter_length; void *filter; double x; filter_length = 2 * half_length + 1; filter = emallocaligned(filter_length * realsize); for (n = 0; n < filter_length; n++) { x = M_PI * ((double)(n - half_length) - offset); if (realsize == 4) { ((float *)filter)[n] = (float)sinc(x); } else { ((double *)filter)[n] = sinc(x); } } firwindow_kaiser(filter, filter_length, offset, 9, realsize); return filter; } static void copy_to_delaybuf(void *dbuf, void *buf, int sample_size, int sample_spacing, int n_samples) { int n, i; if (sample_spacing == 1) { memcpy(dbuf, buf, n_samples * sample_size); return; } switch (sample_size) { case 1: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint8_t *)dbuf)[n] = ((uint8_t *)buf)[i]; } break; case 2: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint16_t *)dbuf)[n] = ((uint16_t *)buf)[i]; } break; case 3: n_samples *= 3; sample_spacing = sample_spacing * 3 - 3; for (n = i = 0; n < n_samples; i += sample_spacing) { ((uint8_t *)dbuf)[n++] = ((uint8_t *)buf)[i++]; ((uint8_t *)dbuf)[n++] = ((uint8_t *)buf)[i++]; ((uint8_t *)dbuf)[n++] = ((uint8_t *)buf)[i++]; } break; case 4: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint32_t *)dbuf)[n] = ((uint32_t *)buf)[i]; } break; case 8: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint64_t *)dbuf)[n] = ((uint64_t *)buf)[i]; } break; default: fprintf(stderr, "Sample byte size %d not supported.\n", sample_size); bf_exit(BF_EXIT_OTHER); break; } } static void copy_from_delaybuf(void *buf, void *dbuf, int sample_size, int sample_spacing, int n_samples) { int n, i; if (sample_spacing == 1) { memcpy(buf, dbuf, n_samples * sample_size); return; } switch (sample_size) { case 1: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint8_t *)buf)[i] = ((uint8_t *)dbuf)[n]; } break; case 2: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint16_t *)buf)[i] = ((uint16_t *)dbuf)[n]; } break; case 3: n_samples *= 3; sample_spacing = sample_spacing * 3 - 3; for (n = i = 0; n < n_samples; i += sample_spacing) { ((uint8_t *)buf)[i++] = ((uint8_t *)dbuf)[n++]; ((uint8_t *)buf)[i++] = ((uint8_t *)dbuf)[n++]; ((uint8_t *)buf)[i++] = ((uint8_t *)dbuf)[n++]; } break; case 4: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint32_t *)buf)[i] = ((uint32_t *)dbuf)[n]; } break; case 8: for (n = i = 0; n < n_samples; n++, i += sample_spacing) { ((uint64_t *)buf)[i] = ((uint64_t *)dbuf)[n]; } break; default: fprintf(stderr, "Sample byte size %d not supported.\n", sample_size); bf_exit(BF_EXIT_OTHER); break; } } static void shift_samples(void *buf, int sample_size, int sample_spacing, int n_samples, int distance) { int n, i; n = (n_samples - 1) * sample_spacing; i = (n_samples + distance - 1) * sample_spacing; switch (sample_size) { case 1: for (; n >= 0; n -= sample_spacing, i -= sample_spacing) { ((uint8_t *)buf)[i] = ((uint8_t *)buf)[n]; } break; case 2: for (; n >= 0; n -= sample_spacing, i -= sample_spacing) { ((uint16_t *)buf)[i] = ((uint16_t *)buf)[n]; } break; case 3: n *= 3; i *= 3; sample_spacing *= 3; for (; n >= 0; n -= sample_spacing, i -= sample_spacing) { ((uint8_t *)buf)[i++] = ((uint8_t *)buf)[n++]; ((uint8_t *)buf)[i++] = ((uint8_t *)buf)[n++]; ((uint8_t *)buf)[i++] = ((uint8_t *)buf)[n++]; } break; case 4: for (; n >= 0; n -= sample_spacing, i -= sample_spacing) { ((uint32_t *)buf)[i] = ((uint32_t *)buf)[n]; } break; case 8: for (; n >= 0; n -= sample_spacing, i -= sample_spacing) { ((uint64_t *)buf)[i] = ((uint64_t *)buf)[n]; } break; default: fprintf(stderr, "Sample byte size %d not supported.\n", sample_size); bf_exit(BF_EXIT_OTHER); break; } } static void update_delay_buffer(delaybuffer_t *db, int sample_size, int sample_spacing, uint8_t *buf) { uint8_t *lastbuf; lastbuf = (db->curbuf == db->n_fbufs - 1) ? db->fbufs[0] : db->fbufs[db->curbuf + 1]; /* 1. copy buffer to current full-sized delay buffer */ copy_to_delaybuf(db->fbufs[db->curbuf], buf, sample_size, sample_spacing, db->fragsize); if (db->n_rest != 0) { /* 2. copy from delay rest buffer to the start of buffer */ copy_from_delaybuf(buf, db->rbuf, sample_size, sample_spacing, db->n_rest); /* 3. copy from end of last full-sized delay buffer to rest buffer */ memcpy(db->rbuf, lastbuf + (db->fragsize - db->n_rest) * sample_size, db->n_rest * sample_size); } /* 4. copy from start of last full-sized buffer to end of buffer */ copy_from_delaybuf(buf + db->n_rest * sample_size * sample_spacing, lastbuf, sample_size, sample_spacing, db->fragsize - db->n_rest); if (++db->curbuf == db->n_fbufs) { db->curbuf = 0; } } static void update_delay_short_buffer(delaybuffer_t *db, int sample_size, int sample_spacing, uint8_t *buf) { copy_to_delaybuf(db->shortbuf[db->curbuf], buf + (db->fragsize - db->n_rest) * sample_size * sample_spacing, sample_size, sample_spacing, db->n_rest); shift_samples(buf, sample_size, sample_spacing, db->fragsize - db->n_rest, db->n_rest); db->curbuf = !db->curbuf; copy_from_delaybuf(buf, db->shortbuf[db->curbuf], sample_size, sample_spacing, db->n_rest); } static void change_delay(delaybuffer_t *db, int sample_size, int newdelay) { int size, i; if (newdelay == db->curdelay || newdelay > db->maxdelay) { return; } if (newdelay <= db->fragsize) { db->n_rest = newdelay; size = newdelay * sample_size; if (db->curdelay > db->fragsize || db->curdelay < newdelay) { memset(db->shortbuf[0], 0, size); memset(db->shortbuf[1], 0, size); } db->n_fbufs = 0; db->curbuf = 0; db->curdelay = newdelay; return; } db->n_rest = newdelay % db->fragsize; db->n_fbufs = newdelay / db->fragsize + 1; size = db->fragsize * sample_size; if (db->curdelay < newdelay) { for (i = 0; i < db->n_fbufs; i++) { memset(db->fbufs[i], 0, size); } if (db->n_rest != 0) { memset(db->rbuf, 0, db->n_rest * sample_size); } } db->curbuf = 0; db->curdelay = newdelay; } void delay_update(delaybuffer_t *db, void *buf, int sample_size, int sample_spacing, int delay, void *optional_target_buf) { change_delay(db, sample_size, delay); if (optional_target_buf != NULL) { copy_to_delaybuf(optional_target_buf, buf, sample_size, sample_spacing, db->fragsize); buf = optional_target_buf; sample_spacing = 1; } if (db->n_fbufs > 0) { update_delay_buffer(db, sample_size, sample_spacing, buf); } else if (db->n_rest > 0) { update_delay_short_buffer(db, sample_size, sample_spacing, buf); } } delaybuffer_t * delay_allocate_buffer(int fragment_size, int initdelay, int maxdelay, int sample_size) { delaybuffer_t *db; int n, delay; int size; /* if maxdelay is negative, no delay changing will be allowed, thus memory need only to be allocated for the current delay */ db = emalloc(sizeof(delaybuffer_t)); memset(db, 0, sizeof(delaybuffer_t)); db->fragsize = fragment_size; delay = (maxdelay <= 0) ? initdelay : maxdelay; if (maxdelay >= 0 && delay > maxdelay) { delay = initdelay = maxdelay; } db->curdelay = initdelay; db->maxdelay = maxdelay; if (delay == 0) { return db; } if (delay <= fragment_size) { /* optimise for short delay */ db->n_rest = initdelay; /* current value */ size = delay * sample_size; db->shortbuf[0] = emallocaligned(size); db->shortbuf[1] = emallocaligned(size); memset(db->shortbuf[0], 0, size); memset(db->shortbuf[1], 0, size); return db; } if (maxdelay > 0) { /* allocate full-length short buffers to keep this option if the delay is reduced in run-time */ size = fragment_size * sample_size; db->shortbuf[0] = emallocaligned(size); db->shortbuf[1] = emallocaligned(size); memset(db->shortbuf[0], 0, size); memset(db->shortbuf[1], 0, size); } db->n_rest = initdelay % fragment_size; db->n_fbufs = initdelay / fragment_size + 1; if (db->n_fbufs == 1) { db->n_fbufs = 0; } db->n_fbufs_cap = delay / fragment_size + 1; db->fbufs = emalloc(db->n_fbufs_cap * sizeof(void *)); size = fragment_size * sample_size; for (n = 0; n < db->n_fbufs_cap; n++) { db->fbufs[n] = emallocaligned(size); memset(db->fbufs[n], 0, size); } if (maxdelay > 0) { db->rbuf = emallocaligned(size); memset(db->rbuf, 0, size); } else if (db->n_rest != 0) { size = db->n_rest * sample_size; db->rbuf = emallocaligned(size); memset(db->rbuf, 0, size); } return db; } int delay_subsample_filterblocksize(void) { return subdelay_filterblock_size; } void delay_subsample_update(void *buf, void *rest, int subdelay) { void *cbuf_low, *cbuf_high, *cbuffer; int i, blocksize; uint64_t t1, t2; if (subdelay <= -subdelay_step_count || subdelay >= subdelay_step_count) { return; } timestamp(&t1); blocksize = subdelay_filterblock_size * realsize; cbuffer = alloca(blocksize << 1); cbuf_low = cbuffer; cbuf_high = &((uint8_t *)cbuf_low)[blocksize]; for (i = 0; i < subdelay_fragment_size * realsize; i += blocksize) { memcpy(cbuf_low, rest, blocksize); memcpy(cbuf_high, &((uint8_t *)buf)[i], blocksize); memcpy(rest, cbuf_high, blocksize); convolver_td_convolve(subdelay_filter[subdelay], cbuffer); memcpy(&((uint8_t *)buf)[i], cbuf_low, blocksize); } timestamp(&t2); t2 -= t1; /*fprintf(stderr, "%" PRIu64 "\n", t2 / (uint64_t)bfconf->cpu_mhz);*/ } bool_t delay_subsample_init(int step_count, int half_filter_length, double kaiser_beta, int fragment_size, int _realsize) { void *filter; int n; realsize = _realsize; subdelay_filter_length = 2 * half_filter_length + 1; subdelay_filterblock_size = convolver_td_block_length(subdelay_filter_length); if (step_count < 2) { fprintf(stderr, "Invalid step_count %d.\n", step_count); return false; } if (half_filter_length < 1) { fprintf(stderr, "Invalid half filter length %d.\n", half_filter_length); return false; } if (fragment_size % subdelay_filterblock_size != 0) { fprintf(stderr, "Incompatible fragment/filter sizes (%d/%d).\n", fragment_size, subdelay_filter_length); return false; } if (realsize != 4 && realsize != 8) { fprintf(stderr, "Invalid real size %d.\n", realsize); return false; } subdelay_fragment_size = fragment_size; subdelay_step_count = step_count; subdelay_filter = emalloc((2 * step_count + 1) * sizeof(td_conv_t *)); subdelay_filter = &subdelay_filter[step_count]; filter = emalloc(subdelay_filter_length * realsize); memset(filter, 0, subdelay_filter_length * realsize); if (realsize == 4) { ((float *)filter)[subdelay_filter_length >> 1] = 1.0; } else { ((double *)filter)[subdelay_filter_length >> 1] = 1.0; } subdelay_filter[0] = convolver_td_new(filter, subdelay_filter_length); efree(filter); for (n = 1; n < step_count; n++) { filter = sample_sinc(subdelay_filter_length >> 1, (double)n / step_count, kaiser_beta); subdelay_filter[n] = convolver_td_new(filter, subdelay_filter_length); efree(filter); } for (n = -1; n > -step_count; n--) { filter = sample_sinc(subdelay_filter_length >> 1, (double)n / step_count, kaiser_beta); subdelay_filter[n] = convolver_td_new(filter, subdelay_filter_length); efree(filter); } pinfo("Created %d subsample delay filters with %d taps " "and kaiser beta %g.\n", 2 * step_count - 1, subdelay_filter_length, kaiser_beta); return true; } brutefir-1.0o/delay.h0000664000175000017500000000166212752354353014154 0ustar andersanders/* * (c) Copyright 2002, 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _DELAY_H_ #define _DELAY_H_ #include "defs.h" typedef struct _delaybuffer_t_ delaybuffer_t; /* optional_target_buf has sample_spacing == 1 */ void delay_update(delaybuffer_t *db, void *buf, int sample_size, int sample_spacing, int delay, void *optional_target_buf); delaybuffer_t * delay_allocate_buffer(int fragment_size, int initdelay, int maxdelay, int sample_size); int delay_subsample_filterblocksize(void); void delay_subsample_update(void *buf, void *rest, int subdelay); bool_t delay_subsample_init(int step_count, int half_filter_length, double kaiser_beta, int fragment_size, int _realsize); #endif brutefir-1.0o/directpath.txt0000644000175000017500000027354612752354353015607 0ustar andersanders1.7957818272407167e-06 -4.1996008803835139e-06 6.2716891989111900e-05 -4.3457521314849146e-06 3.2076168281491846e-05 -4.4984435589867644e-06 6.2463019276037812e-05 -4.6585564632550813e-06 1.2876130313088652e-06 2.5689299945952371e-05 -5.9895977756241336e-05 -3.5521785321179777e-05 9.8324517239234410e-07 -3.5706550988834351e-05 8.1883354141609743e-07 -3.5899516660720110e-05 6.4598498283885419e-07 -5.5825003073550761e-06 -3.0052276997594163e-05 -5.7907172958948649e-06 3.0792471079621464e-05 2.4510913135600276e-05 7.7979166235309094e-08 -6.2310855355462991e-06 -1.2845612218370661e-07 -6.4626565290382132e-06 -3.4210688681923784e-07 -6.7017963374382816e-06 -5.6277031035278924e-07 -3.7465346395038068e-05 -7.9139022091112565e-07 2.3316066290135495e-05 2.9488817745004781e-05 2.3056034478941001e-05 -1.2724603948299773e-06 -7.7287122621783055e-06 -3.2038482459029183e-05 -3.8519287045346573e-05 2.8739606932504103e-05 -8.2811529864557087e-06 2.8477297746576369e-05 -8.5666124505223706e-06 -2.3102959403331624e-06 -8.8566039266879670e-06 2.7933658202528022e-05 2.1365183783927932e-05 -2.8639760785154067e-06 -9.4527422334067523e-06 2.7369045710656792e-05 -4.0275408537127078e-05 -3.4375948416709434e-06 -1.0065581591334194e-05 5.7305172958876938e-05 2.0139577827649191e-05 -4.0269733290188015e-06 -1.0692649993870873e-05 -4.3265499698463827e-06 -1.1010960406565573e-05 -4.6289146666822489e-06 4.9704762204783037e-05 -3.5451757867122069e-05 -1.1652067769318819e-05 2.5276596716139466e-05 -1.1975391316809691e-05 -5.5490454542450607e-06 -1.2298775800445583e-05 2.4661841962370090e-05 -1.2622040230780840e-05 -6.1662235566473100e-06 1.7572368960827589e-05 -6.4746059251774568e-06 -1.3267177564557642e-05 -6.7824448706232943e-06 -1.3588481124315877e-05 2.3428447093465365e-05 -4.4424454245017841e-05 -7.3940173024311662e-06 1.6293393855448812e-05 -7.6961123340879567e-06 -1.4537298739014659e-05 2.2521227947436273e-05 1.5670409993617795e-05 2.2226531655178405e-05 1.5364999853773043e-05 -8.5827450675424188e-06 4.5581218728329986e-05 5.2165531087666750e-05 -1.5749457816127688e-05 -9.1515967142186128e-06 1.4478170669462997e-05 2.1090183508931659e-05 -4.6840432332828641e-05 2.0819705241592601e-05 -1.6600131857558154e-05 2.0557223251671530e-05 -1.6869475075509399e-05 2.0301267795730382e-05 1.3386812497628853e-05 2.0052386389579624e-05 -1.7384034435963258e-05 1.9811866877716966e-05 4.3406806071288884e-05 1.9580509615479968e-05 1.2653581507038325e-05 1.9359018551767804e-05 1.2428498848748859e-05 1.9147091734339483e-05 1.2214071830385365e-05 -1.1573598385439254e-05 4.2526778997853398e-05 1.8752331016003154e-05 1.1816585356427822e-05 1.8573213310446590e-05 4.2152569221798331e-05 -1.2114338460378349e-05 1.1465423085610382e-05 -1.2271077139303088e-05 -1.9209004676667973e-05 1.8100559827871621e-05 1.1164823263243306e-05 1.7969323380384594e-05 4.1551338654244319e-05 -4.3185398681089282e-05 1.0916383871517610e-05 -4.3292187910992652e-05 4.1329920350108296e-05 4.8168250941671431e-05 1.0722730621637311e-05 -4.3462499888846651e-05 1.0647498129401356e-05 1.7505153664387763e-05 1.0586723874439485e-05 -1.3059968296147417e-05 -1.9976218027295545e-05 -1.3095740541757550e-05 -2.0006847989861853e-05 -4.3634467147057876e-05 -2.0021714590257034e-05 -1.3122203199600335e-05 1.0495725291548297e-05 1.7405109247192740e-05 -5.0522794481366873e-05 1.7431204469176009e-05 1.0544318683969323e-05 1.7470340026193298e-05 -1.9925713786506094e-05 1.7527736417832784e-05 1.0655903679435141e-05 -1.2917792446387466e-05 1.0736339390859939e-05 -1.2830123523599468e-05 -1.9684848666656762e-05 -1.2726692148135044e-05 -1.9572600649553351e-05 1.7910773749463260e-05 -1.9443814380792901e-05 -1.2471999980334658e-05 -1.9298704501125030e-05 -1.2319945199124049e-05 1.1379649549780879e-05 -1.2152190720371436e-05 -1.8960432498715818e-05 4.9064037739299238e-05 4.2267856770195067e-05 -1.1773398000514135e-05 4.2476858652662486e-05 -1.1559452104847878e-05 -1.8333492334932089e-05 1.9187242287443951e-05 -1.8093196558766067e-05 -4.1604493162594736e-05 -1.7837233826867305e-05 -1.0827988262462895e-05 1.2951397366123274e-05 5.0480120989959687e-05 -1.7280453903367743e-05 2.0252751710359007e-05 -1.6979854990495369e-05 2.0553017748170532e-05 1.3852668416802771e-05 2.0868934370810166e-05 -1.6335516193066724e-05 2.1197689420660026e-05 -1.5993136912584305e-05 2.1539985027629882e-05 1.4880378330417443e-05 -3.9139969885582104e-05 1.5249218449753243e-05 -8.2543547250679694e-06 -1.4886043572914787e-05 2.2642261683358811e-05 1.6024307115003467e-05 2.3032534954836592e-05 -1.4087612726143561e-05 2.3435199182131328e-05 -4.4189233449287713e-05 2.3846656404202804e-05 1.7273132471018471e-05 -6.2477697611029726e-06 1.7710059182718396e-05 -5.8164750953437760e-06 -1.2360707842162810e-05 2.5141736841760576e-05 -1.1905163773917593e-05 -3.5445471439743415e-05 -1.1441671631473582e-05 2.6046562197734602e-05 -1.0970368748530746e-05 2.6509263989282772e-05 2.0024872355861589e-05 2.6977106244885363e-05 -1.0007714081439190e-05 2.7452319045551121e-05 -9.5169743872247636e-06 5.8450175856705755e-05 2.1495361579582095e-05 2.8415677661541849e-05 -3.9041035051923245e-05 -1.6151514046214288e-06 -3.8538088119821623e-05 -1.1255718845859519e-06 2.3002872694632970e-05 -6.3390956483999616e-07 -7.0083628997963388e-06 -1.4287581961980322e-07 -3.7017663998994976e-05 3.5151191468685283e-07 -3.6509212804958224e-05 8.4185467130737379e-07 2.5033106794580817e-05 -2.9184526283643208e-05 2.5539578928146511e-05 1.8205888636657619e-06 -4.4739281293004751e-06 3.2822765206219628e-05 -3.9733204175718129e-06 2.7873225008079316e-06 -3.3994281693594530e-05 3.2627583550493000e-06 -3.3501888538012281e-05 3.7344329939514864e-06 -2.4979344743769616e-06 6.5232568886131048e-05 -2.0185889297863469e-06 4.6538420974684414e-06 -1.5453795185749186e-06 5.1024671847699210e-06 -1.0808169008669211e-06 5.5421842262148857e-06 -3.1143135856837034e-05 5.9714607232308481e-06 3.0338313081301749e-05 3.6908219044562429e-05 3.0773568141739815e-05 6.7983805820404086e-06 3.1197669159155339e-05 7.1943422881304286e-06 6.2127539422363043e-05 7.5801494858751539e-06 1.4916306554368930e-06 7.9472465586150065e-06 1.8779772972266073e-06 8.3012128015980124e-06 2.2498788894154131e-06 8.6423242464661598e-06 2.6069983505294658e-06 -2.1550384190049954e-05 -2.7568115910980850e-05 3.9794158510630950e-05 3.3793279726523906e-05 9.5694977062521502e-06 3.5862153708876576e-06 4.0363094740314409e-05 3.8795888031017967e-06 1.0102179658133537e-05 3.4673805203055963e-05 -2.0173683878965676e-05 -2.6101959520019591e-05 -1.9950364730902947e-05 3.5174878576071933e-05 4.1288472857559100e-05 4.8805663936946075e-06 1.0957155609503388e-05 5.0867211029981263e-06 1.1124801858386490e-05 5.2739837883564178e-06 1.1274080861767288e-05 5.4436254686152097e-06 4.1920451621990651e-05 3.6112251109443605e-05 -1.9002605768037029e-05 -2.4789898816379718e-05 -1.8909930076915771e-05 5.8429623095435090e-06 1.1682846889016218e-05 -5.5095522839110345e-05 1.1739786714315414e-05 6.0202173699508421e-06 4.2296131141483784e-05 -2.4434370061499067e-05 1.1800093489000574e-05 6.1298346736293752e-06 -4.9229267460759729e-05 -2.4356750145670958e-05 1.1794041711254977e-05 -2.4341707103303634e-05 -1.8750377421383746e-05 6.1771761465934105e-06 -1.8791370166582055e-05 6.1639734667551238e-06 -4.9364007281837985e-05 -2.4379358364967629e-05 -4.9431997467763722e-05 -5.4934858781052753e-05 -1.8994775018654764e-05 -2.4466546165058389e-05 1.1431888196966611e-05 5.9925860114162788e-06 1.1331645509926602e-05 -2.4592605768702924e-05 -1.9297278413432650e-05 -2.4668008336448111e-05 -4.9931983085116372e-05 -2.4748957002884708e-05 1.0981442756019533e-05 -2.4834482246660627e-05 -5.0181628466816619e-05 5.5931350289029069e-06 -1.9794675608864054e-05 -2.5015595383592881e-05 -1.9927352695958689e-05 3.5928293073084205e-05 1.0457981261424720e-05 -5.5714117479510605e-05 -2.0189523638691753e-05 5.2338455134304240e-06 1.0200306860497221e-05 -5.5883279856061563e-05 1.0078073501063045e-05 -2.5441406251047738e-05 4.0480921597918496e-05 5.0089442993339617e-06 -2.0660463633248582e-05 -2.5565643227309920e-05 4.0278926462633535e-05 4.9069776650867425e-06 9.6789044619072229e-06 4.8759611672721803e-06 9.6093990578083321e-06 -2.5656026991782710e-05 -2.0960713300155476e-05 4.8643919399182778e-06 -2.0994928490836173e-05 -2.5630828531575389e-05 9.5094819698715582e-06 4.9310920076095499e-06 9.5162886282196268e-06 -5.6035707530099899e-05 -5.1486713346093893e-05 5.0923267735925037e-06 9.6070334620890208e-06 5.2141167543595657e-06 9.6928188213496469e-06 5.3639341786038131e-06 9.8074469860875979e-06 5.5452110245823860e-06 9.9542376119643450e-06 5.7599363572080620e-06 -5.0900966016342863e-05 6.0087468227720819e-06 1.0349968761147466e-05 6.2936314861872233e-06 1.0601873327686917e-05 6.6170778154628351e-06 1.0891038982663304e-05 3.7497164157684892e-05 7.2255024861078709e-05 7.3830997280310839e-06 -1.8926793927676044e-05 7.8291623140103184e-06 1.2004213203908876e-05 -2.2198288206709549e-05 -1.8056969565805048e-05 8.8538599811727181e-06 1.2960124877281487e-05 9.4345996330957860e-06 1.3508619304047897e-05 1.0061583452625200e-05 1.4104358342592604e-05 1.0736932381405495e-05 1.4746405213372782e-05 -1.9055933080380782e-05 1.5435121895279735e-05 1.2235110261826776e-05 -1.4341827409225516e-05 1.3058162039669696e-05 -1.3552308701036964e-05 1.3931641660747118e-05 1.7802507500164211e-05 1.4855229892418720e-05 -1.1826496120193042e-05 4.6347442548722029e-05 1.9628430891316384e-05 1.6853889974299818e-05 5.1135590183548629e-05 1.7929829482454807e-05 2.1654966985806823e-05 1.9054366930504330e-05 2.2741985958418809e-05 2.0229814253980294e-05 2.3876360501162708e-05 -9.0637877292465419e-06 2.5061261112568900e-05 -7.7922395576024428e-06 2.6291265385225415e-05 2.4044273231993429e-05 2.7567821234697476e-05 2.5409517547814175e-05 5.9406746004242450e-05 5.7337128964718431e-05 3.0255147066782229e-05 -2.2444996830017772e-06 3.1661875254940242e-05 2.9768925742246211e-05 2.5924186957126949e-06 9.2339963885024190e-05 -5.6956250773509964e-05 -2.8156035114079714e-05 -2.4915801986935548e-05 3.9725046008243226e-06 -5.3874471632298082e-05 5.6180824685725383e-06 8.7524222180945799e-06 -2.3221826268127188e-05 -5.0661634304560721e-05 9.0032663138117641e-06 -4.9012003728421405e-05 -1.9778901332756504e-05 -4.7336390707641840e-05 -1.8018552509602159e-05 -7.6156225986778736e-05 -4.6754172217333689e-05 -4.3920543248532340e-05 1.6082893125712872e-05 -4.2184259655186906e-05 -4.3133757571922615e-05 -7.0950438384898007e-05 -4.1301238525193185e-05 -6.9188965426292270e-05 -3.9458223909605294e-05 -6.3809102357481606e-06 -3.7607176636811346e-05 -9.6159004897344857e-05 -5.2344344112498220e-06 -3.3345200790790841e-05 2.7140478778164834e-05 -3.1567011319566518e-05 -1.5225944025587523e-06 -6.0311198467388749e-05 3.2814858741403441e-07 -2.8026805011904798e-05 2.1699765966332052e-06 -5.6788427173160017e-05 4.0012864701566286e-06 -2.4528475478291512e-05 -5.5218184570549056e-05 -2.2803289539297111e-05 7.6160322350915521e-06 -2.1096024283906445e-05 -2.1124727936694399e-05 1.1102991265943274e-05 1.1146481483592652e-05 -4.8275080189341679e-05 1.2872664228780195e-05 -1.6130974472616799e-05 -1.5949142834870145e-05 -4.5054366637486964e-05 -1.4285857105278410e-05 -1.2977197002328467e-05 1.7858910723589361e-05 -4.1972914914367720e-05 4.9965114158112556e-05 -4.0493250708095729e-05 -9.5216719273594208e-06 -8.5361389210447669e-06 -8.0185400292975828e-06 -7.1447470872954000e-06 2.3957201847224496e-05 -5.7997781368612777e-06 -5.1511742640286684e-06 -3.5023564123548567e-05 -3.7924019125057384e-06 -3.3781248930608854e-05 -3.3003114367602393e-05 -6.3110994233284146e-05 2.9284881748026237e-05 -3.1461673643207178e-05 3.0481525755021721e-05 1.3161945844331058e-07 1.1042925507354084e-06 1.1486631592561025e-06 2.1857213141629472e-06 -5.8930261729983613e-05 3.2065593131846981e-06 3.0036101179575780e-06 3.4687574952840805e-05 3.8382795537472703e-06 5.0699495659500826e-06 -2.5906418159138411e-05 3.6426110455067828e-05 3.5839719203067943e-05 6.7722416133619845e-05 5.9809444792335853e-06 3.7920661270618439e-05 -2.3960143153090030e-05 8.0581230577081442e-06 -2.3434829927282408e-05 6.9687244831584394e-05 -2.2970401914790273e-05 -2.1331781681510620e-05 -2.2568947315448895e-05 9.6603680503903888e-06 -5.2743082051165402e-05 -2.0442617824301124e-05 -2.1941923478152603e-05 4.0951730625238270e-05 -5.2231960580684245e-05 1.0737235243141185e-05 -5.2060724556213245e-05 1.0984855180140585e-05 -2.1424686565296724e-05 1.1181538866367191e-05 -2.1359868696890771e-05 -1.9189927115803584e-05 -2.1344036213122308e-05 1.1425650882301852e-05 -5.1893835916416720e-05 1.1477795851533301e-05 -5.1971412176499143e-05 -1.9030338080483489e-05 -5.2090410463279113e-05 1.1456077118054964e-05 -5.2249371947254986e-05 1.1388723578420468e-05 -2.1928306523477659e-05 4.1803508793236688e-05 -5.2675160986837000e-05 1.1152510523970705e-05 -5.2935210987925529e-05 4.1508028516545892e-05 -5.3221927373670042e-05 -5.0230657507199794e-05 -8.4050319856032729e-05 4.1114457417279482e-05 7.1718118306307588e-06 1.0371976713940967e-05 -8.4727435023523867e-05 -2.0384741219459102e-05 -2.4052511435002089e-05 4.0400882426183671e-05 -2.4422548449365422e-05 -2.0890500309178606e-05 -5.5313477787422016e-05 9.3678972916677594e-06 -5.5690732551738620e-05 -5.1925191655755043e-05 4.9693298933561891e-06 -2.1661773644154891e-05 -8.6953681602608413e-05 -2.1907544578425586e-05 -2.6279281883034855e-05 -2.2141459339763969e-05 -2.6626319595379755e-05 -2.2360711227520369e-05 -5.7474655477562919e-05 7.9572400863980874e-06 -5.7786735851550475e-05 -5.3255982493283227e-05 -5.8077075664186850e-05 -2.2890928448759951e-05 -5.8337729569757357e-05 7.5033267421531491e-06 -5.8570683904690668e-05 7.4115168899879791e-06 -8.9289511379320174e-05 7.3540309131203685e-06 -2.8419532100087963e-05 3.7851834349567071e-05 -5.9064499509986490e-05 -2.3163782316260040e-05 -5.9151192544959486e-05 7.4165564001305029e-06 -5.9196714573772624e-05 7.5227644629194401e-06 -8.9712782937567681e-05 7.6762089520343579e-06 -5.9148635045858100e-05 -2.2639836970483884e-05 -2.8536489480757155e-05 3.8646583561785519e-05 -2.8392516469466500e-05 6.9465830165427178e-05 -2.8196840503369458e-05 3.9302391087403521e-05 -5.8468151109991595e-05 -5.1843286200892180e-05 -5.8168450777884573e-05 -5.1383398385951295e-05 -5.7816629123408347e-05 -2.0352425053715706e-05 -2.6894067559624091e-05 4.1249273635912687e-05 -5.6956050684675574e-05 -4.9683119868859649e-05 -2.5930501578841358e-05 1.2024610441585537e-05 -2.5370334697072394e-05 1.2749022062052973e-05 -5.5277330829994753e-05 1.3523358575184830e-05 -2.4101027520373464e-05 -1.6169931768672541e-05 -2.3393389710690826e-05 -4.5816297642886639e-05 -8.3677361544687301e-05 1.6135643818415701e-05 -8.2879349065478891e-05 1.7095751900342293e-05 -8.2043152360711247e-05 -1.2420718121575192e-05 -5.0648690375965089e-05 1.9135295588057488e-05 -1.9220104150008410e-05 -1.0307367119821720e-05 -4.8791876906761900e-05 -9.2015616246499121e-06 -1.7300992112723179e-05 -3.8584319554502144e-05 -1.6300724382745102e-05 -6.9067191361682490e-06 -4.5794484321959317e-05 -5.7260522225988097e-06 -4.4755837734555826e-05 -3.5045533877564594e-05 -4.3700427340809256e-05 2.7200570912100375e-05 -4.2635128920665011e-05 2.8420377930160612e-05 -4.1565195715520531e-05 2.9644552341778763e-05 -7.1009788371156901e-05 3.5138270959578222e-07 -8.9049481175607070e-06 1.5708554883531178e-06 -6.8878835008945316e-05 2.7815076464321464e-06 -6.7831177148036659e-05 3.9776509765943047e-06 -6.6799912019632757e-05 5.1541837819968350e-06 -3.5272289096610621e-05 3.6825669667450711e-05 -3.4290242183487862e-05 7.4333479460619856e-06 -3.3339823858113959e-05 8.5259116531233303e-06 -1.9069143490924034e-06 4.0098213503370062e-05 -6.2068036640994251e-05 4.1110666643362492e-05 -6.1239174101501703e-05 4.2075604142155498e-05 -2.9944541893200949e-05 1.2473352398956195e-05 -2.9216726034064777e-05 1.3332555681699887e-05 -8.9587934780865908e-05 7.5167561590205878e-05 -5.8465146139496937e-05 1.4868974176351912e-05 3.1084709917195141e-06 -1.4978159924794454e-05 -5.7458131777821109e-05 7.7173790486995131e-05 -2.6543602871242911e-05 1.6665604562149383e-05 3.4812663216143847e-05 4.7632969653932378e-05 4.5369288272922859e-06 1.7486927390564233e-05 -2.5819310394581407e-05 4.8294321459252387e-05 4.7763160182512365e-06 4.8500336561119184e-05 4.7684384298918303e-06 4.8622292524669319e-05 -2.5842968170763925e-05 1.8140344764105976e-05 4.4938233259017579e-06 1.8088070646626875e-05 4.2245992517564446e-06 1.7948103050002828e-05 3.8660909922327846e-06 1.7719459719955921e-05 3.4149693419749383e-06 1.7404074242222123e-05 -2.7635514925350435e-05 1.7000760635710321e-05 -2.8260368708288297e-05 1.6510115528944880e-05 -2.8971702704438940e-05 4.6453220420517027e-05 7.4974860808652011e-07 7.6312004239298403e-05 -1.3060243020390772e-07 4.5054213842377067e-05 -3.1608822609996423e-05 1.3717630281462334e-05 -3.2647625630488619e-05 4.3340765842003748e-05 -6.4279614889528602e-05 4.2372332245577127e-05 -3.4947108360938728e-05 4.1336872527608648e-05 -3.6201887269271538e-05 9.7170068329432979e-06 -3.7519228499149904e-05 8.5556612248183228e-06 -8.3781333160004579e-06 3.7854752008570358e-05 -9.8096898000221699e-06 6.7104338086210191e-05 -4.1805364162428305e-05 3.5273264074930921e-05 -7.3845949373207986e-05 3.3920230634976178e-05 -7.5406533142086118e-05 3.2534080673940480e-05 -1.5962688848958351e-05 6.1638063925784081e-05 -7.8614422818645835e-05 2.9685514164157212e-05 -4.9731705075828359e-05 5.8754878409672529e-05 -5.1376831834204495e-05 2.6781635824590921e-05 -5.3027204558020458e-05 -5.1911720220232382e-06 -2.4157170628313906e-05 2.3877615603851154e-05 -2.5795028705033474e-05 -3.8590787880821154e-05 -2.7414571377448738e-05 -4.0002138121053576e-05 -9.0045854449272156e-05 -1.0866573575185612e-05 -6.1090839153621346e-05 1.8305596313439310e-05 -9.3131624453235418e-05 1.7005226254696026e-05 -6.4089479565154761e-05 1.5757010260131210e-05 -9.6027259132824838e-05 1.4567407561116852e-05 -3.6351288144942373e-05 1.3444451724353712e-05 -6.8159570218995214e-05 -1.8122169421985745e-05 -9.9889628472737968e-05 1.1426396667957306e-05 -7.0500667789019644e-05 1.0545940313022584e-05 -4.1025297832675278e-05 -2.0758767277584411e-05 -7.2486778663005680e-05 9.0717148850671947e-06 -7.3328337748534977e-05 8.4911262092646211e-06 -1.0457998723722994e-04 -2.2496062229038216e-05 -7.4682218837551773e-05 -2.2847918444313109e-05 -1.0570159065537155e-04 7.4398440119693987e-06 -1.0607953299768269e-04 -2.3181570213637315e-05 -7.5812917202711105e-05 -2.3155116650741547e-05 -1.0644966096151620e-04 -2.2993994207354262e-05 -7.5915770139545202e-05 7.8218545240815729e-06 -7.5762727647088468e-05 8.2587575889192522e-06 -1.0598773224046454e-04 -2.1679388737538829e-05 -1.0555286280578002e-04 4.0078517486108467e-05 -1.0497483162907884e-04 -2.0090197722311132e-05 -7.3735543992370367e-05 -4.9596561439102516e-05 -1.0338960419176146e-04 -4.8441419494338334e-05 -1.0238042159471661e-04 -4.7142078983597457e-05 -7.0713234890718013e-05 1.5334189811255783e-05 -6.9422552769538015e-05 -4.4120046368334442e-05 -6.7994995333719999e-05 -1.1883987099281512e-05 -9.6949457656592131e-05 -1.0031346391770057e-05 -9.5254901680164039e-05 -8.0481622717343271e-06 -9.3432674475479871e-05 -5.9391768445493653e-06 -6.0970221966272220e-05 -3.7095733205205761e-06 -1.1994420492555946e-04 -3.1881540053291246e-05 -8.7253676610998809e-05 -2.9427825211314484e-05 -5.4459222155855969e-05 3.4162982046836987e-05 -5.2082352340221405e-05 6.2970657381811179e-06 -8.0133999290410429e-05 3.9553011447424069e-05 -7.7585842518601567e-05 -1.8664421077119187e-05 -7.4962241342291236e-05 1.4740086044184864e-05 -7.2278446168638766e-05 1.7688756997813471e-05 -6.9535497459582984e-05 5.1206261559855193e-05 -6.6747430537361652e-05 2.3729317035758868e-05 -6.3925232097972184e-05 5.7319455663673580e-05 -3.0559222068404779e-05 2.9894683393649757e-05 -2.7696787583408877e-05 3.2997028029058129e-05 -2.4832326744217426e-05 6.6615306423045695e-05 -2.1974266928737052e-05 6.9703055487480015e-05 -4.9654390750220045e-05 1.1731648555723950e-05 -4.6847242629155517e-05 4.5277531171450391e-05 -1.3565743756771553e-05 1.0929307609330863e-04 -1.0855339496629313e-05 8.1697056884877384e-05 -8.2103033491875976e-06 8.4548832091968507e-05 -3.6158806324237958e-05 5.6799810408847407e-05 -3.1610336463927524e-06 5.9476053138496354e-05 -7.8244727319543017e-07 6.2047984101809561e-05 1.4867562185827410e-06 6.4504631154704839e-05 -2.6881676603807136e-05 9.7352902230340987e-05 -2.4866427338565700e-05 3.8512298488058150e-05 7.5258594733895734e-06 4.0561026253271848e-05 -2.1269448552629910e-05 7.2972208727151155e-05 1.0810930689331144e-05 1.3666426639247220e-05 -4.8832152970135212e-05 7.6257434557192028e-05 -4.7617351810913533e-05 4.7115463530644774e-05 -4.6588284021709114e-05 4.8302244977094233e-05 -4.5751734432997182e-05 7.9812991316430271e-05 -1.4595050743082538e-05 8.0603924288880080e-05 1.6355552361346781e-05 8.1189857155550271e-05 -1.3938275515101850e-05 1.1208229261683300e-04 -4.4443986553233117e-05 1.1224273475818336e-04 -1.4132245269138366e-05 8.1667887570802122e-05 -1.4556530004483648e-05 8.1390920968260616e-05 -4.5719920308329165e-05 8.0893871199805290e-05 -1.6073530787252821e-05 8.0174024333246052e-05 1.3353258509596344e-05 1.0975141776725650e-04 -4.8994734243024141e-05 1.0859165922738612e-04 1.0507776096346788e-05 1.0721416037995368e-04 -2.1757357899332419e-05 7.5104719144292176e-05 6.7978471633978188e-06 7.3302413511555642e-05 4.6270047278085258e-06 7.1294503868557513e-05 -5.8782847190741450e-05 6.9087756855878979e-05 -6.1353493947535753e-05 6.6687614889815450e-05 -3.3598516893107444e-05 9.4619688752572984e-05 -3.6542391171678901e-05 3.0821807740721852e-05 -9.1419760792632587e-06 8.8926630269270390e-05 -1.2422128747857641e-05 2.4803634005365893e-05 1.4661760360468179e-05 1.1312065907986835e-04 -1.9431383407209069e-05 -1.2319160305196419e-05 -8.4173341747373343e-05 1.4705706234963145e-05 -5.7478195230942219e-05 7.2138005634769797e-05 -3.0887491448083892e-05 3.7920781323919073e-05 -9.5940122264437377e-05 3.4139473427785560e-05 -6.9514135248027742e-05 -2.2745780370314606e-07 -1.0418624151498079e-04 2.6389436243334785e-05 -4.7349923988804221e-05 2.2451640688814223e-05 -5.1578957936726511e-05 4.9010755901690573e-05 -5.5819262342993170e-05 1.4530108273902442e-05 -9.0575565991457552e-05 1.0579496120044496e-05 -9.4794588221702725e-05 6.6575671553437132e-06 -6.8459317844826728e-05 3.3299096685368568e-05 -4.2070270865224302e-05 2.9486152925528586e-05 -1.0716348333517089e-04 -4.7644184633099940e-06 -1.1113437358289957e-04 2.2115236788522452e-05 -1.1499869287945330e-04 -4.2443090933375061e-05 -1.1874626216012985e-04 1.5198007531580515e-05 -9.1838977823499590e-05 -1.8568516679806635e-05 -1.2581254122778773e-04 -2.1654859665432014e-05 -1.5961812459863722e-04 5.9524886637518648e-06 -1.0168723383685574e-04 -2.7283071176498197e-05 -1.6562687233090401e-04 -6.0312344430712983e-05 -1.6832063556648791e-04 -3.2086609280668199e-05 -1.7078718519769609e-04 -3.4146334655815735e-05 -1.1198198626516387e-04 -3.5960569221060723e-05 -1.7499580280855298e-04 -7.0007972681196406e-06 -1.4619748981203884e-04 -6.9326575612649322e-05 -1.4764298975933343e-04 -7.0341789978556335e-05 -1.4881126116961241e-04 -4.0553008147981018e-05 -1.4968938194215298e-04 -4.0989791159518063e-05 -1.5027247718535364e-04 -1.0609877790557221e-05 -1.8107214418705553e-04 -4.0960228943731636e-05 -1.5053122479002923e-04 -4.0484235796611756e-05 -1.1967863247264177e-04 -9.1790861915796995e-06 -1.4955055667087436e-04 -3.8595364458160475e-05 -1.4859186194371432e-04 -3.7180692743277177e-05 -1.4731851115357131e-04 -4.9333393690176308e-06 -1.7625282634980977e-04 -2.8934568945260253e-06 -1.4384115638677031e-04 -3.1062631023814902e-05 -1.4164239109959453e-04 2.1061698589619482e-06 -1.3914250303059816e-04 3.5573964851209894e-05 -1.0583366383798420e-04 8.2963761087739840e-06 -1.0275499516865239e-04 -1.8699152860790491e-05 -6.8882407504133880e-05 -1.4904686395311728e-05 -1.2629508273676038e-04 -1.0849853424588218e-05 -1.2241864169482142e-04 -6.5433769123046659e-06 -8.7782842456363142e-05 -2.0017919268866535e-06 -8.3436527347657830e-05 6.3796680478844792e-05 -1.0939652565866709e-04 3.8247053453233093e-05 -1.0463875514687970e-04 1.2886141121271066e-05 -9.9701755971182138e-05 4.8733760195318609e-05 -6.4086008933372796e-05 5.4214295232668519e-05 -8.9364541054237634e-05 2.9312954211491160e-05 -8.4000035712961107e-05 6.5559026552364230e-05 -7.8533666965086013e-05 4.0862170862965286e-05 -7.2987939347513020e-05 7.7271346526686102e-05 -6.7382687120698392e-05 5.2694082114612684e-05 -3.1222680263454095e-05 8.9176508481614292e-05 -8.6603584350086749e-05 6.4626336097717285e-05 -1.9924038497265428e-05 7.0572554250247777e-05 -4.4830263504991308e-05 1.0699141421355307e-04 -8.7585967776249163e-06 8.2305443356744945e-05 -3.3805197745095938e-05 8.8045482698362321e-05 -2.8439248126232997e-05 6.3150342612061650e-05 -5.3719519200967625e-05 1.2966738722752780e-04 -1.8118047591997311e-05 1.0446711530676112e-04 1.7306765585090034e-05 1.4011446910444647e-04 -3.9021302654873580e-05 1.1451624595792964e-04 -6.5052343416027725e-05 1.4972170174587518e-04 -3.0293456802610308e-05 1.5415497182402760e-04 -2.6316984076402150e-05 1.5831302152946591e-04 3.8408252294175327e-05 1.9269359472673386e-04 1.1277483281446621e-05 1.3520737411454320e-04 4.4856420572614297e-05 1.6894172586034983e-04 -1.3458936336974148e-05 1.4129152987152338e-04 -1.1097024980699643e-05 1.4379380445461720e-04 2.1410673070931807e-05 1.1539860133780167e-04 -7.5058387665194459e-06 1.1712833656929433e-04 -6.3035240600584075e-06 1.4896973152644932e-04 -5.5143709687399678e-06 1.4987964823376387e-04 2.5375444238306955e-05 1.5036384866107255e-04 2.5317691324744374e-05 1.5041838923934847e-04 2.4825490982038900e-05 1.5003547014202923e-04 2.3892673198133707e-05 1.7972836212720722e-04 -7.9999799709185027e-06 1.7846103582996875e-04 2.0698931621154770e-05 1.4623285096604377e-04 1.8436343452776782e-05 1.4408008428290486e-04 1.5733206964796409e-05 1.1097083188360557e-04 4.3109124817419797e-05 1.6898094327189028e-04 9.0196190285496414e-06 1.6552829765714705e-04 5.0238650146638975e-06 1.9217528461012989e-04 3.1131465220823884e-05 1.5737849753350019e-04 -3.4715572837740183e-05 1.2218595657031983e-04 -3.9916380046634004e-05 8.6611871665809304e-05 1.5542575056315400e-05 1.1170704965479672e-04 -2.0908879378112033e-05 1.3645424041897058e-04 3.3351384445268195e-06 1.3035340816713870e-04 -3.3776323107304052e-05 9.3427363026421517e-05 -4.0667171560926363e-05 8.6732012277934700e-05 -4.7833989810897037e-05 1.1029204324586317e-04 -5.5251101002795622e-05 4.2060473788296804e-05 -3.2376952731283382e-05 6.5170774178113788e-05 -4.0217822970589623e-05 8.8097396655939519e-05 -1.0926253889920190e-04 4.9834106903290376e-05 -5.6380336900474504e-05 7.2480281232856214e-05 -9.5159739430528134e-05 6.4514722907915711e-05 -1.0350190859753639e-04 5.6484735978301615e-05 -1.4240918972063810e-04 4.8424320993945003e-05 -1.2029567733407021e-04 9.8479376902105287e-06 -9.8165553936269134e-05 3.2340245525119826e-05 -1.3701984426006675e-04 2.4384144126088358e-05 -1.7578824190422893e-04 -1.3987999409437180e-05 -1.8392410129308701e-04 -5.2225310355424881e-05 -1.6138939827214926e-04 -2.9258662834763527e-05 -1.3866841618437320e-04 -3.6607605579774827e-05 -1.7676598508842289e-04 -4.3721942347474396e-05 -1.5357468510046601e-04 -2.0050398234161548e-05 -2.2165531117934734e-04 3.9204451240948401e-06 -1.9786931807175279e-04 -9.3849404947832227e-05 -2.3477280046790838e-04 -6.9188296038191766e-05 -1.7974853108171374e-04 -7.4655428761616349e-05 -2.4639151524752378e-04 -7.9704965173732489e-05 -2.8208628646098077e-04 -8.4311141108628362e-05 -2.5628966977819800e-04 -1.1896579235326499e-04 -2.6053082547150552e-04 -1.2261133815627545e-04 -2.6426848489791155e-04 -6.4706160628702492e-05 -2.6748212985694408e-04 -9.7819654911290854e-05 -2.7015211526304483e-04 -9.9862620118074119e-05 -2.7226132806390524e-04 -1.3185542775318027e-04 -2.4327734718099236e-04 -1.3274773664306849e-04 -2.4422214482910931e-04 -1.0252701758872718e-04 -2.1405007282737643e-04 -7.1702772402204573e-05 -2.7482316363602877e-04 -1.0130143346032128e-04 -2.4342746473848820e-04 -9.9764809419866651e-05 -2.4193301214836538e-04 -9.7610063676256686e-05 -2.7033616788685322e-04 -6.4318199292756617e-05 -2.3708553635515273e-04 -6.0926555306650698e-05 -2.3373818839900196e-04 -8.7440865172538906e-05 -2.2978305059950799e-04 -1.1335050658090040e-04 -2.5574635947123170e-04 -4.7113619075389579e-05 -2.2008542146068066e-04 -4.1329385567223653e-05 -2.1436809038277715e-04 -3.4979671909241006e-05 -2.0809331908822060e-04 2.4370870050915983e-06 -2.6231404626742005e-04 -2.0652551029343158e-05 -2.5498558534309268e-04 -1.2718690413748845e-05 -1.8612446729093790e-04 -3.4821568988263607e-05 -2.0835027680732310e-04 -5.6470835261279717e-05 -1.9961822545155883e-04 -1.6660749679431319e-05 -1.5996124420780689e-04 2.3543896531919017e-05 -1.5044494648464024e-04 -2.7446272724773735e-05 -1.1007006105501205e-04 1.3439101167023182e-05 -1.6094351303763688e-04 -6.4233399825752713e-06 -1.8103138427250087e-04 6.5514490415807813e-05 -1.7037606448866427e-04 7.6622280175797641e-05 -1.2901662557851523e-04 8.7895750766620040e-05 -1.1803373490693048e-04 6.8772336817346513e-05 -7.6431933848652989e-05 4.9726611905498430e-05 -6.5294123487547040e-05 9.1746143880300224e-05 -8.4665560279972851e-05 7.2716873546596617e-05 -4.3040759919676930e-05 1.1466229625511914e-04 -6.2533559685107321e-05 1.2598096509464085e-04 -2.1126079445821233e-05 1.0662675049388781e-04 -4.0931998228188604e-05 1.4810489665251225e-04 3.0588224035454914e-05 1.8933232058770955e-04 1.0281955837854184e-05 1.9974561291746795e-04 -1.0345862392568961e-05 2.0981485431548208e-04 2.9696760975639336e-05 2.1949503570795059e-04 8.2908136391779408e-06 2.2874145361129194e-04 4.7466062824241817e-05 2.3751119442749768e-04 8.6140753410290927e-05 2.4575970019213855e-04 3.2721844036132097e-05 2.5344843743368983e-04 7.0275411417242140e-05 2.3001895169727504e-04 4.6175922761904076e-05 2.9750628164038062e-04 8.2455677329562604e-05 2.7276741457171738e-04 8.7528482254128903e-05 2.7783957193605602e-04 6.1367463786154985e-05 2.8217761428095400e-04 9.5477764261886477e-05 2.8574882890097797e-04 6.7786466388497502e-05 2.8853138792328537e-04 1.0032379213953391e-04 3.2101690885610878e-04 7.1002912591211498e-05 2.9163615545257926e-04 1.0187726002186537e-04 2.9192186775617301e-04 1.3189240416977555e-04 2.6082649128511548e-04 1.3052119174972177e-04 2.5937537429854274e-04 9.7755044407676905e-05 2.5704246945679188e-04 1.2514063564594835e-04 2.5382524472661316e-04 9.0604684373829514e-05 2.4972175015136600e-04 8.5703773947898299e-05 2.7525523910298944e-04 7.9922472650650889e-05 2.3887831775937229e-04 4.2755127651616931e-05 2.6267164503224194e-04 6.5764485043473542e-05 2.2457983868662268e-04 5.7412806199863553e-05 2.4668904370628297e-04 4.8239544412354007e-05 2.3746970691718161e-04 3.8265978218987584e-05 2.5798083515837789e-04 5.8036228438140824e-05 2.4721503723412752e-04 1.6026308003347367e-05 2.0520316320471466e-04 3.4342829167144373e-05 1.9301705469843000e-04 -9.0502071543596685e-06 1.4965828449930996e-04 -5.3078310884302482e-05 1.6672122001182288e-04 -6.7180546466261148e-05 9.1660585894715041e-05 -5.1311642891960219e-05 1.6866718942765146e-04 -3.5940276575274765e-05 1.2312993931118399e-04 -5.1534294470911846e-05 1.0768921492854133e-04 -6.7519591539166868e-05 1.2239988427609205e-04 -1.4488029410131276e-04 7.5767347880173475e-05 -1.3096617476549000e-04 5.9402620536275208e-05 -1.4778928016312420e-04 4.2848503653658554e-05 -1.3425396173261106e-04 -4.3515938159544021e-06 -1.8184729560744017e-04 3.9937542169354856e-05 -1.9895688456017524e-04 -7.3240657911810558e-06 -2.1603242203127593e-04 -5.4519481636816636e-05 -2.0249043882358819e-04 -7.1065769589040428e-05 -2.4981726892292500e-04 -5.6893244618549943e-05 -2.3587603936903179e-04 -7.2973074566107243e-05 -2.8266970184631646e-04 -8.8720807980280370e-05 -3.2909822766669095e-04 -1.0406989167677239e-04 -3.4457916626706719e-04 -1.1895471834577620e-04 -3.5956106148660183e-04 -1.6382941976189613e-04 -3.4346687607467175e-04 -8.6042186012491584e-05 -3.8778566522523761e-04 -1.6019024769775569e-04 -3.3986647031269968e-04 -1.7258922162000090e-04 -3.5224095336161554e-04 -1.8421759887132794e-04 -4.5536804827861488e-04 -1.6450107796117663e-04 -4.0505419019609690e-04 -2.3545860312879086e-04 -4.1486968984827399e-04 -1.8341524992138147e-04 -4.2373227188363671e-04 -2.2194605844561011e-04 -4.6210858272388577e-04 -1.9842163601424545e-04 -3.7737414822913706e-04 -2.3486967256758362e-04 -3.8311089156195521e-04 -2.0918283553328365e-04 -4.1824925574474037e-04 -2.4339993251487613e-04 -4.2172119719907641e-04 -2.1541969908867031e-04 -4.8505471204407513e-04 -2.4728869902901351e-04 -3.9460355765186250e-04 -2.1691551955882460e-04 -4.2500570998527110e-04 -2.1583838679362088e-04 -4.5417886576615274e-04 -2.4404391297139227e-04 -4.2107899207621813e-04 -2.0997395040467381e-04 -4.4777159928344190e-04 -2.3569703625980765e-04 -4.1218838305212557e-04 -1.9914566655643284e-04 -4.3640346848405898e-04 -2.2239744430407882e-04 -4.2887532617896795e-04 -1.8339756934437901e-04 -4.2013902566395700e-04 -2.0423205569386482e-04 -3.7969267577864230e-04 -1.6285301535390317e-04 -3.6860097316093743e-04 -1.5084359620232135e-04 -3.8689351640641689e-04 -1.3771830708719790e-04 -3.4305153531022370e-04 -1.2351603072602302e-04 -3.2867200206965208e-04 -1.3879404286853969e-04 -3.7431326927617192e-04 -1.2256740592420101e-04 -2.9692132375203073e-04 -1.0540449147811159e-04 -3.1017485889606178e-04 -8.7361622718162835e-05 -2.9205650207586586e-04 -6.8497909524012357e-05 -2.7315202169120312e-04 -4.8879741370910779e-05 -2.5352567899972200e-04 -5.9093828895129263e-05 -1.7221340385731310e-04 2.2859680029796436e-05 -1.8187522073276341e-04 -1.6718251572456211e-05 -2.2155526676215231e-04 5.1993847591802478e-06 -1.3874477008357644e-04 8.8529908680357039e-05 -1.1663226905511692e-04 8.0603211245033890e-05 -1.2478590360842645e-04 7.2883180109784007e-05 -1.0225633741356432e-04 9.5801020506769419e-05 -4.9135407607536763e-05 1.7978268442675471e-04 3.9688843571639154e-06 2.0266702631488442e-04 -4.0678987716091797e-06 1.9487825920805335e-04 4.8728259571362287e-05 2.4787717848084867e-04 7.0711401349399239e-05 3.0053392401896417e-04 9.2308095190674067e-05 3.2223830930888653e-04 8.2904633018188179e-05 3.4341504215262830e-04 7.2928720328491181e-05 3.3345338306389749e-04 1.2332205369602889e-04 3.5329750971868634e-04 1.4244318299461156e-04 3.4182434319518507e-04 1.6071849677246064e-04 3.5997980739921331e-04 1.7806459800340235e-04 4.0767958853393793e-04 2.2491262643598020e-04 4.2380497325211763e-04 2.0963136921636760e-04 4.0827936027199030e-04 2.2369102225638926e-04 4.5257573947310448e-04 2.3650568618904799e-04 4.3455345439724624e-04 2.4800104438327253e-04 4.4569582678377628e-04 2.2759562125429511e-04 4.8594028339721262e-04 2.6677915593609214e-04 4.6367355389520526e-04 2.4342381220776588e-04 4.3987602111883461e-04 2.4903161101974547e-04 4.4501796946860850e-04 2.5304025621153414e-04 4.7905460814945400e-04 2.5540590286254883e-04 4.8091457574628294e-04 2.2557828924618661e-04 4.8108262126334012e-04 2.2456330771092325e-04 4.7953287139534950e-04 2.5234062923118472e-04 4.7624704893678427e-04 2.4785657296888530e-04 4.7121365787461400e-04 2.4162115005310625e-04 4.6442530583590269e-04 2.3363200307358056e-04 4.8640105524100363e-04 2.5441162870265543e-04 4.7611442278139293e-04 2.1241577633190900e-04 4.9461476737633348e-04 2.6025497936643660e-04 4.8088922630995512e-04 2.1484780882019550e-04 4.3496579746715724e-04 1.9829369557555765e-04 3.8739977753721178e-04 2.1063780877739191e-04 3.9926852332428098e-04 1.2985266221221536e-04 3.7906767101958394e-04 1.0857945017050952e-04 3.5737291909754276e-04 1.4688014925923198e-04 3.6476433160714805e-04 6.1711471062153578e-05 3.0976138077676296e-04 9.7290248959325254e-05 3.1450620735995471e-04 7.0589783717878163e-05 2.8753114747814834e-04 -1.8302553144167177e-05 2.2892186825629324e-04 -1.6712156138964929e-05 1.9981154764536768e-04 -4.6612818550784141e-05 2.0029392908327281e-04 -4.6864177420502529e-05 1.0840577306225896e-04 -1.3943607336841524e-04 1.0735599062172696e-04 -1.4110608026385307e-04 4.4677974074147642e-05 -2.3486680584028363e-04 4.2553496314212680e-05 -2.6801397325471044e-04 -2.0967219825251959e-05 -3.0145933851599693e-04 6.8280114646768197e-06 -3.3507778425700963e-04 -5.7040990213863552e-05 -3.3822670229710639e-04 -9.0375935542397201e-05 -3.7180946674197912e-04 -1.5408018953166902e-04 -4.0518050082027912e-04 -1.8698979692999274e-04 -4.0768715552985668e-04 -2.1948966605123132e-04 -4.4023766531608999e-04 -2.2093008738011122e-04 -4.7217705287039280e-04 -2.8273195493966341e-04 -5.0337612628936768e-04 -2.8269333415664732e-04 -5.6422216584905982e-04 -3.1223788391798735e-04 -5.6303542805835605e-04 -2.7968519134446979e-04 -5.9123843675479293e-04 -3.6801470560021698e-04 -6.1819294933229685e-04 -3.3296455512754619e-04 -6.1325507704168558e-04 -3.5751934046857059e-04 -6.0683151241391897e-04 -4.1104719275608659e-04 -6.5984169486910105e-04 -4.6292020124383271e-04 -6.8062363425269723e-04 -4.5199634041637182e-04 -6.6907505970448256e-04 -5.0024635856971145e-04 -7.1665161522105336e-04 -4.8550454084761441e-04 -7.6222559437155724e-04 -5.2975444123148918e-04 -7.4467831291258335e-04 -4.8032577615231276e-04 -7.8600103734061122e-04 -5.2024918841198087e-04 -7.6405442086979747e-04 -5.2738940576091409e-04 -8.0084480578079820e-04 -5.0168717280030251e-04 -7.4372976087033749e-04 -5.6516297627240419e-04 -8.3680485840886831e-04 -5.6518556084483862e-04 -7.7485840301960707e-04 -5.3224188741296530e-04 -7.7148270793259144e-04 -4.9682438839226961e-04 -7.6562410686165094e-04 -5.1995628746226430e-04 -7.5727351941168308e-04 -4.7955961781553924e-04 -7.7694974606856704e-04 -5.2822817815467715e-04 -7.6362566323950887e-04 -4.2183868936263025e-04 -7.4784131720662117e-04 -4.9610686255618930e-04 -6.9910660386085510e-04 -4.4589393655769527e-04 -7.0901733124628663e-04 -4.2383110849186778e-04 -6.8606872810050845e-04 -4.6049267984926701e-04 -6.6083762794733047e-04 -3.7283703568391502e-04 -6.0287775704637170e-04 -3.4404458710923791e-04 -5.7330488925799727e-04 -3.4368212800472975e-04 -5.4168974747881293e-04 -3.4132832661271095e-04 -5.6916545145213604e-04 -2.4553094408474863e-04 -5.0325237680226564e-04 -1.7847349226940423e-04 -4.6613853191956878e-04 -1.7079523240681738e-04 -3.3587889629416168e-04 -1.0055566235678270e-04 -3.5674535320140421e-04 -8.9960485638584942e-05 -3.1525988015346229e-04 -4.7605364670744166e-05 -2.7260329807177186e-04 5.6877583119785413e-05 -2.2893362620379776e-04 7.0740235969424248e-05 -1.8441106658428907e-04 1.1588829511310905e-04 -1.0868781828321517e-04 1.6163379768840969e-04 -9.3488539278041571e-05 2.0779950136784464e-04 -1.6921690985327587e-05 2.5420502061024308e-04 2.9277660360094160e-05 3.0066774343140423e-04 4.4922198867425323e-05 3.4699880052357912e-04 1.2137968587921932e-04 4.2352749733254313e-04 1.6690239135641605e-04 4.3850747169926763e-04 2.1181793999858201e-04 4.8330071149393916e-04 2.8645043494179845e-04 5.2719388622790575e-04 3.2957043731585145e-04 5.6999613298103213e-04 3.4098606556653976e-04 5.8099732268601656e-04 3.5102377296425402e-04 6.2104454264044762e-04 3.2898134668357670e-04 6.5942812943831086e-04 4.2725255480036139e-04 7.2648603236302733e-04 4.9307424342259765e-04 7.6100026490166783e-04 4.9574841978028417e-04 7.6279905624687672e-04 5.2665668772533536e-04 7.9274480231106281e-04 5.5511534446850419e-04 8.5067684995010495e-04 6.1147997621446848e-04 8.4488553693518043e-04 6.3456647330895066e-04 8.9729356113821268e-04 6.5474660368636250e-04 9.1620709281414747e-04 6.7188532557338476e-04 9.0149650350213051e-04 6.8585650296881795e-04 9.4459444517269731e-04 6.9654639810323715e-04 1.0453894501551986e-03 7.3436531238257885e-04 9.5964228967204690e-04 7.6870224438607693e-04 9.9243992008268833e-04 7.9947715857997537e-04 1.0216368827968836e-03 7.3506735498085618e-04 1.0166533757001162e-03 7.2800013003870845e-04 1.0079542407765985e-03 7.1720342384651303e-04 9.9550536833703518e-04 7.3316082125529647e-04 1.0097998892888427e-03 7.4533949373289943e-04 9.5927697839215398e-04 6.9270213134586811e-04 9.6601108089089394e-04 6.6681322641670704e-04 9.6898904303088784e-04 6.9822079967707396e-04 8.7669061031192541e-04 6.0385774122551084e-04 8.4175536176189780e-04 5.9740641154348850e-04 7.7269313624128699e-04 4.9583573127165437e-04 7.3061999864876270e-04 4.5181522727943957e-04 7.1562925586476922e-04 4.0441431337967515e-04 6.3627713825553656e-04 3.5374891012907028e-04 5.8424123562872410e-04 2.9995001386851072e-04 5.5965740466490388e-04 2.4316247436217964e-04 5.0164503045380116e-04 1.8354486383032054e-04 4.1036604670807719e-04 1.2126826186431572e-04 3.7755767698399723e-04 5.6522523664170876e-05 3.1185580883175135e-04 2.0021027012262493e-05 2.1346556604839861e-04 -1.1009722948074341e-04 2.0467353169806302e-04 -1.5050273214001209e-04 1.3311930524650961e-04 -2.2303339210338891e-04 -9.5842153768899152e-07 -2.6641096337698400e-04 -1.4206005289452150e-05 -3.7193536991253495e-04 -8.9471839601173997e-05 -3.8675623363815248e-04 -1.6545644029974937e-04 -4.9371039494872093e-04 -2.7240184135735035e-04 -5.3993333131074905e-04 -3.4899418824352324e-04 -6.4721290254965425e-04 -3.9495050441473722e-04 -6.9319311296567321e-04 -4.7101225936785340e-04 -8.3016610005870461e-04 -5.4637115681543946e-04 -8.4421649808064103e-04 -6.2072958098724484e-04 -9.4866834115236998e-04 -6.9379119668155909e-04 -9.6012221183627844e-04 -7.3473801603540778e-04 -1.0613795602694154e-03 -7.7379122376441956e-04 -1.1300768237560987e-03 -8.4117281949147582e-04 -1.1659192387014627e-03 -9.3658885452896357e-04 -1.2296476634219289e-03 -9.9871587008237839e-04 -1.2904615141451359e-03 -1.0272689396515489e-03 -1.3175609055906534e-03 -1.1135241948068142e-03 -1.3717073015868664e-03 -1.1351405410096049e-03 -1.4221173478290439e-03 -1.1834113392978907e-03 -1.4685338828712702e-03 -1.2580816401168704e-03 -1.5412282664328814e-03 -1.2673595920205116e-03 -1.5789306489750743e-03 -1.3025713851675391e-03 -1.5814183279871941e-03 -1.3329810462892056e-03 -1.6400372842326760e-03 -1.3583862455561757e-03 -1.6630383906885982e-03 -1.4091202756389976e-03 -1.7112764762714505e-03 -1.4545029262080789e-03 -1.6930440906435251e-03 -1.4638447901234031e-03 -1.7607808113098145e-03 -1.5285777626559138e-03 -1.7617730190977454e-03 -1.4654768165200949e-03 -1.7569483024999499e-03 -1.4880709350109100e-03 -1.7462224932387471e-03 -1.4436930650845170e-03 -1.7295295838266611e-03 -1.4238430885598063e-03 -1.6763085732236505e-03 -1.3979701325297356e-03 -1.6170556191354990e-03 -1.3355462579056621e-03 -1.6128041315823793e-03 -1.2976194266229868e-03 -1.4804779784753919e-03 -1.1926728766411543e-03 -1.4032267499715090e-03 -1.1428355937823653e-03 -1.3506039977073669e-03 -1.0566398268565536e-03 -1.2616730527952313e-03 -9.6471019787713885e-04 -1.1365528916940093e-03 -8.0614618491381407e-04 -1.0669416515156627e-03 -7.6420808909460902e-04 -9.9197262898087502e-04 -6.8649131571874022e-04 -8.8132178643718362e-04 -5.7320238556712866e-04 -7.9624739009886980e-04 -4.5508763287216425e-04 -7.0646929088979959e-04 -3.6292028380557895e-04 -5.5122043704614043e-04 -2.6646064361557364e-04 -5.1389628788456321e-04 -1.0497565381228924e-04 -3.5066914279013872e-04 -3.1377559935208410e-05 -2.7549761580303311e-04 7.6056057878304273e-05 -2.2770398936700076e-04 6.4372659835498780e-05 -8.5598243458662182e-05 2.6887407875619829e-04 -3.2682946766726673e-05 2.9242207529023290e-04 8.3210310549475253e-05 4.0924697532318532e-04 1.7009170551318675e-04 4.9683410907164216e-04 2.8854043921455741e-04 6.1575352447107434e-04 3.4653264447115362e-04 7.0449104532599449e-04 5.5720622185617685e-04 8.8462373241782188e-04 6.4539816230535507e-04 1.0335700353607535e-03 7.6318689389154315e-04 1.0592647595331073e-03 9.4056176021695137e-04 1.2358278036117554e-03 1.0854349238798022e-03 1.3796169077977538e-03 1.1667456710711122e-03 1.5206020325422287e-03 1.3365285703912377e-03 1.6582292737439275e-03 1.4721595216542482e-03 1.8224554369226098e-03 1.5730746090412140e-03 1.8906487384811044e-03 1.7607829067856073e-03 2.0453522447496653e-03 1.8516137497499585e-03 2.1639298647642136e-03 1.9670762121677399e-03 2.2763398010283709e-03 2.0760924089699984e-03 2.3820218630135059e-03 2.1475895773619413e-03 2.4804268032312393e-03 2.2725691087543964e-03 2.5404889602214098e-03 2.3589401971548796e-03 2.6227077469229698e-03 2.4366881698369980e-03 2.6960393879562616e-03 2.4747746065258980e-03 2.7904924936592579e-03 2.5032116100192070e-03 2.7834912762045860e-03 2.4909914936870337e-03 2.7661090716719627e-03 2.4986735079437494e-03 2.8599500656127930e-03 2.5563205126672983e-03 2.8204296249896288e-03 2.5719436816871166e-03 2.8607447165995836e-03 2.5756482500582933e-03 2.8279041871428490e-03 2.5670418981462717e-03 2.8130840510129929e-03 2.5152429006993771e-03 2.7548973448574543e-03 2.4504312314093113e-03 2.6835363823920488e-03 2.3417768534272909e-03 2.6292221155017614e-03 2.2500345949083567e-03 2.5001380126923323e-03 2.1444337908178568e-03 2.3875988554209471e-03 2.0857958588749170e-03 2.2303669247776270e-03 1.8603097414597869e-03 2.0893123000860214e-03 1.7119632102549076e-03 1.9337825942784548e-03 1.6101211076602340e-03 1.7331570852547884e-03 1.3716055545955896e-03 1.6094517195597291e-03 1.2405072338879108e-03 1.4405677793547511e-03 1.0947514092549682e-03 1.2265176046639681e-03 8.1230065552517772e-04 1.0283903684467077e-03 6.0685031348839402e-04 7.5472565367817879e-04 3.8695934927091002e-04 5.5824074661359191e-04 2.1381206170190126e-04 3.7806981708854437e-04 -3.4471726394258440e-05 1.5338894445449114e-04 -2.0507363660726696e-04 6.5182002799701877e-06 -3.8927671266719699e-04 -1.5380604600068182e-04 -5.5625505046918988e-04 -3.5777248558588326e-04 -7.0565915666520596e-04 -5.1345967222005129e-04 -9.2865276383236051e-04 -6.8149430444464087e-04 -1.0417074663564563e-03 -8.9195423061028123e-04 -1.2274624314159155e-03 -1.0833146516233683e-03 -1.4854163164272904e-03 -1.3466147938743234e-03 -1.7539992695674300e-03 -1.6507776454091072e-03 -2.0631537772715092e-03 -1.9646985456347466e-03 -2.3817566689103842e-03 -2.2572374437004328e-03 -2.7091691736131907e-03 -2.6192895602434874e-03 -3.0752366874366999e-03 -3.0196523293852806e-03 -3.5097757354378700e-03 -3.4270908217877150e-03 -3.8899867795407772e-03 -3.8713805843144655e-03 -4.3676979839801788e-03 -4.3212319724261761e-03 -4.8810956068336964e-03 -4.8674056306481361e-03 -5.4293721914291382e-03 -5.4785660468041897e-03 -6.0422169044613838e-03 -6.0318070463836193e-03 -6.6272318363189697e-03 -6.6483393311500549e-03 -7.1835503913462162e-03 -7.1136718615889549e-03 -7.6797776855528355e-03 -7.6100197620689869e-03 -8.0539844930171967e-03 -7.9838996753096581e-03 -8.4273368120193481e-03 -8.2954382523894310e-03 -8.5853002965450287e-03 -8.3911269903182983e-03 -8.6795426905155182e-03 -8.4226354956626892e-03 -8.6175911128520966e-03 -8.2974918186664581e-03 -8.5511077195405960e-03 -8.0758025869727135e-03 -8.2045141607522964e-03 -7.8482031822204590e-03 -7.9736169427633286e-03 -7.5527410954236984e-03 -7.5523289851844311e-03 -7.0664361119270325e-03 -7.0923357270658016e-03 -6.5104584209620953e-03 -6.5011889673769474e-03 -5.8534089475870132e-03 -5.7169753126800060e-03 -5.0944117829203606e-03 -4.9524530768394470e-03 -4.2326124384999275e-03 -4.0541905909776688e-03 -3.3587289508432150e-03 -3.0213634017854929e-03 -2.0141864661127329e-03 -2.0362760405987501e-03 -1.1137198889628053e-03 -1.1591856600716710e-03 -3.2087691943161190e-04 -4.1986262658610940e-04 3.0404137214645743e-04 6.0338097682688385e-05 6.7018414847552776e-04 3.7366041215136647e-04 9.0029416605830193e-04 5.2075367420911789e-04 9.9500513169914484e-04 5.6327198399230838e-04 1.1075052898377180e-03 7.4593455065041780e-04 1.3909386470913887e-03 1.1303158244118094e-03 2.0289346575737000e-03 2.0220943260937929e-03 3.0524877365678549e-03 3.3301752991974354e-03 4.6451427042484283e-03 5.2075558342039585e-03 6.8378043361008167e-03 7.6546063646674156e-03 9.3866763636469841e-03 1.0335958562791348e-02 1.2200511060655117e-02 1.3129818253219128e-02 1.4943869784474373e-02 1.5761753544211388e-02 1.7342291772365570e-02 1.7926774919033051e-02 1.9121279940009117e-02 1.9289316609501839e-02 2.0097836852073669e-02 5.2097856998443604e-01 2.0088912919163704e-02 1.9271552562713623e-02 1.9155547022819519e-02 1.7891261726617813e-02 1.7328225076198578e-02 1.5769559890031815e-02 1.4942547306418419e-02 1.3150474056601524e-02 1.2181469239294529e-02 1.0308497585356236e-02 9.3805100768804550e-03 7.5791100971400738e-03 6.8140844814479351e-03 5.2061961032450199e-03 4.6650180593132973e-03 3.2504794653505087e-03 3.0550425872206688e-03 1.9862649496644735e-03 1.9532737787812948e-03 1.1690178653225303e-03 1.3898044126108289e-03 7.3725724359974265e-04 1.0895140003412962e-03 5.3791055688634515e-04 9.6034159651026130e-04 4.4838391477242112e-04 8.7967573199421167e-04 3.1552067957818508e-04 6.3330424018204212e-04 1.6649930330459028e-05 2.5112734874710441e-04 -4.4886878458783031e-04 -3.5906728589907289e-04 -1.1427421122789383e-03 -1.1369365965947509e-03 -2.0656813867390156e-03 -2.0526829175651073e-03 -2.9742778278887272e-03 -3.0460176058113575e-03 -3.9608497172594070e-03 -3.9651365950703621e-03 -4.8736012540757656e-03 -4.8108389601111412e-03 -5.6828260421752930e-03 -5.5839507840573788e-03 -6.3588451594114304e-03 -6.2242811545729637e-03 -6.9635473191738129e-03 -6.7632142454385757e-03 -7.4367662891745567e-03 -7.2321430779993534e-03 -7.8709451481699944e-03 -7.5404071249067783e-03 -8.1449057906866074e-03 -7.8414985910058022e-03 -8.3816293627023697e-03 -8.0447718501091003e-03 -8.5209896788001060e-03 -8.1511447206139565e-03 -8.5944253951311111e-03 -8.1310197710990906e-03 -8.5418177768588066e-03 -7.9547949135303497e-03 -8.3640897646546364e-03 -7.7454573474824429e-03 -8.0316346138715744e-03 -7.3513351380825043e-03 -7.6063992455601692e-03 -6.8954071030020714e-03 -7.0892879739403725e-03 -6.3480478711426258e-03 -6.5117068588733673e-03 -5.7711834087967873e-03 -5.9355739504098892e-03 -5.2267177961766720e-03 -5.3617516532540321e-03 -4.6544745564460754e-03 -4.8216073773801327e-03 -4.1468446142971516e-03 -4.3159695342183113e-03 -3.6436130758374929e-03 -3.8761636242270470e-03 -3.1760921701788902e-03 -3.3809037413448095e-03 -2.7450562920421362e-03 -3.0445747543126345e-03 -2.4122872855514288e-03 -2.6542874984443188e-03 -2.0259188022464514e-03 -2.3328179959207773e-03 -1.7087113810703158e-03 -1.9892905838787556e-03 -1.4002927346155047e-03 -1.6853861743584275e-03 -1.1012906907126307e-03 -1.4522322453558445e-03 -8.7333435658365488e-04 -1.2293771142140031e-03 -6.8647391162812710e-04 -1.0173632763326168e-03 -5.1071139751002192e-04 -8.4721547318622470e-04 -3.4654367482289672e-04 -6.8889034446328878e-04 -1.6390271775890142e-04 -5.7333818404003978e-04 -2.4240564016508870e-05 -3.7888568476773798e-04 1.6361882444471121e-04 -2.2796231496613473e-04 3.6881922278553247e-04 3.1701983971288428e-05 5.3002696949988604e-04 1.8620509945321828e-04 7.6905224705114961e-04 3.5737844882532954e-04 9.6360436873510480e-04 6.6708977101370692e-04 1.1745382798835635e-03 8.7103841360658407e-04 1.3711963547393680e-03 1.0606570867821574e-03 1.5839941333979368e-03 1.2358620297163725e-03 1.7823486123234034e-03 1.4271278632804751e-03 1.9662363920360804e-03 1.5428949845954776e-03 2.1356770303100348e-03 1.7968263709917665e-03 2.2601932287216187e-03 1.9143191166222095e-03 2.4314322508871555e-03 2.0480779930949211e-03 2.5579652283340693e-03 2.1677203476428986e-03 2.6704631745815277e-03 2.3039432708173990e-03 2.7691198047250509e-03 2.3959151003509760e-03 2.9151949565857649e-03 2.4743955582380295e-03 2.8953212313354015e-03 2.5396544951945543e-03 2.9844413511455059e-03 2.5614781770855188e-03 2.9692486859858036e-03 2.6012253947556019e-03 3.0331891030073166e-03 2.5677015073597431e-03 3.0240432824939489e-03 2.5528396945446730e-03 3.0642757192254066e-03 2.5265275035053492e-03 2.9711953829973936e-03 2.4891886860132217e-03 2.9283459298312664e-03 2.5023075286298990e-03 2.9361820779740810e-03 2.4747967254370451e-03 2.8731059283018112e-03 2.3766215890645981e-03 2.8311673086136580e-03 2.3913872428238392e-03 2.7803513221442699e-03 2.2754631936550140e-03 2.6906565763056278e-03 2.2124806419014931e-03 2.5931324344128370e-03 2.0809010602533817e-03 2.4883178994059563e-03 1.9728227052837610e-03 2.3462395183742046e-03 1.8887937767431140e-03 2.2590041626244783e-03 1.7683338373899460e-03 2.1050965879112482e-03 1.5814816579222679e-03 1.9766287878155708e-03 1.4508682070299983e-03 1.8741587409749627e-03 1.3770509976893663e-03 1.7372102010995150e-03 1.2079989537596703e-03 1.5968553489074111e-03 1.0968541027978063e-03 1.4536463422700763e-03 9.8312844056636095e-04 1.3386445352807641e-03 8.0632872413843870e-04 1.1913493508473039e-03 6.8905804073438048e-04 1.0428141104057431e-03 5.0977140199393034e-04 9.8510226234793663e-04 4.5208242954686284e-04 8.0510042607784271e-04 3.3338798675686121e-04 7.1692478377372026e-04 2.1521010785363615e-04 5.9898232575505972e-04 1.2853565567638725e-04 5.4329057456925511e-04 4.3301915866322815e-05 4.2822305113077164e-04 -1.0108882270287722e-04 3.1524506630375981e-04 -1.2110955140087754e-04 2.3528306337539107e-04 -1.9946871907450259e-04 1.5821139095351100e-04 -2.7475052047520876e-04 8.4396902821026742e-05 -4.3814827222377062e-04 -4.6846111217746511e-05 -5.3673691581934690e-04 -1.4363852096721530e-04 -5.7020160602405667e-04 -2.3619440617039800e-04 -7.5187283800914884e-04 -3.8526914431713521e-04 -8.0682476982474327e-04 -4.0749786421656609e-04 -9.1791671002283692e-04 -5.4679164895787835e-04 -1.0238919639959931e-03 -6.1983393970876932e-04 -1.1245540808886290e-03 -7.4850977398455143e-04 -1.2197311734780669e-03 -8.4110663738101721e-04 -1.2787571176886559e-03 -8.9748809114098549e-04 -1.3625426217913628e-03 -1.0090968571603298e-03 -1.4099526451900601e-03 -1.0537733323872089e-03 -1.5429838094860315e-03 -1.0924895759671926e-03 -1.6089978162199259e-03 -1.1862431420013309e-03 -1.6384801128879189e-03 -1.2129449751228094e-03 -1.6619464149698615e-03 -1.2641558423638344e-03 -1.6794144175946712e-03 -1.2788698077201843e-03 -1.6909248661249876e-03 -1.2571357656270266e-03 -1.6965370159596205e-03 -1.2600641930475831e-03 -1.6963357338681817e-03 -1.2572280829772353e-03 -1.6293872613459826e-03 -1.2487435014918447e-03 -1.6178810037672520e-03 -1.2042106827721000e-03 -1.6009323298931122e-03 -1.1848319554701447e-03 -1.5786892035976052e-03 -1.1602487647905946e-03 -1.5818479005247355e-03 -1.1306431842967868e-03 -1.5190459089353681e-03 -1.0656957747414708e-03 -1.4820423675701022e-03 -1.0266583412885666e-03 -1.4405371621251106e-03 -1.0137531207874417e-03 -1.3947647530585527e-03 -9.3566509895026684e-04 -1.2839328264817595e-03 -8.8419811800122261e-04 -1.2608813121914864e-03 -8.2909170305356383e-04 -1.1732900748029351e-03 -7.7061471529304981e-04 -1.1434960179030895e-03 -7.3955929838120937e-04 -1.0802306933328509e-03 -6.4465601462870836e-04 -1.0142918908968568e-03 -6.0826150001958013e-04 -1.0070051066577435e-03 -5.6963210226967931e-04 -9.6711312653496861e-04 -5.2906316705048084e-04 -8.6439051665365696e-04 -4.2581415618769825e-04 -7.9069280764088035e-04 -3.5173801006749272e-04 -7.4631348252296448e-04 -3.0712987063452601e-04 -6.3999666599556804e-04 -2.0073396444786340e-04 -6.2462431378662586e-04 -1.8542942416388541e-04 -5.4790021385997534e-04 -1.0892117279581726e-04 -4.7115018242038786e-04 -3.2526702852919698e-05 -3.9465352892875671e-04 4.3472242396092042e-05 -2.8817181009799242e-04 1.4932046178728342e-04 -2.7404323918744922e-04 1.6268025501631200e-04 -1.6942509682849050e-04 2.0536564989015460e-04 -1.2716071796603501e-04 3.3816177165135741e-04 -2.5422285034437664e-05 3.7772319046780467e-04 1.3483887414622586e-05 4.1537417564541101e-04 5.0371156248729676e-05 5.4244953207671642e-04 1.4606142940465361e-04 6.0615350957959890e-04 2.0880319061689079e-04 6.3681451138108969e-04 2.3840596259105951e-04 7.2579894913360476e-04 3.2625091262161732e-04 7.5087137520313263e-04 3.8062210660427809e-04 8.0342905130237341e-04 4.3188958079554141e-04 8.5281481733545661e-04 4.7992411418817937e-04 9.2941999901086092e-04 5.5511947721242905e-04 1.0026156669482589e-03 5.9633859200403094e-04 9.8075252026319504e-04 6.0349318664520979e-04 1.0468529071658850e-03 6.6805968526750803e-04 1.0787807404994965e-03 6.9842615630477667e-04 1.1375172762200236e-03 7.2506524156779051e-04 1.1009566951543093e-03 7.4794661486521363e-04 1.1521802516654134e-03 7.6705479295924306e-04 1.1996278772130609e-03 8.1290694652125239e-04 1.1822700034826994e-03 7.9396297223865986e-04 1.1916796211153269e-03 7.7128579141572118e-04 1.1973722139373422e-03 7.7543186489492655e-04 1.1993971420451999e-03 7.4542220681905746e-04 1.1672966647893190e-03 7.7287538442760706e-04 1.1621775338426232e-03 7.6631898991763592e-04 1.1536098318174481e-03 7.5635936809703708e-04 1.1416884372010827e-03 7.4310263153165579e-04 1.0960050858557224e-03 6.6562200663611293e-04 1.0166772408410907e-03 6.4611609559506178e-04 1.0564217809587717e-03 6.2367925420403481e-04 1.0322700254619122e-03 5.9845601208508015e-04 9.7488641040399671e-04 5.7059415848925710e-04 9.7597768763080239e-04 5.4025388089939952e-04 9.1363419778645039e-04 5.0759734585881233e-04 9.1009528841823339e-04 4.7279641148634255e-04 8.4346171934157610e-04 4.3602727237157524e-04 8.3598576020449400e-04 4.2798899812623858e-04 7.6577544678002596e-04 3.8782655610702932e-04 7.2457245551049709e-04 3.1573633896186948e-04 7.1256671799346805e-04 3.3397047081962228e-04 6.6891254391521215e-04 2.9013573657721281e-04 6.2431913102045655e-04 2.4546086206100881e-04 5.7898083468899131e-04 2.0013720495626330e-04 5.3308909991756082e-04 1.2383821012917906e-04 4.8683534259907901e-04 1.0831073450390249e-04 4.7092817840166390e-04 6.2186998547986150e-05 4.2451810440979898e-04 -1.4344735973281786e-05 3.1727278837934136e-04 -6.0067097365390509e-05 2.7144266641698778e-04 -1.0531843145145103e-04 2.5668853777460754e-04 -1.4992644719313830e-04 1.8162674678023905e-04 -1.9372042152099311e-04 1.9901365158148110e-04 -2.6705698110163212e-04 9.5383904408663511e-05 -3.0874201911501586e-04 5.3996282076695934e-05 -3.7966293166391551e-04 1.3964530808152631e-05 -3.8812175625935197e-04 5.9432345551613253e-06 -4.2553816456347704e-04 -6.1489918152801692e-05 -4.6126771485432982e-04 -1.2717608478851616e-04 -4.9519038293510675e-04 -1.2996557052247226e-04 -5.2719912491738796e-04 -1.6130731091834605e-04 -5.5719050578773022e-04 -1.9058783072978258e-04 -5.8507703943178058e-04 -2.4823687272146344e-04 -6.4129120437428355e-04 -3.0366051942110062e-04 -6.3420890364795923e-04 -2.9575746157206595e-04 -6.8583962274715304e-04 -3.1602146918885410e-04 -7.0457643596455455e-04 -2.7285443502478302e-04 -6.9037883076816797e-04 -3.1880501774139702e-04 -7.0425024023279548e-04 -3.3177799195982516e-04 -7.1564788231626153e-04 -3.7278959644027054e-04 -7.5507833389565349e-04 -4.1131360922008753e-04 -7.3099031578749418e-04 -4.1683643939904869e-04 -7.3493388481438160e-04 -3.8936911732889712e-04 -7.3641684139147401e-04 -3.5945090348832309e-04 -7.0494378451257944e-04 -3.5762955667451024e-04 -7.3210289701819420e-04 -3.5342594492249191e-04 -7.2638527490198612e-04 -3.7740677362307906e-04 -7.1835931157693267e-04 -3.6859247484244406e-04 -7.0808775490149856e-04 -2.9652967350557446e-04 -7.2615372482687235e-04 -2.8336085961200297e-04 -6.5056490711867809e-04 -3.5968283191323280e-04 -6.6450802842155099e-04 -3.1195822521112859e-04 -6.1548384837806225e-04 -2.3183251323644072e-04 -6.2565860571339726e-04 -2.7199127362109721e-04 -6.0358247719705105e-04 -2.1891671349294484e-04 -5.7987810578197241e-04 -2.5582191301509738e-04 -5.5465754121541977e-04 -1.6919695190154016e-04 -5.2803225116804242e-04 -1.7278411542065442e-04 -5.0012592691928148e-04 -1.7514984938316047e-04 -4.4054188765585423e-04 -8.4866442193742841e-05 -4.1044186218641698e-04 -1.1568227637326345e-04 -4.4047058327123523e-04 -5.4103733418742195e-05 -3.7816734402440488e-04 -5.2847804909106344e-05 -3.7625117693096399e-04 1.0025234587374143e-05 -3.4330252674408257e-04 1.2312273611314595e-05 -3.0996641726233065e-04 7.5954150815960020e-05 -3.0689523555338383e-04 7.8749435488134623e-05 -2.4266367836389691e-04 1.1212364188395441e-04 -2.0895375928375870e-04 1.4542926510330290e-04 -1.4485533756669611e-04 1.7854160978458822e-04 -1.1153060768265277e-04 2.4185347137972713e-04 -1.0909867705777287e-04 2.4369356106035411e-04 -7.6647978858090937e-05 2.7550169033929706e-04 -4.4803331547882408e-05 3.0664048972539604e-04 -4.4194475776748732e-05 3.3700402127578855e-04 1.6620257156318985e-05 3.9700669003650546e-04 4.5987388148205355e-05 3.9499578997492790e-04 7.4329029303044081e-05 4.2243002098985016e-04 1.3207207666710019e-04 4.4870338751934469e-04 1.2757755757775158e-04 5.3476780885830522e-04 1.5231897123157978e-04 5.5847747717052698e-04 1.4518253738060594e-04 5.5027718190103769e-04 2.2817900753580034e-04 5.4062175331637263e-04 2.4865401792339981e-04 5.9048878028988838e-04 2.3707257059868425e-04 6.0827401466667652e-04 2.5442295009270310e-04 5.9393356787040830e-04 2.7014579973183572e-04 6.0846511041745543e-04 2.5368994101881981e-04 6.5183843253180385e-04 2.9658249695785344e-04 6.6299445461481810e-04 3.3776802592910826e-04 6.7243573721498251e-04 3.1619379296898842e-04 6.8015238502994180e-04 2.9289588565006852e-04 6.8614113843068480e-04 3.2890611328184605e-04 6.9040688686072826e-04 3.3268099650740623e-04 6.6244223853573203e-04 3.0423441785387695e-04 6.3278275774791837e-04 3.0462114955298603e-04 6.3196918927133083e-04 3.3386913128197193e-04 6.2951375730335712e-04 3.3097443520091474e-04 6.5597071079537272e-04 2.9597862157970667e-04 6.5034703584387898e-04 2.8995925094932318e-04 6.1268842546269298e-04 3.1296681845560670e-04 6.0408323770388961e-04 3.3453843207098544e-04 6.2458968022838235e-04 2.3266603238880634e-04 6.1323499539867043e-04 2.5155241019092500e-04 6.0060265241190791e-04 2.6919480296783149e-04 5.5624480592086911e-04 2.2463536879513413e-04 5.7179195573553443e-04 2.4001707788556814e-04 5.2524940110743046e-04 1.9334899843670428e-04 5.3876882884651423e-04 1.7626737826503813e-04 5.2088050870224833e-04 1.8885673489421606e-04 5.0218956312164664e-04 1.7017044592648745e-04 4.5226584188640118e-04 1.2029615754727274e-04 4.3223379179835320e-04 1.0035940067609772e-04 4.1166736627928913e-04 7.9932578955776989e-05 3.6014025681652129e-04 2.8593696697498672e-05 3.6929766065441072e-04 3.7985173548804596e-05 3.1716166995465755e-04 -1.3871627743355930e-05 2.9537780210375786e-04 2.5705197913339362e-05 3.0403607524931431e-04 -5.6816508731571957e-05 3.1271061743609607e-04 -7.8242679592221975e-05 2.6045623235404491e-04 -1.3003648200538009e-04 2.0839445642195642e-04 -9.0041816292796284e-05 1.8713007739279419e-04 -1.4127635222394019e-04 1.6622748808003962e-04 -1.6159031656570733e-04 1.4576906687580049e-04 -2.1193939028307796e-04 1.2583256466314197e-04 -2.0069222955498844e-04 1.0649276373442262e-04 -1.8881201685871929e-04 8.7822001660242677e-05 -2.6778117171488702e-04 3.9370112062897533e-05 -2.2391110542230308e-04 5.2755684009753168e-05 -2.7075875550508499e-04 3.6485522286966443e-05 -2.5568285491317511e-04 -9.3841535999672487e-06 -3.0069655622355640e-04 6.7499731812858954e-06 -2.8368041967041790e-04 -6.6146690187451895e-06 -3.2666142215020955e-04 -1.8919430658570491e-05 -3.3804436679929495e-04 -3.0126266210572794e-05 -3.4831216908060014e-04 -4.0203558455687016e-05 -3.8795050932094455e-04 -4.9119003961095586e-05 -3.3486430766060948e-04 -5.6851717090466991e-05 -3.1110327108763158e-04 -6.3383318774867803e-05 -3.4716748632490635e-04 -9.9217511888127774e-05 -3.5149452742189169e-04 -7.2792128776200116e-05 -3.8511300226673484e-04 -7.5658506830222905e-05 -3.5646991454996169e-04 -4.6780434786342084e-05 -3.5712012322619557e-04 -7.7717180829495192e-05 -3.8707404746674001e-04 -1.0744553583208472e-04 -4.1582217090763152e-04 -1.3597958604805171e-04 -4.1287153726443648e-04 -1.0230470070382580e-04 -3.7823873572051525e-04 -3.6962162994313985e-05 -3.7298948154784739e-04 -6.2050334236118942e-05 -3.9714964805170894e-04 -2.5015722712851129e-05 -3.8972243783064187e-04 -7.8479082731064409e-05 -3.2022554660215974e-04 -1.0041329369414598e-04 -3.7180783692747355e-04 -6.0341983044054359e-05 -3.9192484109662473e-04 -4.9865710025187582e-05 -3.5011040745303035e-04 -3.8516147469636053e-05 -3.6848217132501304e-04 -5.6862914789235219e-05 -3.2502517569810152e-04 -1.3409271559794433e-05 -2.8083214419893920e-04 -3.0281644285423681e-05 -2.9699300648644567e-04 1.4530889529851265e-05 -3.1253584893420339e-04 2.9414220989565365e-05 -2.6648514904081821e-04 -1.6212376067414880e-05 -2.1994202688802034e-04 3.0176597647368908e-05 -2.3400252393912524e-04 4.6445729822153226e-05 -1.5614631411153823e-04 9.3563496193382889e-05 -1.6954410239122808e-04 1.4094644575379789e-04 -1.5219260239973664e-04 9.6977048087865114e-05 -1.6519214841537178e-04 1.1417634959798306e-04 -1.4757238386664540e-04 1.6196086653508246e-04 -1.2991705443710089e-04 1.4871382154524326e-04 -1.1229358642594889e-04 1.9643991254270077e-04 -6.4244071836583316e-05 2.1352333715185523e-04 -7.7385971962939948e-05 2.3042054090183228e-04 -6.0227899666642770e-05 1.8603529315441847e-04 -1.2828421859012451e-05 2.6341510238125920e-04 -2.6798948965733871e-05 2.7939787833020091e-04 -1.0640798791428097e-05 2.6444569812156260e-04 5.0732678573695011e-06 2.7954191318713129e-04 2.0292662156862207e-05 2.9412060393951833e-04 3.4970442357007414e-05 2.7761555975303054e-04 4.9060916353482753e-05 3.2153644133359194e-04 6.2518927734345198e-05 3.3428866299800575e-04 4.4790445826947689e-05 3.7687076837755740e-04 5.6872548157116398e-05 3.2717612339183688e-04 1.2925011105835438e-04 3.6827885196544230e-04 7.8788303653709590e-05 3.7808134220540524e-04 1.1908018495887518e-04 3.8707608473487198e-04 1.2803800927940756e-04 3.6472271312959492e-04 1.0563805699348450e-04 4.0255882777273655e-04 1.1290174734313041e-04 3.7849848740734160e-04 1.4981492131482810e-04 4.1460085776634514e-04 1.2481780140660703e-04 4.1930750012397766e-04 6.8422668846324086e-05 3.9261454367078841e-04 1.3321384903974831e-04 3.6503866431303322e-04 1.3608952576760203e-04 3.6710247513838112e-04 7.7053555287420750e-05 3.9881217526271939e-04 1.3922143261879683e-04 3.9914259104989469e-04 1.7001458036247641e-04 3.6810603342019022e-04 1.3893375580664724e-04 3.9727429975755513e-04 1.3754767132923007e-04 4.2562998714856803e-04 1.3535880134440958e-04 3.9216128061525524e-04 1.0188022861257195e-04 3.5792886046692729e-04 9.8159754998050630e-05 3.5347885568626225e-04 1.5475795953534544e-04 3.7883923505432904e-04 1.4963143621571362e-04 3.4249230520799756e-04 1.4384834503289312e-04 3.6654277937486768e-04 7.6411917689256370e-05 3.2895870390348136e-04 9.9946140835527331e-05 3.5184790613129735e-04 9.2421061708591878e-05 3.4369918284937739e-04 8.4395389421842992e-05 3.3507106127217412e-04 7.5914722401648760e-05 2.9549078317359090e-04 3.6499572161119431e-05 3.1655502971261740e-04 5.7754255976760760e-05 3.0675693415105343e-04 4.8167305067181587e-05 2.9666005866602063e-04 3.8304664485622197e-05 2.8631149325519800e-04 5.8732650359161198e-05 2.4523987667635083e-04 4.8460715333931148e-05 2.6504721608944237e-04 3.8054280594224110e-05 2.2370857186615467e-04 5.8077814173884690e-05 2.7385973953641951e-04 -4.4007039832649752e-05 2.3244280600920320e-04 -2.4015427698031999e-05 2.2157275816425681e-04 -3.4490061807446182e-05 1.8026080215349793e-04 -7.5384225056041032e-05 1.6958563355728984e-04 -8.5619489254895598e-05 2.2010931570548564e-04 -6.5152387833222747e-05 1.1825057299574837e-04 -1.0549560829531401e-04 1.3870724069420248e-04 -1.1505593283800408e-04 1.2893370876554400e-04 -9.3795308202970773e-05 1.1948141036555171e-04 -1.0271115752402693e-04 1.4090567128732800e-04 -1.1125027958769351e-04 1.0168823791900650e-04 -1.4990121417213231e-04 6.2895509472582489e-05 -1.5759406960569322e-04 8.5591258539352566e-05 -1.3430039689410478e-04 7.8251367085613310e-05 -1.4102931891102344e-04 1.0193661000812426e-04 -1.4724404900334775e-04 3.4598579077282920e-05 -1.5291543968487531e-04 2.8846712666563690e-05 -1.5802784764673561e-04 5.4181022278498858e-05 -1.6256525123026222e-04 1.9064078514929861e-05 -1.6650940233375877e-04 4.5579326979350299e-05 -1.6985450929496437e-04 7.2702197940088809e-05 -2.3362203501164913e-04 8.8881188275991008e-06 -2.0521946134977043e-04 3.7247751606628299e-05 -2.0671130914706737e-04 3.5713186662178487e-05 -1.7706243670545518e-04 4.2847873373830225e-06 -1.4679128071293235e-04 3.9967053453437984e-06 -1.7693582049105316e-04 3.4843833418563008e-05 -2.0646562916226685e-04 5.2650452744273935e-06 -1.7435598419979215e-04 6.8063036451349035e-06 -2.6372069260105491e-04 8.9363629740546457e-06 -1.9991515728179365e-04 1.1642544450296555e-05 -1.9657568191178143e-04 4.5425746066030115e-05 -2.2320583229884505e-04 4.9234076868742704e-05 -1.8826783343683928e-04 2.3047834474709816e-05 -1.8333618936594576e-04 -2.6378756956546567e-06 -1.7791485879570246e-04 3.3188844099640846e-05 -1.7202802700921893e-04 3.8951951864873990e-05 -1.6570222214795649e-04 4.5140979636926204e-05 -1.5896446711849421e-04 5.1730472478084266e-05 -1.5184198855422437e-04 2.8171269150334410e-05 -1.1384629760868847e-04 6.5988817368634045e-05 -1.0604370618239045e-04 7.3597162554506212e-05 -1.5898472338449210e-04 5.0967519200639799e-05 -1.5062768943607807e-04 8.9617606136016548e-05 -8.1007048720493913e-05 9.7964220913127065e-05 -7.2225448093377054e-05 1.0649027535691857e-04 -9.3798051238991320e-05 1.1516278755152598e-04 -5.4205989727051929e-05 1.2394745135679841e-04 -1.0607410513330251e-04 1.3281211431603879e-04 -9.6844298241194338e-05 1.1120488488813862e-04 -8.7585329310968518e-05 1.5064541366882622e-04 -4.7813267883611843e-05 1.2903060996904969e-04 -3.8595237128902227e-05 1.6840004536788911e-04 -5.9963927924400195e-05 1.4664899208582938e-04 -5.0913396989926696e-05 1.5530212840531021e-04 -4.1994819184765220e-05 1.6381069144699723e-04 -2.7184178179595619e-06 2.0266363571863621e-04 5.8541395446809474e-06 1.8028066551778466e-04 -1.6308165868395008e-05 1.5766953583806753e-04 2.2325459212879650e-05 1.9584060646593571e-04 3.0175133360899054e-05 2.3373521980829537e-04 7.2182824624178465e-06 2.1029516938142478e-04 1.4468638255493715e-05 2.4756978382356465e-04 5.1903927669627592e-05 2.5398685829713941e-04 2.7955997211392969e-05 2.2952875588089228e-04 3.4157790651079267e-05 2.3521436378359795e-04 3.9977239794097841e-05 2.4051129003055394e-04 7.5919713708572090e-05 2.1488894708454609e-04 5.0418802857166156e-05 2.4989066878333688e-04 5.5018084822222590e-05 2.5395301054231822e-04 5.9192483604419976e-05 2.5758604169823229e-04 9.3449547421187162e-05 2.6078688097186387e-04 9.6754098194651306e-05 2.6354906731285155e-04 3.8583559216931462e-05 2.6587140746414661e-04 4.1008228436112404e-05 2.3723716731183231e-04 7.3509741923771799e-05 2.6919893571175635e-04 7.5055562774650753e-05 2.3969095491338521e-04 7.6168653322383761e-05 2.7078829589299858e-04 7.6854761573486030e-05 2.7094496181234717e-04 1.0763668979052454e-04 2.7068678173236549e-04 1.0749175271485001e-04 2.7002347633242607e-04 7.6428026659414172e-05 2.6896572671830654e-04 7.5493313488550484e-05 2.6752738631330431e-04 7.4183990363962948e-05 2.6572099886834621e-04 7.2513932536821812e-05 2.3304660862777382e-04 7.0500311267096549e-05 2.6107105077244341e-04 6.8156878114677966e-05 2.5826031924225390e-04 6.5506232203915715e-05 2.8566786204464734e-04 3.2047402783064172e-05 2.2124331735540181e-04 5.9352169046178460e-05 2.1759394439868629e-04 8.6408559582196176e-05 2.7474231319501996e-04 5.2200732170604169e-05 2.0960348774679005e-04 1.7787266187951900e-05 2.3582369612995535e-04 4.4225169403944165e-05 2.0083723939023912e-04 9.4668475867365487e-06 1.6570284788031131e-04 5.0888093028333969e-06 2.2199646627996117e-04 3.1116753234528005e-05 2.1715454931836575e-04 -3.9819824451114982e-06 1.5119914314709604e-04 2.1887357434025034e-05 1.7674203263595700e-04 -1.3319517165655270e-05 1.7173805099446326e-04 -1.8029884813586250e-05 1.6672768106218427e-04 -2.2735282982466742e-05 1.6173190670087934e-04 3.3620115573285148e-05 1.5677564078941941e-04 -3.2046384148998186e-05 1.2136313307564706e-04 -3.6605484638130292e-05 1.1654963600449264e-04 -4.1073544707614928e-05 1.4235764683689922e-04 -4.5427608711179346e-05 1.0725419997470453e-04 -4.9650869186734781e-05 1.3332860544323921e-04 -5.3719981224276125e-05 1.2904762115795165e-04 -8.8138491264544427e-05 9.4429626187775284e-05 -6.1335464124567807e-05 1.2104084453312680e-04 -6.4838903199415654e-05 5.6310651416424662e-05 -6.8135981564410031e-05 8.3361170254647732e-05 -7.1195740019902587e-05 1.1065269063692540e-04 -7.4007490184158087e-05 7.7160213550087065e-05 -1.0708157788030803e-04 7.4450843385420740e-05 -7.8851000580471009e-05 4.1497798520140350e-05 -8.0860474554356188e-05 8.8273882283829153e-06 -8.2584978372324258e-05 6.7999462771695107e-05 -5.3498413763009012e-05 6.6432905441615731e-05 -5.4630399972666055e-05 6.5168780565727502e-05 -1.1649446969386190e-04 6.4210333221126348e-05 -8.6501007899641991e-05 1.2459434219636023e-04 -8.6715983343310654e-05 1.2425330351106822e-04 -1.1714017455233261e-04 9.3704307801090181e-05 -5.5703912948956713e-05 6.3462990510743111e-05 -1.1603009625105187e-04 3.3526655897730961e-05 -1.1501942935865372e-04 6.4928550273180008e-05 -1.1371007713023573e-04 6.6111599153373390e-05 -8.1588397733867168e-05 6.7584565840661526e-05 -1.1021332466043532e-04 6.9344212533906102e-05 -7.7525473898276687e-05 7.1379683504346758e-05 -1.0559960355749354e-04 7.3683746450114995e-05 -7.2375893068965524e-05 1.0676345846150070e-04 -9.9935612524859607e-05 1.0957296763081104e-04 -6.6219028667546809e-05 5.1583949243649840e-05 -1.2382546265143901e-04 8.5372361354529858e-05 -8.9662185928318650e-05 8.8853099441621453e-05 -5.5296004575211555e-05 9.2533082352019846e-05 -8.1774684076663107e-05 6.5877291490323842e-05 -7.7560922363772988e-05 1.0042673966381699e-04 -4.2668110836530104e-05 7.4094175943173468e-05 -9.9182929261587560e-05 4.7900015488266945e-05 -3.3499221899546683e-05 8.2864033174701035e-05 -2.8737087632180192e-05 8.7416949099861085e-05 -5.4394164180848747e-05 9.2060465249232948e-05 -4.9452119128545746e-05 6.6260647145099938e-05 -7.4962707003578544e-05 1.0155374184250832e-04 -8.8704227891867049e-06 7.5854033639188856e-05 -3.4298212995054200e-05 1.1121499119326472e-04 -2.9193121008574963e-05 1.1606815678533167e-04 -5.4604945034952834e-05 1.2091475946363062e-04 -1.8994433048646897e-05 1.2573995627462864e-04 -7.4967771070078015e-05 1.0001157352235168e-04 -8.9155319074052386e-06 1.0474918963154778e-04 2.6558507670415565e-05 1.0942057269858196e-04 9.2481786850839853e-07 8.3495411672629416e-05 3.6238983739167452e-05 1.1851254384964705e-04 -2.0099758330616169e-05 1.2290674203541130e-04 1.5002515283413231e-05 1.5770106983836740e-04 1.9462255295366049e-05 1.0081467917189002e-04 2.3787415557308123e-05 1.6585871344432235e-04 -2.5505619305477012e-06 1.6971855075098574e-04 3.1990417483029887e-05 8.1866957771126181e-05 5.3355797717813402e-06 1.7695472342893481e-04 9.0253679445595481e-06 1.1927974992431700e-04 4.3053849367424846e-05 1.5297667414415628e-04 4.6378576371353120e-05 1.5596963930875063e-04 4.9515536375110969e-05 1.5877056284807622e-04 5.2455572586040944e-05 1.3085764658171684e-04 2.4680566639290191e-05 1.3326377666089684e-04 5.7737674069358036e-05 1.3546756235882640e-04 2.9557926609413698e-05 1.6798614524304867e-04 9.2724796559195966e-05 1.6978266648948193e-04 6.4134117565117776e-05 1.4085727161727846e-04 9.6374169515911490e-05 1.4224745973479003e-04 3.6858415114693344e-05 1.7395315808244050e-04 6.8693814682774246e-05 1.4442541578318924e-04 3.9296472095884383e-05 1.1470117897260934e-04 7.0738635258749127e-05 1.4582194853574038e-04 4.0956245356937870e-05 1.4623763854615390e-04 4.1507064452162012e-05 1.4647214266005903e-04 4.1877196053974330e-05 1.4653158723376691e-04 7.2591901698615402e-05 1.1590466601774096e-04 4.2106730688828975e-05 1.7666917119640857e-04 1.1461076610430609e-05 1.7624416796024889e-04 4.1701121517689899e-05 1.1463857663329691e-04 4.1280309233115986e-05 1.4444875705521554e-04 1.0209888387180399e-05 1.7413181194569916e-04 4.0049024391919374e-05 1.4266082143876702e-04 3.9256883610505611e-05 1.4159851707518101e-04 3.8360722101060674e-05 1.7095537623390555e-04 3.7368183257058263e-05 1.0867224773392081e-04 3.6292589356889948e-05 1.3786205090582371e-04 4.6253026084741578e-06 1.3646796287503093e-04 3.4128124752896838e-06 1.3501655485015363e-04 3.2665549952071160e-05 1.3351884263101965e-04 3.1358780688606203e-05 1.0146848944714293e-04 -4.9669142754282802e-07 1.3042792852502316e-04 -3.2374220609199256e-05 6.7820983531419188e-05 2.7293461243971251e-05 9.6761999884620309e-05 -4.5935721573187038e-06 9.5192175649572164e-05 -5.9525200413190760e-06 9.3638074758928269e-05 -7.2903903856058605e-06 9.2109985416755080e-05 -8.5993142420193180e-06 9.0617715613916516e-05 -9.8702339528244920e-06 5.8651807194110006e-05 -4.1608502215240151e-05 8.7775071733631194e-05 1.8261569493915886e-05 8.6441854364238679e-05 -1.3357329407881480e-05 8.5178246081341058e-05 -1.4385486792889424e-05 8.3991602878086269e-05 -1.5334762792917900e-05 8.2890008343383670e-05 -1.6196479919017293e-05 8.1878941273316741e-05 -1.6964420865406282e-05 8.0964251537807286e-05 -4.8152494855457917e-05 8.0152698501478881e-05 -1.8200358681497164e-05 4.8930531193036586e-05 -4.9175247113453224e-05 4.8338573833461851e-05 -1.9002611225005239e-05 4.7862049541436136e-05 -8.0264391726814210e-05 4.7505058319075033e-05 -4.9855487304739654e-05 4.7270015784306452e-05 -4.9840269639389589e-05 1.6641246475046501e-05 -4.9703077820595354e-05 4.7173849452519789e-05 -4.9438625865150243e-05 1.6798265278339386e-05 -4.9048492655856535e-05 1.7067784938262776e-05 -1.8013923181570135e-05 1.7465137716499157e-05 -4.7889221605146304e-05 7.9024197475519031e-05 -4.7119530790951103e-05 -1.1879440535267349e-05 -1.5710222214693204e-05 4.9930378736462444e-05 -4.5211483666207641e-05 2.0308614693931304e-05 -4.4077947677578777e-05 2.1324009139789268e-05 -4.2826453864108771e-05 5.2973009587731212e-05 -4.1460767533862963e-05 5.4217860451899469e-05 -3.9984206523513421e-05 2.5054097932297736e-05 -3.8404148654080927e-05 2.6511937903705984e-05 -3.6723002267535776e-05 -2.4484770619892515e-06 -4.4261405491852202e-06 2.9722536055487581e-05 -3.3075080864364281e-05 6.1982340412214398e-05 -9.2153670266270638e-05 6.3808176491875201e-05 -5.9601276007015258e-05 6.5711516072042286e-05 -5.7492019550409168e-05 6.7686429247260094e-05 -2.4798962840577587e-05 3.9209655369631946e-05 -2.2561709556612186e-05 7.1826849307399243e-05 1.0247826139675453e-05 4.3459920561872423e-05 -1.7930644389707595e-05 7.6173819252289832e-05 -4.6067932998994365e-05 4.7890534915495664e-05 -1.3136796951584984e-05 8.0674202763475478e-05 -1.0695444871089421e-05 5.2447554480750114e-05 -8.2350397860864177e-06 5.4755633755121380e-05 -5.7601409935159609e-06 8.7591717601753771e-05 -3.2813140933285467e-06 8.9914319687522948e-05 -3.1318653782363981e-05 6.1716105847153813e-05 -2.8846738132415339e-05 3.3508404158055782e-05 -2.6388974220026284e-05 9.6837567980401218e-05 -2.3949560272740200e-05 6.8591791205108166e-05 8.9798368207993917e-06 4.0317518141819164e-05 -1.9159364455845207e-05 7.3044604505412281e-05 -1.6814843547763303e-05 1.0573071631370112e-04 -1.4517969248117879e-05 7.7337281254585832e-05 4.8766407417133451e-05 4.8893263738136739e-05 -1.0073285011458211e-05 8.1429163401480764e-05 2.2581107259611599e-05 8.3387778431642801e-05 -5.8627692851587199e-06 8.5283078078646213e-05 -3.8548832890228368e-06 5.6592212786199525e-05 2.8599741199286655e-05 5.8348930906504393e-05 3.0463359507848509e-05 9.0548048319760710e-05 1.7321908671874553e-06 9.2153291916474700e-05 3.3957621781155467e-05 6.3161431171465665e-05 5.0667404138948768e-06 6.4605483203195035e-05 3.7127476389287040e-05 1.2700137449428439e-04 8.0682393672759645e-06 6.7243374360259622e-05 -2.1077434212202206e-05 9.8952339612878859e-05 1.0723533705458976e-05 1.0005823423853144e-04 1.1926285878871568e-05 7.0560970925725996e-05 1.3037841199547984e-05 1.3253034558147192e-04 4.4581593101611361e-05 7.2344417276326567e-05 1.5003515727585182e-05 1.0362693865317851e-04 1.5860978237469681e-05 7.3791285103652626e-05 4.7150475438684225e-05 1.0490985005162656e-04 1.7323840438621119e-05 4.4395972508937120e-05 4.8453250201418996e-05 7.5357915193308145e-05 -1.2047465133946389e-05 1.0624410788295791e-04 1.8930728401755914e-05 4.5505850721383467e-05 -1.1198106221854687e-05 7.6251060818322003e-05 1.9640121536212973e-05 7.6413067290559411e-05 1.9895411242032424e-05 7.6512420491781086e-05 5.0604983698576689e-05 7.6553340477403253e-05 2.0224295440129936e-05 7.6538242865353823e-05 8.1339887401554734e-05 1.0699051199480891e-04 2.0334919099695981e-05 7.6360884122550488e-05 2.0323070202721283e-05 4.5688520913245156e-05 2.0267390937078744e-05 7.6012693170923740e-05 2.0174597011646256e-05 7.5785537774208933e-05 2.0049972590641119e-05 1.0604596172925085e-04 -1.0620787179504987e-05 4.4729866203851998e-05 -1.0797895811265334e-05 7.4945368396583945e-05 1.9523276932886802e-05 7.4627765570767224e-05 1.9313880329718813e-05 7.4298834078945220e-05 -1.1423388968978543e-05 7.3963870818261057e-05 1.8868344341171905e-05 7.3625240474939346e-05 1.8641007045516744e-05 7.3289491410832852e-05 -1.2098656043235678e-05 7.2960465331561863e-05 1.8202055798610672e-05 7.2641036240383983e-05 -1.2519477422756609e-05 4.1818559111561626e-05 1.7811980796977878e-05 4.1533032344887033e-05 1.7638474673731253e-05 1.0750840374385007e-05 -4.3541822378756478e-05 7.1547154220752418e-05 -4.3661639210768044e-05 7.1337577537633479e-05 -1.3234446669230238e-05 4.0641825762577355e-05 -1.3292130461195484e-05 4.0500406612409279e-05 1.7203605239046738e-05 4.0396171243628487e-05 -1.3297325494932011e-05 4.0332484786631539e-05 -4.3758009269367903e-05 4.0312414057552814e-05 -4.3657157220877707e-05 4.0337006794288754e-05 -4.3511470721568912e-05 4.0408151107840240e-05 -7.3836534284055233e-05 4.0528098907088861e-05 -4.3077205191366374e-05 4.0697588701732457e-05 1.8247614207211882e-05 4.0918617742136121e-05 1.8589555111248046e-05 4.1191186028299853e-05 -1.1534732948348392e-05 4.1516213968861848e-05 -1.1090543921454810e-05 4.1894392779795453e-05 -4.1111514292424545e-05 4.2326246330048889e-05 -1.0046138413599692e-05 4.2811145249288529e-05 -9.4443157649948262e-06 4.3347688915673643e-05 -8.7916978372959420e-06 1.3419744391285349e-05 -8.0880527093540877e-06 1.4060961802897509e-05 -7.3337473622814287e-06 1.4752746210433543e-05 -6.5308022385579534e-06 4.6011333324713632e-05 -5.6813337323546875e-06 -1.4234898117138073e-05 -4.7818994062254205e-06 1.7117894458351657e-05 -3.4358257835265249e-05 -1.2518617950263433e-05 -2.8554595701280050e-06 -1.1595700925681740e-05 -1.8290065781911835e-06 5.0403203204041347e-05 -7.6369360613171011e-07 2.0888659491902217e-05 -3.0179780878825113e-05 2.1927184207015671e-05 -2.9044051188975573e-05 2.3000160581432283e-05 -2.7875757950823754e-05 5.4622229072265327e-05 -5.7197103160433471e-05 5.5754811910446733e-05 5.0611338338057976e-06 5.6914024753496051e-05 3.6825957067776471e-05 2.7578345907386392e-05 7.5772213676827960e-06 5.9298989071976393e-05 -2.1654175725416280e-05 -5.1635015552164987e-07 -2.0353214495116845e-05 7.1847262006485835e-07 4.1996267100330442e-05 -2.8552594812936150e-05 -4.8233509005513042e-05 3.2203861337620765e-06 -1.6384992704843171e-05 6.5516993345227093e-05 1.5465680917259306e-05 5.7461975302430801e-06 -1.3717412912228610e-05 3.7528625398408622e-05 1.8132963305106387e-05 3.8790702092228457e-05 1.9461147530819289e-05 9.5300711109302938e-06 -9.7364945759181865e-06 1.0780086086015217e-05 -8.4253479144535959e-06 7.3053997766692191e-05 5.3907850087853149e-05 4.3761923734564334e-05 2.4675393433426507e-05 -1.6062256690929644e-05 -4.5773558667860925e-06 4.6166012907633558e-05 -3.3847682061605155e-05 4.7339381126221269e-05 5.8929912484018132e-05 4.8491634515812621e-05 2.9615246603498235e-05 4.9619502533460036e-05 -3.0244485969888046e-05 2.0205814507789910e-05 -2.9093715056660585e-05 5.1799757784465328e-05 2.5440049284952693e-06 2.2329604689730331e-05 3.6353130781208165e-06 -7.1694103098707274e-06 4.6966761146904901e-06 5.4853448091307655e-05 -2.4793156626401469e-05 2.5291334168286994e-05 6.7199189288658090e-06 2.6214172976324335e-05 7.6812848419649526e-06 2.7103707907372154e-05 8.6081563495099545e-06 -2.5579274733900093e-06 9.5004324975889176e-06 2.8780068532796577e-05 1.0363880392105784e-05 6.0084239521529526e-05 -1.9340328435646370e-05 6.0837446653749794e-05 4.2482926801312715e-05 3.1037845474202186e-05 1.2716134733636864e-05 3.1721083360025659e-05 1.3432971172733232e-05 1.8519331206334755e-06 1.4112648386799265e-05 3.2986114092636853e-05 4.5278859033714980e-05 3.0518294806824997e-06 1.5374898794107139e-05 3.4120344935217872e-05 1.5957128198351711e-05 6.5156571508850902e-05 -1.4009753613208886e-05 3.5129036405123770e-05 -1.3489850061887410e-05 3.5589124308899045e-05 1.7517220840090886e-05 3.6022167478222400e-05 -1.2537317161331885e-05 3.6427536542760208e-05 -1.2101442735001910e-05 -2.4226559617090970e-05 1.8826980522135273e-05 3.7165009416639805e-05 -1.1304813597234897e-05 3.7498852179851383e-05 -1.0939470485027414e-05 3.7812689697602764e-05 1.9919907572329976e-05 7.5897942224401049e-06 2.0245002815499902e-05 3.8384067011065781e-05 -9.9647741080843844e-06 3.8645499444101006e-05 2.0844618120463565e-05 6.9410074502229691e-05 8.2157057477161288e-05 8.6093350546434522e-06 2.1387182641774416e-05 3.9351281884592026e-05 -8.8749666247167625e-06 7.0083937316667289e-05 -8.6286736404872499e-06 3.9774669858161360e-05 -8.3894983617938124e-06 3.9977148844627663e-05 2.2361857190844603e-05 4.0176651964429766e-05 -7.9256969911511987e-06 4.0373299270868301e-05 -7.6973055911366828e-06 1.0052395737147890e-05 2.3048434741212986e-05 1.0250257219013292e-05 2.3277651052922010e-05 4.0968254324980080e-05 -7.0072865128167905e-06 1.0654922334651928e-05 -6.7734836193267256e-06 1.0865319381991867e-05 2.3986569431144744e-05 4.1599407268222421e-05 -6.2817971411277540e-06 4.1824205254670233e-05 2.4492088414262980e-05 1.1541183994268067e-05 2.4757986466283910e-05 7.2821021603886038e-05 -5.4833790272823535e-06 1.2041733498335816e-05 -3.5714638215722516e-05 4.2827858123928308e-05 -4.8978336053551175e-06 1.2591308404807933e-05 2.5931192794814706e-05 4.3404168536653742e-05 -4.2601859604474157e-06 4.3713807826861739e-05 5.7112370996037498e-05 1.3520530956157017e-05 -3.5679281609191094e-06 1.3860882972949184e-05 2.7318779757479206e-05 1.4216686395229772e-05 2.7702586521627381e-05 7.5624222517944872e-05 2.8101218049414456e-05 -1.5540676031378098e-05 -2.0008933461213019e-06 1.5381365301436745e-05 -1.5711138985352591e-06 1.5802226698724553e-05 2.9393506338237785e-05 -1.4278542948886752e-05 -3.1180003134068102e-05 1.6691952623659745e-05 -1.8601258489070460e-07 -1.3357910574995913e-05 3.0823655833955854e-05 1.7643193132244051e-05 8.1196048995479941e-07 -1.2375850928947330e-05 1.3321241567609832e-06 -1.1863916370202787e-05 3.2382493373006582e-05 -1.1337028809066396e-05 3.2928732252912596e-05 -4.1315401176689193e-05 2.9686343623325229e-06 2.0271723769837990e-05 3.4055799915222451e-05 -9.6832445706240833e-06 4.1168377720168792e-06 2.1408774045994505e-05 -2.5811154046095908e-05 -8.5255232988856733e-06 5.3051089707878418e-06 2.2585212718695402e-05 5.9112489907420240e-06 -3.7849389627808705e-05 6.5243193603237160e-06 -3.7240373785607517e-05 -2.3378948753816076e-05 -3.6626053770305589e-05 -2.2751188225811347e-05 -5.4889792409085203e-06 3.8913880416657776e-05 2.5652003387222067e-05 -2.1490139261004515e-05 -4.2384840526210610e-06 -2.0857329218415543e-05 2.6907997380476445e-05 1.0295278116245754e-05 -2.9795055525028147e-06 -5.0104121328331530e-05 -3.2866450055735186e-05 -1.8953212929773144e-05 -1.7183750742333359e-06 -4.8838504881132394e-05 -1.0906155694101471e-06 -1.7691872926661745e-05 3.0052611691644415e-05 1.3451435734168626e-05 1.5721553836556268e-07 -4.6962231863290071e-05 -2.9742062906734645e-05 -1.5828281902940944e-05 -2.9129951144568622e-05 1.5298383004846983e-05 3.2512503821635619e-05 1.5901663573458791e-05 2.5951530915335752e-06 1.6497286196681671e-05 3.3705597161315382e-05 -1.3432821106107440e-05 -2.6743531634565443e-05 -1.2852812687924597e-05 4.3503832785063423e-06 1.8233415175927803e-05 3.5436481994111091e-05 1.8793671188177541e-05 5.4778229241492227e-06 -1.1173589882673696e-05 3.6544173781294376e-05 5.0401009502820671e-05 6.5659451138344593e-06 -1.0105686669703573e-05 7.0951796260487754e-06 5.1448008889565244e-05 -2.2903883291292004e-05 5.1954484661109746e-05 8.1215730460826308e-06 -8.5848287199041806e-06 8.6190939327934757e-06 2.2416301362682134e-05 3.9622958865948021e-05 2.2889753381605260e-05 4.0099650504998863e-05 -7.1649101300863549e-06 1.0047047908301465e-05 -6.7133141783415340e-06 1.0502335499040782e-05 -6.2724648159928620e-06 -1.9568615243770182e-05 -5.8429918681213167e-06 1.1384051504137460e-05 2.5094619559240527e-05 1.1810296200565062e-05 2.5504197765258141e-05 1.2228010746184736e-05 2.5906536393449642e-05 -1.7881167877931148e-05 -4.2191973079752643e-06 -1.7480291717220098e-05 -3.8352900446625426e-06 -4.7605244617443532e-05 -3.4597642297740094e-06 1.3815848433296196e-05 2.7426003725850023e-05 1.4195353287504986e-05 -2.7295172912999988e-06 -4.6466335334116593e-05 -2.3731854525976814e-06 1.4937248124624602e-05 -2.0233092072885484e-06 -1.5216033716569655e-05 2.8839513106504455e-05 1.5660963981645182e-05 2.9180650017224252e-05 1.6017995221773162e-05 -9.9898898042738438e-07 1.6371648598578759e-05 2.9853652449673973e-05 1.6724303350201808e-05 3.0186653020791709e-05 4.7592962800990790e-05 1.0745679901447147e-09 -1.3091909750073683e-05 3.0850122129777446e-05 4.8293757572537288e-05 3.1181694794213399e-05 4.8645633796695620e-05 3.1513605790678412e-05 1.8480892322259024e-05 1.3292847143020481e-06 1.8835788068827242e-05 3.2181076676351950e-05 1.9194258129573427e-05 2.0027337086503394e-06 1.9555327526177280e-05 3.2860181818250567e-05 1.9920205886592157e-05 3.3203919883817434e-05 -1.0228099199593998e-05 3.3552129025338218e-05 -9.8541440820554271e-06 3.3874912332976237e-06 2.1042811567895114e-05 3.7449790397658944e-06 -9.0900593932019547e-06 3.4625620173756033e-05 2.1818843379151076e-05 4.4758962758351117e-06 5.2733354095835239e-05 4.8513029469177127e-06 -7.8977009252412245e-06 5.2318155212560669e-06 2.3030079319141805e-05 5.6181406762334518e-06 -3.7588561099255458e-05 6.0099118854850531e-06 2.3870667064329609e-05 -2.4103825126076117e-05 -6.2158737819117960e-06 6.8166737037245184e-06 -5.7772576838033274e-06 7.2294087658519857e-06 -5.3332287279772572e-06 7.6491669460665435e-06 -4.8818874347489327e-06 8.0731151683721691e-06 -3.4941007470479235e-05 -2.2012703993823379e-05 2.6558540412224829e-05 -2.1576111976173706e-05 2.7029185730498284e-05 9.3837716121925041e-06 2.7505366233526729e-05 9.8307536973152310e-06 2.7987025532638654e-05 1.0283975825586822e-05 -2.0438401406863704e-06 1.0738957826106343e-05 -1.5522155081271194e-06 1.1199586879229173e-05 -1.0573960480542155e-06 -1.8855453163268976e-05 -6.1594146245624870e-05 1.2128522939747199e-05 3.0459887057077140e-05 4.3114399886690080e-05 -3.0071536457398906e-05 -1.7451175153837539e-05 -2.9566394005087204e-05 1.3537397535401396e-05 -2.9059439839329571e-05 -1.6508814951521344e-05 3.2483327231602743e-05 -1.6035262888181023e-05 2.4837622731865849e-06 1.4961143278924283e-05 3.0106202757451683e-06 -1.5074641851242632e-05 3.5465484415908577e-06 -1.4591083527193405e-05 4.0884465306589846e-06 -1.4105242371442728e-05 4.6361901695490815e-06 1.6897953173611313e-05 5.1881838771805633e-06 1.7380561985191889e-05 -5.5292177421506494e-05 -1.2657478691835422e-05 -2.4218614271376282e-05 4.8853842599783093e-05 6.8555282268789597e-06 -1.1710454600688536e-05 7.4101831160078291e-06 1.9271237761131488e-05 3.8480531657114625e-05 -1.0790119631565176e-05 -2.2005397113389336e-05 -1.0341810593672562e-05 9.0559051386662759e-06 -9.9041817520628683e-06 9.5945151770138182e-06 -9.4761962827760726e-06 1.0126551387656946e-05 -3.9578662835992873e-05 4.1168201278196648e-05 2.1860154447495006e-05 -4.9868911446537822e-05 2.2250393158174120e-05 -1.8845204976969399e-05 2.2627196813118644e-05 1.2168547073088121e-05 -7.5269808803568594e-06 -4.8380785301560536e-05 2.3339811377809383e-05 -1.7388660126016475e-05 -6.8433391788857989e-06 1.3591979950433597e-05 5.4509102483280003e-05 -1.6474097719765268e-05 2.4295306502608582e-05 -1.6035448425100185e-05 -5.9339349718356971e-06 1.4909564924892038e-05 -5.6604449127917178e-06 4.5841276005376130e-05 -3.5920154914492741e-05 -4.5308843255043030e-05 -3.5678429412655532e-05 4.6634162572445348e-05 -4.9327231863571797e-06 -1.4023419680597726e-05 -4.7178500608424656e-06 -1.3657554518431425e-05 2.5998400815296918e-05 1.7214842955581844e-05 -3.4849908843170851e-05 1.7558299077791162e-05 -4.1589823922549840e-06 -1.2626505849766545e-05 2.6520445317146368e-05 1.8213175280834548e-05 -3.8472990127047524e-06 -1.1991690371360164e-05 -3.7091110698384000e-06 -1.1688022823364008e-05 -3.5805403513222700e-06 -1.1393218301236629e-05 2.7056108592660166e-05 -1.1106343663414009e-05 2.7166737709194422e-05 -1.0826381185324863e-05 -3.3765147236408666e-05 -1.0553148058534134e-05 -3.1515476166532608e-06 -1.0285672942700330e-05 -3.0624139526480576e-06 5.1011073082918301e-05 2.7539823349798098e-05 2.0750982002937235e-05 5.8136982261203229e-05 -9.5132336355163716e-06 5.8213019656250253e-05 2.1254121747915633e-05 -2.7488274554343661e-06 5.2018946007592604e-05 2.7839972972287796e-05 5.2264102123444900e-05 -2.6090651772392448e-06 -3.9045280573191121e-05 5.8494912082096562e-05 2.2231872208067216e-05 -2.4723481146793347e-06 2.2472891942015849e-05 2.8112994186813012e-05 2.2713767975801602e-05 -2.3354766653937986e-06 -7.5626830948749557e-06 -2.2652352527074981e-06 -7.3215569500462152e-06 -2.1929622562311124e-06 2.3437867639586329e-05 -2.1201581148488913e-06 -6.8374733928067144e-06 -3.2561412808718160e-05 2.3923243134049699e-05 -1.9651085949590197e-06 -6.3504403442493640e-06 -1.8838273945220863e-06 -6.1060695770720486e-06 -1.8011364772974048e-06 2.4656936147948727e-05 -3.2232732337433845e-05 -3.6132518289377913e-05 -1.6263348925349419e-06 2.5148898203042336e-05 -1.5351129150076304e-06 2.5394865588168614e-05 -1.4425070276047336e-06 2.5640874810051173e-05 -1.3473578519551666e-06 -3.5149128962075338e-05 -1.2506027360359440e-06 2.6130399419344030e-05 -1.1522797649377026e-06 -4.1442740439379122e-06 -3.1570943974656984e-05 -3.9034553083183710e-06 -9.5354806717296015e-07 -3.6650360470957821e-06 2.9664277462870814e-05 2.7088082788395695e-05 2.9763861675746739e-05 -3.1975393994798651e-06 -3.1171868613455445e-05 -3.3487343898741528e-05 -5.5694380307613756e-07 -2.7466089704830665e-06 3.0056431569391862e-05 2.7988802685285918e-05 -3.6747599096997874e-07 2.8200702217873186e-05 -3.0794210033491254e-05 -3.2628769986331463e-05 -1.8934268553039146e-07 -1.9124599930364639e-06 -3.0623141356045380e-05 -1.7213690171047347e-06 3.0491137295030057e-05 -1.5382369156213826e-06 4.8294857890596177e-08 2.9154251024010591e-05 3.0635372240794823e-05 2.9320059184101410e-05 3.0699404305778444e-05 -1.0408731441202690e-06 2.4011939103729674e-07 -3.1411294912686571e-05 -3.0225446607801132e-05 -3.1274037610273808e-05 3.3792551334954624e-07 2.9888224162277766e-05 3.7713442679887521e-07 -5.1237657316960394e-07 4.0986279259414005e-07 -4.0561729974797345e-07 -3.0081640943535604e-05 -3.0922959126655769e-07 4.5522619984694757e-07 -2.2311236591576744e-07 4.6793053343208157e-07 3.0370349122676998e-05 -3.0043331207707524e-05 3.0436291126534343e-05 3.0991999665275216e-05 6.1010134231764823e-05 -3.0049113775021397e-05 2.1820524054305679e-08 4.5692985395362484e-07 5.9694166054669040e-08 4.4012105604451790e-07 8.9025633087658207e-08 3.0936247640056536e-05 1.1039758618380802e-07 3.9289324149649474e-07 3.0641971534350887e-05 -3.0154051273711957e-05 1.3161694312202599e-07 6.1366299632936716e-05 1.3289761113810528e-07 2.9646275834238622e-07 1.2892948575426999e-07 3.0777813663007692e-05 1.2054421461016318e-07 2.2322610959690792e-07 1.0861798926953270e-07 3.0703846277901903e-05 -6.0941078118048608e-05 1.5023039168227115e-07 7.7841960433033819e-08 1.1598179128213815e-07 6.0894336684214068e-08 8.4447407289189869e-08 -3.0473358492599800e-05 5.6535959913617262e-08 3.0546420020982623e-05 3.3202159244183349e-08 1.5781736806275148e-08 3.0532952223438770e-05 3.0523642635671422e-05 3.9975565080396791e-09 6.1035869293846190e-05 brutefir-1.0o/dither.c0000644000175000017500000000745112752354353014330 0ustar andersanders/* * (c) Copyright 2001, 2003 - 2004 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include "defs.h" #include "dai.h" #include "dither.h" #include "shmalloc.h" #include "emalloc.h" #include "pinfo.h" /* desired spacing between channels in random number table in seconds */ #define RANDTAB_SPACING 10 #define MIN_RANDTAB_SPACING 1 int8_t *dither_randtab; int dither_randtab_size; void *dither_randmap = NULL; static int realsize; /* * This is a maximally equidistributed combined Tausworthe generator, stolen * from GNU Scientific Library (GSL). See GSL documentation for further * information. * * Generates numbers between 0x0 - 0xFFFFFFFF */ static inline uint32_t tausrand(uint32_t state[3]) { #define TAUSWORTHE(s,a,b,c,d) ((s & c) << d) ^ (((s <> b) state[0] = TAUSWORTHE(state[0], 13, 19, (uint32_t)4294967294U, 12); state[1] = TAUSWORTHE(state[1], 2, 25, (uint32_t)4294967288U, 4); state[2] = TAUSWORTHE(state[2], 3, 11, (uint32_t)4294967280U, 17); return (state[0] ^ state[1] ^ state[2]); } static void tausinit(uint32_t state[3], uint32_t seed) { /* default seed is 1 */ if (seed == 0) { seed = 1; } #define LCG(n) ((69069 * n) & 0xFFFFFFFFU) state[0] = LCG(seed); state[1] = LCG(state[0]); state[2] = LCG(state[1]); /* "warm it up" */ tausrand(state); tausrand(state); tausrand(state); tausrand(state); tausrand(state); tausrand(state); } bool_t dither_init(int n_channels, int sample_rate, int _realsize, int max_size, int max_samples_per_loop, struct dither_state *dither_states[]) { int n, spacing = RANDTAB_SPACING * sample_rate, minspacing; uint32_t state[3]; realsize = _realsize; minspacing = (MIN_RANDTAB_SPACING * sample_rate > max_samples_per_loop) ? MIN_RANDTAB_SPACING * sample_rate : max_samples_per_loop; if (spacing < minspacing) { spacing = minspacing; } if (max_size > 0) { if (n_channels * spacing > max_size) { spacing = max_size / n_channels; } } if (spacing < minspacing) { fprintf(stderr, "Maximum dither table size %d bytes is too small, must " "at least be %d bytes.\n", max_size, n_channels * sample_rate * minspacing); return false; } dither_randtab_size = n_channels * spacing + 1; pinfo("Dither table size is %d bytes.\n" "Generating random numbers...", dither_randtab_size); tausinit(state, 0); dither_randtab = emallocaligned(dither_randtab_size); for (n = 0; n < dither_randtab_size; n++) { dither_randtab[n] = (int8_t)(tausrand(state) & 0x000000FF); } pinfo("finished.\n"); /* make a map for conversion of integer dither random numbers to floating point ranging from -1.0 to +1.0, plus an offset of +0.5, used to make the sample truncation be mid-tread requantisation */ dither_randmap = emallocaligned(realsize * 511); dither_randmap = &((uint8_t *)dither_randmap)[256 * realsize]; if (realsize == 4) { ((float *)dither_randmap)[-256] = -0.5; for (n = -255; n < 254; n++) { ((float *)dither_randmap)[n] = 0.5 + 1.0 / 255.0 + 1.0 / 255.0 * (float)n; } ((float *)dither_randmap)[254] = 1.5; } else { ((double *)dither_randmap)[-256] = -0.5; for (n = -255; n < 254; n++) { ((double *)dither_randmap)[n] = 0.5 + 1.0 / 255.0 + 1.0 / 255.0 * (double)n; } ((double *)dither_randmap)[254] = 1.5; } for (n = 0; n < n_channels; n++) { dither_states[n] = emalloc(sizeof(struct dither_state)); memset(dither_states[n], 0, sizeof(struct dither_state)); dither_states[n]->randtab_ptr = n * spacing + 1; } return true; } brutefir-1.0o/dither.h0000644000175000017500000000304112752354353014324 0ustar andersanders/* * (c) Copyright 2001, 2003 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _DITHER_H_ #define _DITHER_H_ #include #include "defs.h" #include "dai.h" #include "bfmod.h" #include "convolver.h" struct dither_state { int randtab_ptr; int8_t *randtab; float sf[2]; double sd[2]; }; extern int8_t *dither_randtab; extern int dither_randtab_size; extern void *dither_randmap; static inline void dither_preloop_real2int_hp_tpdf(struct dither_state *state, int samples_per_loop) { if (state->randtab_ptr + samples_per_loop >= dither_randtab_size) { dither_randtab[0] = dither_randtab[state->randtab_ptr - 1]; state->randtab_ptr = 1; } state->randtab = &dither_randtab[state->randtab_ptr]; state->randtab_ptr += samples_per_loop; } #define real_t float #define REALSIZE 4 #define REAL2INT_HP_TPDF_NAME ditherf_real2int_hp_tpdf #define REAL2INT_NO_DITHER_NAME ditherf_real2int_no_dither #include "dither_funs.h" #undef REAL2INT_HP_TPDF_NAME #undef REAL2INT_NO_DITHER_NAME #undef REALSIZE #undef real_t #define real_t double #define REALSIZE 8 #define REAL2INT_HP_TPDF_NAME ditherd_real2int_hp_tpdf #define REAL2INT_NO_DITHER_NAME ditherd_real2int_no_dither #include "dither_funs.h" #undef REAL2INT_HP_TPDF_NAME #undef REAL2INT_NO_DITHER_NAME #undef REALSIZE #undef real_t bool_t dither_init(int n_channels, int sample_rate, int realsize, int max_size, int max_samples_per_loop, struct dither_state *dither_states[]); #endif brutefir-1.0o/dither_funs.h0000644000175000017500000000641512752354353015367 0ustar andersanders/* * (c) Copyright 2001, 2003 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ static inline int32_t REAL2INT_HP_TPDF_NAME(real_t real_sample, real_t rmin, /* (real_t)imin */ real_t rmax, /* (real_t)imax */ int32_t imin, int32_t imax, struct bfoverflow *overflow, struct dither_state *state, int loop_counter) { int32_t sample; real_t dithered_sample; /* apply error feedback, with coefficients {1, -1} (high pass) */ #if REALSIZE == 4 real_sample += state->sf[0] - state->sf[1]; state->sf[1] = state->sf[0]; #else real_sample += state->sd[0] - state->sd[1]; state->sd[1] = state->sd[0]; #endif /* apply dither and offset */ dithered_sample = real_sample + ((real_t *)dither_randmap)[state->randtab[loop_counter] - state->randtab[loop_counter - 1]]; if (dithered_sample < 0) { if (dithered_sample <= rmin) { sample = imin; overflow->n_overflows++; if (real_sample < -overflow->largest) { overflow->largest = (double)-dithered_sample; } } else { sample = (int32_t)dithered_sample; sample--; if (sample < -overflow->intlargest) { overflow->intlargest = -sample; } } } else { if (dithered_sample > rmax) { sample = imax; overflow->n_overflows++; if (real_sample > overflow->largest) { overflow->largest = (double)dithered_sample; } } else { sample = (int32_t)dithered_sample; if (sample > overflow->intlargest) { overflow->intlargest = sample; } } } #if REALSIZE == 4 state->sf[0] = real_sample - (real_t)sample; #else state->sd[0] = real_sample - (real_t)sample; #endif return sample; } static inline int32_t REAL2INT_NO_DITHER_NAME(real_t real_sample, real_t rmin, /* (real_t)imin */ real_t rmax, /* (real_t)imax */ int32_t imin, int32_t imax, struct bfoverflow *overflow) { int32_t sample; /* * Truncation is here made downwards, meaning 3.8 -> 3 and -3.2 -> -4. By * adding 0.5 before our truncation we get a mid-tread requantiser. */ real_sample += 0.5; if (real_sample < 0) { if (real_sample <= rmin) { sample = imin; overflow->n_overflows++; if (real_sample < -overflow->largest) { overflow->largest = (double)-real_sample; } } else { sample = (int32_t)real_sample; sample--; if (sample < -overflow->intlargest) { overflow->intlargest = -sample; } } } else { if (real_sample > rmax) { sample = imax; overflow->n_overflows++; if (real_sample > overflow->largest) { overflow->largest = (double)real_sample; } } else { sample = (int32_t)real_sample; if (sample > overflow->intlargest) { overflow->intlargest = sample; } } } return sample; } brutefir-1.0o/emalloc.c0000644000175000017500000000726312752354353014466 0ustar andersanders/* * (c) Copyright 2001 - 2004, 2009, 2016 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include #include "defs.h" #include "emalloc.h" static void (*exit_func)(int) = NULL; static int exit_status = 1; #define EXIT_PROGRAM if (exit_func == NULL) exit(exit_status); \ else exit_func(exit_status); #define PRINT_MESSAGE \ fprintf(stderr, "Memory allocation failure (%d bytes), " \ "terminating program.\n", (int)size); static void check_avail(size_t alloc) { #ifdef __OS_LINUX__ int memtotal, memfree, buffers, cached, allocsize, fill; FILE *stream; char s[100]; /* on any error, just return ok */ if ((stream = fopen("/proc/meminfo", "rt")) == NULL) { return; } allocsize = alloc / 1024; memtotal = memfree = buffers = cached = 0; s[sizeof(s)-1] = '\0'; while (fgets(s, sizeof(s)-1, stream) != NULL) { if (memtotal == 0 && strstr(s, "MemTotal:") == s) { memtotal = atoi(&s[9]); } else if (memfree == 0 && strstr(s, "MemFree:") == s) { memfree = atoi(&s[8]); } else if (buffers == 0 && strstr(s, "Buffers:") == s) { buffers = atoi(&s[8]); } else if (cached == 0 && strstr(s, "Cached:") == s) { cached = atoi(&s[7]); } if (memtotal != 0 && memfree != 0 && buffers != 0 && cached != 0) { break; } } fclose(stream); if (memtotal == 0 || memfree == 0) { return; } fill = 100 * (memtotal - memfree - buffers - cached + allocsize) / memtotal; if (fill > 90) { fprintf(stderr, "Too much (%d%%) of the available memory is allocated, " "exiting\n", fill); EXIT_PROGRAM; } #endif } void * emallocaligned(size_t size) { int err; void *p = NULL; if (size == 0) { return NULL; } check_avail(size); #if defined(__OS_LINUX__) #if (__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 3) /* use old memalign, posix_memalign may be buggy in these glibc versions */ p = memalign(ALIGNMENT, size < ALIGNMENT ? ALIGNMENT : size); err = !p; #else err = posix_memalign(&p, ALIGNMENT, size < ALIGNMENT ? ALIGNMENT : size); #endif #elif defined(__OS_FREEBSD__) if (size < getpagesize()) { size = getpagesize(); } if (ALIGNMENT > size) { fprintf(stderr, "ALIGNMENT (%d) is larger than the pagesize (%d), aborting.\n" " Recompile with a smaller alignment.\n", (int)ALIGNMENT, (int)getpagesize()); EXIT_PROGRAM; } p = malloc(size); err = !p; #else p = memalign(ALIGNMENT, size < ALIGNMENT ? ALIGNMENT : size); err = !p; #endif if (err != 0) { PRINT_MESSAGE; EXIT_PROGRAM; } return p; } void * emalloc(size_t size) { void *p; if (size == 0) { return NULL; } check_avail(size); p = malloc(size); if (p == NULL) { PRINT_MESSAGE; EXIT_PROGRAM; } return p; } void * erealloc(void *p, size_t size) { p = realloc(p, size); if (size > 0) { check_avail(0); } if (p == NULL) { PRINT_MESSAGE; EXIT_PROGRAM; } return p; } char * estrdup(const char str[]) { size_t size = strlen(str) + 1; char *newstr = emalloc(size); memcpy(newstr, str, size); return newstr; } void emalloc_set_exit_function(void (*exit_function)(int), int status) { exit_func = exit_function; exit_status = status; } void efree(void *p) { free(p); } brutefir-1.0o/emalloc.h0000644000175000017500000000070012752354353014460 0ustar andersanders/* * (c) Copyright 2001, 2004 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _EMALLOC_H_ #define _EMALLOC_H_ #include void * emallocaligned(size_t size); void * emalloc(size_t size); void * erealloc(void *p, size_t size); char * estrdup(const char str[]); void emalloc_set_exit_function(void (*exit_function)(int), int status); void efree(void *p); #endif brutefir-1.0o/fdrw.h0000644000175000017500000000214712752354353014015 0ustar andersanders/* * (c) Copyright 2000, 2001, 2002 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef FDRW_H_ #define FDRW_H_ #include #include #include #include #include #include "defs.h" static inline bool_t readfd(int fd, void *buf, size_t count) { int n, i = 0; do { if ((n = read(fd, &((uint8_t *)buf)[i], count - i)) < 1) { if (n == 0 || errno != EINTR) { fprintf(stderr, "(%d) read from fd %d failed: %s\n", (int)getpid(), fd, strerror(errno)); return false; } continue; } i += n; } while (i != count); return true; } static inline bool_t writefd(int fd, const void *buf, size_t count) { int n, i = 0; do { if ((n = write(fd, &((const uint8_t *)buf)[i], count - i)) < 1) { if (n == 0 || errno != EINTR) { fprintf(stderr, "(%d) write to fd %d failed: %s\n", (int)getpid(), fd, strerror(errno)); return false; } continue; } i += n; } while (i != count); return true; } #endif brutefir-1.0o/fftw_convfuns.h0000644000175000017500000004412512752354353015744 0ustar andersanders/* * (c) Copyright 2001 - 2003, 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ static void MIXNSCALE_NAME(void *input_cbufs[], void *output_cbuf, double scales[], int n_bufs, int mixmode) { int n, i; real_t sscales[n_bufs], **ibufs = (real_t **)input_cbufs; real_t *obuf = (real_t *)output_cbuf; for (n = 0; n < n_bufs; n++) { sscales[n] = (real_t)scales[n]; } switch (mixmode) { case CONVOLVER_MIXMODE_INPUT: switch (n_bufs) { case 1: for (n = 0; n < n_fft >> 1; n += 4) { obuf[(n<<1)+0] = ibufs[0][n+0] * sscales[0]; obuf[(n<<1)+1] = ibufs[0][n+1] * sscales[0]; obuf[(n<<1)+2] = ibufs[0][n+2] * sscales[0]; obuf[(n<<1)+3] = ibufs[0][n+3] * sscales[0]; } obuf[4] = ibufs[0][n_fft >> 1] * sscales[0]; ibufs[0] = &ibufs[0][n_fft]; obuf[5] = ibufs[0][-1] * sscales[0]; obuf[6] = ibufs[0][-2] * sscales[0]; obuf[7] = ibufs[0][-3] * sscales[0]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[(n<<1)+4] = ibufs[0][-n-0] * sscales[0]; obuf[(n<<1)+5] = ibufs[0][-n-1] * sscales[0]; obuf[(n<<1)+6] = ibufs[0][-n-2] * sscales[0]; obuf[(n<<1)+7] = ibufs[0][-n-3] * sscales[0]; } ibufs[0] = &ibufs[0][-n_fft]; break; case 2: for (n = 0; n < n_fft >> 1; n += 4) { obuf[(n<<1)+0] = ibufs[0][n+0] * sscales[0] + ibufs[1][n+0] * sscales[1]; obuf[(n<<1)+1] = ibufs[0][n+1] * sscales[0] + ibufs[1][n+1] * sscales[1]; obuf[(n<<1)+2] = ibufs[0][n+2] * sscales[0] + ibufs[1][n+2] * sscales[1]; obuf[(n<<1)+3] = ibufs[0][n+3] * sscales[0] + ibufs[1][n+3] * sscales[1]; } obuf[4] = ibufs[0][n_fft >> 1] * sscales[0] + ibufs[1][n_fft >> 1] * sscales[1]; ibufs[0] = &ibufs[0][n_fft]; ibufs[1] = &ibufs[1][n_fft]; obuf[5] = ibufs[0][-1] * sscales[0] + ibufs[1][-1] * sscales[1]; obuf[6] = ibufs[0][-2] * sscales[0] + ibufs[1][-2] * sscales[1]; obuf[7] = ibufs[0][-3] * sscales[0] + ibufs[1][-3] * sscales[1]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[(n<<1)+4] = ibufs[0][-n-0] * sscales[0] + ibufs[1][-n-0] * sscales[1]; obuf[(n<<1)+5] = ibufs[0][-n-1] * sscales[0] + ibufs[1][-n-1] * sscales[1]; obuf[(n<<1)+6] = ibufs[0][-n-2] * sscales[0] + ibufs[1][-n-2] * sscales[1]; obuf[(n<<1)+7] = ibufs[0][-n-3] * sscales[0] + ibufs[1][-n-3] * sscales[1]; } ibufs[0] = &ibufs[0][-n_fft]; ibufs[1] = &ibufs[1][-n_fft]; break; case 3: for (n = 0; n < n_fft >> 1; n += 4) { obuf[(n<<1)+0] = ibufs[0][n+0] * sscales[0] + ibufs[1][n+0] * sscales[1] + ibufs[2][n+0] * sscales[2]; obuf[(n<<1)+1] = ibufs[0][n+1] * sscales[0] + ibufs[1][n+1] * sscales[1] + ibufs[2][n+1] * sscales[2]; obuf[(n<<1)+2] = ibufs[0][n+2] * sscales[0] + ibufs[1][n+2] * sscales[1] + ibufs[2][n+2] * sscales[2]; obuf[(n<<1)+3] = ibufs[0][n+3] * sscales[0] + ibufs[1][n+3] * sscales[1] + ibufs[2][n+3] * sscales[2]; } obuf[4] = ibufs[0][n_fft >> 1] * sscales[0] + ibufs[1][n_fft >> 1] * sscales[1] + ibufs[2][n_fft >> 1] * sscales[2]; ibufs[0] = &ibufs[0][n_fft]; ibufs[1] = &ibufs[1][n_fft]; ibufs[2] = &ibufs[2][n_fft]; obuf[5] = ibufs[0][-1] * sscales[0] + ibufs[1][-1] * sscales[1] + ibufs[2][-1] * sscales[2]; obuf[6] = ibufs[0][-2] * sscales[0] + ibufs[1][-2] * sscales[1] + ibufs[2][-2] * sscales[2]; obuf[7] = ibufs[0][-3] * sscales[0] + ibufs[1][-3] * sscales[1] + ibufs[2][-3] * sscales[2]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[(n<<1)+4] = ibufs[0][-n-0] * sscales[0] + ibufs[1][-n-0] * sscales[1] + ibufs[2][-n-0] * sscales[2]; obuf[(n<<1)+5] = ibufs[0][-n-1] * sscales[0] + ibufs[1][-n-1] * sscales[1] + ibufs[2][-n-1] * sscales[2]; obuf[(n<<1)+6] = ibufs[0][-n-2] * sscales[0] + ibufs[1][-n-2] * sscales[1] + ibufs[2][-n-2] * sscales[2]; obuf[(n<<1)+7] = ibufs[0][-n-3] * sscales[0] + ibufs[1][-n-3] * sscales[1] + ibufs[2][-n-3] * sscales[2]; } ibufs[0] = &ibufs[0][-n_fft]; ibufs[1] = &ibufs[1][-n_fft]; ibufs[2] = &ibufs[2][-n_fft]; break; case 4: for (n = 0; n < n_fft >> 1; n += 4) { obuf[(n<<1)+0] = ibufs[0][n+0] * sscales[0] + ibufs[1][n+0] * sscales[1] + ibufs[2][n+0] * sscales[2] + ibufs[3][n+0] * sscales[3]; obuf[(n<<1)+1] = ibufs[0][n+1] * sscales[0] + ibufs[1][n+1] * sscales[1] + ibufs[2][n+1] * sscales[2] + ibufs[3][n+1] * sscales[3]; obuf[(n<<1)+2] = ibufs[0][n+2] * sscales[0] + ibufs[1][n+2] * sscales[1] + ibufs[2][n+2] * sscales[2] + ibufs[3][n+2] * sscales[3]; obuf[(n<<1)+3] = ibufs[0][n+3] * sscales[0] + ibufs[1][n+3] * sscales[1] + ibufs[2][n+3] * sscales[2] + ibufs[3][n+3] * sscales[3]; } obuf[4] = ibufs[0][n_fft >> 1] * sscales[0] + ibufs[1][n_fft >> 1] * sscales[1] + ibufs[2][n_fft >> 1] * sscales[2] + ibufs[3][n_fft >> 1] * sscales[3]; ibufs[0] = &ibufs[0][n_fft]; ibufs[1] = &ibufs[1][n_fft]; ibufs[2] = &ibufs[2][n_fft]; ibufs[3] = &ibufs[3][n_fft]; obuf[5] = ibufs[0][-1] * sscales[0] + ibufs[1][-1] * sscales[1] + ibufs[2][-1] * sscales[2] + ibufs[3][-1] * sscales[3]; obuf[6] = ibufs[0][-2] * sscales[0] + ibufs[1][-2] * sscales[1] + ibufs[2][-2] * sscales[2] + ibufs[3][-2] * sscales[3]; obuf[7] = ibufs[0][-3] * sscales[0] + ibufs[1][-3] * sscales[1] + ibufs[2][-3] * sscales[2] + ibufs[3][-3] * sscales[3]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[(n<<1)+4] = ibufs[0][-n-0] * sscales[0] + ibufs[1][-n-0] * sscales[1] + ibufs[2][-n-0] * sscales[2] + ibufs[3][-n-0] * sscales[3]; obuf[(n<<1)+5] = ibufs[0][-n-1] * sscales[0] + ibufs[1][-n-1] * sscales[1] + ibufs[2][-n-1] * sscales[2] + ibufs[3][-n-1] * sscales[3]; obuf[(n<<1)+6] = ibufs[0][-n-2] * sscales[0] + ibufs[1][-n-2] * sscales[1] + ibufs[2][-n-2] * sscales[2] + ibufs[3][-n-2] * sscales[3]; obuf[(n<<1)+7] = ibufs[0][-n-3] * sscales[0] + ibufs[1][-n-3] * sscales[1] + ibufs[2][-n-3] * sscales[2] + ibufs[3][-n-3] * sscales[3]; } ibufs[0] = &ibufs[0][-n_fft]; ibufs[1] = &ibufs[1][-n_fft]; ibufs[2] = &ibufs[2][-n_fft]; ibufs[3] = &ibufs[3][-n_fft]; break; default: for (n = 0; n < n_fft >> 1; n += 4) { obuf[(n<<1)+0] = ibufs[0][n+0] * sscales[0]; obuf[(n<<1)+1] = ibufs[0][n+1] * sscales[0]; obuf[(n<<1)+2] = ibufs[0][n+2] * sscales[0]; obuf[(n<<1)+3] = ibufs[0][n+3] * sscales[0]; for (i = 1; i < n_bufs; i++) { obuf[(n<<1)+0] += ibufs[i][n+0] * sscales[i]; obuf[(n<<1)+1] += ibufs[i][n+1] * sscales[i]; obuf[(n<<1)+2] += ibufs[i][n+2] * sscales[i]; obuf[(n<<1)+3] += ibufs[i][n+3] * sscales[i]; } } obuf[4] = ibufs[0][n_fft >> 1] * sscales[0]; ibufs[0] = &ibufs[0][n_fft]; obuf[5] = ibufs[0][-1] * sscales[0]; obuf[6] = ibufs[0][-2] * sscales[0]; obuf[7] = ibufs[0][-3] * sscales[0]; for (i = 1; i < n_bufs; i++) { obuf[4] += ibufs[i][n_fft >> 1] * sscales[i]; ibufs[i] = &ibufs[i][n_fft]; obuf[5] += ibufs[i][-1] * sscales[i]; obuf[6] += ibufs[i][-2] * sscales[i]; obuf[7] += ibufs[i][-3] * sscales[i]; } for (n = 4; n < n_fft >> 1; n += 4) { obuf[(n<<1)+4] = ibufs[0][-n-0] * sscales[0]; obuf[(n<<1)+5] = ibufs[0][-n-1] * sscales[0]; obuf[(n<<1)+6] = ibufs[0][-n-2] * sscales[0]; obuf[(n<<1)+7] = ibufs[0][-n-3] * sscales[0]; for (i = 1; i < n_bufs; i++) { obuf[(n<<1)+4] += ibufs[i][-n-0] * sscales[i]; obuf[(n<<1)+5] += ibufs[i][-n-1] * sscales[i]; obuf[(n<<1)+6] += ibufs[i][-n-2] * sscales[i]; obuf[(n<<1)+7] += ibufs[i][-n-3] * sscales[i]; } } for (i = 0; i < n_bufs; i++) { ibufs[i] = &ibufs[i][-n_fft]; } break; } break; case CONVOLVER_MIXMODE_OUTPUT: switch (n_bufs) { case 1: for (n = 0; n < n_fft >> 1; n += 4) { obuf[n+0] = ibufs[0][(n<<1)+0] * sscales[0]; obuf[n+1] = ibufs[0][(n<<1)+1] * sscales[0]; obuf[n+2] = ibufs[0][(n<<1)+2] * sscales[0]; obuf[n+3] = ibufs[0][(n<<1)+3] * sscales[0]; } obuf[n_fft >> 1] = ibufs[0][4] * sscales[0]; obuf = &obuf[n_fft]; obuf[-1] = ibufs[0][5] * sscales[0]; obuf[-2] = ibufs[0][6] * sscales[0]; obuf[-3] = ibufs[0][7] * sscales[0]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[-n-0] = ibufs[0][(n<<1)+4] * sscales[0]; obuf[-n-1] = ibufs[0][(n<<1)+5] * sscales[0]; obuf[-n-2] = ibufs[0][(n<<1)+6] * sscales[0]; obuf[-n-3] = ibufs[0][(n<<1)+7] * sscales[0]; } break; case 2: for (n = 0; n < n_fft >> 1; n += 4) { obuf[n+0] = ibufs[0][(n<<1)+0] * sscales[0] + ibufs[1][(n<<1)+0] * sscales[1]; obuf[n+1] = ibufs[0][(n<<1)+1] * sscales[0] + ibufs[1][(n<<1)+1] * sscales[1]; obuf[n+2] = ibufs[0][(n<<1)+2] * sscales[0] + ibufs[1][(n<<1)+2] * sscales[1]; obuf[n+3] = ibufs[0][(n<<1)+3] * sscales[0] + ibufs[1][(n<<1)+3] * sscales[1]; } obuf[n_fft >> 1] = ibufs[0][4] * sscales[0] + ibufs[1][4] * sscales[1]; obuf = &obuf[n_fft]; obuf[-1] = ibufs[0][5] * sscales[0] + ibufs[1][5] * sscales[1]; obuf[-2] = ibufs[0][6] * sscales[0] + ibufs[1][6] * sscales[1]; obuf[-3] = ibufs[0][7] * sscales[0] + ibufs[1][7] * sscales[1]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[-n-0] = ibufs[0][(n<<1)+4] * sscales[0] + ibufs[1][(n<<1)+4] * sscales[1]; obuf[-n-1] = ibufs[0][(n<<1)+5] * sscales[0] + ibufs[1][(n<<1)+5] * sscales[1]; obuf[-n-2] = ibufs[0][(n<<1)+6] * sscales[0] + ibufs[1][(n<<1)+6] * sscales[1]; obuf[-n-3] = ibufs[0][(n<<1)+7] * sscales[0] + ibufs[1][(n<<1)+7] * sscales[1]; } break; case 3: for (n = 0; n < n_fft >> 1; n += 4) { obuf[n+0] = ibufs[0][(n<<1)+0] * sscales[0] + ibufs[1][(n<<1)+0] * sscales[1] + ibufs[2][(n<<1)+0] * sscales[2]; obuf[n+1] = ibufs[0][(n<<1)+1] * sscales[0] + ibufs[1][(n<<1)+1] * sscales[1] + ibufs[2][(n<<1)+1] * sscales[2]; obuf[n+2] = ibufs[0][(n<<1)+2] * sscales[0] + ibufs[1][(n<<1)+2] * sscales[1] + ibufs[2][(n<<1)+2] * sscales[2]; obuf[n+3] = ibufs[0][(n<<1)+3] * sscales[0] + ibufs[1][(n<<1)+3] * sscales[1] + ibufs[2][(n<<1)+3] * sscales[2]; } obuf[n_fft >> 1] = ibufs[0][4] * sscales[0] + ibufs[1][4] * sscales[1] + ibufs[2][4] * sscales[2]; obuf = &obuf[n_fft]; obuf[-1] = ibufs[0][5] * sscales[0] + ibufs[1][5] * sscales[1] + ibufs[2][5] * sscales[2]; obuf[-2] = ibufs[0][6] * sscales[0] + ibufs[1][6] * sscales[1] + ibufs[2][6] * sscales[2]; obuf[-3] = ibufs[0][7] * sscales[0] + ibufs[1][7] * sscales[1] + ibufs[2][7] * sscales[2]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[-n-0] = ibufs[0][(n<<1)+4] * sscales[0] + ibufs[1][(n<<1)+4] * sscales[1] + ibufs[2][(n<<1)+4] * sscales[2]; obuf[-n-1] = ibufs[0][(n<<1)+5] * sscales[0] + ibufs[1][(n<<1)+5] * sscales[1] + ibufs[2][(n<<1)+5] * sscales[2]; obuf[-n-2] = ibufs[0][(n<<1)+6] * sscales[0] + ibufs[1][(n<<1)+6] * sscales[1] + ibufs[2][(n<<1)+6] * sscales[2]; obuf[-n-3] = ibufs[0][(n<<1)+7] * sscales[0] + ibufs[1][(n<<1)+7] * sscales[1] + ibufs[2][(n<<1)+7] * sscales[2]; } break; case 4: for (n = 0; n < n_fft >> 1; n += 4) { obuf[n+0] = ibufs[0][(n<<1)+0] * sscales[0] + ibufs[1][(n<<1)+0] * sscales[1] + ibufs[2][(n<<1)+0] * sscales[2] + ibufs[3][(n<<1)+0] * sscales[3]; obuf[n+1] = ibufs[0][(n<<1)+1] * sscales[0] + ibufs[1][(n<<1)+1] * sscales[1] + ibufs[2][(n<<1)+1] * sscales[2] + ibufs[3][(n<<1)+1] * sscales[3]; obuf[n+2] = ibufs[0][(n<<1)+2] * sscales[0] + ibufs[1][(n<<1)+2] * sscales[1] + ibufs[2][(n<<1)+2] * sscales[2] + ibufs[3][(n<<1)+2] * sscales[3]; obuf[n+3] = ibufs[0][(n<<1)+3] * sscales[0] + ibufs[1][(n<<1)+3] * sscales[1] + ibufs[2][(n<<1)+3] * sscales[2] + ibufs[3][(n<<1)+3] * sscales[3]; } obuf[n_fft >> 1] = ibufs[0][4] * sscales[0] + ibufs[1][4] * sscales[1] + ibufs[2][4] * sscales[2] + ibufs[3][4] * sscales[3]; obuf = &obuf[n_fft]; obuf[-1] = ibufs[0][5] * sscales[0] + ibufs[1][5] * sscales[1] + ibufs[2][5] * sscales[2] + ibufs[3][5] * sscales[3]; obuf[-2] = ibufs[0][6] * sscales[0] + ibufs[1][6] * sscales[1] + ibufs[2][6] * sscales[2] + ibufs[3][6] * sscales[3]; obuf[-3] = ibufs[0][7] * sscales[0] + ibufs[1][7] * sscales[1] + ibufs[2][7] * sscales[2] + ibufs[3][7] * sscales[3]; for (n = 4; n < n_fft >> 1; n += 4) { obuf[-n-0] = ibufs[0][(n<<1)+4] * sscales[0] + ibufs[1][(n<<1)+4] * sscales[1] + ibufs[2][(n<<1)+4] * sscales[2] + ibufs[3][(n<<1)+4] * sscales[3]; obuf[-n-1] = ibufs[0][(n<<1)+5] * sscales[0] + ibufs[1][(n<<1)+5] * sscales[1] + ibufs[2][(n<<1)+5] * sscales[2] + ibufs[3][(n<<1)+5] * sscales[3]; obuf[-n-2] = ibufs[0][(n<<1)+6] * sscales[0] + ibufs[1][(n<<1)+6] * sscales[1] + ibufs[2][(n<<1)+6] * sscales[2] + ibufs[3][(n<<1)+6] * sscales[3]; obuf[-n-3] = ibufs[0][(n<<1)+7] * sscales[0] + ibufs[1][(n<<1)+7] * sscales[1] + ibufs[2][(n<<1)+7] * sscales[2] + ibufs[3][(n<<1)+7] * sscales[3]; } break; default: for (n = 0; n < n_fft >> 1; n += 4) { obuf[n+0] = ibufs[0][(n<<1)+0] * sscales[0]; obuf[n+1] = ibufs[0][(n<<1)+1] * sscales[0]; obuf[n+2] = ibufs[0][(n<<1)+2] * sscales[0]; obuf[n+3] = ibufs[0][(n<<1)+3] * sscales[0]; for (i = 1; i < n_bufs; i++) { obuf[n+0] += ibufs[i][(n<<1)+0] * sscales[i]; obuf[n+1] += ibufs[i][(n<<1)+1] * sscales[i]; obuf[n+2] += ibufs[i][(n<<1)+2] * sscales[i]; obuf[n+3] += ibufs[i][(n<<1)+3] * sscales[i]; } } obuf[n_fft >> 1] = ibufs[0][4] * sscales[0]; for (i = 1; i < n_bufs; i++) { obuf[n_fft >> 1] += ibufs[i][4] * sscales[i]; } obuf = &obuf[n_fft]; obuf[-1] = ibufs[0][5] * sscales[0]; obuf[-2] = ibufs[0][6] * sscales[0]; obuf[-3] = ibufs[0][7] * sscales[0]; for (i = 1; i < n_bufs; i++) { obuf[-1] += ibufs[i][5] * sscales[i]; obuf[-2] += ibufs[i][6] * sscales[i]; obuf[-3] += ibufs[i][7] * sscales[i]; } for (n = 4; n < n_fft >> 1; n += 4) { obuf[-n-0] = ibufs[0][(n<<1)+4] * sscales[0]; obuf[-n-1] = ibufs[0][(n<<1)+5] * sscales[0]; obuf[-n-2] = ibufs[0][(n<<1)+6] * sscales[0]; obuf[-n-3] = ibufs[0][(n<<1)+7] * sscales[0]; for (i = 1; i < n_bufs; i++) { obuf[-n-0] += ibufs[i][(n<<1)+4] * sscales[i]; obuf[-n-1] += ibufs[i][(n<<1)+5] * sscales[i]; obuf[-n-2] += ibufs[i][(n<<1)+6] * sscales[i]; obuf[-n-3] += ibufs[i][(n<<1)+7] * sscales[i]; } } break; } break; default: fprintf(stderr, "Invalid mixmode: %d.\n", mixmode); bf_exit(BF_EXIT_OTHER); break; } } static void CONVOLVE_INPLACE_NAME(void *cbuf, void *coeffs) { int n; real_t a[4]; real_t *b = (real_t *)cbuf; real_t *c = (real_t *)coeffs; real_t d1s, d2s; d1s = b[0] * c[0]; d2s = b[4] * c[4]; for (n = 0; n < n_fft; n += 8) { a[0] = b[n+0]; a[1] = b[n+1]; a[2] = b[n+2]; a[3] = b[n+3]; b[n+0] = a[0] * c[n+0] - b[n+4] * c[n+4]; b[n+1] = a[1] * c[n+1] - b[n+5] * c[n+5]; b[n+2] = a[2] * c[n+2] - b[n+6] * c[n+6]; b[n+3] = a[3] * c[n+3] - b[n+7] * c[n+7]; b[n+4] = a[0] * c[n+4] + b[n+4] * c[n+0]; b[n+5] = a[1] * c[n+5] + b[n+5] * c[n+1]; b[n+6] = a[2] * c[n+6] + b[n+6] * c[n+2]; b[n+7] = a[3] * c[n+7] + b[n+7] * c[n+3]; } b[0] = d1s; b[4] = d2s; } static void CONVOLVE_NAME(void *input_cbuf, void *coeffs, void *output_cbuf) { int n; real_t *b = (real_t *)input_cbuf; real_t *c = (real_t *)coeffs; real_t *d = (real_t *)output_cbuf; real_t d1s, d2s; d1s = b[0] * c[0]; d2s = b[4] * c[4]; for (n = 0; n < n_fft; n += 8) { d[n+0] = b[n+0] * c[n+0] - b[n+4] * c[n+4]; d[n+1] = b[n+1] * c[n+1] - b[n+5] * c[n+5]; d[n+2] = b[n+2] * c[n+2] - b[n+6] * c[n+6]; d[n+3] = b[n+3] * c[n+3] - b[n+7] * c[n+7]; d[n+4] = b[n+0] * c[n+4] + b[n+4] * c[n+0]; d[n+5] = b[n+1] * c[n+5] + b[n+5] * c[n+1]; d[n+6] = b[n+2] * c[n+6] + b[n+6] * c[n+2]; d[n+7] = b[n+3] * c[n+7] + b[n+7] * c[n+3]; } d[0] = d1s; d[4] = d2s; } static void CONVOLVE_ADD_NAME(void *input_cbuf, void *coeffs, void *output_cbuf) { real_t *b = (real_t *)input_cbuf; real_t *c = (real_t *)coeffs; real_t *d = (real_t *)output_cbuf; real_t d1s, d2s; int n; d1s = d[0] + b[0] * c[0]; d2s = d[4] + b[4] * c[4]; for (n = 0; n < n_fft; n += 8) { d[n+0] += b[n+0] * c[n+0] - b[n+4] * c[n+4]; d[n+1] += b[n+1] * c[n+1] - b[n+5] * c[n+5]; d[n+2] += b[n+2] * c[n+2] - b[n+6] * c[n+6]; d[n+3] += b[n+3] * c[n+3] - b[n+7] * c[n+7]; d[n+4] += b[n+0] * c[n+4] + b[n+4] * c[n+0]; d[n+5] += b[n+1] * c[n+5] + b[n+5] * c[n+1]; d[n+6] += b[n+2] * c[n+6] + b[n+6] * c[n+2]; d[n+7] += b[n+3] * c[n+7] + b[n+7] * c[n+3]; } d[0] = d1s; d[4] = d2s; } static void DIRAC_CONVOLVE_INPLACE_NAME(void *cbuf) { real_t fraction = 1.0 / (real_t)n_fft; int n; for (n = 0; n < n_fft; n += 4) { ((real_t *)cbuf)[n+0] *= +fraction; ((real_t *)cbuf)[n+1] *= -fraction; ((real_t *)cbuf)[n+2] *= +fraction; ((real_t *)cbuf)[n+3] *= -fraction; } } static void DIRAC_CONVOLVE_NAME(void *input_cbuf, void *output_cbuf) { real_t fraction = 1.0 / (real_t)n_fft; int n; for (n = 0; n < n_fft; n += 4) { ((real_t *)output_cbuf)[n+0] = ((real_t *)input_cbuf)[n+0] * +fraction; ((real_t *)output_cbuf)[n+1] = ((real_t *)input_cbuf)[n+1] * -fraction; ((real_t *)output_cbuf)[n+2] = ((real_t *)input_cbuf)[n+2] * +fraction; ((real_t *)output_cbuf)[n+3] = ((real_t *)input_cbuf)[n+3] * -fraction; } } brutefir-1.0o/fftw_convolver.c0000664000175000017500000006126012752354353016114 0ustar andersanders/* * (c) Copyright 2001 - 2004, 2006, 2009, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include "defs.h" #include #include #include #include #include #include #ifdef __OS_SUNOS__ #include #endif #include #include "convolver.h" #include "log2.h" #include "emalloc.h" #include "bfrun.h" #include "dai.h" #include "bit.h" #include "swap.h" #include "dither.h" #include "asmprot.h" #include "pinfo.h" #include "inout.h" #include "timestamp.h" #include "numunion.h" #define ifftplans fftplan_table[1][0] #define ifftplans_inplace fftplan_table[1][1] #define fftplans fftplan_table[0][0] #define fftplans_inplace fftplan_table[0][1] static void *fftplan_table[2][2][32]; static uint32_t fftplan_generated[2][2]; static int realsize = 0; static int n_fft, n_fft2, fft_order; #define OPT_CODE_GCC 0 #define OPT_CODE_SSE 1 #define OPT_CODE_SSE2 2 static int opt_code; #if defined(__ARCH_IA32__) || defined(__ARCH_X86_64__) static inline void cpuid(uint32_t op, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { asm volatile ("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "a" (op)); } static void decide_opt_code(void) { uint32_t level, junk, cap; numunion_t vendor[2]; opt_code = OPT_CODE_GCC; vendor[1].u8[4] = '\0'; cpuid(0x00000000, &level, &vendor->u32[0], &vendor->u32[2], &vendor->u32[1]); if (strcmp((char *)vendor->u8, "GenuineIntel") == 0 && level >= 0x00000001) { cpuid(0x00000001, &junk, &junk, &junk, &cap); if (realsize == 8) { if ((cap & (1 << 26)) != 0) { opt_code = OPT_CODE_SSE2; pinfo("SSE2 capability detected -- optimisation enabled.\n"); } } else { if ((cap & (1 << 25)) != 0) { opt_code = OPT_CODE_SSE; pinfo("SSE capability detected -- optimisation enabled.\n"); } } } } #else static void decide_opt_code(void) { opt_code = OPT_CODE_GCC; } #endif static void * create_fft_plan(int length, bool_t inplace, bool_t invert) { void *plan, *buf[2]; buf[0] = emallocaligned(length * realsize); memset(buf[0], 0, length * realsize); buf[1] = buf[0]; if (!inplace) { buf[1] = emallocaligned(length * realsize); memset(buf[1], 0, length * realsize); } if (realsize == 4) { plan = fftwf_plan_r2r_1d(length, buf[0], buf[1], invert ? FFTW_HC2R : FFTW_R2HC, FFTW_MEASURE); } else { plan = fftw_plan_r2r_1d(length, buf[0], buf[1], invert ? FFTW_HC2R : FFTW_R2HC, FFTW_MEASURE); } efree(buf[0]); if (!inplace) { efree(buf[1]); } return plan; } #define real_t float #define REALSIZE 4 #define RAW2REAL_NAME raw2realf #define MIXNSCALE_NAME mixnscalef #define CONVOLVE_INPLACE_NAME convolve_inplacef #define CONVOLVE_NAME convolvef #define CONVOLVE_ADD_NAME convolve_addf #define DIRAC_CONVOLVE_INPLACE_NAME dirac_convolve_inplacef #define DIRAC_CONVOLVE_NAME dirac_convolvef #include "raw2real.h" #include "fftw_convfuns.h" #undef real_t #undef REALSIZE #undef RAW2REAL_NAME #undef MIXNSCALE_NAME #undef CONVOLVE_INPLACE_NAME #undef CONVOLVE_NAME #undef CONVOLVE_ADD_NAME #undef DIRAC_CONVOLVE_INPLACE_NAME #undef DIRAC_CONVOLVE_NAME #define real_t double #define REALSIZE 8 #define RAW2REAL_NAME raw2reald #define MIXNSCALE_NAME mixnscaled #define CONVOLVE_INPLACE_NAME convolve_inplaced #define CONVOLVE_NAME convolved #define CONVOLVE_ADD_NAME convolve_addd #define DIRAC_CONVOLVE_INPLACE_NAME dirac_convolve_inplaced #define DIRAC_CONVOLVE_NAME dirac_convolved #include "raw2real.h" #include "fftw_convfuns.h" #undef real_t #undef REALSIZE #undef RAW2REAL_NAME #undef MIXNSCALE_NAME #undef CONVOLVE_INPLACE_NAME #undef CONVOLVE_NAME #undef CONVOLVE_ADD_NAME #undef DIRAC_CONVOLVE_INPLACE_NAME #undef DIRAC_CONVOLVE_NAME void convolver_raw2cbuf(void *rawbuf, void *cbuf, void *next_cbuf, struct buffer_format *bf, void (*postprocess)(void *realbuf, int n_samples, void *arg), void *pp_arg) { if (realsize == 4) { raw2realf(next_cbuf, (void *)&((uint8_t *)rawbuf)[bf->byte_offset], bf->sf.bytes, bf->sf.isfloat, bf->sample_spacing, bf->sf.swap, n_fft2); } else { raw2reald(next_cbuf, (void *)&((uint8_t *)rawbuf)[bf->byte_offset], bf->sf.bytes, bf->sf.isfloat, bf->sample_spacing, bf->sf.swap, n_fft2); } if (postprocess != NULL) { postprocess(next_cbuf, n_fft2, pp_arg); } memcpy(&((uint8_t *)cbuf)[n_fft2 * realsize], next_cbuf, n_fft2 * realsize); } void convolver_time2freq(void *input_cbuf, void *output_cbuf) { void *fftplan; if (input_cbuf == output_cbuf) { fftplan = fftplans_inplace[fft_order]; } else { fftplan = fftplans[fft_order]; } if (realsize == 4) { fftwf_execute_r2r((const fftwf_plan)fftplan, (float *)input_cbuf, (float *)output_cbuf); } else { fftw_execute_r2r((const fftw_plan)fftplan, (double *)input_cbuf, (double *)output_cbuf); } } void convolver_mixnscale(void *input_cbufs[], void *output_cbuf, double scales[], int n_bufs, int mixmode) { if (realsize == 4) { mixnscalef(input_cbufs, output_cbuf, scales, n_bufs, mixmode); } else { mixnscaled(input_cbufs, output_cbuf, scales, n_bufs, mixmode); } } void convolver_convolve_inplace(void *cbuf, void *coeffs) { if (realsize == 4) { convolve_inplacef(cbuf, coeffs); } else { convolve_inplaced(cbuf, coeffs); } } void convolver_convolve(void *input_cbuf, void *coeffs, void *output_cbuf) { if (realsize == 4) { convolvef(input_cbuf, coeffs, output_cbuf); } else { convolved(input_cbuf, coeffs, output_cbuf); } } void convolver_convolve_add(void *input_cbuf, void *coeffs, void *output_cbuf) { /* #define real_t double real_t *_b = (real_t *)emallocaligned(n_fft * sizeof(real_t)); real_t *_c = (real_t *)emallocaligned(n_fft * sizeof(real_t)); real_t *_d = (real_t *)emallocaligned(n_fft * sizeof(real_t)); memcpy(_b, input_cbuf, n_fft * sizeof(real_t)); memcpy(_c, coeffs, n_fft * sizeof(real_t)); memcpy(_d, output_cbuf, n_fft * sizeof(real_t)); */ switch (opt_code) { #ifdef __SSE__ case OPT_CODE_SSE: convolver_sse_convolve_add(input_cbuf, coeffs, output_cbuf, n_fft >> 3); break; #ifdef __SSE2__ case OPT_CODE_SSE2: convolver_sse2_convolve_add(input_cbuf, coeffs, output_cbuf, n_fft >> 3); break; #endif #endif default: case OPT_CODE_GCC: if (realsize == 4) { convolve_addf(input_cbuf, coeffs, output_cbuf); } else { convolve_addd(input_cbuf, coeffs, output_cbuf); } } /* { real_t d1s, d2s, err, e; int n; d1s = _d[0] + _b[0] * _c[0]; d2s = _d[4] + _b[4] * _c[4]; for (n = 0; n < n_fft; n += 8) { _d[n+0] += _b[n+0] * _c[n+0] - _b[n+4] * _c[n+4]; _d[n+1] += _b[n+1] * _c[n+1] - _b[n+5] * _c[n+5]; _d[n+2] += _b[n+2] * _c[n+2] - _b[n+6] * _c[n+6]; _d[n+3] += _b[n+3] * _c[n+3] - _b[n+7] * _c[n+7]; _d[n+4] += _b[n+0] * _c[n+4] + _b[n+4] * _c[n+0]; _d[n+5] += _b[n+1] * _c[n+5] + _b[n+5] * _c[n+1]; _d[n+6] += _b[n+2] * _c[n+6] + _b[n+6] * _c[n+2]; _d[n+7] += _b[n+3] * _c[n+7] + _b[n+7] * _c[n+3]; } _d[0] = d1s; _d[4] = d2s; err = 0; for (n = 0; n < n_fft; n++) { e = (((real_t *)output_cbuf)[n] - _d[n]); if (e < 0.0) { err -= e; } else { err += e; } } if (err > 0.0) { fprintf(stderr, "err: %.10e\n", err); } efree(_b); efree(_c); efree(_d); #undef real_t } */ } void convolver_crossfade_inplace(void *input_cbuf, void *crossfade_cbuf, void *buffer_cbuf) { void *buf1, *buf2; double scale, d; float f; int n; buf1 = buffer_cbuf; buf2 = &((uint8_t *)buffer_cbuf)[n_fft * realsize]; scale = 1.0; convolver_mixnscale(&crossfade_cbuf, buffer_cbuf, &scale, 1, CONVOLVER_MIXMODE_OUTPUT); convolver_freq2time(buffer_cbuf, crossfade_cbuf); convolver_mixnscale(&input_cbuf, buffer_cbuf, &scale, 1, CONVOLVER_MIXMODE_OUTPUT); convolver_freq2time(buffer_cbuf, buffer_cbuf); if (realsize == 4) { f = 1.0 / (float)(n_fft2 - 1); for (n = 0; n < n_fft2; n++) { ((float *)buffer_cbuf)[n] = ((float *)crossfade_cbuf)[n] * (1.0 - f * (float)n) + ((float *)buffer_cbuf)[n] * f * (float)n; } } else { d = 1.0 / (double)(n_fft2 - 1); for (n = 0; n < n_fft2; n++) { ((double *)buf1)[n] = ((double *)buf1)[n] * (1.0 - d * (double)n) + ((double *)buf2)[n] * d * (double)n; } } convolver_time2freq(buffer_cbuf, buffer_cbuf); scale = 1.0 / (double)n_fft; convolver_mixnscale(&buffer_cbuf, input_cbuf, &scale, 1, CONVOLVER_MIXMODE_INPUT); } void convolver_dirac_convolve_inplace(void *cbuf) { if (realsize == 4) { dirac_convolve_inplacef(cbuf); } else { dirac_convolve_inplaced(cbuf); } } void convolver_dirac_convolve(void *input_cbuf, void *output_cbuf) { if (realsize == 4) { dirac_convolvef(input_cbuf, output_cbuf); } else { dirac_convolved(input_cbuf, output_cbuf); } } void convolver_freq2time(void *input_cbuf, void *output_cbuf) { void *ifftplan; if (input_cbuf == output_cbuf) { ifftplan = ifftplans_inplace[fft_order]; } else { ifftplan = ifftplans[fft_order]; } if (realsize == 4) { fftwf_execute_r2r((const fftwf_plan)ifftplan, (float *)input_cbuf, (float *)output_cbuf); } else { fftw_execute_r2r((const fftw_plan)ifftplan, (double *)input_cbuf, (double *)output_cbuf); } } void convolver_convolve_eval(void *input_cbuf, void *buffer_cbuf, /* 1.5 x size */ void *output_cbuf) { if (realsize == 4) { fftwf_execute_r2r((const fftwf_plan)ifftplans[fft_order], (float *)input_cbuf, &((float *)buffer_cbuf)[n_fft2]); fftwf_execute_r2r((const fftwf_plan)fftplans[fft_order], (float *)buffer_cbuf, (float *)output_cbuf); } else { fftw_execute_r2r((const fftw_plan)ifftplans[fft_order], (double *)input_cbuf, &((double *)buffer_cbuf)[n_fft2]); fftw_execute_r2r((const fftw_plan)fftplans[fft_order], (double *)buffer_cbuf, (double *)output_cbuf); } memcpy(buffer_cbuf, &((uint8_t *)buffer_cbuf)[n_fft2 * realsize], n_fft2 * realsize); } #define real_t float #define REALSIZE 4 #define REAL2RAW_NAME real2rawf_hp_tpdf #define REAL2INT_CALL ditherf_real2int_hp_tpdf(((float *)realbuf)[n], rmin, \ rmax, imin, imax, overflow, \ dither_state, n) #define REAL2RAW_EXTRA_PARAMS , struct dither_state *dither_state #include "real2raw.h" #undef REAL2RAW_NAME #undef REAL2INT_CALL #undef REAL2RAW_EXTRA_PARAMS #define REAL2RAW_NAME real2rawf_no_dither #define REAL2INT_CALL ditherd_real2int_no_dither(((float *)realbuf)[n], rmin, \ rmax, imin, imax, overflow) #define REAL2RAW_EXTRA_PARAMS #include "real2raw.h" #undef REAL2RAW_NAME #undef REAL2INT_CALL #undef REAL2RAW_EXTRA_PARAMS #undef REALSIZE #undef real_t #define real_t double #define REALSIZE 8 #define REAL2RAW_NAME real2rawd_hp_tpdf #define REAL2INT_CALL ditherd_real2int_hp_tpdf(((double *)realbuf)[n], rmin, \ rmax, imin, imax, overflow, \ dither_state, n) #define REAL2RAW_EXTRA_PARAMS , struct dither_state *dither_state #include "real2raw.h" #undef REAL2RAW_NAME #undef REAL2INT_CALL #undef REAL2RAW_EXTRA_PARAMS #define REAL2RAW_NAME real2rawd_no_dither #define REAL2INT_CALL ditherd_real2int_no_dither(((double *)realbuf)[n], rmin, \ rmax, imin, imax, overflow) #define REAL2RAW_EXTRA_PARAMS #include "real2raw.h" #undef REAL2RAW_NAME #undef REAL2INT_CALL #undef REAL2RAW_EXTRA_PARAMS #undef REALSIZE #undef real_t void convolver_cbuf2raw(void *cbuf, void *outbuf, struct buffer_format *bf, bool_t apply_dither, void *dither_state, struct bfoverflow *overflow) { if (realsize == 4) { if (apply_dither && !bf->sf.isfloat) { dither_preloop_real2int_hp_tpdf(dither_state, n_fft2); real2rawf_hp_tpdf((void *)&((uint8_t *)outbuf)[bf->byte_offset], cbuf, bf->sf.sbytes << 3, bf->sf.bytes, bf->sf.isfloat, bf->sample_spacing, bf->sf.swap, n_fft2, overflow, dither_state); } else { real2rawf_no_dither((void *)&((uint8_t *)outbuf)[bf->byte_offset], cbuf, bf->sf.sbytes << 3, bf->sf.bytes, bf->sf.isfloat, bf->sample_spacing, bf->sf.swap, n_fft2, overflow); } } else { if (apply_dither && !bf->sf.isfloat) { dither_preloop_real2int_hp_tpdf(dither_state, n_fft2); real2rawd_hp_tpdf((void *)&((uint8_t *)outbuf)[bf->byte_offset], cbuf, bf->sf.sbytes << 3, bf->sf.bytes, bf->sf.isfloat, bf->sample_spacing, bf->sf.swap, n_fft2, overflow, dither_state); } else { real2rawd_no_dither((void *)&((uint8_t *)outbuf)[bf->byte_offset], cbuf, bf->sf.sbytes << 3, bf->sf.bytes, bf->sf.isfloat, bf->sample_spacing, bf->sf.swap, n_fft2, overflow); } } } int convolver_cbufsize(void) { return n_fft * realsize; } void * convolver_coeffs2cbuf(void *coeffs, int n_coeffs, double scale, void *optional_dest) { void *rcoeffs, *coeffs_data; int n, len; len = (n_coeffs > n_fft2) ? n_fft2 : n_coeffs; rcoeffs = emallocaligned(n_fft * realsize); memset(rcoeffs, 0, n_fft * realsize); if (realsize == 4) { for (n = 0; n < len; n++) { ((float *)rcoeffs)[n_fft2 + n] = ((float *)coeffs)[n] * (float)scale; if (!finite((double)((float *)rcoeffs)[n_fft2 + n])) { fprintf(stderr, "NaN or Inf value among coefficients.\n"); return NULL; } } fftwf_execute_r2r((const fftwf_plan)fftplans_inplace[fft_order], (float *)rcoeffs, (float *)rcoeffs); } else { for (n = 0; n < len; n++) { ((double *)rcoeffs)[n_fft2 + n] = ((double *)coeffs)[n] * scale; if (!finite(((double *)rcoeffs)[n_fft2 + n])) { fprintf(stderr, "NaN or Inf value among coefficients.\n"); return NULL; } } fftw_execute_r2r((const fftw_plan)fftplans_inplace[fft_order], (double *)rcoeffs, (double *)rcoeffs); } scale = 1.0 / (double)n_fft; if (optional_dest != NULL) { coeffs_data = optional_dest; } else { coeffs_data = emallocaligned(n_fft * realsize); } convolver_mixnscale(&rcoeffs, coeffs_data, &scale, 1, CONVOLVER_MIXMODE_INPUT); efree(rcoeffs); return coeffs_data; } void convolver_runtime_coeffs2cbuf(void *src, /* nfft / 2 */ void *dest) /* nfft */ { static void *tmp = NULL; double scale; if (tmp == NULL) { tmp = emallocaligned(n_fft * realsize); } memset(dest, 0, n_fft2 * realsize); memcpy(&((uint8_t *)dest)[n_fft2 * realsize], src, n_fft2 * realsize); if (realsize == 4) { fftwf_execute_r2r((const fftwf_plan)fftplans[fft_order], (float *)dest, (float *)tmp); } else { fftw_execute_r2r((const fftw_plan)fftplans[fft_order], (double *)dest, (double *)tmp); } scale = 1.0 / (double)n_fft; convolver_mixnscale(&tmp, dest, &scale, 1, CONVOLVER_MIXMODE_INPUT); } bool_t convolver_verify_cbuf(void *cbufs[], int n_cbufs) { int n, i; for (n = 0; n < n_cbufs; n++) { if (realsize == 4) { for (i = 0; i < n_fft; i++) { if (!finite((double)((float *)cbufs[n])[i])) { fprintf(stderr, "NaN or Inf value among coefficients.\n"); return false; } } } else { for (i = 0; i < n_fft; i++) { if (!finite(((double *)cbufs[n])[i])) { fprintf(stderr, "NaN or Inf value among coefficients.\n"); return false; } } } } return true; } void convolver_debug_dump_cbuf(const char filename[], void *cbufs[], int n_cbufs) { void *coeffs; double scale; FILE *stream; int n, i; if ((stream = fopen(filename, "wt")) == NULL) { fprintf(stderr, "Could not open \"%s\" for writing: %s", filename, strerror(errno)); return; } coeffs = emallocaligned(n_fft * realsize); scale = 1.0; for (n = 0; n < n_cbufs; n++) { convolver_mixnscale(&cbufs[n], coeffs, &scale, 1, CONVOLVER_MIXMODE_OUTPUT); if (realsize == 4) { fftwf_execute_r2r((const fftwf_plan)ifftplans_inplace[fft_order], (float *)coeffs, (float *)coeffs); for (i = 0; i < n_fft2; i++) { fprintf(stream, "%.16e\n", ((float *)coeffs)[n_fft2 + i]); } } else { fftw_execute_r2r((const fftw_plan)ifftplans_inplace[fft_order], (double *)coeffs, (double *)coeffs); for (i = 0; i < n_fft2; i++) { fprintf(stream, "%.16e\n", ((double *)coeffs)[n_fft2 + i]); } } } efree(coeffs); fclose(stream); } void * convolver_fftplan(int order, int invert, int inplace) { invert = !!invert; inplace = !!inplace; if (!bit_isset(&fftplan_generated[invert][inplace], order)) { pinfo("Creating %s%sFFTW plan of size %d using wisdom...", invert ? "inverse " : "forward ", inplace ? "inplace " : "", 1 << order); fftplan_table[invert][inplace][order] = create_fft_plan(1 << order, inplace, invert); pinfo("finished\n"); bit_set(&fftplan_generated[invert][inplace], order); } return fftplan_table[invert][inplace][order]; } struct _td_conv_t_ { void *fftplan; void *ifftplan; void *coeffs; int blocklen; }; int convolver_td_block_length(int n_coeffs) { if (n_coeffs < 1) { return -1; } return 1 << log2_roof(n_coeffs); } td_conv_t * convolver_td_new(void *coeffs, int n_coeffs) { int blocklen, n; td_conv_t *tdc; double scaled; float scalef; if ((blocklen = convolver_td_block_length(n_coeffs)) == -1) { return NULL; } tdc = emalloc(sizeof(td_conv_t)); memset(tdc, 0, sizeof(td_conv_t)); tdc->fftplan = convolver_fftplan(log2_get(blocklen) + 1, false, true); tdc->ifftplan = convolver_fftplan(log2_get(blocklen) + 1, true, true); tdc->coeffs = emallocaligned(2 * blocklen * realsize); tdc->blocklen = blocklen; memset(tdc->coeffs, 0, blocklen * realsize); memset(&((uint8_t *)tdc->coeffs)[(blocklen + n_coeffs) * realsize], 0, (blocklen - n_coeffs) * realsize); memcpy(&((uint8_t *)tdc->coeffs)[blocklen * realsize], coeffs, n_coeffs * realsize); if (realsize == 4) { fftwf_execute_r2r(tdc->fftplan, tdc->coeffs, tdc->coeffs); scalef = 1.0 / (float)(blocklen << 1); for (n = 0; n < blocklen << 1; n++) { ((float *)tdc->coeffs)[n] *= scalef; } } else { fftw_execute_r2r(tdc->fftplan, tdc->coeffs, tdc->coeffs); scaled = 1.0 / (double)(blocklen << 1); for (n = 0; n < blocklen << 1; n++) { ((double *)tdc->coeffs)[n] *= scaled; } } return tdc; } static void convolve_inplace_ordered(void *cbuf, void *coeffs, int size) { int n, size2 = size >> 1; if (realsize == 4) { float a, *b = (float *)cbuf, *c = (float *)coeffs; b[0] *= c[0]; for (n = 1; n < size2; n++) { a = b[n]; b[n] = a * c[n] - b[size - n] * c[size - n]; b[size - n] = a * c[size - n] + b[size - n] * c[n]; } b[size2] *= c[size2]; } else { double a, *b = (double *)cbuf, *c = (double *)coeffs; b[0] *= c[0]; for (n = 1; n < size2; n++) { a = b[n]; b[n] = a * c[n] - b[size - n] * c[size - n]; b[size - n] = a * c[size - n] + b[size - n] * c[n]; } b[size2] *= c[size2]; } } void convolver_td_convolve(td_conv_t *tdc, void *overlap_block) { if (realsize == 4) { fftwf_execute_r2r(tdc->fftplan, overlap_block, overlap_block); convolve_inplace_ordered(overlap_block, tdc->coeffs, tdc->blocklen << 1); fftwf_execute_r2r(tdc->ifftplan, overlap_block, overlap_block); } else { fftw_execute_r2r(tdc->fftplan, overlap_block, overlap_block); convolve_inplace_ordered(overlap_block, tdc->coeffs, tdc->blocklen << 1); fftw_execute_r2r(tdc->ifftplan, overlap_block, overlap_block); } } bool_t convolver_init(const char config_filename[], int length, int _realsize) { int order; FILE *stream; bool_t quiet; realsize = _realsize; decide_opt_code(); if (realsize != 4 && realsize != 8) { fprintf(stderr, "Invalid real size %d.\n", realsize); return false; } if ((order = log2_get(length)) == -1) { fprintf(stderr, "Invalid length %d.\n", length); return false; } order++; fft_order = order; n_fft = 2 * length; n_fft2 = length; if ((stream = fopen(config_filename, "rt")) == NULL) { if (errno != ENOENT) { fprintf(stderr, "Could not open \"%s\" for reading: %s.\n", config_filename, strerror(errno)); return false; } } else { /* We ignore if we can't read wisdom, we just overwrite with new */ if (realsize == 4) { fftwf_import_wisdom_from_file(stream); } else { fftw_import_wisdom_from_file(stream); } fclose(stream); } memset(fftplan_generated, 0, sizeof(fftplan_generated)); pinfo("Creating 4 FFTW plans of size %d...", 1 << fft_order); quiet = bfconf->quiet; bfconf->quiet = true; convolver_fftplan(fft_order, false, false); convolver_fftplan(fft_order, false, true); convolver_fftplan(fft_order, true, false); convolver_fftplan(fft_order, true, true); bfconf->quiet = quiet; pinfo("finished.\n"); /* Wisdom is cumulative, save it each time (and get wiser) */ if ((stream = fopen(config_filename, "wt")) == NULL) { fprintf(stderr, "Warning: could not save wisdom:\n" " could not open \"%s\" for writing: %s.\n", config_filename, strerror(errno)); } else { if (realsize == 4) { fftwf_export_wisdom_to_file(stream); } else { fftw_export_wisdom_to_file(stream); } fclose(stream); } return true; } brutefir-1.0o/firwindow.c0000664000175000017500000001117212752354353015056 0ustar andersanders/* * (c) Copyright 2002 - 2003, 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include #include #include #include "firwindow.h" /* zeroth order modified bessel function */ static double i_zero(double x) { /* The zeroth order Modified Bessel function: I_0(x) = 1 + x^2 / 2^2 + x^4 / (2^2 * 4^2) + x^6 / (2^2 * 4^2 * 6^2) + ... to infinity This function grows quite quickly towards very large numbers. By re-organising the calculations somewhat we minimise the dynamic range in the floating point numbers, and can thus calculate the function for larger x than we could do with a naive implementation. */ double n, a, halfx, sum; halfx = x / 2.0; sum = 1.0; a = 1.0; n = 1.0; do { a *= halfx; a /= n; sum += a * a; n += 1.0; /* either 'sum' will reach +inf or 'a' zero... */ } while (a != 0.0 && finite(sum)); return sum; } static double kaiser(double x, double beta, double inv_izbeta) /* inv_izbeta = 1.0 / i_zero(beta) */ { /* The Kaiser Window, with discrete n on odd length window: w(n) = I_0(Beta * sqrt(1 - 4*n^2 / (N-1)^2)) / I_0(Beta) with -(N-1)/2 <= n <= (N-1)/2 I_0 is the zeroth order modified bessel function. modified to -1.0 <= x <= x: y(x) = I_O(Beta * sqrt(1 - x^2)) / I_0(Beta) */ /* check input parameter x, assume, accept and correct possible rounding error. */ if (x < -1.0) { assert(x > -1.00001); x = -1.0; } if (x > 1.0) { assert(x < +1.00001); x = 1.0; } return i_zero(beta * sqrt(1.0 - x * x)) * inv_izbeta; } void firwindow_kaiser(void *target, int len, double offset, double beta, int realsize) { int n, len_div2, max; double x, y, inv_izbeta, step; len_div2 = len >> 1; inv_izbeta = 1.0 / i_zero(beta); if (offset != 0.0) { max = len_div2; max += (int)floor(offset); offset -= floor(offset); if (fabs(offset) < 1e-20) { offset = 0.0; } step = 1.0 / ((double)max + offset); if (offset == 0.0) { /* if offset is a whole number, don't run the loop up to zero to avoid unnecessary rounding errors on zero */ max -= 1; } for (n = 0; n <= max; n++) { x = -1.0 + (double)n * step; y = kaiser(x, beta, inv_izbeta); if (realsize == 4) { ((float *)target)[n] *= y; ((float *)target)[n] *= y; } else { ((double *)target)[n] *= y; ((double *)target)[n] *= y; } } if (offset == 0.0) { max += 1; } step = 1.0 / ((double)(len - max - 1) - offset); for (; n < len; n++) { x = ((double)(n - max) - offset) * step; y = kaiser(x, beta, inv_izbeta); if (realsize == 4) { ((float *)target)[n] *= y; ((float *)target)[n] *= y; } else { ((double *)target)[n] *= y; ((double *)target)[n] *= y; } } } else if ((len & 1) != 0) { /* odd length, center is the middle sample; this is the 'standard' case for windowing functions */ step = 1.0 / (double)len_div2; for (n = 1; n <= len_div2; n++) { x = (double)n * step; y = kaiser(x, beta, inv_izbeta); if (realsize == 4) { ((float *)target)[len_div2 + n] *= y; ((float *)target)[len_div2 - n] *= y; } else { ((double *)target)[len_div2 + n] *= y; ((double *)target)[len_div2 - n] *= y; } } } else { /* even length, center is in between the two middle samples; this case is equivalent to even length and offset = '-0.5'. */ step = 1.0 / (double)len_div2; step *= ((double)(len_div2) / ((double)len_div2 - 0.5)); for (n = 1; n <= len_div2; n++) { x = ((double)n - 0.5) * step; y = kaiser(x, beta, inv_izbeta); if (realsize == 4) { ((float *)target)[len_div2 + n - 1] *= y; ((float *)target)[len_div2 - n] *= y; } else { ((double *)target)[len_div2 + n - 1] *= y; ((double *)target)[len_div2 - n] *= y; } } } } brutefir-1.0o/firwindow.h0000644000175000017500000000234312752354353015061 0ustar andersanders/* * (c) Copyright 2002 - 2003, 2006 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _FIRWINDOW_H_ #define _FIRWINDOW_H_ #include "defs.h" /**** * firwindow_kaiser() * * Apply kaiser window with given 'beta' to 'target' of length 'len'. * For odd length targets, it is assumed that the center of the filter is * the middle sample (index len >> 1). For even length targets, it is assumed * that the center is exactly in between the two middle samples, and thus the * center is not sampled. * * If the center of 'target' is not placed as described above, a non-zero * offset can be specified with 'offset', which is the distance in samples * from sample index len >> 1. * * Note: due to the assumption of the center position in an even length * filter is the case when 'offset' is 0.0 equivalent to the case when it is * set to -0.5 (for even length filters only). * * 'beta' is a parameter for the kaiser window which specifies its shape. * A typical value suitable for general audio processing is 9.0. */ void firwindow_kaiser(void *target, int len, double offset, double beta, int realsize); #endif brutefir-1.0o/inout.c0000644000175000017500000000023112752354353014174 0ustar andersanders/* * (c) Copyright 2001 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include "inout.h" int IO; brutefir-1.0o/inout.h0000644000175000017500000000044612752354353014211 0ustar andersanders/* * (c) Copyright 2001 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _INOUT_H_ #define _INOUT_H_ #include "bfmod.h" #define IN BF_IN #define OUT BF_OUT #define FOR_IN_AND_OUT for (IO = 0; IO < 2; IO++) extern int IO; #endif brutefir-1.0o/log2.h0000644000175000017500000000130112752354353013705 0ustar andersanders/* * (c) Copyright 2001 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _LOG2_H_ #define _LOG2_H_ #include /* FIXME: these implementations does not look very elegant */ static inline int log2_get(uint32_t x) { int lg; for (lg = 0; (x & 1) == 0 && lg < 32; x = x >> 1, lg++); if (lg == 32 || (x & ~1) != 0) { return -1; } return lg; } static inline int log2_roof(uint32_t x) { int lg; for (lg = 31; (x & (1 << lg)) == 0 && lg > 0; lg--); if (lg == 0 || (lg == 31 && (x & 0x7FFFFFFF) != 0)) { return -1; } if ((x & ~(1 << lg)) != 0) { lg++; } return lg; } #endif brutefir-1.0o/massive_config0000644000175000017500000000401612752354353015616 0ustar andersanderssampling_rate: 44100; filter_length: 8192,16; show_progress: true; coeff "dummy" { filename: "dirac pulse"; }; input 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "alsa" { device: "hw:0"; }; sample: "S24_LE"; channels: 26; }; output 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 { device: "alsa" { device: "hw:0"; }; sample: "S24_LE"; channels: 26; dither: true; }; filter 0 { from_inputs: 0; to_outputs: 0; coeff: 0; }; filter 1 { from_inputs: 1; to_outputs: 1; coeff: 0; }; filter 2 { from_inputs: 2; to_outputs: 2; coeff: 0; }; filter 3 { from_inputs: 3; to_outputs: 3; coeff: 0; }; filter 4 { from_inputs: 4; to_outputs: 4; coeff: 0; }; filter 5 { from_inputs: 5; to_outputs: 5; coeff: 0; }; filter 6 { from_inputs: 6; to_outputs: 6; coeff: 0; }; filter 7 { from_inputs: 7; to_outputs: 7; coeff: 0; }; filter 8 { from_inputs: 8; to_outputs: 8; coeff: 0; }; filter 9 { from_inputs: 9; to_outputs: 9; coeff: 0; }; filter 10 { from_inputs: 10; to_outputs: 10; coeff: 0; }; filter 11 { from_inputs: 11; to_outputs: 11; coeff: 0; }; filter 12 { from_inputs: 12; to_outputs: 12; coeff: 0; }; filter 13 { from_inputs: 13; to_outputs: 13; coeff: 0; }; filter 14 { from_inputs: 14; to_outputs: 14; coeff: 0; }; filter 15 { from_inputs: 15; to_outputs: 15; coeff: 0; }; filter 16 { from_inputs: 16; to_outputs: 16; coeff: 0; }; filter 17 { from_inputs: 17; to_outputs: 17; coeff: 0; }; filter 18 { from_inputs: 18; to_outputs: 18; coeff: 0; }; filter 19 { from_inputs: 19; to_outputs: 19; coeff: 0; }; filter 20 { from_inputs: 20; to_outputs: 20; coeff: 0; }; filter 21 { from_inputs: 21; to_outputs: 21; coeff: 0; }; filter 22 { from_inputs: 22; to_outputs: 22; coeff: 0; }; filter 23 { from_inputs: 23; to_outputs: 23; coeff: 0; }; filter 24 { from_inputs: 24; to_outputs: 24; coeff: 0; }; filter 25 { from_inputs: 25; to_outputs: 25; coeff: 0; }; brutefir-1.0o/numunion.h0000644000175000017500000000065212752354353014722 0ustar andersanders/* * (c) Copyright 2004 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _NUMUNION_H_ #define _NUMUNION_H_ #include typedef union { float r32[2]; double r64[1]; int8_t i8[8]; int16_t i16[4]; int32_t i32[2]; int64_t i64[1]; uint8_t u8[8]; uint16_t u16[4]; uint32_t u32[2]; uint64_t u64[1]; } numunion_t; #endif brutefir-1.0o/pinfo.h0000644000175000017500000000046412752354353014166 0ustar andersanders/* * (c) Copyright 2001 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _PINFO_H_ #define _PINFO_H_ #include "bfconf.h" #define pinfo(format, args...) if (!bfconf->quiet) \ fprintf(stderr, format, ##args); #endif brutefir-1.0o/raw2real.h0000644000175000017500000001154512752354353014574 0ustar andersanders/* * (c) Copyright 2001 - 2004, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ static void RAW2REAL_NAME(void *_realbuf, void *_rawbuf, int bytes, bool_t isfloat, int spacing, bool_t swap, int n_samples) { numunion_t *realbuf, *rawbuf, sample; int n, i; realbuf = (numunion_t *)_realbuf; rawbuf = (numunion_t *)_rawbuf; if (isfloat) { #if REALSIZE == 4 #define RXX r32 #define REAL_T float switch (bytes) { case 4: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->u32[n] = SWAP32(rawbuf->u32[i]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->r32[n] = rawbuf->r32[i]; } } break; case 8: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { sample.u64[0] = SWAP64(rawbuf->u64[i]); realbuf->r32[n] = (float)sample.r64[0]; } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->r32[n] = (float)rawbuf->r64[i]; } } break; default: goto raw2real_invalid_byte_size; } #elif REALSIZE == 8 #define RXX r64 #define REAL_T double switch (bytes) { case 4: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { sample.u32[0] = SWAP32(rawbuf->u32[i]); realbuf->r64[n] = (double)sample.r32[0]; } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->r64[n] = (double)rawbuf->r32[i]; } } break; case 8: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->u64[n] = SWAP64(rawbuf->u64[i]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->r64[n] = rawbuf->r64[i]; } } break; default: goto raw2real_invalid_byte_size; } #else #error invalid REALSIZE #endif return; } sample.u64[0] = 0; switch (bytes) { case 1: for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->RXX[n] = (REAL_T)rawbuf->i8[i]; } break; case 2: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->RXX[n] = (REAL_T)((int16_t)SWAP16(rawbuf->u16[i])); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->RXX[n] = (REAL_T)rawbuf->i16[i]; } } break; case 3: spacing = spacing * 3 - 3; #ifdef __BIG_ENDIAN__ if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { sample.u8[2] = rawbuf->u8[i++]; sample.u8[1] = rawbuf->u8[i++]; sample.u8[0] = rawbuf->u8[i++]; realbuf->RXX[n] = (REAL_T)(sample.i32[0] >> 8); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { sample.u8[0] = rawbuf->u8[i++]; sample.u8[1] = rawbuf->u8[i++]; sample.u8[2] = rawbuf->u8[i++]; realbuf->RXX[n] = (REAL_T)(sample.i32[0] >> 8); } } #endif #ifdef __LITTLE_ENDIAN__ if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { sample.u8[3] = rawbuf->u8[i++]; sample.u8[2] = rawbuf->u8[i++]; sample.u8[1] = rawbuf->u8[i++]; realbuf->RXX[n] = (REAL_T)(sample.i32[0] >> 8); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { sample.u8[1] = rawbuf->u8[i++]; sample.u8[2] = rawbuf->u8[i++]; sample.u8[3] = rawbuf->u8[i++]; realbuf->RXX[n] = (REAL_T)(sample.i32[0] >> 8); } } #endif break; case 4: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->RXX[n] = (REAL_T)((int32_t)SWAP32(rawbuf->u32[i])); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { realbuf->RXX[n] = (REAL_T)rawbuf->i32[i]; } } break; default: raw2real_invalid_byte_size: fprintf(stderr, "Sample byte size %d is not supported.\n", bytes); bf_exit(BF_EXIT_OTHER); break; } } #undef RXX #undef REAL_T brutefir-1.0o/real2raw.h0000644000175000017500000002146212752354353014573 0ustar andersanders/* * (c) Copyright 2001 - 2004, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #if REALSIZE == 4 #define RXX r32 #define REAL_T float #elif REALSIZE == 8 #define RXX r64 #define REAL_T double #else #error invalid REALSIZE #endif #ifndef CONCAT_EVAL_ #define CONCAT_EVAL_(a, b) a ## b #define CONCAT_(a, b) CONCAT_EVAL_(a, b) #endif #define REAL2RAW_SAMPLE_TEST CONCAT_(REAL2RAW_NAME, _sample_test) static inline void REAL2RAW_SAMPLE_TEST(REAL_T real_sample, struct bfoverflow *overflow) { if (!isfinite(real_sample)) { fprintf(stderr, "NaN or Inf values in the output! Bad output. " "Aborting.\n"); abort(); } if (bfconf->safety_limit != 0.0 && (real_sample < -bfconf->safety_limit * overflow->max || real_sample > bfconf->safety_limit * overflow->max)) { fprintf(stderr, "Safety limit exceeded on output (%.2f > %.2f). " "Aborting.\n", 20.0 * log10(fabs(real_sample / overflow->max)), 20.0 * log10(bfconf->safety_limit)); bf_exit(BF_EXIT_OTHER); } } #define REAL_OVERFLOW_UPDATE \ if (realbuf->RXX[n] < 0.0) { \ if (realbuf->RXX[n] < rmin) { \ overflow->n_overflows++; \ } \ if (realbuf->RXX[n] < -overflow->largest) { \ overflow->largest = -realbuf->RXX[n]; \ } \ } else { \ if (realbuf->RXX[n] > rmax) { \ overflow->n_overflows++; \ } \ if (realbuf->RXX[n] > overflow->largest) { \ overflow->largest = realbuf->RXX[n]; \ } \ } static void REAL2RAW_NAME(void *_rawbuf, void *_realbuf, int bits, int bytes, bool_t isfloat, int spacing, bool_t swap, int n_samples, struct bfoverflow *overflow REAL2RAW_EXTRA_PARAMS) { numunion_t *rawbuf, *realbuf, sample; int32_t imin, imax; REAL_T rmin, rmax; int n, i; /* * It is assumed that sbytes only can have the values possible from the * supported sample formats specified in bfmod.h */ realbuf = (numunion_t *)_realbuf; rawbuf = (numunion_t *)_rawbuf; if (isfloat) { rmin = -overflow->max; rmax = overflow->max; #if REALSIZE == 4 switch (bytes) { case 4: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; rawbuf->u32[i] = SWAP32(realbuf->u32[n]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; rawbuf->r32[i] = realbuf->r32[n]; } } break; case 8: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; sample.r64[0] = (double)realbuf->r32[n]; rawbuf->u64[i] = SWAP64(sample.u64[0]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; rawbuf->r64[i] = (double)realbuf->r32[n]; } } break; default: goto real2raw_invalid_byte_size; } #elif REALSIZE == 8 switch (bytes) { case 4: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; sample.r32[0] = (float)realbuf->r64[n]; rawbuf->u32[i] = SWAP32(sample.u32[0]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; rawbuf->r32[i] = (float)realbuf->r64[n]; } } break; case 8: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; rawbuf->u64[i] = SWAP64(realbuf->u64[n]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); REAL_OVERFLOW_UPDATE; rawbuf->r64[i] = realbuf->r64[n]; } } break; default: goto real2raw_invalid_byte_size; } #else #error invalid REALSIZE #endif return; } imin = -((uint64_t)1 << (bits - 1)); imax = ((uint64_t)1 << (bits - 1)) - 1; rmin = (REAL_T)imin; rmax = (REAL_T)imax; switch (bytes) { case 1: for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); rawbuf->i8[i] = (int8_t)REAL2INT_CALL; } break; case 2: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); sample.i16[0] = (int16_t)REAL2INT_CALL; rawbuf->u16[i] = SWAP16(sample.u16[0]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); rawbuf->i16[i] = (int16_t)REAL2INT_CALL; } } break; case 3: spacing = spacing * 3 - 3; #ifdef __BIG_ENDIAN__ if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); sample.i32[0] = REAL2INT_CALL; rawbuf->u8[i++] = sample.u8[3]; rawbuf->u8[i++] = sample.u8[2]; rawbuf->u8[i++] = sample.u8[1]; } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); sample.i32[0] = REAL2INT_CALL; rawbuf->u8[i++] = sample.u8[1]; rawbuf->u8[i++] = sample.u8[2]; rawbuf->u8[i++] = sample.u8[3]; } } #endif #ifdef __LITTLE_ENDIAN__ if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); sample.i32[0] = REAL2INT_CALL; rawbuf->u8[i++] = sample.u8[2]; rawbuf->u8[i++] = sample.u8[1]; rawbuf->u8[i++] = sample.u8[0]; } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); sample.i32[0] = REAL2INT_CALL; rawbuf->u8[i++] = sample.u8[0]; rawbuf->u8[i++] = sample.u8[1]; rawbuf->u8[i++] = sample.u8[2]; } } #endif break; case 4: if (swap) { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); sample.i32[0] = REAL2INT_CALL; rawbuf->u32[i] = SWAP32(sample.u32[0]); } } else { for (n = i = 0; n < n_samples; n++, i += spacing) { REAL2RAW_SAMPLE_TEST(realbuf->RXX[n], overflow); rawbuf->i32[i] = REAL2INT_CALL; } } break; default: real2raw_invalid_byte_size: fprintf(stderr, "Sample byte size %d is not supported.\n", bytes); bf_exit(BF_EXIT_OTHER); break; } } #undef REAL_OVERFLOW_UPDATE #undef RXX #undef REAL_T brutefir-1.0o/rendereq.h0000644000175000017500000000636212752354353014663 0ustar andersanders/* * (c) Copyright 2002 - 2005 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ static inline real_t COSINE_INT_NAME(real_t mag1, real_t mag2, real_t freq1, real_t freq2, real_t curfreq) { return (mag1 - mag2) * 0.5 * cos(M_PI * (curfreq - freq1) / (freq2 - freq1)) + (mag1 + mag2) * 0.5; } static void RENDER_EQUALISER_NAME(struct realtime_eq *eq) { real_t mag, rad, curfreq, scale, divtaps, tapspi; real_t *eqmag, *eqfreq, *eqphase; struct timeval tv1, tv2; char path[1024]; FILE *stream; int n, i; /* poll until ready */ while (eq->not_changed) { usleep(10000); } gettimeofday(&tv1, NULL); /* generate smoothed frequency domain filter */ eqmag = alloca(eq->band_count * sizeof(real_t)); eqfreq = alloca(eq->band_count * sizeof(real_t)); eqphase = alloca(eq->band_count * sizeof(real_t)); for (n = 0; n < eq->band_count; n++) { eqmag[n] = (real_t)eq->mag[n]; eqfreq[n] = (real_t)eq->freq[n]; eqphase[n] = (real_t)eq->phase[n]; } scale = 1.0 / (real_t)eq->taps; divtaps = 1.0 / (real_t)eq->taps; tapspi = -(real_t)eq->taps * M_PI; ((real_t *)rbuf)[0] = eqmag[0] * scale; for (n = 1, i = 0; n < eq->taps >> 1; n++) { curfreq = (real_t)n * divtaps; while (curfreq > eqfreq[i+1]) { i++; } mag = COSINE_INT_NAME(eqmag[i], eqmag[i+1], eqfreq[i], eqfreq[i+1], curfreq) * scale; rad = tapspi * curfreq + COSINE_INT_NAME(eqphase[i], eqphase[i+1], eqfreq[i], eqfreq[i+1], curfreq); ((real_t *)rbuf)[n] = cos(rad) * mag; ((real_t *)rbuf)[eq->taps-n] = sin(rad) * mag; } ((real_t *)rbuf)[eq->taps>>1] = eqmag[eq->band_count - 1] * scale; /* convert to time-domain */ #if REALSIZE == 4 fftwf_execute_r2r((const fftwf_plan)eq->ifftplan, (real_t *)rbuf, (real_t *)rbuf); #elif REALSIZE == 8 fftw_execute_r2r((const fftw_plan)eq->ifftplan, (real_t *)rbuf, (real_t *)rbuf); #else #error invalid REALSIZE #endif if (debug_dump_filter_path != NULL) { snprintf(path, 1024, debug_dump_filter_path, eq->coeff[0]); path[1023] = '\0'; stream = fopen(path, "wt"); if (stream != NULL) { for (n = 0; n < eq->taps; n++) { fprintf(stream, "%.16e\n", ((real_t *)rbuf)[n]); } fclose(stream); } } /* put to target BruteFIR coeffients */ for (n = 0; n < coeffs[eq->coeff[0]].n_blocks; n++) { bfaccess->convolver_coeffs2cbuf(&((real_t *)rbuf)[block_length * n], bfaccess->coeffs_data [eq->coeff[!eq->active_coeff]][n]); } gettimeofday(&tv2, NULL); timersub(&tv2, &tv1, &tv1); eq->active_coeff = !eq->active_coeff; eq->not_changed = true; if (debug) { fprintf(stderr, "EQ: rendering coefficient set %d took %.2f ms\n", eq->coeff[eq->active_coeff], (double)tv1.tv_sec * 1000.0 + (double)tv1.tv_usec / 1000.0); } } brutefir-1.0o/shmalloc.c0000644000175000017500000000510112752354353014641 0ustar andersanders/* * (c) Copyright 2001 - 2004 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #include "defs.h" #include #include #include #include #include #include #include #ifdef __OS_LINUX__ #include #else #include #endif #include "shmalloc.h" static void print_shmget_error(size_t size) { switch (errno) { case EINVAL: fprintf(stderr, "shmget() failed when requesting %d bytes.\n" " The OS shmmax parameter must be increased.\n", (int)size); break; case ENOSPC: fprintf(stderr, "shmget() failed due to exceeded id limit.\n" " The OS shmmni parameter must be increased.\n"); break; case ENOMEM: fprintf(stderr, "shmget() failed when requesting %d bytes.\n" "Out of memory, or the OS parameters shmmax and/or shmmni " "may be too small.\n", (int)size); break; default: fprintf(stderr, "shmget() failed when requesting %d bytes: %s.\n", (int)size, strerror(errno)); break; } } void * shmalloc(size_t size) { struct shmid_ds shmid_ds; void *p; int n; if ((n = shmget(IPC_PRIVATE, size, IPC_CREAT | SHM_R | SHM_W)) == -1) { print_shmget_error(size); return NULL; } memset(&shmid_ds, 0, sizeof(shmid_ds)); /* to get rid of valgrind warning */ if ((p = shmat(n, 0, 0)) == (char *)-1 || shmctl(n, IPC_RMID, &shmid_ds) == -1) { return NULL; } if (((ptrdiff_t)p & (ALIGNMENT - 1)) != 0) { fprintf(stderr, "alignment error\n"); } return p; } void * shmalloc_id(int *shmid, size_t size) { static key_t key = 1; struct shmid_ds shmid_ds; void *p; int n; if (shmid != NULL) { *shmid = -1; } while ((n = shmget(key, size, IPC_CREAT | IPC_EXCL | SHM_R | SHM_W)) == -1 && errno == EEXIST) { key++; } if (n == -1) { print_shmget_error(size); return NULL; } if ((p = shmat(n, 0, 0)) == (char *)-1) { fprintf(stderr, "Failed to attach to shared memory (shmid %d): %s.\n", n, strerror(errno)); return NULL; } memset(&shmid_ds, 0, sizeof(shmid_ds)); if (shmctl(n, IPC_RMID, &shmid_ds) == -1) { fprintf(stderr, "Failed to set IPC_RMID (shmid %d): %s.\n", n, strerror(errno)); return NULL; } key++; if (shmid != NULL) { *shmid = n; } return p; } brutefir-1.0o/shmalloc.h0000644000175000017500000000041612752354353014652 0ustar andersanders/* * (c) Copyright 2001, 2003 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _SHMALLOC_H_ #define _SHMALLOC_H_ void * shmalloc(size_t size); void * shmalloc_id(int *shmid, size_t size); #endif brutefir-1.0o/swap.h0000644000175000017500000000315612752354353014026 0ustar andersanders/* * (c) Copyright 1999, 2002 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _SWAP_H_ #define _SWAP_H_ #include #ifdef __LITTLE_ENDIAN__ #include #define SWAP32(x) ntohl((uint32_t)(x)) #define SWAP16(x) ntohs((uint16_t)(x)) #endif #ifdef __BIG_ENDIAN__ #define SWAP16(x) \ (((((uint16_t)(x)) & 0xff00U) >> 8) | \ ((((uint16_t)(x)) & 0x00ffU) << 8)) #define SWAP32(x) \ (((((uint32_t)(x)) & 0xff000000UL) >> 24) | \ ((((uint32_t)(x)) & 0x00ff0000UL) >> 8) | \ ((((uint32_t)(x)) & 0x0000ff00UL) << 8) | \ ((((uint32_t)(x)) & 0x000000ffUL) << 24)) #endif #define SWAP64(x) \ (((((uint64_t)(x)) & 0xff00000000000000ULL) >> 56) | \ ((((uint64_t)(x)) & 0x00ff000000000000ULL) >> 40) | \ ((((uint64_t)(x)) & 0x0000ff0000000000ULL) >> 24) | \ ((((uint64_t)(x)) & 0x000000ff00000000ULL) >> 8) | \ ((((uint64_t)(x)) & 0x00000000ff000000ULL) << 8) | \ ((((uint64_t)(x)) & 0x0000000000ff0000ULL) << 24) | \ ((((uint64_t)(x)) & 0x000000000000ff00ULL) << 40) | \ ((((uint64_t)(x)) & 0x00000000000000ffULL) << 56)) #endif brutefir-1.0o/sysarch.h0000644000175000017500000000213112752354353014520 0ustar andersanders/* * (c) Copyright 2002 - 2004, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _SYSARCH_H_ #define _SYSARCH_H_ #define ALIGNMENT 32 /* * Find out CPU architecture */ #if defined(__i386__) || defined(_M_IX86) #define __ARCH_IA32__ #define __LITTLE_ENDIAN__ #elif defined(__sparc__) || defined(__sparc) #define __ARCH_SPARC__ #define __BIG_ENDIAN__ #elif defined(__x86_64__) #define __ARCH_X86_64__ #define __LITTLE_ENDIAN__ #else #define __ARCH_GENERIC__ #include #if __BYTE_ORDER == __LITTLE_ENDIAN #undef __LITTLE_ENDIAN__ #define __LITTLE_ENDIAN__ #endif #if __BYTE_ORDER == __BIG_ENDIAN #undef __BIG_ENDIAN__ #define __BIG_ENDIAN__ #endif #endif #if (defined(__LITTLE_ENDIAN__) && defined(__BIG_ENDIAN__)) || \ (!defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) #error unknown byte order #endif /* * Find out OS */ #ifdef linux #define __OS_LINUX__ #elif defined(__SVR4) && defined(__sun) #define __OS_SUNOS__ #elif __FreeBSD__ #define __OS_FREEBSD__ #else #define __OS_GENERIC__ #endif #endif brutefir-1.0o/timermacros.h0000644000175000017500000000347012752354353015400 0ustar andersanders/* * (c) Copyright 2002, 2003 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _TIMERMACROS_H_ #define _TIMERMACROS_H_ /* These two macros are defined in sys/time.h on most systems */ #include #ifndef timeradd #define timeradd(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ if ((result)->tv_usec >= 1000000) { \ (result)->tv_sec++; \ (result)->tv_usec -= 1000000; \ } \ } while (0) #endif #ifndef timersub #define timersub(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ (result)->tv_sec--; \ (result)->tv_usec += 1000000; \ } \ } while (0) #endif #ifndef timercmp #define timercmp(a, b, CMP) \ (((a)->tv_sec == (b)->tv_sec) ? \ ((a)->tv_usec CMP (b)->tv_usec) : \ ((a)->tv_sec CMP (b)->tv_sec)) #endif #endif brutefir-1.0o/timestamp.h0000644000175000017500000000217412752354353015056 0ustar andersanders/* * (c) Copyright 2000, 2002, 2004, 2006, 2013 -- Anders Torger * * This program is open source. For license terms, see the LICENSE file. * */ #ifndef _TIMESTAMP_H_ #define _TIMESTAMP_H_ #include #include #if defined(__ARCH_GENERIC__) #define TIMESTAMP_NOT_CLOCK_CYCLE_COUNTER static inline void timestamp(volatile uint64_t * volatile ts) { *ts = (uint64_t)clock() * 1000; } #endif #if defined(__ARCH_IA32__) #include static inline void timestamp(volatile uint64_t * volatile ts) { *ts = __rdtsc(); /*__asm__ volatile("rdtsc" : "=A"(*ts));*/ } #endif #if defined(__ARCH_X86_64__) #include static inline void timestamp(volatile uint64_t * volatile ts) { *ts = __rdtsc(); /*__asm__ volatile("rdtsc":"=a"(*ts)::"dx");*/ } #endif #ifdef __ARCH_SPARC__ static inline void timestamp(volatile uint64_t *ts) { __asm__ volatile ( "rd %%tick, %0 \n\t \ clruw %0, %1 \n\t \ srlx %0, 32, %0 \n\t" : "=r"(((volatile uint32_t *)ts)[0]), "=r"(((volatile uint32_t *)ts)[1]) : "0"(((volatile uint32_t *)ts)[0]), "1"(((volatile uint32_t *)ts)[1])); } #endif #endif brutefir-1.0o/xtc_config0000644000175000017500000000165212752354353014750 0ustar andersanderssampling_rate: 44100; filter_length: 64,64; show_progress: true; ## COEFFS ## coeff "direct path" { filename: "directpath.txt"; }; coeff "cross path" { filename: "crosspath.txt"; }; input "left", "right" { device: "alsa" { device: "hw:0"; }; sample: "S24_LE"; channels: 26/24,25; }; output "left", "right" { device: "alsa" { device: "hw:0"; }; sample: "S24_LE"; channels: 26/24,25; dither: true; }; filter "left speaker direct path" { from_inputs: "left"; to_outputs: "left"; coeff: "direct path"; }; filter "left speaker cross path" { from_inputs: "right"; to_outputs: "left"; coeff: "cross path"; }; filter "right speaker direct path" { from_inputs: "right"; to_outputs: "right"; coeff: "direct path"; }; filter "right speaker cross path" { from_inputs: "left"; to_outputs: "right"; coeff: "cross path"; };