From: yomguy <> Date: Wed, 20 Aug 2008 22:13:18 +0000 (+0000) Subject: * Remove/add audiolab X-Git-Tag: 1.1~821 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=732b57875d53cad9de5b18158c2e31c8c5f56f46;p=telemeta.git * Remove/add audiolab * Install audiolab with setup.py * Cleanup * Audiolab waveform is default view --- diff --git a/setup.py b/setup.py index bc5b867a..5a7ab860 100644 --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ setup( url = "/http://svn.parisson.org/telemeta", description = "web frontend to backup, transcode and tag any audio content with metadata", author = ["Guillaume Pellerin, Olivier Guilyardi"], - author_email = ["pellerin@parisson.com"], + author_email = ["pellerin@parisson.com","olivier@samalyse.com"], version = version, packages = packages, data_files = data_files, @@ -82,3 +82,7 @@ See http://svn.parisson.org/telemeta/ for more informations. """ ) +# Install audiolab +os.chdir('telemeta/util/audiolab/') +os.system('python setup.py install') + diff --git a/telemeta/util/audiolab/COPYING.txt b/telemeta/util/audiolab/COPYING.txt new file mode 100644 index 00000000..5ab7695a --- /dev/null +++ b/telemeta/util/audiolab/COPYING.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/telemeta/util/audiolab/Changelog b/telemeta/util/audiolab/Changelog new file mode 100644 index 00000000..58d64a75 --- /dev/null +++ b/telemeta/util/audiolab/Changelog @@ -0,0 +1,139 @@ +audiolab (0.7) Sun, 09 Sep 2007 20:10:27 +0900 + + * More robust wrapping of libsndfile: sf_file are objects pointers, + the destructor checks that the handle already exists, and NULL + pointers are better detected. + * Add preliminary alsa version (not enabled yet). + * Opening non existing files now throws meaningful exception. + +audiolab (0.7dev) Tue, 17 Jul 2007 12:03:41 +0900 + + * Rename pyaudiolab to audiolab. + +pyaudiolab (0.7dev) Fri, 25 May 2007 16:00:24 +0900 + + * Add the possibility to open an audio file from file descriptor + +pyaudiolab (0.7dev) Thu, 24 May 2007 13:34:11 +0900 + + * Support for read and write seek + * Set correct location of pysndfile.py in setup.py, so that it is + correctly removed during setup. + +pyaudiolab (0.7dev) Tue, 22 May 2007 10:50:53 +0900 + + * Relicense under the LGPL + * Add data files with distutils instead of setuptools because of + setuptools bug with sdist targets + +pyaudiolab (0.7dev) Mon, 21 May 2007 11:09:32 +0900 + + * Convert the layout to scikits convention + setuptools + +pyaudiolab (0.6.7) Mon, 19 Mar 2007 15:19:10 +0900 + + * Add some meta-data for setup.py + +pyaudiolab (0.6.7) Mon, 12 Mar 2007 15:48:12 +0900 + + * Mainly Bug fixes for win32 and libsndfile without FLAC support. + * Add a site.cfg.win32 + instructions in the doc to show how to use with + win32. + * Replace all remove functions in tests for temp files (Does not work + under win32) + * If FLAC is not supported, set flacread and flacwrite functions to None + and catch exceptions related to unavailable FLAC support to avoid tests + failing. + * Replace non FLAC tests using FLAC files to WAV files. + +pyaudiolab (0.6.6dev) Mon, 05 Feb 2007 20:37:49 +0900 + + * Add reader/writer factory to matlab-like API. + * Generate au/wav/aiff/sdif/flac read/write functions + with Matlab-like behaviour. + * disable seek support for FLAC files by default, because of problems + when using sf_seek(offset, SEEK_SET) with some FLAC versions. + +pyaudiolab (0.6.6dev) Mon, 05 Feb 2007 12:06:10 +0900 + + * Change system to detect sndfile shared library file (based on + sys.plaform instead of so_ext which does not seem to work on darwin at + least). + +pyaudiolab (0.6.5) Fri, 02 Feb 2007 14:53:01 +0900 + + * bump to 0.6.5 + * Add mechanism to automatic release+upload + * release 0.6.5 + +pyaudiolab (0.6.5dev) Fri, 02 Feb 2007 14:53:01 +0900 + + * Put a proper LICENSE in COPYING.txt, and note in the README.txt + that pyaudiolab depends on LGPL package + +pyaudiolab (0.6.5dev) Thu, 01 Feb 2007 15:06:51 +0900 + + * Add int and short support for reading/writing + * fix write and read such as float arguments for number of frames is + possible + test. + * Whenever a long long is expected by libsndfile, check that the number + given by user is valid. + * Give more meaningful error messages when bad parameters are given for + opening a file for writing. + * Give more meaningful error messages when trying to read a file opened + only for writing. + * Add examples scripts into doc/examples. Run them in test.sh + +pyaudiolab (0.6.1) Wed, 31 Jan 2007 16:03:02 +0900 + + * Bump to 0.6.1 + * Change the name to pyaudiolab to avoid clashes with existing pyaudio + toolboxes. + +pyaudio (0.6) Wed, 31 Jan 2007 14:32:41 +0900 + + * Bump to 0.6. + * Add support for seeking. + * Add sync to sndfile to force flushing OS IO buffers to file without need + to close. + * read takes dtype argument instead of read_*_float, read_*_double. + * wavread, wavwrite are working and similar to matlab's ones. + +pyaudio (0.5) Tue, 30 Jan 2007 14:40:36 +0900 + + * Bump to 0.5 + * More explicit import error when used with bad ctypes + * Add matlab-like functions wavread, wavwrite, aiffread, aiffwrite + * Add API to have useful strings from format in sndfile class + +pyaudio (0.4) Mon, 06 Nov 2006 12:56:55 +0900 + + * Bump to 0.4 + * replace c_longlong with c_int64; if ctypes does not support 64 bits + integer, it will result in an import error instead of having c_longlong + silently converted to 32 bits integer, which would result in difficult + to trace bugs + * add support for raw files (which requires a format, even for reading) + +pyaudio (0.3.1) Mon, 06 Nov 2006 12:56:55 +0900 + + * Bump to 0.3.1 + * Check that samplerate and number of channels are int. If not, + warn the user about potential unsafe conversion. + * Move functions used to parse enum in header files into + a other module, for easier sharing with other packages. + +pyaudio (0.3) Tue, 31 Oct 2006 21:38:46 +0900 + + * Bump to 0.3 + * Added a class format, which can be used to open + file for writing. The class hides all internal representation + used by libsndfile, for a much saner API. + * Various Bug fixes + +pyaudio (0.2) Mon, 30 Oct 2006 17:45:14 +0900 + + * Bump to 0.2 + * Added a sndfile_info in setup.py for an automatic detection + of sndfile under the installed system + * mode is now a simple string in sndfile class ctor diff --git a/telemeta/util/audiolab/FLAC_SUPPORT.txt b/telemeta/util/audiolab/FLAC_SUPPORT.txt new file mode 100644 index 00000000..1f96d274 --- /dev/null +++ b/telemeta/util/audiolab/FLAC_SUPPORT.txt @@ -0,0 +1,28 @@ +ABOUT SEEK AND FLAC SUPPORT: + + audiolab supports flac, because libsndfile does. Unfortunately, some FLAC +libraries seem buggy (at least the one with ubuntu Dapper is) with respect to +their seek implementation. That's why by default, seeking in flac support is +NOT enabled. If you want to test whether you FLAC library has the problem, +compile the file badflac.c in the misc directory, eg on Linux: + +gcc -W -Wall badflac.c -lsndfile -o badflac + +and try running ./badflac ./badflac.flac. If the program looks locked up, this +means you should NOT enable flac support. If it works OK, then you can enable +seek support in audiolab by commenting the following lines in +pysndfile.py.in (take care to modify the pysndfile.py.in, and not the +pysndfile.py file), in sndfile.__init__ (around line 500): + +if self.get_file_format() == 'flac': + def SeekNotEnabled(self, *args): + raise FlacUnsupported("seek not supported on Flac by default, because"\ + "\n some version of FLAC libraries are buggy. Read FLAC_SUPPORT.txt") + self.seek = SeekNotEnabled + +Then install the package. + +The pre releases version 1.0.18pre7 of libsndfile is known to work, libsndfile +1.0.17 and 1.0.16 known NOT to work (those version rely on an installed FLAC +library, whether starting from 1.0.18, libsndfile will include the FLAC library +instead of using an externally supplied one). diff --git a/telemeta/util/audiolab/INSTALL.txt b/telemeta/util/audiolab/INSTALL.txt new file mode 100644 index 00000000..b812afac --- /dev/null +++ b/telemeta/util/audiolab/INSTALL.txt @@ -0,0 +1,36 @@ +Suported platforms: +------------------- + +audiolab has been successfully run and tested on the following platforms: + + - windows + - mac OS X (intel and ppc) + - linux (intel and ppc). + +This may work on other platforms: in particular, I expect the package to work +on most recent Unices. + +Requirements +------------ + +To run correctly, audiolab requires the following softwares: + + - python (> 2.3): http://www.python.org + - setuptools: + http://peak.telecommunity.com/DevCenter/setuptools#installing-setuptools + - ctypes (included in python starting from version 2.5) + - numpy: http://www.scipy.org/Numpy + - libsndfile: http://www.mega-nerd.com/libsndfile/ + +On Ubuntu, you can ensure to get the necessary packages by doing apt-get +install python python-setuptools libsndfile-dev python-numpy + +Installing +---------- + +Installing is easy: once in the audiolab directory, simply do: python setup.py +install. If the installation failed because libsndfile was not found, check +that you have correctly installed libsndfile, including its headers +(sndfile.h). On windows, as there is no standard for 3rd party libraries and +header location, you can put them wherever you want, as long as the +corresponding directories are in site.cfg (see site.cfg.win32 for an example). diff --git a/telemeta/util/audiolab/MANIFEST.in b/telemeta/util/audiolab/MANIFEST.in new file mode 100644 index 00000000..2288cf92 --- /dev/null +++ b/telemeta/util/audiolab/MANIFEST.in @@ -0,0 +1,7 @@ +include scikits/audiolab/pysndfile.py.in +include site.cfg +include site.cfg.win32 +include COPYING.txt +include FLAC_SUPPORT.txt +include header_parser.py +include generate_const.py diff --git a/telemeta/util/audiolab/Makefile b/telemeta/util/audiolab/Makefile new file mode 100644 index 00000000..94b2f8a1 --- /dev/null +++ b/telemeta/util/audiolab/Makefile @@ -0,0 +1,114 @@ +# Last Change: Tue Jul 17 11:00 AM 2007 J +# +# This makefile is used to do all the "tricky things" before a release, +# including updating the doc, installing and testing the package, uploading the +# release to the website, etc... +# +# TODO: not fake dependencies.... + +PKG_VER = $(shell cat scikits/audiolab/info.py | grep __version__ | tr -s " " | cut -f 3 -d" " \ + | cut -f 2 -d\') + +BASEPATH = $(PWD) +DATAPATH = $(PWD)/scikits/audiolab/test_data/ +DOCPATH = $(PWD)/scikits/audiolab/docs/ +EXAMPATH = $(DOCPATH)/examples + +SCIPYPATH = $(HOME)/local/lib/python2.5/site-packages +TMPPATH = $(CURDIR)/../tmp + +PYTHONCMD = PYTHONPATH=$(TMPPATH)/lib/python2.5/site-packages:$(SCIPYPATH) python -c +PYTHONRUN = PYTHONPATH=$(TMPPATH)/lib/python2.5/site-packages:$(SCIPYPATH) python + +RELEASELOC = $(WWWHOMEDIR)/archives/audiolab/releases + +do_release: clean src examples tests + +release: do_release upload_release + +upload_release: dist/audiolab-$(PKG_VER).tar.gz \ + dist/audiolab-$(PKG_VER).tar.bz2 \ + dist/audiolab-$(PKG_VER).zip + @echo "Uploading version $(PKG_VER) ..." + @read n + rcp dist/audiolab-$(PKG_VER).tar.gz $(RELEASELOC) + rcp dist/audiolab-$(PKG_VER).tar.bz2 $(RELEASELOC) + rcp dist/audiolab-$(PKG_VER).zip $(RELEASELOC) + +src: build_src + +build_src: dist/audiolab-$(PKG_VER).tar.gz \ + dist/audiolab-$(PKG_VER).tar.bz2 \ + dist/audiolab-$(PKG_VER).zip + +dist/audiolab-$(PKG_VER).tar.gz: doc + python setup.py sdist --format=gztar + +dist/audiolab-$(PKG_VER).zip: doc + python setup.py sdist --format=zip + +dist/audiolab-$(PKG_VER).tar.bz2: doc + python setup.py sdist --format=bztar + +#======================================================= +# Code related to building audiolab in a tmp directory +#======================================================= +# Install the package in a tmp directory +$(TMPPATH): build_test + +build_test: + $(PYTHONRUN) setup.py install --prefix=$(TMPPATH) + +# Clean the tmp dir +clean_before_run: + rm -rf $(TMPPATH) + +#=========================== +# Code related to examples +#=========================== +examples: run_examples + +# Run examples in docs/examples +run_examples: $(TMPPATH) + #for i in $(BASEPATH)/docs/examples/*.py; do\ + # echo "========== runing example $$i ==========";\ + # $(PYTHONRUN) $$i; \ + #done; + # Why when using the above loop, Make does not stop when one script fails ? + @echo "---------- runing example quick1.py ----------"; + cd $(DATAPATH) && $(PYTHONRUN) $(EXAMPATH)/quick1.py + @echo "---------- runing example usage1.py ----------"; + cd $(DATAPATH) && $(PYTHONRUN) $(EXAMPATH)/usage1.py + @echo "---------- runing example usage2.py ----------"; + cd $(DATAPATH) && $(PYTHONRUN) $(EXAMPATH)/usage2.py + @echo "---------- runing example format1.py ----------"; + cd $(DATAPATH) && $(PYTHONRUN) $(EXAMPATH)/format1.py + @echo "---------- runing example format2.py ----------"; + cd $(DATAPATH) && $(PYTHONRUN) $(EXAMPATH)/format2.py + @echo "---------- runing example write1.py ----------"; + cd $(DATAPATH) && $(PYTHONRUN) $(EXAMPATH)/write1.py + @echo "---------- runing example matlab1.py ----------"; + cd $(DATAPATH) && $(PYTHONRUN) $(EXAMPATH)/matlab1.py + +#===================== +# Code related to test +#===================== +tests: run_tests examples + @echo "==================================" + @echo " RELEASE $(PKG_VER) IS OK !" + @echo "==================================" + +# Run all tests +run_tests: $(TMPPATH) + @echo "=============== running test ============" + cd .. && $(PYTHONCMD) "import scikits.audiolab as audiolab; print audiolab; audiolab.test()" + @echo "=============== Done ============" + +#===================== +# Code related to doc +#===================== +doc: + cd $(DOCPATH) && $(MAKE) + +clean: clean_before_run + cd $(DOCPATH) && $(MAKE) clean diff --git a/telemeta/util/audiolab/README.txt b/telemeta/util/audiolab/README.txt new file mode 100644 index 00000000..7c6a3f43 --- /dev/null +++ b/telemeta/util/audiolab/README.txt @@ -0,0 +1,22 @@ +audiolab: a small toolbox to read, write and play audio to and from +numpy arrays. + +audiolab provides two API: + - one similar to matlab: this gives you wavread/wavwrite/auread/auwrite + functions similar to matlab's ones. + - a more complete API, which can be used to read, write to many audio files + (including wav, aiff, flac, au, IRCAM, htk, etc...), with IO capabilities + not available to matlab (seek, append data, etc...) + +It is a thin wrapper around libsndfile from Erik Castro Lopo. + +See the docs directory for more details + +LICENSE: + +audiolab itself is licensed under the LGPL license +(see COPYING.txt in main source directory) + +audiolab depends on libsndfile to work; libsndfile is licensed under LGPL. + +See http://www.mega-nerd.com/libsndfile/ for details. diff --git a/telemeta/util/audiolab/TODO b/telemeta/util/audiolab/TODO new file mode 100644 index 00000000..8dd984a7 --- /dev/null +++ b/telemeta/util/audiolab/TODO @@ -0,0 +1,28 @@ +.. vim:syntax=rest + +Things to do before a release +----------------------------- + - better error reporting (support error logging of sndfile ?): the + exceptions launched are pretty random... + - add inout argument in read method to avoid recreating a buffer. + - more tests + - add an audio player for at least linux/windows/mac os x (0.8-0.9) -> This + will be done in a different branch + +Pending issues +-------------- + - There is no check for supported format, the way get_supported_format is + implemented now it totally broken. Instead of using values from the + header, should use sf_command + - removing most warning from pylint + - handling in audiolab when first is not 0 (now, we have seek :)) + +Maybe ? +------- + - change array layout convention to numpy's ones (eg row major, not column + major), and add an axis argument. -> this is a bit tricky, actually, as + sndfile only supports interleaved data, where F order actually make sense. + Not sure what to do + - ogg vorbis support ? libsndfile does not support ogg (yet). Maybe use the + reference lib, but this may be a bit complicated to support both library + at the same time. diff --git a/telemeta/util/audiolab/build/lib/scikits/__init__.py b/telemeta/util/audiolab/build/lib/scikits/__init__.py new file mode 100644 index 00000000..de40ea7c --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/__init__.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/__init__.py new file mode 100644 index 00000000..12911595 --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/__init__.py @@ -0,0 +1,35 @@ +#! /usr/bin/env python +# Last Change: Mon Sep 10 07:00 PM 2007 J +""" +audiolab: a small toolbox to read, write and play audio to and from +numpy arrays. + +audiolab provides two API: + - one similar to matlab: this gives you wavread, wavwrite functions really + similar to matlab's functions. + - a more complete API, which can be used to read, write to many audio file + (including wav, aiff, flac, au, IRCAM, htk, etc...), with IO capabilities + not available to matlab (seek, append data, etc...) + +It is a thin wrapper around libsndfile from Erik Castro Lopo. + +Copyright (C) 2006-2007 Cournapeau David + +LICENSE: audiolab is licensed under the LGPL, as is libsndfile itself. See +COPYING.txt for details. """ + +from info import VERSION +__version__ = VERSION + +from pysndfile import formatinfo, sndfile +from pysndfile import supported_format, supported_endianness, \ + supported_encoding +#from scikits.audiolab.matapi import wavread, aiffread, flacread, auread, \ +# sdifread, wavwrite, aiffwrite, flacwrite, auwrite, sdifwrite +from matapi import * + +__all__ = filter(lambda s:not s.startswith('_'),dir()) + +from numpy.testing import NumpyTest +def test(): + return NumpyTest().test() diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/info.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/info.py new file mode 100644 index 00000000..47dd67f6 --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/info.py @@ -0,0 +1,2 @@ +VERSION = '0.8dev' +ignore = False diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/matapi.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/matapi.py new file mode 100644 index 00000000..1568fe9c --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/matapi.py @@ -0,0 +1,148 @@ +#! /usr/bin/env python +# Last Change: Mon Sep 10 07:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +"""This module implements functions to read and write to audio files easily +(ala matlab: wavread, etc...).""" + +import numpy as N + +from pysndfile import formatinfo, sndfile +from pysndfile import PyaudioException, FlacUnsupported + +__all__ = [] +_MATAPI_FORMAT = ['wav', 'aiff', 'au', 'sdif', 'flac'] +for i in _MATAPI_FORMAT: + __all__.extend(['%sread' % i, '%swrite' % i]) + +# writer function factory +def _writer_factory(name, format, def_fs, descr): + """ Create a writer function with fileformat described by format, default + sampling rate def_fs, and docstring descr.""" + def basic_writer(data, filename, fs = def_fs, enc = format.get_encoding()): + """Common "template" to all write functions.""" + if N.ndim(data) <= 1: + nc = 1 + nframes = N.size(data) + elif N.ndim(data) == 2: + nc = data.shape[1] + nframes = data.shape[0] + else: + RuntimeError("Only rank 0, 1, and 2 arrays supported as audio data") + + hdl = sndfile(filename, 'write', format, nc, fs) + try: + hdl.write_frames(data, nframes) + finally: + hdl.close() + doc = \ + """ wrapper around pysndfile to write %s file, + in a similar manner to matlab's wavwrite/auwrite and the likes. + + OVERWRITES EXISTING FILE ! + + Args: + - data: a rank 0, 1 (mono) or 2 (one channel per col) numpy array + - filename: a string for the audio file name + - fs: the sampling rate in Hz (%d Hz by default). + - enc: a string for the encoding such as 'pcm16', etc...(%s by + default). Not supported yet ! + + For a total control over options, such as endianness, append data to an + existing file, etc... you should use sndfile class instances instead""" \ + % (str(descr), def_fs, format.get_encoding()) + basic_writer.__doc__ = doc + basic_writer.__name__ = name + return basic_writer + +# template for reader functions +def _reader_factory(name, filetype, descr): + """Factory for reader functions ala matlab.""" + def basic_reader(filename, last = None, first = 0): + """Common "template" to all read functions.""" + hdl = sndfile(filename, 'read') + try: + if not hdl.get_file_format() == filetype: + raise PyaudioException("%s is not a %s file (is %s)" \ + % (filename, filetype, hdl.get_file_format())) + + fs = hdl.get_samplerate() + enc = hdl.get_encoding() + # Set the pointer to start position + nf = hdl.seek(first, 1) + if not nf == first: + raise IOError("Error while seeking at starting position") + + if last is None: + nframes = hdl.get_nframes() - first + data = hdl.read_frames(nframes) + else: + data = hdl.read_frames(last) + finally: + hdl.close() + + return data, fs, enc + doc = \ + """ wrapper around pysndfile to read a %s file in float64, + in a similar manner to matlab wavread/auread/etc... + + Returns a tuple (data, fs, enc), where : + - data are the read data (one column per channel) + - fs, the sampling rate + - enc, a string which is the encoding of the file, such as 'pcm16', + 'float32', etc... + + For a total control over options, such as output's dtype, etc..., + you should use sndfile class instances instead""" % (str(descr),) + basic_reader.__doc__ = doc + basic_reader.__name__ = name + return basic_reader + +wavread = _reader_factory('wavread', 'wav', + formatinfo('wav', 'pcm16').get_major_str()) +auread = _reader_factory('auread', 'au', + formatinfo('au', 'pcm16').get_major_str()) +aiffread = _reader_factory('aiffread', 'aiff', + formatinfo('aiff', 'pcm16').get_major_str()) +sdifread = _reader_factory('sdifread', 'ircam', + formatinfo('ircam', 'pcm16').get_major_str()) + +_f1 = formatinfo('wav', 'pcm16') +wavwrite = _writer_factory('wavwrite', _f1, 8000, _f1.get_major_str()) + +_f2 = formatinfo('au', 'ulaw') +auwrite = _writer_factory('auwrite', _f2, 8000, _f2.get_major_str()) + +_f3 = formatinfo('aiff', 'pcm16') +aiffwrite = _writer_factory('aiffwrite', _f3, 8000, _f3.get_major_str()) + +_f4 = formatinfo('ircam', 'pcm16') +sdifwrite = _writer_factory('sdifwrite', _f4, 44100, _f4.get_major_str()) + +try: + flacread = _reader_factory('flacread', 'flac', + formatinfo('flac', 'pcm16').get_major_str()) + _f5 = formatinfo('flac', 'pcm16') + flacwrite = _writer_factory('flacwrite', _f5, 44100, _f5.get_major_str()) +except FlacUnsupported,e: + print e + print "Matlab API for FLAC is disabled" + def missing_flacread(*args): + raise UnimplementedError("Matlab API for FLAC is disabled on your "\ + "installation") + flacread = missing_flacread + flacwrite = missing_flacread diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/pyaudioio.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/pyaudioio.py new file mode 100644 index 00000000..94c1e9fc --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/pyaudioio.py @@ -0,0 +1,68 @@ +#! /usr/bin/env python +# Last Change: Tue May 22 10:00 AM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) any +# later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this library; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# TODO: +# - find out why finally does not work with KeyboardInterrupt instances + +from tempfile import mkstemp +from os import remove, popen + +from pysndfile import sndfile, formatinfo as format + +def play(input, sr = 22050): + """play(input, sr = 22050): "play" a numpy array input + to the audio device using aplay command, @ sampling rate + sr. + + Warning: This is really crude, as it copies the numpy array + into an audio file before writing: I am too lazy to write + interfaces to also, windows and co...""" + # Check inputs are OK + if input.ndim == 1: + nc = 1 + nframes = input.size + else: + (nframes, nc) = input.shape + + # Create tmp file + fd, filename = mkstemp('py_player') + + # Copy the data into it + b = sndfile(filename, 'write', format('wav', 'pcm16'), nc, sr) + b.write_frames(input, nframes) + b.close() + + # Play using an audio command + try: + cmd = "aplay %s" % filename + popen(cmd) + remove(filename) + except KeyboardInterrupt, inst: + remove(filename) + raise inst + +if __name__ == '__main__': + # Read the content of a file into numpy array, and play the numpy + # array using the play command + import numpy as N + sr = 22050 + # Play a really small noise to avoid screaming in loudspeakers + # or headphones. + noise = 0.0001 * N.random.randn((sr)) + play(noise, sr) diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/pysndfile.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/pysndfile.py new file mode 100644 index 00000000..2fea1fef --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/pysndfile.py @@ -0,0 +1,992 @@ +#! /usr/bin/env python +# Last Change: Wed Oct 03 05:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# vim:syntax=python + +# TODO: +# - import format classes so that we get meaningful information from an +# existing format +# - better API for reader/writer, including integer formats and partial +# reading +# - ability to get log of sndfile ? +# - check how to play sound under windows, OS X and other UNIX + +"""This module implements the wrappers around libsndfile.""" + +__docformat__ = 'restructuredtext' + +#__all__ = ['sndfile', 'formatinfo'] + +import copy +import warnings + +#================ +# Load libsndfile +#================ +import ctypes +from ctypes import cdll, Structure, c_int, pointer, POINTER, \ + create_string_buffer, c_char_p, sizeof, string_at +try: + from ctypes import c_int64 +except ImportError, e: + print "Cannot import c_int64 from ctypes: if you are on ubuntu/debian," +\ + " this is likely because ctypes was compiled with libffi. see" +\ + " https://launchpad.net/ubuntu/+source/python2.5/+bug/71914" + raise e + +from numpy.ctypeslib import ndpointer +CTYPES_MAJOR = int(ctypes.__version__.split('.')[0]) +CTYPES_MINOR = int(ctypes.__version__.split('.')[1]) +CTYPES_MICRO = int(ctypes.__version__.split('.')[2]) +if CTYPES_MAJOR < 1 or (CTYPES_MINOR == 0 and CTYPES_MICRO < 1): + raise ImportError("version of ctypes is %s, expected at least %s" \ + % (ctypes.__version__, '1.0.1')) +import numpy as N + +_SND = cdll.LoadLibrary('/usr/lib/libsndfile.so.1') + +#========================= +# Definition of constants +#========================= +# READ/WRITE Mode +SFM = { + 'SFM_WRITE' : 0x20, + 'SFM_RDWR' : 0x30, + 'SFM_READ' : 0x10 +} + +# SF BOOL +SF_BOOL = { + 'SF_TRUE' : 1, + 'SF_FALSE' : 0 +} + +# Format +SF_FORMAT = { + 'SF_FORMAT_VOX_ADPCM' : 0x0021, + 'SF_FORMAT_FLOAT' : 0x0006, + 'SF_FORMAT_PCM_S8' : 0x0001, + 'SF_FORMAT_IMA_ADPCM' : 0x0012, + 'SF_FORMAT_SVX' : 0x060000, + 'SF_FORMAT_VOC' : 0x080000, + 'SF_FORMAT_PCM_U8' : 0x0005, + 'SF_FORMAT_ALAW' : 0x0011, + 'SF_FORMAT_G721_32' : 0x0030, + 'SF_FORMAT_DWVW_N' : 0x0043, + 'SF_FORMAT_WAV' : 0x010000, + 'SF_FORMAT_SD2' : 0x160000, + 'SF_FORMAT_HTK' : 0x100000, + 'SF_FORMAT_ENDMASK' : 0x30000000, + 'SF_FORMAT_DPCM_16' : 0x0051, + 'SF_FORMAT_DWVW_24' : 0x0042, + 'SF_FORMAT_PCM_32' : 0x0004, + 'SF_FORMAT_WAVEX' : 0x130000, + 'SF_FORMAT_DOUBLE' : 0x0007, + 'SF_FORMAT_NIST' : 0x070000, + 'SF_FORMAT_PCM_16' : 0x0002, + 'SF_FORMAT_RAW' : 0x040000, + 'SF_FORMAT_W64' : 0x0B0000, + 'SF_FORMAT_PVF' : 0x0E0000, + 'SF_FORMAT_AU' : 0x030000, + 'SF_FORMAT_GSM610' : 0x0020, + 'SF_FORMAT_CAF' : 0x180000, + 'SF_FORMAT_PAF' : 0x050000, + 'SF_FORMAT_ULAW' : 0x0010, + 'SF_FORMAT_MAT4' : 0x0C0000, + 'SF_FORMAT_MAT5' : 0x0D0000, + 'SF_FORMAT_XI' : 0x0F0000, + 'SF_FORMAT_SUBMASK' : 0x0000FFFF, + 'SF_FORMAT_DPCM_8' : 0x0050, + 'SF_FORMAT_G723_24' : 0x0031, + 'SF_FORMAT_G723_40' : 0x0032, + 'SF_FORMAT_DWVW_16' : 0x0041, + 'SF_FORMAT_AIFF' : 0x020000, + 'SF_FORMAT_DWVW_12' : 0x0040, + 'SF_FORMAT_TYPEMASK' : 0x0FFF0000, + 'SF_FORMAT_FLAC' : 0x170000, + 'SF_FORMAT_PCM_24' : 0x0003, + 'SF_FORMAT_SDS' : 0x110000, + 'SF_FORMAT_IRCAM' : 0x0A0000, + 'SF_FORMAT_MS_ADPCM' : 0x0013, + 'SF_FORMAT_AVR' : 0x120000 +} + +# ENDIANESS +SF_ENDIAN = { + 'SF_ENDIAN_BIG' : 0x20000000, + 'SF_ENDIAN_FILE' : 0x00000000, + 'SF_ENDIAN_LITTLE' : 0x10000000, + 'SF_ENDIAN_CPU' : 0x30000000 +} + +# Commands +SF_COMMAND = { + 'SFC_GET_LIB_VERSION' : 0x1000, + 'SFC_CALC_SIGNAL_MAX' : 0x1040, + 'SFC_GET_DITHER_INFO' : 0x10A3, + 'SFC_GET_LOG_INFO' : 0x1001, + 'SFC_GET_FORMAT_SUBTYPE_COUNT' : 0x1032, + 'SFC_FILE_TRUNCATE' : 0x1080, + 'SFC_GET_INSTRUMENT' : 0x10D0, + 'SFC_UPDATE_HEADER_NOW' : 0x1060, + 'SFC_SET_DITHER_ON_WRITE' : 0x10A0, + 'SFC_SET_NORM_DOUBLE' : 0x1012, + 'SFC_GET_CLIPPING' : 0x10C1, + 'SFC_SET_RAW_START_OFFSET' : 0x1090, + 'SFC_CALC_NORM_MAX_ALL_CHANNELS' : 0x1043, + 'SFC_SET_NORM_FLOAT' : 0x1013, + 'SFC_SET_ADD_DITHER_ON_WRITE' : 0x1070, + 'SFC_GET_NORM_FLOAT' : 0x1011, + 'SFC_GET_SIGNAL_MAX' : 0x1044, + 'SFC_GET_MAX_ALL_CHANNELS' : 0x1045, + 'SFC_GET_FORMAT_MAJOR' : 0x1031, + 'SFC_SET_INSTRUMENT' : 0x10D1, + 'SFC_CALC_MAX_ALL_CHANNELS' : 0x1042, + 'SFC_GET_DITHER_INFO_COUNT' : 0x10A2, + 'SFC_SET_BROADCAST_INFO' : 0x10F1, + 'SFC_SET_DITHER_ON_READ' : 0x10A1, + 'SFC_GET_FORMAT_MAJOR_COUNT' : 0x1030, + 'SFC_GET_FORMAT_INFO' : 0x1028, + 'SFC_GET_SIMPLE_FORMAT_COUNT' : 0x1020, + 'SFC_CALC_NORM_SIGNAL_MAX' : 0x1041, + 'SFC_GET_LOOP_INFO' : 0x10E0, + 'SFC_SET_ADD_PEAK_CHUNK' : 0x1050, + 'SFC_SET_ADD_DITHER_ON_READ' : 0x1071, + 'SFC_SET_SCALE_FLOAT_INT_READ' : 0x1014, + 'SFC_GET_FORMAT_SUBTYPE' : 0x1033, + 'SFC_TEST_IEEE_FLOAT_REPLACE' : 0x6001, + 'SFC_SET_UPDATE_HEADER_AUTO' : 0x1061, + 'SFC_GET_SIMPLE_FORMAT' : 0x1021, + 'SFC_SET_CLIPPING' : 0x10C0, + 'SFC_GET_EMBED_FILE_INFO' : 0x10B0, + 'SFC_GET_BROADCAST_INFO' : 0x10F0, + 'SFC_GET_NORM_DOUBLE' : 0x1010 +} + +SF_ERRORS = { + 'SF_ERR_UNRECOGNISED_FORMAT' : 1, + 'SF_ERR_NO_ERROR' : 0, + 'SF_ERR_SYSTEM' : 2, + 'SF_ERR_UNSUPPORTED_ENCODING' : 4, + 'SF_ERR_MALFORMED_FILE' : 3 +} + +# format equivalence: dic used to create internally +# the right enum values from user friendly strings +py_to_snd_encoding_dic = { + 'pcms8' : SF_FORMAT['SF_FORMAT_PCM_S8'], + 'pcm16' : SF_FORMAT['SF_FORMAT_PCM_16'], + 'pcm24' : SF_FORMAT['SF_FORMAT_PCM_24'], + 'pcm32' : SF_FORMAT['SF_FORMAT_PCM_32'], + + 'pcmu8' : SF_FORMAT['SF_FORMAT_PCM_U8'], + + 'float32' : SF_FORMAT['SF_FORMAT_FLOAT'], + 'float64' : SF_FORMAT['SF_FORMAT_DOUBLE'], + + 'ulaw' : SF_FORMAT['SF_FORMAT_ULAW'], + 'alaw' : SF_FORMAT['SF_FORMAT_ALAW'], + 'ima_adpcm' : SF_FORMAT['SF_FORMAT_IMA_ADPCM'], + 'ms_adpcm' : SF_FORMAT['SF_FORMAT_MS_ADPCM'], + + 'gsm610' : SF_FORMAT['SF_FORMAT_GSM610'], + 'vox_adpcm' : SF_FORMAT['SF_FORMAT_VOX_ADPCM'], + + 'g721_32' : SF_FORMAT['SF_FORMAT_G721_32'], + 'g723_24' : SF_FORMAT['SF_FORMAT_G723_24'], + 'g723_40' : SF_FORMAT['SF_FORMAT_G723_40'], + + 'dww12' : SF_FORMAT['SF_FORMAT_DWVW_12'], + 'dww16' : SF_FORMAT['SF_FORMAT_DWVW_16'], + 'dww24' : SF_FORMAT['SF_FORMAT_DWVW_24'], + 'dwwN' : SF_FORMAT['SF_FORMAT_DWVW_N'], + + 'dpcm8' : SF_FORMAT['SF_FORMAT_DPCM_8'], + 'dpcm16': SF_FORMAT['SF_FORMAT_DPCM_16'] +} + +py_to_snd_file_format_dic = { + 'wav' : SF_FORMAT['SF_FORMAT_WAV'], + 'aiff' : SF_FORMAT['SF_FORMAT_AIFF'], + 'au' : SF_FORMAT['SF_FORMAT_AU'], + 'raw' : SF_FORMAT['SF_FORMAT_RAW'], + 'paf' : SF_FORMAT['SF_FORMAT_PAF'], + 'svx' : SF_FORMAT['SF_FORMAT_SVX'], + 'nist' : SF_FORMAT['SF_FORMAT_NIST'], + 'voc' : SF_FORMAT['SF_FORMAT_VOC'], + 'ircam' : SF_FORMAT['SF_FORMAT_IRCAM'], + 'wav64' : SF_FORMAT['SF_FORMAT_W64'], + 'mat4' : SF_FORMAT['SF_FORMAT_MAT4'], + 'mat5' : SF_FORMAT['SF_FORMAT_MAT5'], + 'pvf' : SF_FORMAT['SF_FORMAT_PVF'], + 'xi' : SF_FORMAT['SF_FORMAT_XI'], + 'htk' : SF_FORMAT['SF_FORMAT_HTK'], + 'sds' : SF_FORMAT['SF_FORMAT_SDS'], + 'avr' : SF_FORMAT['SF_FORMAT_AVR'], + 'wavex' : SF_FORMAT['SF_FORMAT_WAVEX'], + 'sd2' : SF_FORMAT['SF_FORMAT_SD2'], + 'flac' : SF_FORMAT['SF_FORMAT_FLAC'], + 'caf' : SF_FORMAT['SF_FORMAT_CAF'] +} + +py_to_snd_endianness_dic = { + 'file' : SF_ENDIAN['SF_ENDIAN_FILE'], + 'little' : SF_ENDIAN['SF_ENDIAN_LITTLE'], + 'big' : SF_ENDIAN['SF_ENDIAN_BIG'], + 'cpu' : SF_ENDIAN['SF_ENDIAN_CPU'] +} + +# Those following dic are used internally to get user-friendly values from +# sndfile enum +SND_TO_PY_ENCODING = \ + dict([(i, j) for j, i in py_to_snd_encoding_dic.items()]) +SND_TO_PY_FILE_FORMAT = \ + dict([(i, j) for j, i in py_to_snd_file_format_dic.items()]) +SND_TO_PY_ENDIANNESS = \ + dict([(i, j) for j, i in py_to_snd_endianness_dic.items()]) + +#========================================== +# Check that libsndfile is expected version +#========================================== +def get_libsndfile_version(): + nverbuff = 256 + verbuff = create_string_buffer(nverbuff) + n = _SND.sf_command(c_int(0), c_int(SF_COMMAND['SFC_GET_LIB_VERSION']), + verbuff, nverbuff) + if n < 1: + raise Exception("Error while getting version of libsndfile") + + # Transform the buffer into a string + ver = "" + for i in range(n): + ver += verbuff[i] + + # Get major, minor and micro from version + version = ver.split('-')[1] + prerelease = 0 + major, minor, micro = [i for i in version.split('.')] + try: + micro = int(micro) + except ValueError,e: + print "micro is " + str(micro) + micro, prerelease = micro.split('pre') + + return int(major), int(minor), int(micro), prerelease + +MAJOR, MINOR, MICRO, PRERELEASE = get_libsndfile_version() +if not(MAJOR == 1): + raise Exception("audiolab expects major version %d of libsndfile" % 1) +if not(MICRO == 17): + if PRERELEASE == 0: + prestr = "No" + else: + prestr = "%s" % PRERELEASE + print "WARNING libsndfile-%d.%d.%d (prerelease: %s) "\ + "this has only been tested with libsndfile 1.0.17 for now, "\ + "use at your own risk !" % (MAJOR, MINOR, MICRO, prestr) + +#================ +# Python wrappers +#================ + +#+++++++++++++++++ +# Public exception +#+++++++++++++++++ +class PyaudioException(Exception): + pass + +class InvalidFormat(PyaudioException): + pass + +class PyaudioIOError(PyaudioException, IOError): + pass + +class WrappingError(PyaudioException): + pass + +class FlacUnsupported(RuntimeError, PyaudioException): + pass + +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Private classes/function (Should not be used outside this file) +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +class _sf_info(Structure): + """Structure representing C structure SF_INFO""" + _fields_ = [('frames', c_int64), + ('samplerate', c_int), + ('channels', c_int), + ('format', c_int), + ('sections', c_int), + ('seekable', c_int)] + def __str__(self): + return "%d frames, sr = %d Hz, %d channels, format is %d" % \ + (self.frames, self.samplerate, self.channels, self.format) + +class _sf_format_info(Structure): + """Structure representing C structure SF_FORMAT_INFO (useful for + sf_command )""" + _fields_ = [('format', c_int), + ('name', c_char_p), + ('extension', c_char_p)] + def __str__(self): + return "format hex is %#010x, name is %s, extension is %s" % \ + (self.format, self.name, self.extension) + + def __repr__(self): + print self.__str__() + +class _sndfile(Structure): + pass + +sf_info_p = POINTER(_sf_info) +sndfile_p = POINTER(_sndfile) + +# functions args +# TODO: is there a way to ensure that arg1 is the right kind of pointer ? +arg1 = c_char_p +arg2 = c_int +arg3 = sf_info_p +_SND.sf_open.argtypes = [arg1, arg2, arg3] +_SND.sf_open.restype = sndfile_p + +arg1 = sndfile_p +_SND.sf_close.argtypes = [arg1] +_SND.sf_close.restype = c_int + +arg1 = c_int +arg2 = c_int +arg3 = sf_info_p +arg4 = c_int +_SND.sf_open_fd.argtypes = [arg1, arg2, arg3, arg4] +_SND.sf_open_fd.restype = sndfile_p + +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.float64) +arg3 = c_int64 + +# double function +_SND.sf_readf_double.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_double.restype = c_int64 + +_SND.sf_writef_double.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_double.restype = c_int64 + +# float function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.float32) +arg3 = c_int64 +_SND.sf_readf_float.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_float.restype = c_int64 + +_SND.sf_writef_float.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_float.restype = c_int64 + +# int function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.int32) +arg3 = c_int64 +_SND.sf_readf_int.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_int.restype = c_int64 + +_SND.sf_writef_int.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_int.restype = c_int64 + +# short function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.int16) +arg3 = c_int64 +_SND.sf_readf_short.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_short.restype = c_int64 + +_SND.sf_writef_short.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_short.restype = c_int64 + +# Error functions +arg1 = sndfile_p +_SND.sf_strerror.argtypes = [arg1] +_SND.sf_strerror.restype = c_char_p + +# Function to sync data to file +arg1 = sndfile_p +_SND.sf_write_sync.argtypes = [arg1] + +# Function to seek +arg1 = sndfile_p +arg2 = c_int64 +arg3 = c_int +_SND.sf_seek.argtypes = [arg1, arg2, arg3] +_SND.sf_seek.restype = c_int64 + +# To pass when a C function needs a NULL arg +_cNULL = POINTER(c_int)() + +class _format_from_internal: + """Class to handle audio format with sndfile. + + DO NOT USE THIS CLASS OUTSIDE pysndfile.py MODULE: YOU MAY CRASH YOUR + INTERPRETER ! + + Basically, we have 3 classes of parameters: + - the main format: (major format), like wav, aiff, etc... + - the subtype format: pcm, bits resolution + - endianness: little, big, as the cpu, default of the format + + This class encapsulates those parameters, and can build a representation of + them from the format integer of sf_info. This should *NOT* be used, use + format instead, which inherits this class to build a valid format from user + friendly arguments. """ + def __init__(self, format_integer): + # Get the internal values which corresponds to the values libsndfile + # can understand + self._int_type = format_integer & SF_FORMAT['SF_FORMAT_TYPEMASK'] + self._int_encoding = format_integer & SF_FORMAT['SF_FORMAT_SUBMASK'] + self._int_endianness = format_integer & SF_FORMAT['SF_FORMAT_ENDMASK'] + + assert format_integer == self._int_type | self._int_encoding |\ + self._int_endianness + self._format = format_integer + + # Now, we need to test if the combination of format, encoding and + # endianness is valid. sf_format_check needs also samplerate and + # channel information, so just give a fake samplerate and channel + # number. Looking at sndfile.c, it looks like samplerate is never + # actually checked, and that when channels is checked, it is only + # checked against values different than 1 or 2, so giving a value of + # 1 to channel should be ok. + self._sfinfo = _sf_info() + self._sfinfo.channels = 1 + self._sfinfo.samplerate = 8000 + self._sfinfo.format = self._format + + ret = _SND.sf_format_check(pointer(self._sfinfo)) + if ret is not SF_BOOL['SF_TRUE']: + raise InvalidFormat() + + # Get the sndfile string description of the format type + blop = _sf_format_info() + blop.format = self._int_type + st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \ + pointer(blop), sizeof(blop)) + if st is not 0: + if SND_TO_PY_FILE_FORMAT[self._int_type] == 'flac': + raise FlacUnsupported("Flac is not supported by your version"\ + " of libsndfile") + else: + raise WrappingError("Could not get format string for format "\ + "%d, " % blop.format + "please report this problem "\ + "to the maintainer") + + self.format_str = blop.name + + # Get the sndfile string description of the format subtype + blop.format = self._int_encoding + st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \ + pointer(blop), sizeof(blop)) + if st is not 0: + raise WrappingError() + + self.encoding_str = blop.name + + def get_format_raw(self): + """Do not use this function !""" + return self._format + + def get_major_str(self): + """Do not use this function !""" + return self.format_str + + def get_encoding_str(self): + """Do not use this function !""" + return self.encoding_str + + def get_file_format(self): + """return user friendly file format string""" + return SND_TO_PY_FILE_FORMAT[self._int_type] + + def get_encoding(self): + """return user friendly encoding string""" + return SND_TO_PY_ENCODING[self._int_encoding] + + def get_endianness(self): + """return user friendly file format string""" + return SND_TO_PY_ENDIANNESS[self._int_endianness] + + # Various function + def is_type(self, t): + return (self._format & SF_FORMAT['SF_FORMAT_TYPEMASK']) \ + == py_to_snd_file_format_dic[t] + + # Syntactic sugar + def __str__(self): + return "Major Format: %s, Encoding Format: %s" % \ + (self.format_str, self.encoding_str) + + def __repr__(self): + return self.__str__() + +#+++++++++++ +# Public API +#+++++++++++ + +class formatinfo(_format_from_internal): + def __init__(self, type = 'wav', encoding = 'pcm16', endianness = 'file'): + """Build a valid format usable by the sndfile class when opening an + audio file for writing. + + Blah blah + + :Parameters: + type : string + represents the major file format (wav, etc...). + encoding : string + represents the encoding (pcm16, etc..). + endianness : string + represents the endianess. + + Notes + ----- + + Valid type strings are listed by file_format_dic.keys() Valid encoding + strings are listed by encoding_dic.keys() Valid endianness strings are + listed by endianness_dic.keys() """ + # Keep the arguments + self.type = type + self.encoding = encoding + self.endianness = endianness + + # Get the internal values which corresponds to the values libsndfile + # can understand + self._int_type = py_to_snd_file_format_dic[type] + self._int_encoding = py_to_snd_encoding_dic[encoding] + self._int_endianness = py_to_snd_endianness_dic[endianness] + + # Build the internal integer from parameters, and pass it to the super + # class, which will do all the work + format = self._int_type | self._int_encoding | self._int_endianness + + _format_from_internal.__init__(self, format) + +class sndfile: + """Main class to open, read and write audio files""" + def __init__(self, filename, mode = 'read', format = None, channels = 0, \ + samplerate = 0): + """Create an instance of sndfile. + + :Parameters: + filename : string or int + name of the file to open (string), or file descriptor (integer) + mode : string + 'read' for read, 'write' for write, or 'rwrite' for read and + write. + format : formatinfo + when opening a new file for writing, give the format to write + in. + channels : int + number of channels. + samplerate : int + sampling rate. + + :Returns: + sndfile: a valid sndfile object + + Notes + ----- + + format, channels and samplerate need to be given only in the write + modes and for raw files. """ + # Check the mode is one of the expected values + if mode == 'read': + sfmode = SFM['SFM_READ'] + elif mode == 'write': + sfmode = SFM['SFM_WRITE'] + if format == None: + raise Exception("For write mode, you should provide"\ + "a format argument !") + elif mode == 'rwrite': + sfmode = SFM['SFM_RDWR'] + if format == None: + raise Exception("For write mode, you should provide"\ + "a format argument !") + else: + raise Exception("mode %s not recognized" % str(mode)) + + sfinfo = _sf_info() + sfinfo_p = pointer(sfinfo) + + # Fill the sfinfo struct + sfinfo.frames = c_int64(0) + if type(channels) is not int: + print "Warning, channels is converted to int, was %s" % \ + str(type(channels)) + sfinfo.channels = int(channels) + else: + sfinfo.channels = channels + + if type(samplerate) is not int: + print "Warning, sampling rate is converted to int, was %s" % \ + str(type(samplerate)) + sfinfo.samplerate = int(samplerate) + else: + sfinfo.samplerate = samplerate + + sfinfo.sections = 0 + sfinfo.seekable = False + if mode == 'read' and format == None: + sfinfo.format = 0 + else: + if sfinfo.channels > 256 or sfinfo.channels < 1: + msg = "number of channels is %d, expected " \ + "between 1 and 256" % sfinfo.channels + raise RuntimeError(msg) + sfinfo.format = format.get_format_raw() + if not _SND.sf_format_check(sfinfo_p): + msg = "unknown error in format specification ?" +\ + " Please report this to the author" + raise WrappingError() + + sfinfo_p = pointer(sfinfo) + self._sfmode = sfmode + self.hdl = 0 + + if type(filename) == int: + res = _SND.sf_open_fd(filename, self._sfmode, sfinfo_p, + SF_BOOL['SF_FALSE']) + self._byfd = True + self.fd = filename + self.filename = "" + else: + res = _SND.sf_open(filename, self._sfmode, sfinfo_p) + self._byfd = False + self.filename = filename + + try: + # If res is NULL, this statement will raise a ValueError exception + a = res[0] + except ValueError: + if self._byfd: + msg = "error while opening file descriptor %d\n\t->" % self.fd + else: + msg = "error while opening file %s\n\t-> " % self.filename + msg += _SND.sf_strerror(res) + if self._byfd: + msg += """ +(Check that the mode argument passed to sndfile is the same than the one used +when getting the file descriptor, eg do not pass 'read' to sndfile if you +passed 'write' to open to get the file descriptor. If you are on win32, you are +out of luck, because its implementation of POSIX open is broken)""" + raise IOError("error while opening %s\n\t->%s" % (filename, msg)) + + if mode == 'read': + tmp = _format_from_internal(sfinfo.format) + self._format = formatinfo(tmp.get_file_format(), \ + tmp.get_encoding(), tmp.get_endianness()) + else: + self._format = format + + self._sfinfo = sfinfo + self.hdl = res + + if self.get_file_format() == 'flac': + def SeekNotEnabled(self, *args): + raise FlacUnsupported("seek not supported on Flac by default,"\ + " because\n some version of FLAC libraries are buggy."\ + " Read FLAC_SUPPORT.txt") + self.seek = SeekNotEnabled + else: + self.seek = self._seek + + def __del__(self, close_func = _SND.sf_close): + # Stupid python needs the close_func, otherwise + # it may clean ctypes before calling here + if hasattr(self,'hdl'): + if not(self.hdl == 0): + close_func(self.hdl) + self.hdl = 0 + + def close(self): + """close the file.""" + self.__del__() + + def sync(self): + """call the operating system's function to force the writing of all + file cache buffers to disk the file. + + No effect if file is open as read""" + _SND.sf_write_sync(self.hdl) + + def _seek(self, offset, whence = 0, mode = 'rw'): + """similar to python seek function, taking only in account audio data. + + :Parameters: + offset : int + the number of frames (eg two samples for stereo files) to move + relatively to position set by whence. + whence : int + only 0 (beginning), 1 (current) and 2 (end of the file) are + valid. + mode : string + If set to 'rw', both read and write pointers are updated. If + 'r' is given, only read pointer is updated, if 'w', only the + write one is (this may of course make sense only if you open + the file in a certain mode). + + Notes + ----- + + - one only takes into accound audio data. + - if an invalid seek is given (beyond or before the file), a + PyaudioIOError is launched.""" + c_offset = _num2int64(offset) + if mode == 'rw': + # Update both read and write pointers + st = _SND.sf_seek(self.hdl, c_offset, whence) + elif mode == 'r': + whence = whence | SFM['SFM_READ'] + st = _SND.sf_seek(self.hdl, c_offset, whence) + elif mode == 'w': + whence = whence | SFM['SFM_WRITE'] + st = _SND.sf_seek(self.hdl, c_offset, whence) + else: + raise ValueError("mode should be one of 'r', 'w' or 'rw' only") + + if st == -1: + msg = "Error while seeking, libsndfile error is %s" \ + % (_SND.sf_strerror(self.hdl)) + raise PyaudioIOError(msg) + return st + + # Functions to get informations about the file + def get_nframes(self): + """ Return the number of frames of the file""" + if self._sfmode == SFM['SFM_READ']: + # XXX: is this reliable for any file (think pipe and co ?) + return self._sfinfo.frames + else: + # In write/rwrite mode, the only reliable way to get the number of + # frames is to use seek. + raise NotImplementedError("Sorry, getting the current number of" + "frames in write modes is not supported yet") + + def get_samplerate(self): + """ Return the samplerate in Hz of the file""" + return self._sfinfo.samplerate + + def get_channels(self): + """ Return the number of channels of the file""" + return self._sfinfo.channels + + def get_file_format(self): + """return user friendly file format string""" + return SND_TO_PY_FILE_FORMAT[self._format._int_type] + + def get_encoding(self): + """return user friendly encoding string""" + return SND_TO_PY_ENCODING[self._format._int_encoding] + + def get_endianness(self): + """return user friendly file format string""" + return SND_TO_PY_ENDIANNESS[self._format._int_endianness] + + #------------------ + # Functions to read + #------------------ + def read_frames(self, nframes, dtype = N.float64): + """Read nframes frames of the file. + + :Parameters: + nframes : int + number of frames to read. + dtype : numpy dtype + dtype of the returned array containing read data (see note). + + Notes + ----- + + - read_frames updates the read pointer. + - One column is one channel. + - if float are requested when the file contains integer data, you will + get normalized data (that is the max possible integer will be 1.0, + and the minimal possible value -1.0). + - if integers are requested when the file contains floating point data, + it may give wrong results because there is an ambiguity: if the + floating data are normalized, you can get a file with only 0 ! + Getting integer data from files encoded in normalized floating point + is not supported (yet: sndfile supports it).""" + c_nframes = _num2int64(nframes) + if c_nframes < 0: + raise ValueError("number of frames has to be >= 0") + + # XXX: inout argument + if self._sfinfo.channels > 1: + y = N.zeros((nframes, self._sfinfo.channels), dtype) + else: + y = N.zeros(nframes, dtype) + + if dtype == N.float64: + res = _SND.sf_readf_double(self.hdl, y, c_nframes) + elif dtype == N.float32: + res = _SND.sf_readf_float(self.hdl, y, c_nframes) + elif dtype == N.int32: + res = _SND.sf_readf_int(self.hdl, y, c_nframes) + elif dtype == N.int16: + res = _SND.sf_readf_short(self.hdl, y, c_nframes) + else: + RuntimeError("Sorry, only float, double, int and short read " + \ + "supported for now") + + if not(res == nframes): + msg = "Read %d frames, expected to read %d" % (res, nframes) + msg += ", libsndfile last msg is \n\t%s" \ + % _SND.sf_strerror(self.hdl) + raise IOError(msg) + + return y + + #------------------- + # Functions to write + #------------------- + # TODO: Think about overflow vs type of input, etc... + def write_frames(self, input, nframes = -1): + """write data to file. + + :Parameters: + input : ndarray + array containing data to write. + nframes : int + number of frames to write. + + Notes + ----- + + - one channel is one column + - updates the write pointer. + - if float are given when the file contains integer data, you should + put normalized data (that is the range [-1..1] will be written as the + maximum range allowed by the integer bitwidth).""" + # First, get the number of channels and frames from input + if input.ndim == 1: + nc = 1 + else: + if input.ndim > 2: + raise Exception("Expect array of rank <= 2, got %d" \ + % input.ndim) + nc = input.shape[1] + + if nframes == -1: + nframes = N.size(input) + # Number of channels should be the one expected + if not(nc == self._sfinfo.channels): + raise Exception("Expected %d channels, got %d" % \ + (self._sfinfo.channels, nc)) + + # Writing to the file + c_nframes = _num2int64(nframes) + if c_nframes < 0: + raise ValueError("number of frames has to be >= 0") + + input = N.require(input, requirements = 'C') + + if input.dtype == N.float32: + if self._check_overflow(input): + warnings.warn("Warning, overflow detected when writing.") + res = _SND.sf_writef_float(self.hdl, input, c_nframes) + elif input.dtype == N.float64: + self._check_overflow(input) + if self._check_overflow(input): + warnings.warn("Warning, overflow detected when writing.") + res = _SND.sf_writef_double(self.hdl, input, c_nframes) + elif input.dtype == N.int32: + res = _SND.sf_writef_int(self.hdl, input, c_nframes) + elif input.dtype == N.int16: + res = _SND.sf_writef_short(self.hdl, input, c_nframes) + else: + raise Exception("type of input not understood: input should" + " be float64 or float32""") + + if not(res == nframes): + raise IOError("write %d frames, expected to write %d" \ + % res, nframes) + + def _check_overflow(self, data): + if N.max(data ** 2) >= 1.: + return True + return False + + # Syntactic sugar + def __repr__(self): + return self.__str__() + + def __str__(self): + repstr = "----------------------------------------\n" + if self._byfd: + repstr += "File : %d (opened by file descriptor)\n" % self.fd + else: + repstr += "File : %s\n" % self.filename + repstr += "Channels : %d\n" % self._sfinfo.channels + repstr += "Sample rate : %d\n" % self._sfinfo.samplerate + repstr += "Frames : %d\n" % self._sfinfo.frames + repstr += "Raw Format : %#010x -> %s\n" % \ + (self._format.get_format_raw(), self._format.get_major_str()) + repstr += "File format : %s\n" % self.get_file_format() + repstr += "Encoding : %s\n" % self.get_encoding() + repstr += "Endianness : %s\n" % self.get_endianness() + repstr += "Sections : %d\n" % self._sfinfo.sections + if self._sfinfo.seekable: + seek = 'True' + else: + seek = 'False' + repstr += "Seekable : %s\n" % seek + repstr += "Duration : %s\n" % self._generate_duration_str() + return repstr + + def _generate_duration_str(self): + if self._sfinfo.samplerate < 1: + return None + tsec = self._sfinfo.frames / self._sfinfo.samplerate + hrs = tsec / 60 / 60 + tsec = tsec % (60 ** 2) + mins = tsec / 60 + tsec = tsec % 60 + secs = tsec + ms = 1000 * self._sfinfo.frames / self._sfinfo.samplerate % 1000 + + return "%02d:%02d:%02d.%3d" % (hrs, mins, secs, ms) + +def supported_format(): + # XXX: broken + return py_to_snd_file_format_dic.keys() + +def supported_endianness(): + # XXX: broken + return py_to_snd_endianness_dic.keys() + +def supported_encoding(): + # XXX: broken + return py_to_snd_encoding_dic.keys() + +def _num2int64(value): + """ Convert a python objet to a c_int64, safely.""" + if not (type(value) == int or type(value) == long): + value = long(value) + print "Warning, converting %s to long" % str(value) + c_value = c_int64(value) + if not c_value.value == value: + raise RuntimeError("Error while converting %s to a c_int64"\ + ", maybe %s is too big ?" % str(value)) + return c_value diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/__init__.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/__init__.py new file mode 100644 index 00000000..7f2fbc0c --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/__init__.py @@ -0,0 +1,2 @@ +#! /usr/bin/env python +# Last Change: Mon May 21 12:00 PM 2007 J diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/test_matapi.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/test_matapi.py new file mode 100644 index 00000000..fe17b8e9 --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/test_matapi.py @@ -0,0 +1,164 @@ +#! /usr/bin/env python +# Last Change: Tue Jul 17 11:00 AM 2007 J +from os.path import join, dirname +from os import remove +from tempfile import mkstemp + +from numpy.testing import * +import numpy as N + +set_package_path() +from audiolab.matapi import wavread, auread, aiffread, sdifread, flacread +from audiolab.matapi import wavwrite, auwrite, aiffwrite, sdifwrite, flacwrite +from audiolab.pysndfile import PyaudioException, sndfile, formatinfo as audio_format +from audiolab.pysndfile import FlacUnsupported +restore_path() + +#Optional: +set_local_path() +# import modules that are located in the same directory as this file. +from testcommon import open_tmp_file, close_tmp_file +restore_path() + +class test_audiolab(NumpyTestCase): + def _test_read(self, func, format, filext): + # Create a tmp audio file, write some random data into it, and check it + # is the expected data when read from a function from the matapi. + rfd, fd, cfilename = open_tmp_file('pysndfiletest.' + filext) + try: + nbuff = 22050 + noise = 0.1 * N.random.randn(nbuff) + + # Open the copy file for writing + b = sndfile(cfilename, 'write', format, 1, nbuff) + b.write_frames(noise, nbuff) + b.close() + + # Reread the data + b = sndfile(cfilename, 'read') + rcnoise = b.read_frames(nbuff) + b.close() + + rnoise = func(cfilename)[0] + + assert_array_equal(rnoise, rcnoise) + finally: + close_tmp_file(rfd, cfilename) + + def test_wavread(self): + """ Check wavread """ + self._test_read(wavread, audio_format('wav', 'pcm16', 'file'), 'wav') + + def test_flacread(self): + """ Check flacread """ + try: + self._test_read(flacread, audio_format('flac', 'pcm16', 'file'), 'flac') + except FlacUnsupported: + print "Flac unsupported, flacread not tested" + + def test_auread(self): + """ Check auread """ + self._test_read(auread, audio_format('au', 'ulaw', 'file'), 'au') + + def test_aiffread(self): + """ Check aiffread """ + self._test_read(aiffread, audio_format('aiff', 'pcm16', 'file'), 'aiff') + + def test_sdifread(self): + """ Check sdifread (ircam format) """ + self._test_read(sdifread, audio_format('ircam', 'pcm16', 'file'), 'sdif') + + def test_bad_wavread(self): + """ Check wavread on bad file""" + # Create a tmp audio file with non wav format, write some random data into it, + # and check it can not be opened by wavread + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + noise = 0.1 * N.random.randn(nbuff) + + # Open the copy file for writing + format = audio_format('aiff', 'pcm16') + b = sndfile(cfilename, 'write', format, 1, nbuff) + + b.write_frames(noise, nbuff) + + b.close() + + b = sndfile(cfilename, 'read') + rcnoise = b.read_frames(nbuff) + b.close() + + try: + rnoise = wavread(cfilename)[0] + raise Exception("wavread on non wav file succeded, expected to fail") + except PyaudioException, e: + pass + #print str(e) + ", as expected" + + finally: + close_tmp_file(rfd, cfilename) + + def _test_write(self, func, format, filext): + """ Check *write functions from matpi """ + rfd1, fd1, cfilename1 = open_tmp_file('pysndfiletest.' + filext) + rfd2, fd2, cfilename2 = open_tmp_file('pysndfiletest.' + filext) + try: + nbuff = 22050 + fs = nbuff + noise = 0.1 * N.random.randn(nbuff) + + # Open the first file for writing with sndfile + b = sndfile(cfilename1, 'write', format, 1, fs) + + b.write_frames(noise, nbuff) + + b.close() + + # Write same data with wavwrite + func(noise, cfilename2, fs) + + # Compare if both files have same hash + f1 = open(cfilename1) + f2 = open(cfilename2) + + import md5 + + m1 = md5.new() + m2 = md5.new() + + m1.update(f1.read()) + m2.update(f2.read()) + + f1.close() + f2.close() + assert m1.hexdigest() == m2.hexdigest() + finally: + close_tmp_file(rfd1, cfilename1) + close_tmp_file(rfd2, cfilename2) + + def test_wavwrite(self): + """ Check wavwrite """ + self._test_write(wavwrite, audio_format('wav', 'pcm16', 'file'), 'wav') + + def test_aiffwrite(self): + """ Check aiffwrite """ + self._test_write(aiffwrite, audio_format('aiff', 'pcm16', 'file'), 'aiff') + + def test_auwrite(self): + """ Check wavwrite """ + self._test_write(auwrite, audio_format('au', 'ulaw', 'file'), 'au') + + def test_sdifwrite(self): + """ Check wavwrite """ + self._test_write(sdifwrite, audio_format('ircam', 'pcm16', 'file'), 'sdif') + + def test_flacwrite(self): + """ Check flacwrite """ + try: + self._test_write(flacwrite, audio_format('flac', 'pcm16', 'file'), 'flac') + except FlacUnsupported: + print "Flac unsupported, flacwrite not tested" + +if __name__ == "__main__": + NumpyTest().run() diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/test_pysndfile.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/test_pysndfile.py new file mode 100644 index 00000000..632a4bc8 --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/test_pysndfile.py @@ -0,0 +1,396 @@ +#! /usr/bin/env python +# Last Change: Tue Jul 17 11:00 AM 2007 J +"""Test for the sndfile class.""" +from os.path import join, dirname +import os +import sys + +from numpy.testing import NumpyTestCase, assert_array_equal, NumpyTest, \ + assert_array_almost_equal, set_package_path, restore_path, set_local_path +import numpy as N + +set_package_path() +from audiolab import pysndfile +from audiolab.pysndfile import sndfile, formatinfo as audio_format +restore_path() + +set_local_path() +from testcommon import open_tmp_file, close_tmp_file +restore_path() + +# XXX: there is a lot to refactor here +class test_pysndfile(NumpyTestCase): + def test_basic_io(self): + """ Check open, close and basic read/write""" + # dirty ! + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + # Open the copy file for writing + format = audio_format('wav', 'pcm16') + b = sndfile(fd, 'write', format, a.get_channels(), + a.get_samplerate()) + + # Copy the data + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff) + assert tmpa.dtype == N.float + b.write_frames(tmpa, nbuff) + nrem = nframes % nbuff + tmpa = a.read_frames(nrem) + assert tmpa.dtype == N.float + b.write_frames(tmpa, nrem) + + a.close() + b.close() + finally: + close_tmp_file(rfd, cfilename) + + + def test_basic_io_fd(self): + """ Check open from fd works""" + # dirty ! + if sys.platform == 'win32': + print "Not testing opening by fd because does not work on win32" + else: + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + fd = os.open(ofilename, os.O_RDONLY) + hdl = sndfile(fd, 'read') + hdl.close() + + def test_raw(self): + rawname = join(dirname(pysndfile.__file__), 'test_data', 'test.raw') + format = audio_format('raw', 'pcm16', 'little') + a = sndfile(rawname, 'read', format, 1, 11025) + assert a.get_nframes() == 11290 + a.close() + + def test_float64(self): + """Check float64 write/read works""" + # dirty ! + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + # Open the copy file for writing + format = audio_format('wav', 'float64') + b = sndfile(fd, 'write', format, a.get_channels(), + a.get_samplerate()) + + # Copy the data in the wav file + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float64) + assert tmpa.dtype == N.float64 + b.write_frames(tmpa, nbuff) + nrem = nframes % nbuff + tmpa = a.read_frames(nrem) + b.write_frames(tmpa, nrem) + + a.close() + b.close() + + # Now, reopen both files in for reading, and check data are + # the same + a = sndfile(ofilename, 'read') + b = sndfile(cfilename, 'read') + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float64) + tmpb = b.read_frames(nbuff, dtype = N.float64) + assert_array_equal(tmpa, tmpb) + + a.close() + b.close() + + finally: + close_tmp_file(rfd, cfilename) + + def test_float32(self): + """Check float write/read works""" + # dirty ! + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + # Open the copy file for writing + format = audio_format('wav', 'float32') + b = sndfile(fd, 'write', format, a.get_channels(), + a.get_samplerate()) + + # Copy the data in the wav file + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float32) + assert tmpa.dtype == N.float32 + b.write_frames(tmpa, nbuff) + nrem = nframes % nbuff + tmpa = a.read_frames(nrem) + b.write_frames(tmpa, nrem) + + a.close() + b.close() + + # Now, reopen both files in for reading, and check data are + # the same + a = sndfile(ofilename, 'read') + b = sndfile(cfilename, 'read') + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float32) + tmpb = b.read_frames(nbuff, dtype = N.float32) + assert_array_equal(tmpa, tmpb) + + a.close() + b.close() + + finally: + close_tmp_file(rfd, cfilename) + + def test_supported_features(self): + msg = "\nsupported file format are : this test is broken FIXME" + #for i in pysndfile.supported_format(): + # msg += str(i) + ', ' + #print msg + #msg = "supported encoding format are : " + #for i in pysndfile.supported_encoding(): + # msg += str(i) + ', ' + #print msg + #msg = "supported endianness are : " + #for i in pysndfile.supported_endianness(): + # msg += str(i) + ', ' + print msg + + def test_short_io(self): + # TODO: check if neg or pos value is the highest in abs + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nb = 2 ** 14 + nbuff = 22050 + fs = 22050 + a = N.random.random_integers(-nb, nb, nbuff) + a = a.astype(N.short) + + # Open the file for writing + format = audio_format('wav', 'pcm16') + b = sndfile(fd, 'write', format, 1, fs) + + b.write_frames(a, nbuff) + b.close() + + b = sndfile(cfilename, 'read') + + read_a = b.read_frames(nbuff, dtype = N.short) + b.close() + + assert_array_equal(a, read_a) + + finally: + close_tmp_file(rfd, cfilename) + + def test_int_io(self): + # TODO: check if neg or pos value is the highest in abs + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nb = 2 ** 25 + nbuff = 22050 + fs = 22050 + a = N.random.random_integers(-nb, nb, nbuff) + a = a.astype(N.int32) + + # Open the file for writing + format = audio_format('wav', 'pcm32') + b = sndfile(fd, 'write', format, 1, fs) + + b.write_frames(a, nbuff) + b.close() + + b = sndfile(cfilename, 'read') + + read_a = b.read_frames(nbuff, dtype = N.int32) + b.close() + + assert_array_equal(a, read_a) + + finally: + close_tmp_file(rfd, cfilename) + + def test_mismatch(self): + # This test open a file for writing, but with bad args (channels and + # nframes inverted) + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + # Open the file for writing + format = audio_format('wav', 'pcm16') + try: + b = sndfile(fd, 'write', \ + format, channels = 22000, samplerate = 1) + raise Exception("Try to open a file with more than 256 "\ + "channels, this should not succeed !") + except RuntimeError, e: + #print "Gave %d channels, error detected is \"%s\"" % (22000, e) + pass + + finally: + close_tmp_file(rfd, cfilename) + + def test_bigframes(self): + """ Try to seek really far""" + rawname = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + a = sndfile(rawname, 'read') + try: + try: + a.seek(2 ** 60) + raise Exception("Seek really succeded ! This should not happen") + except pysndfile.PyaudioIOError, e: + pass + finally: + a.close() + + def test_float_frames(self): + """ Check nframes can be a float""" + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + # Open the file for writing + format = audio_format('wav', 'pcm16') + a = sndfile(fd, 'rwrite', format, channels = 1, + samplerate = 22050) + tmp = N.random.random_integers(-100, 100, 1000) + tmp = tmp.astype(N.short) + a.write_frames(tmp, tmp.size) + a.seek(0) + a.sync() + ctmp = a.read_frames(1e2, dtype = N.short) + a.close() + + finally: + close_tmp_file(rfd, cfilename) + + def test_nofile(self): + """ Check the failure when opening a non existing file.""" + try: + f = sndfile("floupi.wav", "read") + raise AssertionError("call to non existing file should not succeed") + except IOError: + pass + except Exception, e: + raise AssertionError("opening non existing file should raise a IOError exception, got %s instead" % e.__class__) + +class test_seek(NumpyTestCase): + def test_simple(self): + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + buffsize = 1024 + buffsize = min(nframes, buffsize) + + # First, read some frames, go back, and compare buffers + buff = a.read_frames(buffsize) + a.seek(0) + buff2 = a.read_frames(buffsize) + assert_array_equal(buff, buff2) + + a.close() + + # Now, read some frames, go back, and compare buffers + # (check whence == 1 == SEEK_CUR) + a = sndfile(ofilename, 'read') + a.read_frames(buffsize) + buff = a.read_frames(buffsize) + a.seek(-buffsize, 1) + buff2 = a.read_frames(buffsize) + assert_array_equal(buff, buff2) + + a.close() + + # Now, read some frames, go back, and compare buffers + # (check whence == 2 == SEEK_END) + a = sndfile(ofilename, 'read') + buff = a.read_frames(nframes) + a.seek(-buffsize, 2) + buff2 = a.read_frames(buffsize) + assert_array_equal(buff[-buffsize:], buff2) + + def test_rw(self): + """Test read/write pointers for seek.""" + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('rwseektest.wav') + try: + ref = sndfile(ofilename, 'read') + test = sndfile(fd, 'rwrite', format = ref._format, channels = + ref.get_channels(), samplerate = ref.get_samplerate()) + n = 1024 + + rbuff = ref.read_frames(n, dtype = N.int16) + test.write_frames(rbuff) + tbuff = test.read_frames(n, dtype = N.int16) + + assert_array_equal(rbuff, tbuff) + + # Test seeking both read and write pointers + test.seek(0, 0) + test.write_frames(rbuff) + tbuff = test.read_frames(n, dtype = N.int16) + assert_array_equal(rbuff, tbuff) + + # Test seeking only read pointer + rbuff1 = rbuff.copy() + rbuff2 = rbuff1 * 2 + 1 + rbuff2.clip(-30000, 30000) + test.seek(0, 0, 'r') + test.write_frames(rbuff2) + tbuff1 = test.read_frames(n, dtype = N.int16) + try: + tbuff2 = test.read_frames(n, dtype = N.int16) + except IOError, e: + msg = "write pointer was updated in read seek !" + msg += "\n(msg is %s)" % e + raise AssertionError(msg) + + assert_array_equal(rbuff1, tbuff1) + assert_array_equal(rbuff2, tbuff2) + if N.all(rbuff2 == tbuff1): + raise AssertionError("write pointer was updated"\ + " in read seek !") + + # Test seeking only write pointer + rbuff3 = rbuff1 * 2 - 1 + rbuff3.clip(-30000, 30000) + test.seek(0, 0, 'rw') + test.seek(n, 0, 'w') + test.write_frames(rbuff3) + tbuff1 = test.read_frames(n, N.int16) + try: + assert_array_equal(tbuff1, rbuff1) + except AssertionError: + raise AssertionError("read pointer was updated in write seek !") + + try: + tbuff3 = test.read_frames(n, N.int16) + except IOError, e: + msg = "read pointer was updated in write seek !" + msg += "\n(msg is %s)" % e + raise AssertionError(msg) + + assert_array_equal(tbuff3, rbuff3) + test.close() + + finally: + close_tmp_file(rfd, cfilename) + +if __name__ == "__main__": + NumpyTest().run() diff --git a/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/testcommon.py b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/testcommon.py new file mode 100644 index 00000000..dd8c4a42 --- /dev/null +++ b/telemeta/util/audiolab/build/lib/scikits/audiolab/tests/testcommon.py @@ -0,0 +1,20 @@ +import os +from tempfile import mkstemp +import sys + +def open_tmp_file(name): + """On any sane platforms, return a fd on a tmp file. On windows, returns + the filename, and as such, is not secure (someone else can reopen the file + in between).""" + fd, cfilename = mkstemp('pysndfiletest.wav') + if sys.platform == 'win32': + return fd, cfilename, cfilename + else: + return fd, fd, cfilename + +def close_tmp_file(fd, filename): + """On any sane platforms, remove the file . On windows, only close the + file.""" + os.close(fd) + os.remove(filename) + diff --git a/telemeta/util/audiolab/generate.sh b/telemeta/util/audiolab/generate.sh new file mode 100644 index 00000000..e89b0114 --- /dev/null +++ b/telemeta/util/audiolab/generate.sh @@ -0,0 +1,4 @@ +#! /bin/sh +# Last Change: Fri May 25 10:00 AM 2007 J +EPYDOCOPTS="--latex -v --no-private --exclude test_*" +PYTHONPATH=/home/david/local/lib/python2.5/site-packages/ /home/david/local/bin/epydoc $EPYDOCOPTS scikits/pyaudiolab/ diff --git a/telemeta/util/audiolab/generate_const.py b/telemeta/util/audiolab/generate_const.py new file mode 100644 index 00000000..eb174313 --- /dev/null +++ b/telemeta/util/audiolab/generate_const.py @@ -0,0 +1,54 @@ +#! /usr/bin/env python +# Last Change: Thu May 24 02:00 PM 2007 J + +# David Cournapeau 2006 + +# TODO: +# args with the header file to extract info from + +from header_parser import get_dict, put_dict_file + +def generate_enum_dicts(header = '/usr/include/sndfile.h'): + # Open the file and get the content, without trailing '\n' + hdct = [i.split('\n')[0] for i in open(header, 'r').readlines()] + + # Get sf boolean + sf_bool = {} + nameregex = '(SF_FALSE)' + sf = get_dict(hdct, nameregex) + sf_bool['SF_FALSE'] = sf['SF_FALSE'] + nameregex = '(SF_TRUE)' + sf = get_dict(hdct, nameregex) + sf_bool['SF_TRUE'] = sf['SF_TRUE'] + + # Get mode constants + nameregex = '(SFM_[\S]*)' + sfm = get_dict(hdct, nameregex) + + # Get format constants + nameregex = '(SF_FORMAT_[\S]*)' + sf_format = get_dict(hdct, nameregex) + + # Get endianness + nameregex = '(SF_ENDIAN_[\S]*)' + sf_endian = get_dict(hdct, nameregex) + + # Get command constants + nameregex = '(SFC_[\S]*)' + sf_command = get_dict(hdct, nameregex) + + # Get (public) errors + nameregex = '(SF_ERR_[\S]*)' + sf_errors = get_dict(hdct, nameregex) + + # Replace dict: + repdict = { + '%SFM%' : put_dict_file(sfm, 'SFM'), + '%SF_BOOL%' : put_dict_file(sf_bool, 'SF_BOOL'), + '%SF_FORMAT%' : put_dict_file(sf_format, 'SF_FORMAT'), + '%SF_ENDIAN%' : put_dict_file(sf_endian, 'SF_ENDIAN'), + '%SF_COMMAND%' : put_dict_file(sf_command, 'SF_COMMAND'), + '%SF_ERR%' : put_dict_file(sf_errors, 'SF_ERRORS') + } + + return repdict diff --git a/telemeta/util/audiolab/generate_const.pyc b/telemeta/util/audiolab/generate_const.pyc new file mode 100644 index 00000000..28f8b90e Binary files /dev/null and b/telemeta/util/audiolab/generate_const.pyc differ diff --git a/telemeta/util/audiolab/header_parser.py b/telemeta/util/audiolab/header_parser.py new file mode 100644 index 00000000..5640422d --- /dev/null +++ b/telemeta/util/audiolab/header_parser.py @@ -0,0 +1,50 @@ +#! /usr/bin/env python +# Last Change: Thu Nov 02 12:00 PM 2006 J + +# Small module to parse header files, and other tools to use libraries with ctype +# David Cournapeau 2006 + +import re + +def get_dict(content, nameregex, valueregex = '([ABCDEF\dx]*)'): + """Read the content of a header file, parse for a regex nameregex, + and get the corresponding value (HEX values 0x by default) + + This is useful to parse enumerations in header files""" + sf_dict = {} + regex = re.compile(nameregex + '[\s]*= ' + valueregex) + for i in content: + m = regex.search(i) + if m: + sf_dict[m.group(1)] = m.group(2) + return sf_dict + +def put_dict_file(dict, name): + items = dict.items() + string = name + ' = {\n\t' + '\'' + items[0][0] + '\'\t: ' + items[0][1] + for i in range(1, len(items)): + #print "Putting key %s, value %s" % (items[i][0], items[i][1]) + string += ',\n\t\'' + items[i][0] + '\'\t: ' + items[i][1] + string += '\n}' + return string + +# Functions used to substitute values in File. +# Kind of like config.h and autotools +import re +def do_subst_in_file(sourcefile, targetfile, dict): + """Replace all instances of the keys of dict with their values. + For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'}, + then all instances of %VERSION% in the file will be replaced with 1.2345 etc. + """ + #print "sourcefile is %s, target is %s" % (sourcefile, targetfile) + f = open(sourcefile, 'rb') + contents = f.read() + f.close() + + for (k,v) in dict.items(): + contents = re.sub(k, v, contents) + + f = open(targetfile, 'wb') + f.write(contents) + f.close() + diff --git a/telemeta/util/audiolab/header_parser.pyc b/telemeta/util/audiolab/header_parser.pyc new file mode 100644 index 00000000..36fd4acf Binary files /dev/null and b/telemeta/util/audiolab/header_parser.pyc differ diff --git a/telemeta/util/audiolab/scikits.audiolab.egg-info/PKG-INFO b/telemeta/util/audiolab/scikits.audiolab.egg-info/PKG-INFO new file mode 100644 index 00000000..f3e9e52a --- /dev/null +++ b/telemeta/util/audiolab/scikits.audiolab.egg-info/PKG-INFO @@ -0,0 +1,31 @@ +Metadata-Version: 1.0 +Name: scikits.audiolab +Version: 0.8dev.dev +Summary: A python module to make noise from numpy arrays +Home-page: http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/audiolab +Author: David Cournapeau +Author-email: david@ar.media.kyoto-u.ac.jp +License: LGPL +Download-URL: http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/audiolab +Description: audiolab is a small python package to import data from audio + files to numpy arrays and export data from numpy arrays to audio files. It uses + libsndfile from Erik Castro de Lopo for the underlying IO, which supports many + different audio formats: http://www.mega-nerd.com/libsndfile/ + + For now, the python api for audio IO should be stable; a matlab-like API is + also available for quick read/write (ala wavread, wavwrite, etc...). For 1.0 + release, I hope to add support for simple read/write to soundcard, to be able + to record and listen to data in numpy arrays. + + 2006-2007, David Cournapeau + + LICENSE: audiolab is licensed under the LGPL, as is libsndfile itself. See + COPYING.txt for details. +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: LGPL License +Classifier: Topic :: Multimedia :: Sound/Audio +Classifier: Topic :: Scientific/Engineering diff --git a/telemeta/util/audiolab/scikits.audiolab.egg-info/SOURCES.txt b/telemeta/util/audiolab/scikits.audiolab.egg-info/SOURCES.txt new file mode 100644 index 00000000..a155f472 --- /dev/null +++ b/telemeta/util/audiolab/scikits.audiolab.egg-info/SOURCES.txt @@ -0,0 +1,27 @@ +COPYING.txt +FLAC_SUPPORT.txt +MANIFEST.in +README.txt +generate_const.py +header_parser.py +setup.cfg +setup.py +site.cfg.win32 +scikits/__init__.py +scikits.audiolab.egg-info/PKG-INFO +scikits.audiolab.egg-info/SOURCES.txt +scikits.audiolab.egg-info/dependency_links.txt +scikits.audiolab.egg-info/namespace_packages.txt +scikits.audiolab.egg-info/requires.txt +scikits.audiolab.egg-info/top_level.txt +scikits.audiolab.egg-info/zip-safe +scikits/audiolab/__init__.py +scikits/audiolab/info.py +scikits/audiolab/matapi.py +scikits/audiolab/pyaudioio.py +scikits/audiolab/pysndfile.py +scikits/audiolab/pysndfile.py.in +scikits/audiolab/tests/__init__.py +scikits/audiolab/tests/test_matapi.py +scikits/audiolab/tests/test_pysndfile.py +scikits/audiolab/tests/testcommon.py \ No newline at end of file diff --git a/telemeta/util/audiolab/scikits.audiolab.egg-info/dependency_links.txt b/telemeta/util/audiolab/scikits.audiolab.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/telemeta/util/audiolab/scikits.audiolab.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/telemeta/util/audiolab/scikits.audiolab.egg-info/namespace_packages.txt b/telemeta/util/audiolab/scikits.audiolab.egg-info/namespace_packages.txt new file mode 100644 index 00000000..4fc9b05a --- /dev/null +++ b/telemeta/util/audiolab/scikits.audiolab.egg-info/namespace_packages.txt @@ -0,0 +1 @@ +scikits diff --git a/telemeta/util/audiolab/scikits.audiolab.egg-info/requires.txt b/telemeta/util/audiolab/scikits.audiolab.egg-info/requires.txt new file mode 100644 index 00000000..296d6545 --- /dev/null +++ b/telemeta/util/audiolab/scikits.audiolab.egg-info/requires.txt @@ -0,0 +1 @@ +numpy \ No newline at end of file diff --git a/telemeta/util/audiolab/scikits.audiolab.egg-info/top_level.txt b/telemeta/util/audiolab/scikits.audiolab.egg-info/top_level.txt new file mode 100644 index 00000000..4fc9b05a --- /dev/null +++ b/telemeta/util/audiolab/scikits.audiolab.egg-info/top_level.txt @@ -0,0 +1 @@ +scikits diff --git a/telemeta/util/audiolab/scikits.audiolab.egg-info/zip-safe b/telemeta/util/audiolab/scikits.audiolab.egg-info/zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/telemeta/util/audiolab/scikits.audiolab.egg-info/zip-safe @@ -0,0 +1 @@ + diff --git a/telemeta/util/audiolab/scikits/__init__.py b/telemeta/util/audiolab/scikits/__init__.py new file mode 100644 index 00000000..de40ea7c --- /dev/null +++ b/telemeta/util/audiolab/scikits/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/telemeta/util/audiolab/scikits/__init__.pyc b/telemeta/util/audiolab/scikits/__init__.pyc new file mode 100644 index 00000000..68749afc Binary files /dev/null and b/telemeta/util/audiolab/scikits/__init__.pyc differ diff --git a/telemeta/util/audiolab/scikits/audiolab/__init__.py b/telemeta/util/audiolab/scikits/audiolab/__init__.py new file mode 100644 index 00000000..12911595 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/__init__.py @@ -0,0 +1,35 @@ +#! /usr/bin/env python +# Last Change: Mon Sep 10 07:00 PM 2007 J +""" +audiolab: a small toolbox to read, write and play audio to and from +numpy arrays. + +audiolab provides two API: + - one similar to matlab: this gives you wavread, wavwrite functions really + similar to matlab's functions. + - a more complete API, which can be used to read, write to many audio file + (including wav, aiff, flac, au, IRCAM, htk, etc...), with IO capabilities + not available to matlab (seek, append data, etc...) + +It is a thin wrapper around libsndfile from Erik Castro Lopo. + +Copyright (C) 2006-2007 Cournapeau David + +LICENSE: audiolab is licensed under the LGPL, as is libsndfile itself. See +COPYING.txt for details. """ + +from info import VERSION +__version__ = VERSION + +from pysndfile import formatinfo, sndfile +from pysndfile import supported_format, supported_endianness, \ + supported_encoding +#from scikits.audiolab.matapi import wavread, aiffread, flacread, auread, \ +# sdifread, wavwrite, aiffwrite, flacwrite, auwrite, sdifwrite +from matapi import * + +__all__ = filter(lambda s:not s.startswith('_'),dir()) + +from numpy.testing import NumpyTest +def test(): + return NumpyTest().test() diff --git a/telemeta/util/audiolab/scikits/audiolab/__init__.pyc b/telemeta/util/audiolab/scikits/audiolab/__init__.pyc new file mode 100644 index 00000000..31e6dca1 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/__init__.pyc differ diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/Makefile b/telemeta/util/audiolab/scikits/audiolab/docs/Makefile new file mode 100644 index 00000000..5ea6c555 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/Makefile @@ -0,0 +1,54 @@ +py2tex = PYTHONPATH=/home/david/local/lib/python2.5/site-packages pygmentize -l python -f tex +rst2tex = PYTHONPATH=/home/david/local/lib/python2.5/site-packages rst2newlatex.py \ + --stylesheet-path base.tex --user-stylesheet user.tex + +pytexfiles = audiolab.tex quick1.tex \ + usage1.tex \ + usage2.tex \ + format1.tex \ + format2.tex \ + write1.tex \ + matlab1.tex + +SOURCEPATH = $(PWD) + +EXTTOCLEAN=.chk .dvi .log .aux .bbl .blg .blig .ilg .toc .lof .lot .idx .ind .out .bak .ps .pdf .bm + +audiolab.pdf: $(pytexfiles) + pdflatex $< + pdflatex $< + pdflatex $< + +audiolab.tex: index.txt + $(rst2tex) $< > $@ + +quick1.tex: examples/quick1.py + $(py2tex) $< > $@ + +usage1.tex: examples/usage1.py + $(py2tex) $< > $@ + +usage2.tex: examples/usage2.py + $(py2tex) $< > $@ + +format1.tex: examples/format1.py + $(py2tex) $< > $@ + +format2.tex: examples/format2.py + $(py2tex) $< > $@ + +write1.tex: examples/write1.py + $(py2tex) $< > $@ + +matlab1.tex: examples/matlab1.py + $(py2tex) $< > $@ + +clean: + for i in $(pytexfiles); do \ + rm -f `echo $$i`; \ + done; + for i in $(SOURCEPATH); do \ + for j in $(EXTTOCLEAN); do \ + rm -f `echo $$i/*$$j`; \ + done; \ + done; diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/audiolab1.png b/telemeta/util/audiolab/scikits/audiolab/docs/audiolab1.png new file mode 100644 index 00000000..c88e46f5 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/docs/audiolab1.png differ diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/base.tex b/telemeta/util/audiolab/scikits/audiolab/docs/base.tex new file mode 100644 index 00000000..b10ea72c --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/base.tex @@ -0,0 +1,1182 @@ +% System stylesheet for the new LaTeX writer, newlatex2e. + +% Major parts of the rendering are done in this stylesheet and not in the +% Python module. + +% For development notes, see notes.txt. + +% User documentation (in the stylesheet for now; that may change though): + +% Naming conventions: +% All uppercase letters in macro names have a specific meaning. +% \D...: All macros introduced by the Docutils LaTeX writer start with "D". +% \DS: Setup function (called at the bottom of this stylesheet). +% \DN{}: Handler for Docutils document tree node `node`; called by +% the Python module. +% \DEV: External variable, set by the Python module. +% \DEC: External command. It is called by the Python module and must be +% defined in this stylesheet. +% \DNA{}{}{}{}{}: +% Attribute handler for `attribute` set on nodes of type `nodename`. +% See below for a discussion of attribute handlers. +% \DA{}{}{}{}{}: +% Attribute handler for all `attribute`. Called only when no specific +% \DNA handler is defined. +% \DNC{}: +% Handler for `class`, when set on nodes of type `nodename`. +% \DC{}: +% Handler for `class`. Called only when no specific \DNC +% handler is defined. +% \D: Generic variable or function. + +% Attribute handlers: +% TODO + +% --------------------------------------------------------------------------- + +% Having to intersperse code with \makeatletter-\makeatother pairs is very +% annoying, so we call \makeatletter at the top and \makeatother at the +% bottom. Just be aware that you cannot use "@" as a text character inside +% this stylesheet. +\makeatletter + +% Print-mode (as opposed to online mode e.g. with Adobe Reader). +% This causes for example blue hyperlinks. +\providecommand{\Dprinting}{false} + +% \DSearly is called right after \documentclass. +\providecommand{\DSearly}{} +% \DSlate is called at the end of the stylesheet (right before the document +% tree). +\providecommand{\DSlate}{} + +% Use the KOMA script article class. +\providecommand{\Ddocumentclass}{scrartcl} +\providecommand{\Ddocumentoptions}{a4paper} +\providecommand{\DSdocumentclass}{ + \documentclass[\Ddocumentoptions]{\Ddocumentclass} } + +% Todo: This should be movable to the bottom, but it isn't as long as +% we use \usepackage commands at the top level of this stylesheet +% (which we shouldn't). +\DSdocumentclass + +\providecommand{\DSpackages}{ + % Load miscellaneous packages. + % Note 1: Many of the packages loaded here are used throughout this stylesheet. + % If one of these packages does not work on your system or in your scenario, + % please let us know, so we can consider making the package optional. + % Note 2: It would appear cleaner to load packages where they are used. + % However, since using a wrong package loading order can lead to *very* + % subtle bugs, we centralize the loading of most packages here. + \DSfontencoding % load font encoding packages + \DSlanguage % load babel + % Using \ifthenelse conditionals. + \usepackage{ifthen} % before hyperref (really!) + % There is not support for *not* using hyperref because it's used in many + % places. If this is a problem (e.g. because hyperref doesn't work on your + % system), please let us know. + \usepackage[colorlinks=false,pdfborder={0 0 0}]{hyperref} + % Get color, e.g. for links and system messages. + \usepackage{color} + % Get \textnhtt macro (non-hyphenating type writer). + \usepackage{hyphenat} + % For sidebars. + \usepackage{picins} + % We use longtable to create tables. + \usepackage{longtable} + % Images. + \usepackage{graphicx} + % These packages might be useful (some just add magic pixie dust), so + % evaluate them: + %\usepackage{fixmath} + %\usepackage{amsmath} + % Add some missing symbols like \textonehalf. + \usepackage{textcomp} +} + +\providecommand{\DSfontencoding}{ + % Set up font encoding. Called by \DSpackages. + % AE is a T1 emulation. It provides mostly the same characters and + % features as T1-encoded fonts but doesn't use bitmap fonts (which are + % unsuitable for online reading and subtle for printers). + \usepackage{ae} + % Provide the characters not contained in AE from EC bitmap fonts. + \usepackage{aecompl} + % Guillemets ("<<", ">>") in AE. + \usepackage{aeguill} +} + +\providecommand{\DSsymbols}{% + % Fix up symbols. + % The Euro symbol in Computer Modern looks, um, funny. Let's get a + % proper Euro symbol. + \usepackage{eurosym}% + \renewcommand{\texteuro}{\euro}% +} + +% Taken from +% +% and modified. Used with permission. +\providecommand{\Dprovidelength}[2]{% + \begingroup% + \escapechar\m@ne% + \xdef\@gtempa{{\string#1}}% + \endgroup% + \expandafter\@ifundefined\@gtempa% + {\newlength{#1}\setlength{#1}{#2}}% + {}% +} + +\providecommand{\Dprovidecounter}[2]{% + % Like \newcounter except that it doesn't crash if the counter + % already exists. + \@ifundefined{c@#1}{\newcounter{#1}\setcounter{#1}{#2}}{} +} + +\Dprovidelength{\Dboxparindent}{\parindent} + +\providecommand{\Dmakebox}[1]{% + % Make a centered, frameless box. Useful e.g. for block quotes. + % Do not use minipages here, but create pseudo-lists to allow + % page-breaking. (Don't use KOMA-script's addmargin environment + % because it messes up bullet lists.) + \Dmakelistenvironment{}{}{% + \setlength{\parskip}{0pt}% + \setlength{\parindent}{\Dboxparindent}% + \item{#1}% + }% +} + +\providecommand{\Dmakefbox}[1]{% + % Make a centered, framed box. Useful e.g. for admonitions. + \vspace{0.4\baselineskip}% + \begin{center}% + \fbox{% + \begin{minipage}[t]{0.9\linewidth}% + \setlength{\parindent}{\Dboxparindent}% + #1% + \end{minipage}% + }% + \end{center}% + \vspace{0.4\baselineskip}% +} + +% We do not currently recognize the difference between an end-sentence and a +% mid-sentence period (". " vs. ". " in plain text). So \frenchspacing is +% appropriate. +\providecommand{\DSfrenchspacing}{\frenchspacing} + + +\Dprovidelength{\Dblocklevelvspace}{% + % Space between block-level elements other than paragraphs. + 0.7\baselineskip plus 0.3\baselineskip minus 0.2\baselineskip% +} +\providecommand{\DECauxiliaryspace}{% + \ifthenelse{\equal{\Dneedvspace}{true}}{\vspace{\Dblocklevelvspace}}{}% + \par\noindent% +} +\providecommand{\DECparagraphspace}{\par} +\providecommand{\Dneedvspace}{true} + +\providecommand{\DSlanguage}{% + % Set up babel. + \usepackage[\DEVlanguagebabel]{babel} +} + +\providecommand{\Difdefined}[3]{\@ifundefined{#1}{#3}{#2}} + +% Handler for 'classes' attribute (called for each class attribute). +\providecommand{\DAclasses}[5]{% + % Dispatch to \DNC. + \Difdefined{DN#4C#3}{% + % Pass only contents, nothing else! + \csname DN#4C#3\endcsname{#5}% + }{% + % Otherwise, dispatch to \DC. + \Difdefined{DC#3}{% + \csname DC#3\endcsname{#5}% + }{% + #5% + }% + }% +} + +\providecommand{\DECattr}[5]{% + % Global attribute dispatcher, called inside the document tree. + % Parameters: + % 1. Attribute number. + % 2. Attribute name. + % 3. Attribute value. + % 4. Node name. + % 5. Node contents. + \Difdefined{DN#4A#2}{% + % Dispatch to \DNA. + \csname DN#4A#2\endcsname{#1}{#2}{#3}{#4}{#5}% + }{\Difdefined{DA#2}{% + % Otherwise dispatch to \DA. + \csname DA#2\endcsname{#1}{#2}{#3}{#4}{#5}% + }{% + % Otherwise simply run the contents without calling a handler. + #5% + }}% +} + +% ---------- Link handling ---------- +% Targets and references. + +\providecommand{\Draisedlink}[1]{% + % Anchors are placed on the base line by default. This is a bad thing for + % inline context, so we raise the anchor (normally by \baselineskip). + \Hy@raisedlink{#1}% +} + +% References. +% We're assuming here that the "refid" and "refuri" attributes occur +% only in inline context (in TextElements). +\providecommand{\DArefid}[5]{% + \ifthenelse{\equal{#4}{reference}}{% + \Dexplicitreference{\##3}{#5}% + }{% + % If this is not a target node (targets with refids are + % uninteresting and should be silently dropped). + \ifthenelse{\not\equal{#4}{target}}{% + % If this is a footnote reference, call special macro. + \ifthenelse{\equal{#4}{footnotereference}}{% + \Dimplicitfootnotereference{\##3}{#5}% + }{% + \ifthenelse{\equal{#4}{citationreference}}{% + \Dimplicitcitationreference{\##3}{#5}% + }{% + \Dimplicitreference{\##3}{#5}% + }% + }% + }{}% + }% +} +\providecommand{\DArefuri}[5]{% + \ifthenelse{\equal{#4}{target}}{% + % The node name is 'target', so this is a hyperlink target, like this: + % .. _mytarget: URI + % Hyperlink targets are ignored because they are invisible. + }{% + % If a non-target node has a refuri attribute, it must be an explicit URI + % reference (i.e. node name is 'reference'). + \Durireference{#3}{#5}% + }% +} +% Targets. +\providecommand{\DAids}[5]{% + \label{#3}% + \ifthenelse{\equal{#4}{footnotereference}}{% + {% + \renewcommand{\HyperRaiseLinkDefault}{% + % Dirty hack to make backrefs to footnote references work. + % For some reason, \baselineskip is 0pt in fn references. + 0.5\Doriginalbaselineskip% + }% + \Draisedlink{\hypertarget{#3}{}}#5% + }% + }{% + \Draisedlink{\hypertarget{#3}{}}#5% + }% +} +\providecommand{\Dimplicitreference}[2]{% + % Create implicit reference to ID. Implicit references occur + % e.g. in TOC-backlinks of section titles. Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{#2}% +} +\providecommand{\Dimplicitfootnotereference}[2]{% + % Ditto, but for the special case of footnotes. + % We want them to be rendered like explicit references. + \Dexplicitreference{#1}{#2}% +} +\providecommand{\Dimplicitcitationreference}[2]{% + % Ditto for citation references. + \Dimplicitfootnotereference{#1}{#2}% +} +\providecommand{\Dcolorexplicitreference}{% + \ifthenelse{\equal{\Dprinting}{true}}{\color{black}}{\color{blue}}% +} +\providecommand{\Dexplicitreference}[2]{% + % Create explicit reference to ID, e.g. created with "foo_". + % Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{{\Dcolorexplicitreference#2}}% +} +\providecommand{\Dcolorurireference}{\Dcolorexplicitreference} +\providecommand{\Durireference}[2]{% + % Create reference to URI. Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{{\Dcolorurireference#2}}% +} + +\Dprovidecounter{Dpdfbookmarkid}{0}% +\providecommand{\Dpdfbookmark}[1]{% + % Temporarily decrement Desctionlevel counter. + \addtocounter{Dsectionlevel}{-1}% + %\typeout{\arabic{Dsectionlevel}}% + %\typeout{#1}% + %\typeout{docutils\roman{Dpdfbookmarkid}}% + %\typeout{}% + \pdfbookmark[\arabic{Dsectionlevel}]{#1}{docutils\arabic{Dpdfbookmarkid}}% + \addtocounter{Dsectionlevel}{1}% + \addtocounter{Dpdfbookmarkid}{1}% +} +% ---------- End of Link Handling ---------- + +\providecommand{\DNparagraph}[1]{% + \ifthenelse{\equal{\DEVparagraphindented}{true}}{\indent}{\noindent}% + #1% +} +\providecommand{\Dformatboxtitle}[1]{{\Large\textbf{#1}}} +\providecommand{\Dformatboxsubtitle}[1]{{\large\textbf{#1}}} +\providecommand{\Dtopictitle}[1]{% + \Difinsidetoc{\vspace{1em}\par}{}% + \noindent\Dformatboxtitle{#1}% + \ifthenelse{\equal{\DEVhassubtitle}{false}}{\vspace{1em}}{\vspace{0.5em}}% + \par% +} +\providecommand{\Dadmonitiontitle}[1]{% + \Dtopictitle{#1}% +} +\providecommand{\Dtopicsubtitle}[1]{% + \noindent\Dformatboxsubtitle{#1}% + \vspace{1em}% + \par% +} +\providecommand{\Dsidebartitle}[1]{\Dtopictitle{#1}} +\providecommand{\Dsidebarsubtitle}[1]{\Dtopicsubtitle{#1}} +\providecommand{\Ddocumenttitle}[1]{% + \begin{center}{\Huge#1}\end{center}% + \ifthenelse{\equal{\DEVhassubtitle}{true}}{\vspace{0.1cm}}{\vspace{1cm}}% +} +\providecommand{\Ddocumentsubtitle}[1]{% + \begin{center}{\huge#1}\end{center}% + \vspace{1cm}% +} +% Can be overwritten by user stylesheet. +\providecommand{\Dformatsectiontitle}[1]{#1} +\providecommand{\Dformatsectionsubtitle}[1]{\Dformatsectiontitle{#1}} +\providecommand{\Dbookmarksectiontitle}[1]{% + % Return text suitable for use in \section*, \subsection*, etc., + % containing a PDF bookmark. Parameter: The title (as node tree). + \Draisedlink{\Dpdfbookmark{\DEVtitleastext}}% + #1% +} +\providecommand{\Dsectiontitlehook}[1]{#1} +\providecommand{\Dsectiontitle}[1]{% + \Dsectiontitlehook{% + \Ddispatchsectiontitle{\Dbookmarksectiontitle{\Dformatsectiontitle{#1}}}% + }% +} +\providecommand{\Ddispatchsectiontitle}[1]{% + \@ifundefined{Dsectiontitle\roman{Dsectionlevel}}{% + \Ddeepsectiontitle{#1}% + }{% + \csname Dsectiontitle\roman{Dsectionlevel}\endcsname{#1}% + }% +} +\providecommand{\Ddispatchsectionsubtitle}[1]{% + \Ddispatchsectiontitle{#1}% +} +\providecommand{\Dsectiontitlei}[1]{\section*{#1}} +\providecommand{\Dsectiontitleii}[1]{\subsection*{#1}} +\providecommand{\Ddeepsectiontitle}[1]{% + % Anything below \subsubsection (like \paragraph or \subparagraph) + % is useless because it uses the same font. The only way to + % (visually) distinguish such deeply nested sections is to use + % section numbering. + \subsubsection*{#1}% +} +\providecommand{\Dsectionsubtitlehook}[1]{#1} +\Dprovidelength{\Dsectionsubtitleraisedistance}{0.7em} +\providecommand{\Dsectionsubtitlescaling}{0.85} +\providecommand{\Dsectionsubtitle}[1]{% + \Dsectionsubtitlehook{% + % Move the subtitle nearer to the title. + \vspace{-\Dsectionsubtitleraisedistance}% + % Don't create a PDF bookmark. + \Ddispatchsectionsubtitle{% + \Dformatsectionsubtitle{\scalebox{\Dsectionsubtitlescaling}{#1}}% + }% + }% +} +\providecommand{\DNtitle}[1]{% + % Dispatch to \Dtitle. + \csname D\DEVparent title\endcsname{#1}% +} +\providecommand{\DNsubtitle}[1]{% + % Dispatch to \Dsubtitle. + \csname D\DEVparent subtitle\endcsname{#1}% +} + +\providecommand{\DNliteralblock}[1]{% + \Dmakelistenvironment{}{% + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + \setlength{\leftmargin}{0pt}% + }{}% + \setlength{\rightmargin}{0pt}% + }{% + \raggedright\item\noindent\nohyphens{\textnhtt{#1\Dfinalstrut}}% + }% +} +\providecommand{\DNdoctestblock}[1]{\DNliteralblock{#1}} +\providecommand{\DNliteral}[1]{\textnhtt{#1}} +\providecommand{\DNemphasis}[1]{\emph{#1}} +\providecommand{\DNstrong}[1]{\textbf{#1}} +\providecommand{\DECvisitdocument}{\begin{document}\noindent} +\providecommand{\DECdepartdocument}{\end{document}} +\providecommand{\DNtopic}[1]{% + \ifthenelse{\equal{\DEVcurrentNtopicAcontents}{1}}{% + \addtocounter{Dtoclevel}{1}% + \par\noindent% + #1% + \addtocounter{Dtoclevel}{-1}% + }{% + \par\noindent% + \Dmakebox{#1}% + }% +} +\providecommand{\DNadmonition}[1]{% + \DNtopic{#1}% +} +\providecommand{\Dformatrubric}[1]{\textbf{#1}} +\Dprovidelength{\Dprerubricspace}{0.3em} +\providecommand{\DNrubric}[1]{% + \vspace{\Dprerubricspace}\par\noindent\Dformatrubric{#1}\par% +} + +\providecommand{\Dbullet}{} +\providecommand{\DECsetbullet}[1]{\renewcommand{\Dbullet}{#1}} +\providecommand{\DNbulletlist}[1]{% + \Difinsidetoc{% + \Dtocbulletlist{#1}% + }{% + \Dmakelistenvironment{\Dbullet}{}{#1}% + }% +} +% Todo: So what on earth is @pnumwidth? +\renewcommand{\@pnumwidth}{2.2em} +\providecommand{\DNlistitem}[1]{% + \Difinsidetoc{% + \ifthenelse{\equal{\theDtoclevel}{1}\and\equal{\Dlocaltoc}{false}}{% + {% + \par\addvspace{1em}\noindent% + \sectfont% + #1\hfill\pageref{\DEVcurrentNlistitemAtocrefid}% + }% + }{% + \@dottedtocline{0}{\Dtocindent}{0em}{#1}{% + \pageref{\DEVcurrentNlistitemAtocrefid}% + }% + }% + }{% + \item{#1}% + }% +} +\providecommand{\DNenumeratedlist}[1]{#1} +\Dprovidecounter{Dsectionlevel}{0} +\providecommand{\Dvisitsectionhook}{} +\providecommand{\Ddepartsectionhook}{} +\providecommand{\DECvisitsection}{% + \addtocounter{Dsectionlevel}{1}% + \Dvisitsectionhook% +} +\providecommand{\DECdepartsection}{% + \Ddepartsectionhook% + \addtocounter{Dsectionlevel}{-1}% +} + +% Using \_ will cause hyphenation after _ even in \textnhtt-typewriter +% because the hyphenat package redefines \_. So we use +% \textunderscore here. +\providecommand{\DECtextunderscore}{\textunderscore} + +\providecommand{\Dtextinlineliteralfirstspace}{{ }} +\providecommand{\Dtextinlineliteralsecondspace}{{~}} + +\Dprovidelength{\Dlistspacing}{0.8\baselineskip} + +\providecommand{\Dsetlistrightmargin}{% + \ifthenelse{\lengthtest{\linewidth>12em}}{% + % Equal margins. + \setlength{\rightmargin}{\leftmargin}% + }{% + % If the line is narrower than 10em, we don't remove any further + % space from the right. + \setlength{\rightmargin}{0pt}% + }% +} +\providecommand{\Dresetlistdepth}{false} +\Dprovidelength{\Doriginallabelsep}{\labelsep} +\providecommand{\Dmakelistenvironment}[3]{% + % Make list environment with support for unlimited nesting and with + % reasonable default lengths. Parameters: + % 1. Label (same as in list environment). + % 2. Spacing (same as in list environment). + % 3. List contents (contents of list environment). + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + % Unfortunately, vertical spacing doesn't work correctly when + % using lists inside tabular environments, so we use a minipage. + \begin{minipage}[t]{\linewidth}% + }{}% + {% + \renewcommand{\Dneedvspace}{false}% + % \parsep0.5\baselineskip + \renewcommand{\Dresetlistdepth}{false}% + \ifnum \@listdepth>5% + \protect\renewcommand{\Dresetlistdepth}{true}% + \@listdepth=5% + \fi% + \begin{list}{% + #1% + }{% + \setlength{\itemsep}{0pt}% + \setlength{\partopsep}{0pt}% + \setlength{\topsep}{0pt}% + % List should take 90% of total width. + \setlength{\leftmargin}{0.05\linewidth}% + \ifthenelse{\lengthtest{\leftmargin<1.8em}}{% + \setlength{\leftmargin}{1.8em}% + }{}% + \setlength{\labelsep}{\Doriginallabelsep}% + \Dsetlistrightmargin% + #2% + }{% + #3% + }% + \end{list}% + \ifthenelse{\equal{\Dresetlistdepth}{true}}{\@listdepth=5}{}% + }% + \ifthenelse{\equal{\Dinsidetabular}{true}}{\end{minipage}}{}% +} +\providecommand{\Dfinalstrut}{\@finalstrut\@arstrutbox} +\providecommand{\DAlastitem}[5]{#5\Dfinalstrut} + +\Dprovidelength{\Ditemsep}{0pt} +\providecommand{\DECmakeenumeratedlist}[6]{% + % Make enumerated list. + % Parameters: + % - prefix + % - type (\arabic, \roman, ...) + % - suffix + % - suggested counter name + % - start number - 1 + % - list contents + \newcounter{#4}% + \Dmakelistenvironment{#1#2{#4}#3}{% + % Use as much space as needed for the label. + \setlength{\labelwidth}{10em}% + % Reserve enough space so that the label doesn't go beyond the + % left margin of preceding paragraphs. Like that: + % + % A paragraph. + % + % 1. First item. + \setlength{\leftmargin}{2.5em}% + \Dsetlistrightmargin% + \setlength{\itemsep}{\Ditemsep}% + % Use counter recommended by Python module. + \usecounter{#4}% + % Set start value. + \addtocounter{#4}{#5}% + }{% + % The list contents. + #6% + }% +} + + +% Single quote in literal mode. \textquotesingle from package +% textcomp has wrong width when using package ae, so we use a normal +% single curly quote here. +\providecommand{\DECtextliteralsinglequote}{'} + + +% "Tabular lists" are field lists and options lists (not definition +% lists because there the term always appears on its own line). We'll +% use the terminology of field lists now ("field", "field name", +% "field body"), but the same is also analogously applicable to option +% lists. +% +% We want these lists to be breakable across pages. We cannot +% automatically get the narrowest possible size for the left column +% (i.e. the field names or option groups) because tabularx does not +% support multi-page tables, ltxtable needs to have the table in an +% external file and we don't want to clutter the user's directories +% with auxiliary files created by the filecontents environment, and +% ltablex is not included in teTeX. +% +% Thus we set a fixed length for the left column and use list +% environments. This also has the nice side effect that breaking is +% now possible anywhere, not just between fields. +% +% Note that we are creating a distinct list environment for each +% field. There is no macro for a whole tabular list! +\Dprovidelength{\Dtabularlistfieldnamewidth}{6em} +\Dprovidelength{\Dtabularlistfieldnamesep}{0.5em} +\providecommand{\Dinsidetabular}{false} +\providecommand{\Dsavefieldname}{} +\providecommand{\Dsavefieldbody}{} +\Dprovidelength{\Dusedfieldnamewidth}{0pt} +\Dprovidelength{\Drealfieldnamewidth}{0pt} +\providecommand{\Dtabularlistfieldname}[1]{\renewcommand{\Dsavefieldname}{#1}} +\providecommand{\Dtabularlistfieldbody}[1]{\renewcommand{\Dsavefieldbody}{#1}} +\Dprovidelength{\Dparskiptemp}{0pt} +\providecommand{\Dtabularlistfield}[1]{% + {% + % This only saves field name and field body in \Dsavefieldname and + % \Dsavefieldbody, resp. It does not insert any text into the + % document. + #1% + % Recalculate the real field name width everytime we encounter a + % tabular list field because it may have been changed using a + % "raw" node. + \setlength{\Drealfieldnamewidth}{\Dtabularlistfieldnamewidth}% + \addtolength{\Drealfieldnamewidth}{\Dtabularlistfieldnamesep}% + \Dmakelistenvironment{% + \makebox[\Drealfieldnamewidth][l]{\Dsavefieldname}% + }{% + \setlength{\labelwidth}{\Drealfieldnamewidth}% + \setlength{\leftmargin}{\Drealfieldnamewidth}% + \setlength{\rightmargin}{0pt}% + \setlength{\labelsep}{0pt}% + }{% + \item% + \settowidth{\Dusedfieldnamewidth}{\Dsavefieldname}% + \setlength{\Dparskiptemp}{\parskip}% + \ifthenelse{% + \lengthtest{\Dusedfieldnamewidth>\Dtabularlistfieldnamewidth}% + }{% + \mbox{}\par% + \setlength{\parskip}{0pt}% + }{}% + \Dsavefieldbody% + \setlength{\parskip}{\Dparskiptemp}% + %XXX Why did we need this? + %\@finalstrut\@arstrutbox% + }% + \par% + }% +} + +\providecommand{\Dformatfieldname}[1]{\textbf{#1:}} +\providecommand{\DNfieldlist}[1]{#1} +\providecommand{\DNfield}[1]{\Dtabularlistfield{#1}} +\providecommand{\DNfieldname}[1]{% + \Dtabularlistfieldname{% + \Dformatfieldname{#1}% + }% +} +\providecommand{\DNfieldbody}[1]{\Dtabularlistfieldbody{#1}} + +\providecommand{\Dformatoptiongroup}[1]{% + % Format option group, e.g. "-f file, --input file". + \texttt{#1}% +} +\providecommand{\Dformatoption}[1]{% + % Format option, e.g. "-f file". + % Put into mbox to avoid line-breaking at spaces. + \mbox{#1}% +} +\providecommand{\Dformatoptionstring}[1]{% + % Format option string, e.g. "-f". + #1% +} +\providecommand{\Dformatoptionargument}[1]{% + % Format option argument, e.g. "file". + \textsl{#1}% +} +\providecommand{\Dformatoptiondescription}[1]{% + % Format option description, e.g. + % "\DNparagraph{Read input data from file.}" + #1% +} +\providecommand{\DNoptionlist}[1]{#1} +\providecommand{\Doptiongroupjoiner}{,{ }} +\providecommand{\Disfirstoption}{% + % Auxiliary macro indicating if a given option is the first child + % of its option group (if it's not, it has to preceded by + % \Doptiongroupjoiner). + false% +} +\providecommand{\DNoptionlistitem}[1]{% + \Dtabularlistfield{#1}% +} +\providecommand{\DNoptiongroup}[1]{% + \renewcommand{\Disfirstoption}{true}% + \Dtabularlistfieldname{\Dformatoptiongroup{#1}}% +} +\providecommand{\DNoption}[1]{% + % If this is not the first option in this option group, add a + % joiner. + \ifthenelse{\equal{\Disfirstoption}{true}}{% + \renewcommand{\Disfirstoption}{false}% + }{% + \Doptiongroupjoiner% + }% + \Dformatoption{#1}% +} +\providecommand{\DNoptionstring}[1]{\Dformatoptionstring{#1}} +\providecommand{\DNoptionargument}[1]{{ }\Dformatoptionargument{#1}} +\providecommand{\DNdescription}[1]{% + \Dtabularlistfieldbody{\Dformatoptiondescription{#1}}% +} + +\providecommand{\DNdefinitionlist}[1]{% + \begin{description}% + \parskip0pt% + #1% + \end{description}% +} +\providecommand{\DNdefinitionlistitem}[1]{% + % LaTeX expects the label in square brackets; we provide an empty + % label. + \item[]#1% +} +\providecommand{\Dformatterm}[1]{#1} +\providecommand{\DNterm}[1]{\hspace{-5pt}\Dformatterm{#1}} +% I'm still not sure what's the best rendering for classifiers. The +% colon syntax is used by reStructuredText, so it's at least WYSIWYG. +% Use slanted text because italic would cause too much emphasis. +\providecommand{\Dformatclassifier}[1]{\textsl{#1}} +\providecommand{\DNclassifier}[1]{~:~\Dformatclassifier{#1}} +\providecommand{\Dformatdefinition}[1]{#1} +\providecommand{\DNdefinition}[1]{\par\Dformatdefinition{#1}} + +\providecommand{\Dlineblockindentation}{2.5em} +\providecommand{\DNlineblock}[1]{% + \Dmakelistenvironment{}{% + \ifthenelse{\equal{\DEVparent}{lineblock}}{% + % Parent is a line block, so indent. + \setlength{\leftmargin}{\Dlineblockindentation}% + }{% + % At top level; don't indent. + \setlength{\leftmargin}{0pt}% + }% + \setlength{\rightmargin}{0pt}% + \setlength{\parsep}{0pt}% + }{% + #1% + }% +} +\providecommand{\DNline}[1]{\item#1} + +\providecommand{\DNtransition}{% + \raisebox{0.25em}{\parbox{\linewidth}{\hspace*{\fill}\hrulefill\hrulefill\hspace*{\fill}}}% +} + +\providecommand{\Dformatblockquote}[1]{% + % Format contents of block quote. + % This occurs in block-level context, so we cannot use \textsl. + {\slshape#1}% +} +\providecommand{\Dformatattribution}[1]{---\textup{#1}} +\providecommand{\DNblockquote}[1]{% + \Dmakebox{% + \Dformatblockquote{#1} + }% +} +\providecommand{\DNattribution}[1]{% + \par% + \begin{flushright}\Dformatattribution{#1}\end{flushright}% +} + + +% Sidebars: +% Vertical and horizontal margins. +\Dprovidelength{\Dsidebarvmargin}{0.5em} +\Dprovidelength{\Dsidebarhmargin}{1em} +% Padding (space between contents and frame). +\Dprovidelength{\Dsidebarpadding}{1em} +% Frame width. +\Dprovidelength{\Dsidebarframewidth}{2\fboxrule} +% Position ("l" or "r"). +\providecommand{\Dsidebarposition}{r} +% Width. +\Dprovidelength{\Dsidebarwidth}{0.45\linewidth} +\providecommand{\DNsidebar}[1]{ + \parpic[\Dsidebarposition]{% + \begin{minipage}[t]{\Dsidebarwidth}% + % Doing this with nested minipages is ugly, but I haven't found + % another way to place vertical space before and after the fbox. + \vspace{\Dsidebarvmargin}% + {% + \setlength{\fboxrule}{\Dsidebarframewidth}% + \setlength{\fboxsep}{\Dsidebarpadding}% + \fbox{% + \begin{minipage}[t]{\linewidth}% + \setlength{\parindent}{\Dboxparindent}% + #1% + \end{minipage}% + }% + }% + \vspace{\Dsidebarvmargin}% + \end{minipage}% + }% +} + + +% Citations and footnotes. +\providecommand{\Dformatfootnote}[1]{% + % Format footnote. + {% + \footnotesize#1% + % \par is necessary for LaTeX to adjust baselineskip to the + % changed font size. + \par% + }% +} +\providecommand{\Dformatcitation}[1]{\Dformatfootnote{#1}} +\Dprovidelength{\Doriginalbaselineskip}{0pt} +\providecommand{\DNfootnotereference}[1]{% + {% + % \baselineskip is 0pt in \textsuperscript, so we save it here. + \setlength{\Doriginalbaselineskip}{\baselineskip}% + \textsuperscript{#1}% + }% +} +\providecommand{\DNcitationreference}[1]{{[}#1{]}} +\Dprovidelength{\Dfootnotesep}{3.5pt} +\providecommand{\Dsetfootnotespacing}{% + % Spacing commands executed at the beginning of footnotes. + \setlength{\parindent}{0pt}% + \hspace{1em}% +} +\providecommand{\DNfootnote}[1]{% + % See ltfloat.dtx for details. + {% + \insert\footins{% + % BUG: This is too small if the user adds + % \onehalfspacing or \doublespace. + \vspace{\Dfootnotesep}% + \Dsetfootnotespacing% + \Dformatfootnote{#1}% + }% + }% +} +\providecommand{\DNcitation}[1]{\DNfootnote{#1}} +\providecommand{\Dformatfootnotelabel}[1]{% + % Keep \footnotesize in footnote labels (\textsuperscript would + % reduce the font size even more). + \textsuperscript{\footnotesize#1{ }}% +} +\providecommand{\Dformatcitationlabel}[1]{{[}#1{]}{ }} +\providecommand{\Dformatmultiplebackrefs}[1]{% + % If in printing mode, do not write out multiple backrefs. + \ifthenelse{\equal{\Dprinting}{true}}{}{\textsl{#1}}% +} +\providecommand{\Dthislabel}{} +\providecommand{\DNlabel}[1]{% + % Footnote or citatation label. + \renewcommand{\Dthislabel}{#1}% + \ifthenelse{\not\equal{\DEVsinglebackref}{}}{% + \let\Doriginallabel=\Dthislabel% + \def\Dthislabel{% + \Dsinglefootnotebacklink{\DEVsinglebackref}{\Doriginallabel}% + }% + }{}% + \ifthenelse{\equal{\DEVparent}{footnote}}{% + % Footnote label. + \Dformatfootnotelabel{\Dthislabel}% + }{% + \ifthenelse{\equal{\DEVparent}{citation}}{% + % Citation label. + \Dformatcitationlabel{\Dthislabel}% + }{}% + }% + % If there are multiple backrefs, add them now. + \Dformatmultiplebackrefs{\DEVmultiplebackrefs}% +} +\providecommand{\Dsinglefootnotebacklink}[2]{% + % Create normal backlink of a footnote label. Parameters: + % 1. ID. + % 2. Link text. + % Treat like a footnote reference. + \Dimplicitfootnotereference{\##1}{#2}% +} +\providecommand{\DECmultifootnotebacklink}[2]{% + % Create generated backlink, as in (1, 2). Parameters: + % 1. ID. + % 2. Link text. + % Treat like a footnote reference. + \Dimplicitfootnotereference{\##1}{#2}% +} +\providecommand{\Dsinglecitationbacklink}[2]{\Dsinglefootnotebacklink{#1}{#2}} +\providecommand{\DECmulticitationbacklink}[2]{\DECmultifootnotebacklink{#1}{#2}} + + +\providecommand{\DECmaketable}[2]{% + % Make table. Parameters: + % 1. Table spec (like "|p|p|"). + % 2. Table contents. + {% + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + % Inside longtable; we cannot have nested longtables. + \begin{tabular}{#1}% + \hline% + #2% + \end{tabular}% + }{% + \renewcommand{\Dinsidetabular}{true}% + \begin{longtable}{#1}% + \hline% + #2% + \end{longtable}% + }% + }% +} +\providecommand{\DNthead}[1]{% + #1% + \endhead% +} +\providecommand{\DNrow}[1]{% + #1\tabularnewline% + \hline% +} +\providecommand{\Dinsidemulticolumn}{false} +\providecommand{\Dcompensatingmulticol}[3]{% + \multicolumn{#1}{#2}{% + {% + \renewcommand{\Dinsidemulticolumn}{true}% + % Compensate for weird missing vertical space at top of paragraph. + \raisebox{-2.5pt}{#3}% + }% + }% +} +\providecommand{\DECcolspan}[2]{% + % Take care of the morecols attribute (but incremented by 1). + &% + \Dcompensatingmulticol{#1}{l|}{#2}% +} +\providecommand{\DECcolspanleft}[2]{% + % Like \Dmorecols, but called for the leftmost entries in a table + % row. + \Dcompensatingmulticol{#1}{|l|}{#2}% +} +\providecommand{\DECsubsequententry}[1]{% + % +} +\providecommand{\DNentry}[1]{% + % The following sequence adds minimal vertical space above the top + % lines of the first cell paragraph, so that vertical space is + % balanced at the top and bottom of table cells. + \ifthenelse{\equal{\Dinsidemulticolumn}{false}}{% + \vspace{-1em}\vspace{-\parskip}\par% + }{}% + #1% + % No need to add an ampersand ("&"); that's done by \DECsubsequententry. +} +\providecommand{\DAtableheaderentry}[5]{\Dformattableheaderentry{#5}} +\providecommand{\Dformattableheaderentry}[1]{{\bfseries#1}} + + +\providecommand{\DNsystemmessage}[1]{% + {% + \ifthenelse{\equal{\Dprinting}{false}}{\color{red}}{}% + \bfseries% + #1% + }% +} + + +\providecommand{\Dinsidehalign}{false} +\newsavebox{\Dalignedimagebox} +\Dprovidelength{\Dalignedimagewidth}{0pt} +\providecommand{\Dhalign}[2]{% + % Horizontally align the contents to the left or right so that the + % text flows around it. + % Parameters: + % 1. l or r + % 2. Contents. + \renewcommand{\Dinsidehalign}{true}% + % For some obscure reason \parpic consumes some vertical space. + \vspace{-3pt}% + % Now we do something *really* ugly, but this enables us to wrap the + % image in a minipage while still allowing tight frames when + % class=border (see \DNimageCborder). + \sbox{\Dalignedimagebox}{#2}% + \settowidth{\Dalignedimagewidth}{\usebox{\Dalignedimagebox}}% + \parpic[#1]{% + \begin{minipage}[b]{\Dalignedimagewidth}% + % Compensate for previously added space, but not entirely. + \vspace*{2.0pt}% + \vspace*{\Dfloatimagetopmargin}% + \usebox{\Dalignedimagebox}% + \vspace*{1.5pt}% + \vspace*{\Dfloatimagebottommargin}% + \end{minipage}% + }% + \renewcommand{\Dinsidehalign}{false}% +} + + +% Maximum width of an image. +\providecommand{\Dimagemaxwidth}{\linewidth} +\providecommand{\Dfloatimagemaxwidth}{0.5\linewidth} +% Auxiliary variable. +\Dprovidelength{\Dcurrentimagewidth}{0pt} +\providecommand{\DNimageAalign}[5]{% + \ifthenelse{\equal{#3}{left}}{% + \Dhalign{l}{#5}% + }{% + \ifthenelse{\equal{#3}{right}}{% + \Dhalign{r}{#5}% + }{% + \ifthenelse{\equal{#3}{center}}{% + % Text floating around centered figures is a bad idea. Thus + % we use a center environment. Note that no extra space is + % added by the writer, so the space added by the center + % environment is fine. + \begin{center}#5\end{center}% + }{% + #5% + }% + }% + }% +} +% Base path for images. +\providecommand{\Dimagebase}{} +% Auxiliary command. Current image path. +\providecommand{\Dimagepath}{} +\providecommand{\DNimageAuri}[5]{% + % Insert image. We treat the URI like a path here. + \renewcommand{\Dimagepath}{\Dimagebase#3}% + \Difdefined{DcurrentNimageAwidth}{% + \Dwidthimage{\DEVcurrentNimageAwidth}{\Dimagepath}% + }{% + \Dsimpleimage{\Dimagepath}% + }% +} +\Dprovidelength{\Dfloatimagevmargin}{0pt} +\providecommand{\Dfloatimagetopmargin}{\Dfloatimagevmargin} +\providecommand{\Dfloatimagebottommargin}{\Dfloatimagevmargin} +\providecommand{\Dwidthimage}[2]{% + % Image with specified width. + % Parameters: + % 1. Image width. + % 2. Image path. + % Need to make bottom-alignment dependent on align attribute (add + % functional test first). Need to observe height attribute. + %\begin{minipage}[b]{#1}% + \includegraphics[width=#1,height=\textheight,keepaspectratio]{#2}% + %\end{minipage}% +} +\providecommand{\Dcurrentimagemaxwidth}{} +\providecommand{\Dsimpleimage}[1]{% + % Insert image, without much parametrization. + \settowidth{\Dcurrentimagewidth}{\includegraphics{#1}}% + \ifthenelse{\equal{\Dinsidehalign}{true}}{% + \renewcommand{\Dcurrentimagemaxwidth}{\Dfloatimagemaxwidth}% + }{% + \renewcommand{\Dcurrentimagemaxwidth}{\Dimagemaxwidth}% + }% + \ifthenelse{\lengthtest{\Dcurrentimagewidth>\Dcurrentimagemaxwidth}}{% + \Dwidthimage{\Dcurrentimagemaxwidth}{#1}% + }{% + \Dwidthimage{\Dcurrentimagewidth}{#1}% + }% +} +\providecommand{\Dwidthimage}[2]{% + % Image with specified width. + % Parameters: + % 1. Image width. + % 2. Image path. + \Dwidthimage{#1}{#2}% +} + +% Figures. +\providecommand{\DNfigureAalign}[5]{% + % Hack to make it work Right Now. + %\def\DEVcurrentNimageAwidth{\DEVcurrentNfigureAwidth}% + % + %\def\DEVcurrentNimageAwidth{\linewidth}% + \DNimageAalign{#1}{#2}{#3}{#4}{% + \begin{minipage}[b]{0.4\linewidth}#5\end{minipage}}% + %\let\DEVcurrentNimageAwidth=\relax% + % + %\let\DEVcurrentNimageAwidth=\relax% +} +\providecommand{\DNcaption}[1]{\par\noindent{\slshape#1}} +\providecommand{\DNlegend}[1]{\DECauxiliaryspace#1} + +\providecommand{\DCborder}[1]{\fbox{#1}} +% No padding between image and border. +\providecommand{\DNimageCborder}[1]{\frame{#1}} + + +% Need to replace with language-specific stuff. Maybe look at +% csquotes.sty and ask the author for permission to use parts of it. +\providecommand{\DECtextleftdblquote}{``} +\providecommand{\DECtextrightdblquote}{''} + +% Table of contents: +\Dprovidelength{\Dtocininitialsectnumwidth}{2.4em} +\Dprovidelength{\Dtocadditionalsectnumwidth}{0.7em} +% Level inside a table of contents. While this is at -1, we are not +% inside a TOC. +\Dprovidecounter{Dtoclevel}{-1}% +\providecommand{\Dlocaltoc}{false}% +\providecommand{\DNtopicClocal}[1]{% + \renewcommand{\Dlocaltoc}{true}% + \addtolength{\Dtocsectnumwidth}{2\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocindent}{-2\Dtocadditionalsectnumwidth}% + #1% + \addtolength{\Dtocindent}{2\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocsectnumwidth}{-2\Dtocadditionalsectnumwidth}% + \renewcommand{\Dlocaltoc}{false}% +} +\Dprovidelength{\Dtocindent}{0pt}% +\Dprovidelength{\Dtocsectnumwidth}{\Dtocininitialsectnumwidth} +% Compensate for one additional TOC indentation space so that the +% top-level is unindented. +\addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth} +\addtolength{\Dtocindent}{-\Dtocsectnumwidth} +\providecommand{\Difinsidetoc}[2]{% + \ifthenelse{\not\equal{\theDtoclevel}{-1}}{#1}{#2}% +} +\providecommand{\DNgeneratedCsectnum}[1]{% + \Difinsidetoc{% + % Section number inside TOC. + \makebox[\Dtocsectnumwidth][l]{#1}% + }{% + % Section number inside section title. + #1\quad% + }% +} +\providecommand{\Dtocbulletlist}[1]{% + \addtocounter{Dtoclevel}{1}% + \addtolength{\Dtocindent}{\Dtocsectnumwidth}% + \addtolength{\Dtocsectnumwidth}{\Dtocadditionalsectnumwidth}% + #1% + \addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocindent}{-\Dtocsectnumwidth}% + \addtocounter{Dtoclevel}{-1}% +} + + +% For \DECpixelunit, the length value is pre-multiplied with 0.75, so by +% specifying "pt" we get the same notion of "pixel" as graphicx. +\providecommand{\DECpixelunit}{pt} +% Normally lengths are relative to the current linewidth. +\providecommand{\DECrelativeunit}{\linewidth} + + +% ACTION: These commands actually *do* something. +% Ultimately, everything should be done here, and no active content should be +% above (not even \usepackage). + +\DSearly +\DSpackages +\DSfrenchspacing +\DSsymbols +\DSlate + +\makeatother + + \usepackage{fancyvrb} diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/examples/format1.py b/telemeta/util/audiolab/scikits/audiolab/docs/examples/format1.py new file mode 100644 index 00000000..f9166f98 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/examples/format1.py @@ -0,0 +1,7 @@ +from scikits.audiolab import formatinfo as format + +f = format('aiff', 'ulaw') +print f + +f = format('ircam', 'float32') +print f diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/examples/format2.py b/telemeta/util/audiolab/scikits/audiolab/docs/examples/format2.py new file mode 100644 index 00000000..6b176a65 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/examples/format2.py @@ -0,0 +1,6 @@ +from scikits.audiolab import supported_format, supported_encoding, \ + supported_endianness + +print supported_format() +print supported_encoding() +print supported_endianness() diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/examples/matlab1.py b/telemeta/util/audiolab/scikits/audiolab/docs/examples/matlab1.py new file mode 100644 index 00000000..fe1e8fac --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/examples/matlab1.py @@ -0,0 +1,20 @@ +from tempfile import mkstemp +from os.path import join, dirname +from os import remove + +from scikits.audiolab import wavread, wavwrite + +(tmp, fs, enc) = wavread('test.wav') +if tmp.ndim < 2: + nc = 1 +else: + nc = tmp.shape[1] + +print "The file has %d frames, %d channel(s)" % (tmp.shape[0], nc) +print "FS is %f, encoding is %s" % (fs, enc) + +fd, cfilename = mkstemp('pysndfiletest.wav') +try: + wavwrite(tmp, cfilename, fs = 16000, enc = 'pcm24') +finally: + remove(cfilename) diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/examples/quick1.py b/telemeta/util/audiolab/scikits/audiolab/docs/examples/quick1.py new file mode 100644 index 00000000..1d3c4739 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/examples/quick1.py @@ -0,0 +1,5 @@ +import scikits.audiolab as audiolab + +a = audiolab.sndfile('test.flac', 'read') +data = a.read_frames(1000) +a.close() diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/examples/usage1.py b/telemeta/util/audiolab/scikits/audiolab/docs/examples/usage1.py new file mode 100644 index 00000000..a689a01f --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/examples/usage1.py @@ -0,0 +1,6 @@ +import scikits.audiolab as audiolab + +filename = 'test.flac' +a = audiolab.sndfile(filename, 'read') + +print a diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/examples/usage2.py b/telemeta/util/audiolab/scikits/audiolab/docs/examples/usage2.py new file mode 100644 index 00000000..0117e264 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/examples/usage2.py @@ -0,0 +1,12 @@ +import numpy as N + +import scikits.audiolab as audiolab + +filename = 'test.flac' +a = audiolab.sndfile(filename, 'read') + +tmp = a.read_frames(1e4) +float_tmp = a.read_frames(1e4, dtype = N.float32) + +import pylab as P +P.plot(tmp[:]) diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/examples/write1.py b/telemeta/util/audiolab/scikits/audiolab/docs/examples/write1.py new file mode 100644 index 00000000..b0fd1368 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/examples/write1.py @@ -0,0 +1,34 @@ +from tempfile import mkstemp +from os import remove + +import numpy as N +from scikits.audiolab import formatinfo as format +import scikits.audiolab as audiolab + +# Create a temp file in the system temporary dir, and always remove +# it at the end +cd, filename = mkstemp('tmptest.wav') +try: + fmt = format('wav', 'pcm24') + nchannels = 2 + fs = 44100 + + afile = audiolab.sndfile(filename, 'write', fmt, nchannels, fs) + + # Create a stereo white noise, with Gaussian distribution + tmp = 0.1 * N.random.randn(1000, nchannels) + + # Write the first 500 frames of the signal + # Note that the write_frames method uses tmp's numpy dtype to determine how + # to write to the file; sndfile also converts the data on the fly if necessary + afile.write_frames(tmp, 500) + + afile.close() + + # Let's check that the written file has the expected meta data + afile = audiolab.sndfile(filename, 'read') + assert(afile.get_samplerate() == fs) + assert(afile.get_channels() == nchannels) + assert(afile.get_nframes() == 500) +finally: + remove(filename) diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/index.txt b/telemeta/util/audiolab/scikits/audiolab/docs/index.txt new file mode 100644 index 00000000..54110172 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/index.txt @@ -0,0 +1,264 @@ +.. + restindex + page-title: audiolab + crumb: audiolab + link-title: audiolab + encoding: utf-8 + output-encoding: None + file: audiolab1.png + file: quick1.py + file: usage1.py + file: usage2.py + file: format1.py + file: format2.py + file: write1.py + file: matlab1.py + file: audiolab.pdf + /restindex + +.. vim:syntax=rest +.. Last Change: Tue Jul 17 11:00 AM 2007 J + +=============================================================== + Pyaudiolab, a python package to make noise with numpy arrays +=============================================================== + +Introduction +============ + +.. _scipy: http://www.scipy.org +.. _libsndfile: http://www.mega-nerd.com/libsndfile/ + +For people doing audio processing, it is useful to be able to import data from +audio files, and export them back, as well as listening to the results of some +processing; matlab have functions such as wavread, wavwrite, soundsc, etc... +for that purposes. The goal of audiolab is to give those capabilities to the +`scipy`_ environment by wrapping the excellent library `libsndfile`_ from Erik +Castro de Lopo. Pyaudio supports all format supported by libsndfile, including +wav, aiff, ircam files, and flac (an open source lossless compressed format); +see `here `_ for a complete +list. + + **Note**: The library is still in beta stage: reading and writing + data is possible, but only in frames, not per item. + Also, the ability to play data on the system's soundcard is not there yet. + I have never encountered any data corruption, except when using the buggy + ctypes included in Ubuntu's python 2.5 (bug which was solved recently). + + **Note**: The online version of this document is not always up to date. The + pdf included in the package is the reference, and always in sync with the + package. If something does not work, please refer first to the pdf included in + the package. + +.. contents:: Tables of contents + +Download and installation +========================= + +Download +-------- + +audiolab is part of scikits: its source can be downloaded directly from the +scikits svn repository: svn co http://svn.scipy.org/svn/scikits/trunk/audiolab + +Requirements +------------ + +audiolab requires the following softwares: + + - a python interpreter. + - libsndfile (including the header sndfile.h, which means linux users should + download the libsndfile-dev package). + - numpy (any version >= 1.0 should work). + - ctypes (version >= 1.0.1) + +Starting with version 2.5, python include ctypes in its standart library, so you +don't need to install ctypes separately in this case. + +It has been run succesfully on the following platforms: + + - linux ubuntu (32 and 64 bits) + - windows XP + +I would be interested to hear anyone who succeesfully used it on other +plateforms (Mac Os X, solaris, etc...). + + **Note**: the ctypes used in python2.5 package in ubuntu (and maybe debian + as well) *had* a nasty bug which makes it impossible to use 64 bits integers. You + should NOT use this package with audiolab (importing audiolab should fail, + but if the version is not correctly detected, you will have file corruption when + writing data to audio files). Run the test to check everything is working (a + test case tests this issue). + + ``_ + + +Installation +------------ + +For unix users, if libsndfile is installed in standart location (eg /usr/lib, +/usr/local/lib), the installer should be able to find them automatically, and +you only need to do a "python setup.py install". In other cases, you need to +create a file site.cfg to set the location of libsndfile and its header (there +are site.cfg examples which should give you an idea how to use them on your +platform). + +For windows users: the library distributed by Erik Castro de Lopo cannot be +used directly; you need to follow the instructions given in libsndfile +distribution in the file README-precompiled-dll.txt. See also site.cfg.win32. + +License +------- + +audiolab is released under the LGPL, which forces you to release back the +modifications you may make in the version of audiolab you are distributing, +but you can still use it in closed softwares. + +Quick view +========== + +The following code shows you how to open a file for read, reading the first +1000 frames, and closing it: + +.. raw:: html + + {mycolorize;input/softwares/audiolab/quick1.py} + +.. raw:: latex + + \input{quick1.tex} + +Usage +===== + +Opening a file and getting its parameters +----------------------------------------- + +Once imported, audiolab gives you access the sndfile class, which is the +class of audiolab use to open audio files. +You create a sndfile instance when you want +to open a file for reading or writing (the file test.flac is included +in the audiolab package, in the test_data directory): + +.. raw:: html + + {mycolorize;input/softwares/audiolab/usage1.py} + +.. raw:: latex + + \input{usage1.tex} + +Prints you the informations related to the file, like its sampling rate, +the number of frames, etc... You can of course get each parameter +individually by using the corresponding sndfile.get* accessors. + +Importing audio data +-------------------- + +Now that we've opened a file, we would like to read its audio content, +right ? For now, you can only import the data as floating point data, +float (32 bits) or double (64 bits). The function +sndfile.read_frames read n frames, +where a frame contains a sample of each channel (one in mono, 2 in stereo, +etc...): + +.. raw:: html + + {mycolorize;input/softwares/audiolab/usage2.py} + +.. raw:: latex + + \input{usage2.tex} + +The above code import 10000 frames, and plot the first channel using matplotlib +(see below). A frame holds one sample from each channel: 1000 frames of a stereo +file is 2000 samples. Each channel is one column of the numpy array. The read +functions follow numpy conventions, that is by default, the data are read as +double, but you can give a dtype argument to the function. + +.. image:: audiolab1.png + :width: 500 + :height: 400 + +The format class +---------------- + +When opening a file for writing, you need to give various parameters related to +the format such as the file format, the encoding. The format class is used to +create valid formats from those parameters By default, the format class creates +a format object with file type wav, and 16 bits pcm encoding: + +.. raw:: html + + {mycolorize;input/softwares/audiolab/format1.py} + +.. raw:: latex + + \input{format1.tex} + +prints back "Major Format: AIFF (Apple/SGI), Encoding Format: U-Law" +and "Major Format: SF (Berkeley/IRCAM/CARL), Encoding Format: 32 bit float". + +To get a list of all possible file format and encoding, the function +supported_* are available: + +.. raw:: html + + {mycolorize;input/softwares/audiolab/format2.py} + +.. raw:: latex + + \input{format2.tex} + + **Note**: not all combination of encoding, endianness and format are possible. + If you try to create a format with incompatible values, you will get an error + while creating an instance of format. + +Writing data to a file +---------------------- + +Opening a file for writing is a bit more complicated than reading; you need to +say which format you are requesting, the number of channels and the sampling +rate (in Hz) you are requesting; all thoses information are mandatory ! The +class format is used to build a format understable by libsndfile from +'user-friendly' values. Let's see how it works. + +.. raw:: html + + {mycolorize;input/softwares/audiolab/write1.py} + +.. raw:: latex + + \input{write1.tex} + +Matlab-like API +--------------- + +audiolab also have a matlab-like API for audio IO. Its usage is as similar as it +can get using python: + +.. raw:: html + + {mycolorize;input/softwares/audiolab/matlab1.py} + +.. raw:: latex + + \input{matlab1.tex} + +Known bugs: +=========== + + - there seems to be a problem when using libsndfile fseek facilities with flac + files (which are necessary for the functions flacread/flacwrite). The + problem seems to be with libFLAC; for this reason, seek in flac files is not + enabled by default for now. See FLAC_SUPPORT.txt for more informations. + +TODO +==== + +audiolab is still in early stages. Before a release, I would like to implement the +follwings: + + - support (at least some) meta-data embedded in some audio files format. + - support the libsndfile's error system + - player on all major plateforms (at least linux/windows/max OS X) diff --git a/telemeta/util/audiolab/scikits/audiolab/docs/user.tex b/telemeta/util/audiolab/scikits/audiolab/docs/user.tex new file mode 100644 index 00000000..6ea70994 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/docs/user.tex @@ -0,0 +1,64 @@ +% Last Change: Wed Jan 31 08:00 PM 2007 J +% vim:syntax=tex + +\newcommand\at{@} +\newcommand\lb{[} +\newcommand\rb{]} +\newcommand\Cba[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}} +\newcommand\Caz[1]{\textcolor[rgb]{0.00,0.25,0.82}{#1}} +\newcommand\Cay[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}} +\newcommand\Cax[1]{\textcolor[rgb]{0.00,0.63,0.00}{#1}} +\newcommand\Cbc[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand\Cas[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand\Car[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}} +\newcommand\Caq[1]{\textcolor[rgb]{0.73,0.27,0.27}{\textit{#1}}} +\newcommand\Cap[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}} +\newcommand\Caw[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}} +\newcommand\Cav[1]{\textcolor[rgb]{0.60,0.60,0.60}{\textbf{#1}}} +\newcommand\Cau[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand\Cat[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}} +\newcommand\Cak[1]{\textbf{#1}} +\newcommand\Caj[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}} +\newcommand\Cai[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}} +\newcommand\Cah[1]{\textcolor[rgb]{0.63,0.63,0.00}{#1}} +\newcommand\Cao[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}} +\newcommand\Can[1]{\textcolor[rgb]{0.00,0.50,0.00}{#1}} +\newcommand\Cam[1]{\textcolor[rgb]{0.73,0.40,0.13}{\textbf{#1}}} +\newcommand\Cal[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}} +\newcommand\Cac[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}} +\newcommand\Cab[1]{\textit{#1}} +\newcommand\Caa[1]{\textcolor[rgb]{0.50,0.50,0.50}{#1}} +\newcommand\Cag[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand\Caf[1]{\textcolor[rgb]{0.00,0.53,0.00}{\textit{#1}}} +\newcommand\Cae[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand\Cad[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}} +\newcommand\Cbb[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}} +\newcommand\CaZ[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand\CaY[1]{\textcolor[rgb]{0.00,0.00,0.50}{\textbf{#1}}} +\newcommand\CaX[1]{\textcolor[rgb]{0.00,0.50,0.00}{\textbf{#1}}} +\newcommand\Cbd[1]{\textcolor[rgb]{0.73,0.40,0.53}{\textbf{#1}}} +\newcommand\Cbe[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}} +\newcommand\CaS[1]{\textcolor[rgb]{0.50,0.00,0.50}{\textbf{#1}}} +\newcommand\CaR[1]{\textcolor[rgb]{0.00,0.53,0.00}{\textit{#1}}} +\newcommand\CaQ[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}} +\newcommand\CaP[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand\CaW[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}} +\newcommand\CaV[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}} +\newcommand\CaU[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}} +\newcommand\CaT[1]{\textcolor[rgb]{0.00,0.00,1.00}{\textbf{#1}}} +\newcommand\CaK[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}} +\newcommand\CaJ[1]{\textcolor[rgb]{0.00,0.63,0.00}{#1}} +\newcommand\CaI[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}} +\newcommand\CaH[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}} +\newcommand\CaO[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}} +\newcommand\CaN[1]{\textcolor[rgb]{0.00,0.00,0.50}{\textbf{#1}}} +\newcommand\CaM[1]{\textcolor[rgb]{0.00,0.00,1.00}{#1}} +\newcommand\CaL[1]{\textcolor[rgb]{0.00,0.53,0.00}{#1}} +\newcommand\CaC[1]{\textcolor[rgb]{0.00,0.53,0.00}{\textit{#1}}} +\newcommand\CaB[1]{\textcolor[rgb]{0.82,0.25,0.23}{\textbf{#1}}} +\newcommand\CaA[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}} +\newcommand\CaG[1]{\fcolorbox[rgb]{1.00,0.00,0.00}{1,1,1}{#1}} +\newcommand\CaF[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}} +\newcommand\CaE[1]{\textcolor[rgb]{1.00,0.00,0.00}{#1}} +\newcommand\CaD[1]{\textcolor[rgb]{0.63,0.00,0.00}{#1}} + diff --git a/telemeta/util/audiolab/scikits/audiolab/info.py b/telemeta/util/audiolab/scikits/audiolab/info.py new file mode 100644 index 00000000..47dd67f6 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/info.py @@ -0,0 +1,2 @@ +VERSION = '0.8dev' +ignore = False diff --git a/telemeta/util/audiolab/scikits/audiolab/info.pyc b/telemeta/util/audiolab/scikits/audiolab/info.pyc new file mode 100644 index 00000000..08186b2a Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/info.pyc differ diff --git a/telemeta/util/audiolab/scikits/audiolab/matapi.py b/telemeta/util/audiolab/scikits/audiolab/matapi.py new file mode 100644 index 00000000..1568fe9c --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/matapi.py @@ -0,0 +1,148 @@ +#! /usr/bin/env python +# Last Change: Mon Sep 10 07:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +"""This module implements functions to read and write to audio files easily +(ala matlab: wavread, etc...).""" + +import numpy as N + +from pysndfile import formatinfo, sndfile +from pysndfile import PyaudioException, FlacUnsupported + +__all__ = [] +_MATAPI_FORMAT = ['wav', 'aiff', 'au', 'sdif', 'flac'] +for i in _MATAPI_FORMAT: + __all__.extend(['%sread' % i, '%swrite' % i]) + +# writer function factory +def _writer_factory(name, format, def_fs, descr): + """ Create a writer function with fileformat described by format, default + sampling rate def_fs, and docstring descr.""" + def basic_writer(data, filename, fs = def_fs, enc = format.get_encoding()): + """Common "template" to all write functions.""" + if N.ndim(data) <= 1: + nc = 1 + nframes = N.size(data) + elif N.ndim(data) == 2: + nc = data.shape[1] + nframes = data.shape[0] + else: + RuntimeError("Only rank 0, 1, and 2 arrays supported as audio data") + + hdl = sndfile(filename, 'write', format, nc, fs) + try: + hdl.write_frames(data, nframes) + finally: + hdl.close() + doc = \ + """ wrapper around pysndfile to write %s file, + in a similar manner to matlab's wavwrite/auwrite and the likes. + + OVERWRITES EXISTING FILE ! + + Args: + - data: a rank 0, 1 (mono) or 2 (one channel per col) numpy array + - filename: a string for the audio file name + - fs: the sampling rate in Hz (%d Hz by default). + - enc: a string for the encoding such as 'pcm16', etc...(%s by + default). Not supported yet ! + + For a total control over options, such as endianness, append data to an + existing file, etc... you should use sndfile class instances instead""" \ + % (str(descr), def_fs, format.get_encoding()) + basic_writer.__doc__ = doc + basic_writer.__name__ = name + return basic_writer + +# template for reader functions +def _reader_factory(name, filetype, descr): + """Factory for reader functions ala matlab.""" + def basic_reader(filename, last = None, first = 0): + """Common "template" to all read functions.""" + hdl = sndfile(filename, 'read') + try: + if not hdl.get_file_format() == filetype: + raise PyaudioException("%s is not a %s file (is %s)" \ + % (filename, filetype, hdl.get_file_format())) + + fs = hdl.get_samplerate() + enc = hdl.get_encoding() + # Set the pointer to start position + nf = hdl.seek(first, 1) + if not nf == first: + raise IOError("Error while seeking at starting position") + + if last is None: + nframes = hdl.get_nframes() - first + data = hdl.read_frames(nframes) + else: + data = hdl.read_frames(last) + finally: + hdl.close() + + return data, fs, enc + doc = \ + """ wrapper around pysndfile to read a %s file in float64, + in a similar manner to matlab wavread/auread/etc... + + Returns a tuple (data, fs, enc), where : + - data are the read data (one column per channel) + - fs, the sampling rate + - enc, a string which is the encoding of the file, such as 'pcm16', + 'float32', etc... + + For a total control over options, such as output's dtype, etc..., + you should use sndfile class instances instead""" % (str(descr),) + basic_reader.__doc__ = doc + basic_reader.__name__ = name + return basic_reader + +wavread = _reader_factory('wavread', 'wav', + formatinfo('wav', 'pcm16').get_major_str()) +auread = _reader_factory('auread', 'au', + formatinfo('au', 'pcm16').get_major_str()) +aiffread = _reader_factory('aiffread', 'aiff', + formatinfo('aiff', 'pcm16').get_major_str()) +sdifread = _reader_factory('sdifread', 'ircam', + formatinfo('ircam', 'pcm16').get_major_str()) + +_f1 = formatinfo('wav', 'pcm16') +wavwrite = _writer_factory('wavwrite', _f1, 8000, _f1.get_major_str()) + +_f2 = formatinfo('au', 'ulaw') +auwrite = _writer_factory('auwrite', _f2, 8000, _f2.get_major_str()) + +_f3 = formatinfo('aiff', 'pcm16') +aiffwrite = _writer_factory('aiffwrite', _f3, 8000, _f3.get_major_str()) + +_f4 = formatinfo('ircam', 'pcm16') +sdifwrite = _writer_factory('sdifwrite', _f4, 44100, _f4.get_major_str()) + +try: + flacread = _reader_factory('flacread', 'flac', + formatinfo('flac', 'pcm16').get_major_str()) + _f5 = formatinfo('flac', 'pcm16') + flacwrite = _writer_factory('flacwrite', _f5, 44100, _f5.get_major_str()) +except FlacUnsupported,e: + print e + print "Matlab API for FLAC is disabled" + def missing_flacread(*args): + raise UnimplementedError("Matlab API for FLAC is disabled on your "\ + "installation") + flacread = missing_flacread + flacwrite = missing_flacread diff --git a/telemeta/util/audiolab/scikits/audiolab/matapi.pyc b/telemeta/util/audiolab/scikits/audiolab/matapi.pyc new file mode 100644 index 00000000..d1abc0b1 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/matapi.pyc differ diff --git a/telemeta/util/audiolab/scikits/audiolab/misc/Makefile b/telemeta/util/audiolab/scikits/audiolab/misc/Makefile new file mode 100644 index 00000000..2fcbde26 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/misc/Makefile @@ -0,0 +1,23 @@ +CC = colorgcc +LD = gcc + +CFLAGS = -Wall -W + +winfdopen: winfdopen.o + $(LD) $< -o $@ -lsndfile + +winfdopen.o: winfdopen.c + $(CC) $(CFLAGS) -c $< -o $@ + +badflac: badflac.o + $(LD) $< -o $@ -lsndfile + +badflac.o: badflac.c + $(CC) $(CFLAGS) -c $< -o $@ + +test_badflac: badflac badflac.flac + ./badflac badflac.flac + +clean: + rm -f *.o + rm -f badflac diff --git a/telemeta/util/audiolab/scikits/audiolab/misc/Sconstruct b/telemeta/util/audiolab/scikits/audiolab/misc/Sconstruct new file mode 100644 index 00000000..6c29ab33 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/misc/Sconstruct @@ -0,0 +1,5 @@ +# vim:syntax=python +# Last Change: Fri Jun 01 12:00 PM 2007 J +# +mainobj = Object('winfdopen.c') +winfd = Program(mainobj, LIBS = ['sndfile']) diff --git a/telemeta/util/audiolab/scikits/audiolab/misc/badflac.c b/telemeta/util/audiolab/scikits/audiolab/misc/badflac.c new file mode 100644 index 00000000..906ad44f --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/misc/badflac.c @@ -0,0 +1,37 @@ +#include +#include + +#include + +int main(int argc, char *argv[]) +{ + SF_INFO info; + SNDFILE* file; + sf_count_t nf; + + char buffer [2048] ; + if (argc < 2) { + fprintf(stderr, "usage: badflac filename \n"); + exit(EXIT_FAILURE); + } + + info.format = 0; + file = sf_open(argv[1], SFM_READ, &info); + if (file == NULL) { + fprintf(stderr, "%s:%s failed opening file %s\n", __FILE__, __func__, argv[1]); + sf_command (file, SFC_GET_LOG_INFO, buffer, sizeof (buffer)) ; + fprintf(stderr, "sndfile error is %s:\n", buffer); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "Values of seek are on this platform: SET %d, CUR %d, END %d\n", + SEEK_SET, SEEK_CUR, SEEK_END); + + fprintf(stderr, "trying to seek %lld frames\n", (long long)1); + nf = sf_seek(file, 1, SEEK_CUR); + fprintf(stderr, "seeked through %lld frames\n", nf); + + sf_close(file); + + return 0; +} diff --git a/telemeta/util/audiolab/scikits/audiolab/misc/badflac.flac b/telemeta/util/audiolab/scikits/audiolab/misc/badflac.flac new file mode 100644 index 00000000..70d0a657 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/misc/badflac.flac differ diff --git a/telemeta/util/audiolab/scikits/audiolab/misc/winfdopen.c b/telemeta/util/audiolab/scikits/audiolab/misc/winfdopen.c new file mode 100644 index 00000000..de95e453 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/misc/winfdopen.c @@ -0,0 +1,82 @@ +#include +#include +#include + +#include + +#ifdef _MSC_VER +#define __func__ __FUNCTION__ +#else +#include +#endif + +int test(const char* filename, int byfd); + +int main(int argc, char *argv[]) +{ + int st; + + if (argc < 2) { + fprintf(stderr, "usage: %s filename \n", argv[0]); + exit(EXIT_FAILURE); + } + + st = test(argv[1], 0); + if (st) { + fprintf(stderr, "Error while opening directly\n"); + } else { + fprintf(stderr, "Opening directly is fine\n"); + } + + st = test(argv[1], 1); + if (st) { + fprintf(stderr, "Error while opening by fd\n"); + } else { + fprintf(stderr, "Opening by fd is fine\n"); + } + + return 0; +} + +/* If byfd is true, try to open the file with sf_open_fd */ +int test(const char* filename, int byfd) +{ + SF_INFO info; + SNDFILE* file; + int fid, flags, st; + char buffer [2048]; + + st = 0; + + flags = O_RDONLY; +#if (defined (WIN32) || defined (_WIN32)) + flags |= O_BINARY; +#endif + + info.format = 0; + if (byfd) { + fid = open(filename, flags); + if (fid < 0) { + fprintf(stderr, "%s:%s failed opening file %s\n", __FILE__, __func__, filename); + return -1; + } + + file = sf_open_fd(fid, SFM_READ, &info, SF_TRUE); + } else { + file = sf_open(filename, SFM_READ, &info); + } + + if (file == NULL) { + fprintf(stderr, "%s:%s failed opening file %s\n", __FILE__, __func__, filename); + sf_command (file, SFC_GET_LOG_INFO, buffer, sizeof (buffer)) ; + fprintf(stderr, "sndfile error is %s:\n", buffer); + close(fid); + exit(EXIT_FAILURE); + } else { + fprintf(stderr, "%s:%s file %s has %d frames \n", __FILE__, __func__, filename, info.frames); + } + + sf_close(file); + + return st; +} diff --git a/telemeta/util/audiolab/scikits/audiolab/pyaudioio.py b/telemeta/util/audiolab/scikits/audiolab/pyaudioio.py new file mode 100644 index 00000000..94c1e9fc --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/pyaudioio.py @@ -0,0 +1,68 @@ +#! /usr/bin/env python +# Last Change: Tue May 22 10:00 AM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) any +# later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this library; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# TODO: +# - find out why finally does not work with KeyboardInterrupt instances + +from tempfile import mkstemp +from os import remove, popen + +from pysndfile import sndfile, formatinfo as format + +def play(input, sr = 22050): + """play(input, sr = 22050): "play" a numpy array input + to the audio device using aplay command, @ sampling rate + sr. + + Warning: This is really crude, as it copies the numpy array + into an audio file before writing: I am too lazy to write + interfaces to also, windows and co...""" + # Check inputs are OK + if input.ndim == 1: + nc = 1 + nframes = input.size + else: + (nframes, nc) = input.shape + + # Create tmp file + fd, filename = mkstemp('py_player') + + # Copy the data into it + b = sndfile(filename, 'write', format('wav', 'pcm16'), nc, sr) + b.write_frames(input, nframes) + b.close() + + # Play using an audio command + try: + cmd = "aplay %s" % filename + popen(cmd) + remove(filename) + except KeyboardInterrupt, inst: + remove(filename) + raise inst + +if __name__ == '__main__': + # Read the content of a file into numpy array, and play the numpy + # array using the play command + import numpy as N + sr = 22050 + # Play a really small noise to avoid screaming in loudspeakers + # or headphones. + noise = 0.0001 * N.random.randn((sr)) + play(noise, sr) diff --git a/telemeta/util/audiolab/scikits/audiolab/pysndfile.py b/telemeta/util/audiolab/scikits/audiolab/pysndfile.py new file mode 100644 index 00000000..2fea1fef --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/pysndfile.py @@ -0,0 +1,992 @@ +#! /usr/bin/env python +# Last Change: Wed Oct 03 05:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# vim:syntax=python + +# TODO: +# - import format classes so that we get meaningful information from an +# existing format +# - better API for reader/writer, including integer formats and partial +# reading +# - ability to get log of sndfile ? +# - check how to play sound under windows, OS X and other UNIX + +"""This module implements the wrappers around libsndfile.""" + +__docformat__ = 'restructuredtext' + +#__all__ = ['sndfile', 'formatinfo'] + +import copy +import warnings + +#================ +# Load libsndfile +#================ +import ctypes +from ctypes import cdll, Structure, c_int, pointer, POINTER, \ + create_string_buffer, c_char_p, sizeof, string_at +try: + from ctypes import c_int64 +except ImportError, e: + print "Cannot import c_int64 from ctypes: if you are on ubuntu/debian," +\ + " this is likely because ctypes was compiled with libffi. see" +\ + " https://launchpad.net/ubuntu/+source/python2.5/+bug/71914" + raise e + +from numpy.ctypeslib import ndpointer +CTYPES_MAJOR = int(ctypes.__version__.split('.')[0]) +CTYPES_MINOR = int(ctypes.__version__.split('.')[1]) +CTYPES_MICRO = int(ctypes.__version__.split('.')[2]) +if CTYPES_MAJOR < 1 or (CTYPES_MINOR == 0 and CTYPES_MICRO < 1): + raise ImportError("version of ctypes is %s, expected at least %s" \ + % (ctypes.__version__, '1.0.1')) +import numpy as N + +_SND = cdll.LoadLibrary('/usr/lib/libsndfile.so.1') + +#========================= +# Definition of constants +#========================= +# READ/WRITE Mode +SFM = { + 'SFM_WRITE' : 0x20, + 'SFM_RDWR' : 0x30, + 'SFM_READ' : 0x10 +} + +# SF BOOL +SF_BOOL = { + 'SF_TRUE' : 1, + 'SF_FALSE' : 0 +} + +# Format +SF_FORMAT = { + 'SF_FORMAT_VOX_ADPCM' : 0x0021, + 'SF_FORMAT_FLOAT' : 0x0006, + 'SF_FORMAT_PCM_S8' : 0x0001, + 'SF_FORMAT_IMA_ADPCM' : 0x0012, + 'SF_FORMAT_SVX' : 0x060000, + 'SF_FORMAT_VOC' : 0x080000, + 'SF_FORMAT_PCM_U8' : 0x0005, + 'SF_FORMAT_ALAW' : 0x0011, + 'SF_FORMAT_G721_32' : 0x0030, + 'SF_FORMAT_DWVW_N' : 0x0043, + 'SF_FORMAT_WAV' : 0x010000, + 'SF_FORMAT_SD2' : 0x160000, + 'SF_FORMAT_HTK' : 0x100000, + 'SF_FORMAT_ENDMASK' : 0x30000000, + 'SF_FORMAT_DPCM_16' : 0x0051, + 'SF_FORMAT_DWVW_24' : 0x0042, + 'SF_FORMAT_PCM_32' : 0x0004, + 'SF_FORMAT_WAVEX' : 0x130000, + 'SF_FORMAT_DOUBLE' : 0x0007, + 'SF_FORMAT_NIST' : 0x070000, + 'SF_FORMAT_PCM_16' : 0x0002, + 'SF_FORMAT_RAW' : 0x040000, + 'SF_FORMAT_W64' : 0x0B0000, + 'SF_FORMAT_PVF' : 0x0E0000, + 'SF_FORMAT_AU' : 0x030000, + 'SF_FORMAT_GSM610' : 0x0020, + 'SF_FORMAT_CAF' : 0x180000, + 'SF_FORMAT_PAF' : 0x050000, + 'SF_FORMAT_ULAW' : 0x0010, + 'SF_FORMAT_MAT4' : 0x0C0000, + 'SF_FORMAT_MAT5' : 0x0D0000, + 'SF_FORMAT_XI' : 0x0F0000, + 'SF_FORMAT_SUBMASK' : 0x0000FFFF, + 'SF_FORMAT_DPCM_8' : 0x0050, + 'SF_FORMAT_G723_24' : 0x0031, + 'SF_FORMAT_G723_40' : 0x0032, + 'SF_FORMAT_DWVW_16' : 0x0041, + 'SF_FORMAT_AIFF' : 0x020000, + 'SF_FORMAT_DWVW_12' : 0x0040, + 'SF_FORMAT_TYPEMASK' : 0x0FFF0000, + 'SF_FORMAT_FLAC' : 0x170000, + 'SF_FORMAT_PCM_24' : 0x0003, + 'SF_FORMAT_SDS' : 0x110000, + 'SF_FORMAT_IRCAM' : 0x0A0000, + 'SF_FORMAT_MS_ADPCM' : 0x0013, + 'SF_FORMAT_AVR' : 0x120000 +} + +# ENDIANESS +SF_ENDIAN = { + 'SF_ENDIAN_BIG' : 0x20000000, + 'SF_ENDIAN_FILE' : 0x00000000, + 'SF_ENDIAN_LITTLE' : 0x10000000, + 'SF_ENDIAN_CPU' : 0x30000000 +} + +# Commands +SF_COMMAND = { + 'SFC_GET_LIB_VERSION' : 0x1000, + 'SFC_CALC_SIGNAL_MAX' : 0x1040, + 'SFC_GET_DITHER_INFO' : 0x10A3, + 'SFC_GET_LOG_INFO' : 0x1001, + 'SFC_GET_FORMAT_SUBTYPE_COUNT' : 0x1032, + 'SFC_FILE_TRUNCATE' : 0x1080, + 'SFC_GET_INSTRUMENT' : 0x10D0, + 'SFC_UPDATE_HEADER_NOW' : 0x1060, + 'SFC_SET_DITHER_ON_WRITE' : 0x10A0, + 'SFC_SET_NORM_DOUBLE' : 0x1012, + 'SFC_GET_CLIPPING' : 0x10C1, + 'SFC_SET_RAW_START_OFFSET' : 0x1090, + 'SFC_CALC_NORM_MAX_ALL_CHANNELS' : 0x1043, + 'SFC_SET_NORM_FLOAT' : 0x1013, + 'SFC_SET_ADD_DITHER_ON_WRITE' : 0x1070, + 'SFC_GET_NORM_FLOAT' : 0x1011, + 'SFC_GET_SIGNAL_MAX' : 0x1044, + 'SFC_GET_MAX_ALL_CHANNELS' : 0x1045, + 'SFC_GET_FORMAT_MAJOR' : 0x1031, + 'SFC_SET_INSTRUMENT' : 0x10D1, + 'SFC_CALC_MAX_ALL_CHANNELS' : 0x1042, + 'SFC_GET_DITHER_INFO_COUNT' : 0x10A2, + 'SFC_SET_BROADCAST_INFO' : 0x10F1, + 'SFC_SET_DITHER_ON_READ' : 0x10A1, + 'SFC_GET_FORMAT_MAJOR_COUNT' : 0x1030, + 'SFC_GET_FORMAT_INFO' : 0x1028, + 'SFC_GET_SIMPLE_FORMAT_COUNT' : 0x1020, + 'SFC_CALC_NORM_SIGNAL_MAX' : 0x1041, + 'SFC_GET_LOOP_INFO' : 0x10E0, + 'SFC_SET_ADD_PEAK_CHUNK' : 0x1050, + 'SFC_SET_ADD_DITHER_ON_READ' : 0x1071, + 'SFC_SET_SCALE_FLOAT_INT_READ' : 0x1014, + 'SFC_GET_FORMAT_SUBTYPE' : 0x1033, + 'SFC_TEST_IEEE_FLOAT_REPLACE' : 0x6001, + 'SFC_SET_UPDATE_HEADER_AUTO' : 0x1061, + 'SFC_GET_SIMPLE_FORMAT' : 0x1021, + 'SFC_SET_CLIPPING' : 0x10C0, + 'SFC_GET_EMBED_FILE_INFO' : 0x10B0, + 'SFC_GET_BROADCAST_INFO' : 0x10F0, + 'SFC_GET_NORM_DOUBLE' : 0x1010 +} + +SF_ERRORS = { + 'SF_ERR_UNRECOGNISED_FORMAT' : 1, + 'SF_ERR_NO_ERROR' : 0, + 'SF_ERR_SYSTEM' : 2, + 'SF_ERR_UNSUPPORTED_ENCODING' : 4, + 'SF_ERR_MALFORMED_FILE' : 3 +} + +# format equivalence: dic used to create internally +# the right enum values from user friendly strings +py_to_snd_encoding_dic = { + 'pcms8' : SF_FORMAT['SF_FORMAT_PCM_S8'], + 'pcm16' : SF_FORMAT['SF_FORMAT_PCM_16'], + 'pcm24' : SF_FORMAT['SF_FORMAT_PCM_24'], + 'pcm32' : SF_FORMAT['SF_FORMAT_PCM_32'], + + 'pcmu8' : SF_FORMAT['SF_FORMAT_PCM_U8'], + + 'float32' : SF_FORMAT['SF_FORMAT_FLOAT'], + 'float64' : SF_FORMAT['SF_FORMAT_DOUBLE'], + + 'ulaw' : SF_FORMAT['SF_FORMAT_ULAW'], + 'alaw' : SF_FORMAT['SF_FORMAT_ALAW'], + 'ima_adpcm' : SF_FORMAT['SF_FORMAT_IMA_ADPCM'], + 'ms_adpcm' : SF_FORMAT['SF_FORMAT_MS_ADPCM'], + + 'gsm610' : SF_FORMAT['SF_FORMAT_GSM610'], + 'vox_adpcm' : SF_FORMAT['SF_FORMAT_VOX_ADPCM'], + + 'g721_32' : SF_FORMAT['SF_FORMAT_G721_32'], + 'g723_24' : SF_FORMAT['SF_FORMAT_G723_24'], + 'g723_40' : SF_FORMAT['SF_FORMAT_G723_40'], + + 'dww12' : SF_FORMAT['SF_FORMAT_DWVW_12'], + 'dww16' : SF_FORMAT['SF_FORMAT_DWVW_16'], + 'dww24' : SF_FORMAT['SF_FORMAT_DWVW_24'], + 'dwwN' : SF_FORMAT['SF_FORMAT_DWVW_N'], + + 'dpcm8' : SF_FORMAT['SF_FORMAT_DPCM_8'], + 'dpcm16': SF_FORMAT['SF_FORMAT_DPCM_16'] +} + +py_to_snd_file_format_dic = { + 'wav' : SF_FORMAT['SF_FORMAT_WAV'], + 'aiff' : SF_FORMAT['SF_FORMAT_AIFF'], + 'au' : SF_FORMAT['SF_FORMAT_AU'], + 'raw' : SF_FORMAT['SF_FORMAT_RAW'], + 'paf' : SF_FORMAT['SF_FORMAT_PAF'], + 'svx' : SF_FORMAT['SF_FORMAT_SVX'], + 'nist' : SF_FORMAT['SF_FORMAT_NIST'], + 'voc' : SF_FORMAT['SF_FORMAT_VOC'], + 'ircam' : SF_FORMAT['SF_FORMAT_IRCAM'], + 'wav64' : SF_FORMAT['SF_FORMAT_W64'], + 'mat4' : SF_FORMAT['SF_FORMAT_MAT4'], + 'mat5' : SF_FORMAT['SF_FORMAT_MAT5'], + 'pvf' : SF_FORMAT['SF_FORMAT_PVF'], + 'xi' : SF_FORMAT['SF_FORMAT_XI'], + 'htk' : SF_FORMAT['SF_FORMAT_HTK'], + 'sds' : SF_FORMAT['SF_FORMAT_SDS'], + 'avr' : SF_FORMAT['SF_FORMAT_AVR'], + 'wavex' : SF_FORMAT['SF_FORMAT_WAVEX'], + 'sd2' : SF_FORMAT['SF_FORMAT_SD2'], + 'flac' : SF_FORMAT['SF_FORMAT_FLAC'], + 'caf' : SF_FORMAT['SF_FORMAT_CAF'] +} + +py_to_snd_endianness_dic = { + 'file' : SF_ENDIAN['SF_ENDIAN_FILE'], + 'little' : SF_ENDIAN['SF_ENDIAN_LITTLE'], + 'big' : SF_ENDIAN['SF_ENDIAN_BIG'], + 'cpu' : SF_ENDIAN['SF_ENDIAN_CPU'] +} + +# Those following dic are used internally to get user-friendly values from +# sndfile enum +SND_TO_PY_ENCODING = \ + dict([(i, j) for j, i in py_to_snd_encoding_dic.items()]) +SND_TO_PY_FILE_FORMAT = \ + dict([(i, j) for j, i in py_to_snd_file_format_dic.items()]) +SND_TO_PY_ENDIANNESS = \ + dict([(i, j) for j, i in py_to_snd_endianness_dic.items()]) + +#========================================== +# Check that libsndfile is expected version +#========================================== +def get_libsndfile_version(): + nverbuff = 256 + verbuff = create_string_buffer(nverbuff) + n = _SND.sf_command(c_int(0), c_int(SF_COMMAND['SFC_GET_LIB_VERSION']), + verbuff, nverbuff) + if n < 1: + raise Exception("Error while getting version of libsndfile") + + # Transform the buffer into a string + ver = "" + for i in range(n): + ver += verbuff[i] + + # Get major, minor and micro from version + version = ver.split('-')[1] + prerelease = 0 + major, minor, micro = [i for i in version.split('.')] + try: + micro = int(micro) + except ValueError,e: + print "micro is " + str(micro) + micro, prerelease = micro.split('pre') + + return int(major), int(minor), int(micro), prerelease + +MAJOR, MINOR, MICRO, PRERELEASE = get_libsndfile_version() +if not(MAJOR == 1): + raise Exception("audiolab expects major version %d of libsndfile" % 1) +if not(MICRO == 17): + if PRERELEASE == 0: + prestr = "No" + else: + prestr = "%s" % PRERELEASE + print "WARNING libsndfile-%d.%d.%d (prerelease: %s) "\ + "this has only been tested with libsndfile 1.0.17 for now, "\ + "use at your own risk !" % (MAJOR, MINOR, MICRO, prestr) + +#================ +# Python wrappers +#================ + +#+++++++++++++++++ +# Public exception +#+++++++++++++++++ +class PyaudioException(Exception): + pass + +class InvalidFormat(PyaudioException): + pass + +class PyaudioIOError(PyaudioException, IOError): + pass + +class WrappingError(PyaudioException): + pass + +class FlacUnsupported(RuntimeError, PyaudioException): + pass + +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Private classes/function (Should not be used outside this file) +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +class _sf_info(Structure): + """Structure representing C structure SF_INFO""" + _fields_ = [('frames', c_int64), + ('samplerate', c_int), + ('channels', c_int), + ('format', c_int), + ('sections', c_int), + ('seekable', c_int)] + def __str__(self): + return "%d frames, sr = %d Hz, %d channels, format is %d" % \ + (self.frames, self.samplerate, self.channels, self.format) + +class _sf_format_info(Structure): + """Structure representing C structure SF_FORMAT_INFO (useful for + sf_command )""" + _fields_ = [('format', c_int), + ('name', c_char_p), + ('extension', c_char_p)] + def __str__(self): + return "format hex is %#010x, name is %s, extension is %s" % \ + (self.format, self.name, self.extension) + + def __repr__(self): + print self.__str__() + +class _sndfile(Structure): + pass + +sf_info_p = POINTER(_sf_info) +sndfile_p = POINTER(_sndfile) + +# functions args +# TODO: is there a way to ensure that arg1 is the right kind of pointer ? +arg1 = c_char_p +arg2 = c_int +arg3 = sf_info_p +_SND.sf_open.argtypes = [arg1, arg2, arg3] +_SND.sf_open.restype = sndfile_p + +arg1 = sndfile_p +_SND.sf_close.argtypes = [arg1] +_SND.sf_close.restype = c_int + +arg1 = c_int +arg2 = c_int +arg3 = sf_info_p +arg4 = c_int +_SND.sf_open_fd.argtypes = [arg1, arg2, arg3, arg4] +_SND.sf_open_fd.restype = sndfile_p + +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.float64) +arg3 = c_int64 + +# double function +_SND.sf_readf_double.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_double.restype = c_int64 + +_SND.sf_writef_double.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_double.restype = c_int64 + +# float function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.float32) +arg3 = c_int64 +_SND.sf_readf_float.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_float.restype = c_int64 + +_SND.sf_writef_float.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_float.restype = c_int64 + +# int function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.int32) +arg3 = c_int64 +_SND.sf_readf_int.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_int.restype = c_int64 + +_SND.sf_writef_int.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_int.restype = c_int64 + +# short function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.int16) +arg3 = c_int64 +_SND.sf_readf_short.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_short.restype = c_int64 + +_SND.sf_writef_short.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_short.restype = c_int64 + +# Error functions +arg1 = sndfile_p +_SND.sf_strerror.argtypes = [arg1] +_SND.sf_strerror.restype = c_char_p + +# Function to sync data to file +arg1 = sndfile_p +_SND.sf_write_sync.argtypes = [arg1] + +# Function to seek +arg1 = sndfile_p +arg2 = c_int64 +arg3 = c_int +_SND.sf_seek.argtypes = [arg1, arg2, arg3] +_SND.sf_seek.restype = c_int64 + +# To pass when a C function needs a NULL arg +_cNULL = POINTER(c_int)() + +class _format_from_internal: + """Class to handle audio format with sndfile. + + DO NOT USE THIS CLASS OUTSIDE pysndfile.py MODULE: YOU MAY CRASH YOUR + INTERPRETER ! + + Basically, we have 3 classes of parameters: + - the main format: (major format), like wav, aiff, etc... + - the subtype format: pcm, bits resolution + - endianness: little, big, as the cpu, default of the format + + This class encapsulates those parameters, and can build a representation of + them from the format integer of sf_info. This should *NOT* be used, use + format instead, which inherits this class to build a valid format from user + friendly arguments. """ + def __init__(self, format_integer): + # Get the internal values which corresponds to the values libsndfile + # can understand + self._int_type = format_integer & SF_FORMAT['SF_FORMAT_TYPEMASK'] + self._int_encoding = format_integer & SF_FORMAT['SF_FORMAT_SUBMASK'] + self._int_endianness = format_integer & SF_FORMAT['SF_FORMAT_ENDMASK'] + + assert format_integer == self._int_type | self._int_encoding |\ + self._int_endianness + self._format = format_integer + + # Now, we need to test if the combination of format, encoding and + # endianness is valid. sf_format_check needs also samplerate and + # channel information, so just give a fake samplerate and channel + # number. Looking at sndfile.c, it looks like samplerate is never + # actually checked, and that when channels is checked, it is only + # checked against values different than 1 or 2, so giving a value of + # 1 to channel should be ok. + self._sfinfo = _sf_info() + self._sfinfo.channels = 1 + self._sfinfo.samplerate = 8000 + self._sfinfo.format = self._format + + ret = _SND.sf_format_check(pointer(self._sfinfo)) + if ret is not SF_BOOL['SF_TRUE']: + raise InvalidFormat() + + # Get the sndfile string description of the format type + blop = _sf_format_info() + blop.format = self._int_type + st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \ + pointer(blop), sizeof(blop)) + if st is not 0: + if SND_TO_PY_FILE_FORMAT[self._int_type] == 'flac': + raise FlacUnsupported("Flac is not supported by your version"\ + " of libsndfile") + else: + raise WrappingError("Could not get format string for format "\ + "%d, " % blop.format + "please report this problem "\ + "to the maintainer") + + self.format_str = blop.name + + # Get the sndfile string description of the format subtype + blop.format = self._int_encoding + st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \ + pointer(blop), sizeof(blop)) + if st is not 0: + raise WrappingError() + + self.encoding_str = blop.name + + def get_format_raw(self): + """Do not use this function !""" + return self._format + + def get_major_str(self): + """Do not use this function !""" + return self.format_str + + def get_encoding_str(self): + """Do not use this function !""" + return self.encoding_str + + def get_file_format(self): + """return user friendly file format string""" + return SND_TO_PY_FILE_FORMAT[self._int_type] + + def get_encoding(self): + """return user friendly encoding string""" + return SND_TO_PY_ENCODING[self._int_encoding] + + def get_endianness(self): + """return user friendly file format string""" + return SND_TO_PY_ENDIANNESS[self._int_endianness] + + # Various function + def is_type(self, t): + return (self._format & SF_FORMAT['SF_FORMAT_TYPEMASK']) \ + == py_to_snd_file_format_dic[t] + + # Syntactic sugar + def __str__(self): + return "Major Format: %s, Encoding Format: %s" % \ + (self.format_str, self.encoding_str) + + def __repr__(self): + return self.__str__() + +#+++++++++++ +# Public API +#+++++++++++ + +class formatinfo(_format_from_internal): + def __init__(self, type = 'wav', encoding = 'pcm16', endianness = 'file'): + """Build a valid format usable by the sndfile class when opening an + audio file for writing. + + Blah blah + + :Parameters: + type : string + represents the major file format (wav, etc...). + encoding : string + represents the encoding (pcm16, etc..). + endianness : string + represents the endianess. + + Notes + ----- + + Valid type strings are listed by file_format_dic.keys() Valid encoding + strings are listed by encoding_dic.keys() Valid endianness strings are + listed by endianness_dic.keys() """ + # Keep the arguments + self.type = type + self.encoding = encoding + self.endianness = endianness + + # Get the internal values which corresponds to the values libsndfile + # can understand + self._int_type = py_to_snd_file_format_dic[type] + self._int_encoding = py_to_snd_encoding_dic[encoding] + self._int_endianness = py_to_snd_endianness_dic[endianness] + + # Build the internal integer from parameters, and pass it to the super + # class, which will do all the work + format = self._int_type | self._int_encoding | self._int_endianness + + _format_from_internal.__init__(self, format) + +class sndfile: + """Main class to open, read and write audio files""" + def __init__(self, filename, mode = 'read', format = None, channels = 0, \ + samplerate = 0): + """Create an instance of sndfile. + + :Parameters: + filename : string or int + name of the file to open (string), or file descriptor (integer) + mode : string + 'read' for read, 'write' for write, or 'rwrite' for read and + write. + format : formatinfo + when opening a new file for writing, give the format to write + in. + channels : int + number of channels. + samplerate : int + sampling rate. + + :Returns: + sndfile: a valid sndfile object + + Notes + ----- + + format, channels and samplerate need to be given only in the write + modes and for raw files. """ + # Check the mode is one of the expected values + if mode == 'read': + sfmode = SFM['SFM_READ'] + elif mode == 'write': + sfmode = SFM['SFM_WRITE'] + if format == None: + raise Exception("For write mode, you should provide"\ + "a format argument !") + elif mode == 'rwrite': + sfmode = SFM['SFM_RDWR'] + if format == None: + raise Exception("For write mode, you should provide"\ + "a format argument !") + else: + raise Exception("mode %s not recognized" % str(mode)) + + sfinfo = _sf_info() + sfinfo_p = pointer(sfinfo) + + # Fill the sfinfo struct + sfinfo.frames = c_int64(0) + if type(channels) is not int: + print "Warning, channels is converted to int, was %s" % \ + str(type(channels)) + sfinfo.channels = int(channels) + else: + sfinfo.channels = channels + + if type(samplerate) is not int: + print "Warning, sampling rate is converted to int, was %s" % \ + str(type(samplerate)) + sfinfo.samplerate = int(samplerate) + else: + sfinfo.samplerate = samplerate + + sfinfo.sections = 0 + sfinfo.seekable = False + if mode == 'read' and format == None: + sfinfo.format = 0 + else: + if sfinfo.channels > 256 or sfinfo.channels < 1: + msg = "number of channels is %d, expected " \ + "between 1 and 256" % sfinfo.channels + raise RuntimeError(msg) + sfinfo.format = format.get_format_raw() + if not _SND.sf_format_check(sfinfo_p): + msg = "unknown error in format specification ?" +\ + " Please report this to the author" + raise WrappingError() + + sfinfo_p = pointer(sfinfo) + self._sfmode = sfmode + self.hdl = 0 + + if type(filename) == int: + res = _SND.sf_open_fd(filename, self._sfmode, sfinfo_p, + SF_BOOL['SF_FALSE']) + self._byfd = True + self.fd = filename + self.filename = "" + else: + res = _SND.sf_open(filename, self._sfmode, sfinfo_p) + self._byfd = False + self.filename = filename + + try: + # If res is NULL, this statement will raise a ValueError exception + a = res[0] + except ValueError: + if self._byfd: + msg = "error while opening file descriptor %d\n\t->" % self.fd + else: + msg = "error while opening file %s\n\t-> " % self.filename + msg += _SND.sf_strerror(res) + if self._byfd: + msg += """ +(Check that the mode argument passed to sndfile is the same than the one used +when getting the file descriptor, eg do not pass 'read' to sndfile if you +passed 'write' to open to get the file descriptor. If you are on win32, you are +out of luck, because its implementation of POSIX open is broken)""" + raise IOError("error while opening %s\n\t->%s" % (filename, msg)) + + if mode == 'read': + tmp = _format_from_internal(sfinfo.format) + self._format = formatinfo(tmp.get_file_format(), \ + tmp.get_encoding(), tmp.get_endianness()) + else: + self._format = format + + self._sfinfo = sfinfo + self.hdl = res + + if self.get_file_format() == 'flac': + def SeekNotEnabled(self, *args): + raise FlacUnsupported("seek not supported on Flac by default,"\ + " because\n some version of FLAC libraries are buggy."\ + " Read FLAC_SUPPORT.txt") + self.seek = SeekNotEnabled + else: + self.seek = self._seek + + def __del__(self, close_func = _SND.sf_close): + # Stupid python needs the close_func, otherwise + # it may clean ctypes before calling here + if hasattr(self,'hdl'): + if not(self.hdl == 0): + close_func(self.hdl) + self.hdl = 0 + + def close(self): + """close the file.""" + self.__del__() + + def sync(self): + """call the operating system's function to force the writing of all + file cache buffers to disk the file. + + No effect if file is open as read""" + _SND.sf_write_sync(self.hdl) + + def _seek(self, offset, whence = 0, mode = 'rw'): + """similar to python seek function, taking only in account audio data. + + :Parameters: + offset : int + the number of frames (eg two samples for stereo files) to move + relatively to position set by whence. + whence : int + only 0 (beginning), 1 (current) and 2 (end of the file) are + valid. + mode : string + If set to 'rw', both read and write pointers are updated. If + 'r' is given, only read pointer is updated, if 'w', only the + write one is (this may of course make sense only if you open + the file in a certain mode). + + Notes + ----- + + - one only takes into accound audio data. + - if an invalid seek is given (beyond or before the file), a + PyaudioIOError is launched.""" + c_offset = _num2int64(offset) + if mode == 'rw': + # Update both read and write pointers + st = _SND.sf_seek(self.hdl, c_offset, whence) + elif mode == 'r': + whence = whence | SFM['SFM_READ'] + st = _SND.sf_seek(self.hdl, c_offset, whence) + elif mode == 'w': + whence = whence | SFM['SFM_WRITE'] + st = _SND.sf_seek(self.hdl, c_offset, whence) + else: + raise ValueError("mode should be one of 'r', 'w' or 'rw' only") + + if st == -1: + msg = "Error while seeking, libsndfile error is %s" \ + % (_SND.sf_strerror(self.hdl)) + raise PyaudioIOError(msg) + return st + + # Functions to get informations about the file + def get_nframes(self): + """ Return the number of frames of the file""" + if self._sfmode == SFM['SFM_READ']: + # XXX: is this reliable for any file (think pipe and co ?) + return self._sfinfo.frames + else: + # In write/rwrite mode, the only reliable way to get the number of + # frames is to use seek. + raise NotImplementedError("Sorry, getting the current number of" + "frames in write modes is not supported yet") + + def get_samplerate(self): + """ Return the samplerate in Hz of the file""" + return self._sfinfo.samplerate + + def get_channels(self): + """ Return the number of channels of the file""" + return self._sfinfo.channels + + def get_file_format(self): + """return user friendly file format string""" + return SND_TO_PY_FILE_FORMAT[self._format._int_type] + + def get_encoding(self): + """return user friendly encoding string""" + return SND_TO_PY_ENCODING[self._format._int_encoding] + + def get_endianness(self): + """return user friendly file format string""" + return SND_TO_PY_ENDIANNESS[self._format._int_endianness] + + #------------------ + # Functions to read + #------------------ + def read_frames(self, nframes, dtype = N.float64): + """Read nframes frames of the file. + + :Parameters: + nframes : int + number of frames to read. + dtype : numpy dtype + dtype of the returned array containing read data (see note). + + Notes + ----- + + - read_frames updates the read pointer. + - One column is one channel. + - if float are requested when the file contains integer data, you will + get normalized data (that is the max possible integer will be 1.0, + and the minimal possible value -1.0). + - if integers are requested when the file contains floating point data, + it may give wrong results because there is an ambiguity: if the + floating data are normalized, you can get a file with only 0 ! + Getting integer data from files encoded in normalized floating point + is not supported (yet: sndfile supports it).""" + c_nframes = _num2int64(nframes) + if c_nframes < 0: + raise ValueError("number of frames has to be >= 0") + + # XXX: inout argument + if self._sfinfo.channels > 1: + y = N.zeros((nframes, self._sfinfo.channels), dtype) + else: + y = N.zeros(nframes, dtype) + + if dtype == N.float64: + res = _SND.sf_readf_double(self.hdl, y, c_nframes) + elif dtype == N.float32: + res = _SND.sf_readf_float(self.hdl, y, c_nframes) + elif dtype == N.int32: + res = _SND.sf_readf_int(self.hdl, y, c_nframes) + elif dtype == N.int16: + res = _SND.sf_readf_short(self.hdl, y, c_nframes) + else: + RuntimeError("Sorry, only float, double, int and short read " + \ + "supported for now") + + if not(res == nframes): + msg = "Read %d frames, expected to read %d" % (res, nframes) + msg += ", libsndfile last msg is \n\t%s" \ + % _SND.sf_strerror(self.hdl) + raise IOError(msg) + + return y + + #------------------- + # Functions to write + #------------------- + # TODO: Think about overflow vs type of input, etc... + def write_frames(self, input, nframes = -1): + """write data to file. + + :Parameters: + input : ndarray + array containing data to write. + nframes : int + number of frames to write. + + Notes + ----- + + - one channel is one column + - updates the write pointer. + - if float are given when the file contains integer data, you should + put normalized data (that is the range [-1..1] will be written as the + maximum range allowed by the integer bitwidth).""" + # First, get the number of channels and frames from input + if input.ndim == 1: + nc = 1 + else: + if input.ndim > 2: + raise Exception("Expect array of rank <= 2, got %d" \ + % input.ndim) + nc = input.shape[1] + + if nframes == -1: + nframes = N.size(input) + # Number of channels should be the one expected + if not(nc == self._sfinfo.channels): + raise Exception("Expected %d channels, got %d" % \ + (self._sfinfo.channels, nc)) + + # Writing to the file + c_nframes = _num2int64(nframes) + if c_nframes < 0: + raise ValueError("number of frames has to be >= 0") + + input = N.require(input, requirements = 'C') + + if input.dtype == N.float32: + if self._check_overflow(input): + warnings.warn("Warning, overflow detected when writing.") + res = _SND.sf_writef_float(self.hdl, input, c_nframes) + elif input.dtype == N.float64: + self._check_overflow(input) + if self._check_overflow(input): + warnings.warn("Warning, overflow detected when writing.") + res = _SND.sf_writef_double(self.hdl, input, c_nframes) + elif input.dtype == N.int32: + res = _SND.sf_writef_int(self.hdl, input, c_nframes) + elif input.dtype == N.int16: + res = _SND.sf_writef_short(self.hdl, input, c_nframes) + else: + raise Exception("type of input not understood: input should" + " be float64 or float32""") + + if not(res == nframes): + raise IOError("write %d frames, expected to write %d" \ + % res, nframes) + + def _check_overflow(self, data): + if N.max(data ** 2) >= 1.: + return True + return False + + # Syntactic sugar + def __repr__(self): + return self.__str__() + + def __str__(self): + repstr = "----------------------------------------\n" + if self._byfd: + repstr += "File : %d (opened by file descriptor)\n" % self.fd + else: + repstr += "File : %s\n" % self.filename + repstr += "Channels : %d\n" % self._sfinfo.channels + repstr += "Sample rate : %d\n" % self._sfinfo.samplerate + repstr += "Frames : %d\n" % self._sfinfo.frames + repstr += "Raw Format : %#010x -> %s\n" % \ + (self._format.get_format_raw(), self._format.get_major_str()) + repstr += "File format : %s\n" % self.get_file_format() + repstr += "Encoding : %s\n" % self.get_encoding() + repstr += "Endianness : %s\n" % self.get_endianness() + repstr += "Sections : %d\n" % self._sfinfo.sections + if self._sfinfo.seekable: + seek = 'True' + else: + seek = 'False' + repstr += "Seekable : %s\n" % seek + repstr += "Duration : %s\n" % self._generate_duration_str() + return repstr + + def _generate_duration_str(self): + if self._sfinfo.samplerate < 1: + return None + tsec = self._sfinfo.frames / self._sfinfo.samplerate + hrs = tsec / 60 / 60 + tsec = tsec % (60 ** 2) + mins = tsec / 60 + tsec = tsec % 60 + secs = tsec + ms = 1000 * self._sfinfo.frames / self._sfinfo.samplerate % 1000 + + return "%02d:%02d:%02d.%3d" % (hrs, mins, secs, ms) + +def supported_format(): + # XXX: broken + return py_to_snd_file_format_dic.keys() + +def supported_endianness(): + # XXX: broken + return py_to_snd_endianness_dic.keys() + +def supported_encoding(): + # XXX: broken + return py_to_snd_encoding_dic.keys() + +def _num2int64(value): + """ Convert a python objet to a c_int64, safely.""" + if not (type(value) == int or type(value) == long): + value = long(value) + print "Warning, converting %s to long" % str(value) + c_value = c_int64(value) + if not c_value.value == value: + raise RuntimeError("Error while converting %s to a c_int64"\ + ", maybe %s is too big ?" % str(value)) + return c_value diff --git a/telemeta/util/audiolab/scikits/audiolab/pysndfile.py.in b/telemeta/util/audiolab/scikits/audiolab/pysndfile.py.in new file mode 100644 index 00000000..574e5bb1 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/pysndfile.py.in @@ -0,0 +1,886 @@ +#! /usr/bin/env python +# Last Change: Wed Oct 03 05:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# vim:syntax=python + +# TODO: +# - import format classes so that we get meaningful information from an +# existing format +# - better API for reader/writer, including integer formats and partial +# reading +# - ability to get log of sndfile ? +# - check how to play sound under windows, OS X and other UNIX + +"""This module implements the wrappers around libsndfile.""" + +__docformat__ = 'restructuredtext' + +#__all__ = ['sndfile', 'formatinfo'] + +import copy +import warnings + +#================ +# Load libsndfile +#================ +import ctypes +from ctypes import cdll, Structure, c_int, pointer, POINTER, \ + create_string_buffer, c_char_p, sizeof, string_at +try: + from ctypes import c_int64 +except ImportError, e: + print "Cannot import c_int64 from ctypes: if you are on ubuntu/debian," +\ + " this is likely because ctypes was compiled with libffi. see" +\ + " https://launchpad.net/ubuntu/+source/python2.5/+bug/71914" + raise e + +from numpy.ctypeslib import ndpointer +CTYPES_MAJOR = int(ctypes.__version__.split('.')[0]) +CTYPES_MINOR = int(ctypes.__version__.split('.')[1]) +CTYPES_MICRO = int(ctypes.__version__.split('.')[2]) +if CTYPES_MAJOR < 1 or (CTYPES_MINOR == 0 and CTYPES_MICRO < 1): + raise ImportError("version of ctypes is %s, expected at least %s" \ + % (ctypes.__version__, '1.0.1')) +import numpy as N + +_SND = cdll.LoadLibrary('%SHARED_LOCATION%') + +#========================= +# Definition of constants +#========================= +# READ/WRITE Mode +%SFM% + +# SF BOOL +%SF_BOOL% + +# Format +%SF_FORMAT% + +# ENDIANESS +%SF_ENDIAN% + +# Commands +%SF_COMMAND% + +%SF_ERR% + +# format equivalence: dic used to create internally +# the right enum values from user friendly strings +py_to_snd_encoding_dic = { + 'pcms8' : SF_FORMAT['SF_FORMAT_PCM_S8'], + 'pcm16' : SF_FORMAT['SF_FORMAT_PCM_16'], + 'pcm24' : SF_FORMAT['SF_FORMAT_PCM_24'], + 'pcm32' : SF_FORMAT['SF_FORMAT_PCM_32'], + + 'pcmu8' : SF_FORMAT['SF_FORMAT_PCM_U8'], + + 'float32' : SF_FORMAT['SF_FORMAT_FLOAT'], + 'float64' : SF_FORMAT['SF_FORMAT_DOUBLE'], + + 'ulaw' : SF_FORMAT['SF_FORMAT_ULAW'], + 'alaw' : SF_FORMAT['SF_FORMAT_ALAW'], + 'ima_adpcm' : SF_FORMAT['SF_FORMAT_IMA_ADPCM'], + 'ms_adpcm' : SF_FORMAT['SF_FORMAT_MS_ADPCM'], + + 'gsm610' : SF_FORMAT['SF_FORMAT_GSM610'], + 'vox_adpcm' : SF_FORMAT['SF_FORMAT_VOX_ADPCM'], + + 'g721_32' : SF_FORMAT['SF_FORMAT_G721_32'], + 'g723_24' : SF_FORMAT['SF_FORMAT_G723_24'], + 'g723_40' : SF_FORMAT['SF_FORMAT_G723_40'], + + 'dww12' : SF_FORMAT['SF_FORMAT_DWVW_12'], + 'dww16' : SF_FORMAT['SF_FORMAT_DWVW_16'], + 'dww24' : SF_FORMAT['SF_FORMAT_DWVW_24'], + 'dwwN' : SF_FORMAT['SF_FORMAT_DWVW_N'], + + 'dpcm8' : SF_FORMAT['SF_FORMAT_DPCM_8'], + 'dpcm16': SF_FORMAT['SF_FORMAT_DPCM_16'] +} + +py_to_snd_file_format_dic = { + 'wav' : SF_FORMAT['SF_FORMAT_WAV'], + 'aiff' : SF_FORMAT['SF_FORMAT_AIFF'], + 'au' : SF_FORMAT['SF_FORMAT_AU'], + 'raw' : SF_FORMAT['SF_FORMAT_RAW'], + 'paf' : SF_FORMAT['SF_FORMAT_PAF'], + 'svx' : SF_FORMAT['SF_FORMAT_SVX'], + 'nist' : SF_FORMAT['SF_FORMAT_NIST'], + 'voc' : SF_FORMAT['SF_FORMAT_VOC'], + 'ircam' : SF_FORMAT['SF_FORMAT_IRCAM'], + 'wav64' : SF_FORMAT['SF_FORMAT_W64'], + 'mat4' : SF_FORMAT['SF_FORMAT_MAT4'], + 'mat5' : SF_FORMAT['SF_FORMAT_MAT5'], + 'pvf' : SF_FORMAT['SF_FORMAT_PVF'], + 'xi' : SF_FORMAT['SF_FORMAT_XI'], + 'htk' : SF_FORMAT['SF_FORMAT_HTK'], + 'sds' : SF_FORMAT['SF_FORMAT_SDS'], + 'avr' : SF_FORMAT['SF_FORMAT_AVR'], + 'wavex' : SF_FORMAT['SF_FORMAT_WAVEX'], + 'sd2' : SF_FORMAT['SF_FORMAT_SD2'], + 'flac' : SF_FORMAT['SF_FORMAT_FLAC'], + 'caf' : SF_FORMAT['SF_FORMAT_CAF'] +} + +py_to_snd_endianness_dic = { + 'file' : SF_ENDIAN['SF_ENDIAN_FILE'], + 'little' : SF_ENDIAN['SF_ENDIAN_LITTLE'], + 'big' : SF_ENDIAN['SF_ENDIAN_BIG'], + 'cpu' : SF_ENDIAN['SF_ENDIAN_CPU'] +} + +# Those following dic are used internally to get user-friendly values from +# sndfile enum +SND_TO_PY_ENCODING = \ + dict([(i, j) for j, i in py_to_snd_encoding_dic.items()]) +SND_TO_PY_FILE_FORMAT = \ + dict([(i, j) for j, i in py_to_snd_file_format_dic.items()]) +SND_TO_PY_ENDIANNESS = \ + dict([(i, j) for j, i in py_to_snd_endianness_dic.items()]) + +#========================================== +# Check that libsndfile is expected version +#========================================== +def get_libsndfile_version(): + nverbuff = 256 + verbuff = create_string_buffer(nverbuff) + n = _SND.sf_command(c_int(0), c_int(SF_COMMAND['SFC_GET_LIB_VERSION']), + verbuff, nverbuff) + if n < 1: + raise Exception("Error while getting version of libsndfile") + + # Transform the buffer into a string + ver = "" + for i in range(n): + ver += verbuff[i] + + # Get major, minor and micro from version + version = ver.split('-')[1] + prerelease = 0 + major, minor, micro = [i for i in version.split('.')] + try: + micro = int(micro) + except ValueError,e: + print "micro is " + str(micro) + micro, prerelease = micro.split('pre') + + return int(major), int(minor), int(micro), prerelease + +MAJOR, MINOR, MICRO, PRERELEASE = get_libsndfile_version() +if not(MAJOR == 1): + raise Exception("audiolab expects major version %d of libsndfile" % 1) +if not(MICRO == 17): + if PRERELEASE == 0: + prestr = "No" + else: + prestr = "%s" % PRERELEASE + print "WARNING libsndfile-%d.%d.%d (prerelease: %s) "\ + "this has only been tested with libsndfile 1.0.17 for now, "\ + "use at your own risk !" % (MAJOR, MINOR, MICRO, prestr) + +#================ +# Python wrappers +#================ + +#+++++++++++++++++ +# Public exception +#+++++++++++++++++ +class PyaudioException(Exception): + pass + +class InvalidFormat(PyaudioException): + pass + +class PyaudioIOError(PyaudioException, IOError): + pass + +class WrappingError(PyaudioException): + pass + +class FlacUnsupported(RuntimeError, PyaudioException): + pass + +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Private classes/function (Should not be used outside this file) +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +class _sf_info(Structure): + """Structure representing C structure SF_INFO""" + _fields_ = [('frames', c_int64), + ('samplerate', c_int), + ('channels', c_int), + ('format', c_int), + ('sections', c_int), + ('seekable', c_int)] + def __str__(self): + return "%d frames, sr = %d Hz, %d channels, format is %d" % \ + (self.frames, self.samplerate, self.channels, self.format) + +class _sf_format_info(Structure): + """Structure representing C structure SF_FORMAT_INFO (useful for + sf_command )""" + _fields_ = [('format', c_int), + ('name', c_char_p), + ('extension', c_char_p)] + def __str__(self): + return "format hex is %#010x, name is %s, extension is %s" % \ + (self.format, self.name, self.extension) + + def __repr__(self): + print self.__str__() + +class _sndfile(Structure): + pass + +sf_info_p = POINTER(_sf_info) +sndfile_p = POINTER(_sndfile) + +# functions args +# TODO: is there a way to ensure that arg1 is the right kind of pointer ? +arg1 = c_char_p +arg2 = c_int +arg3 = sf_info_p +_SND.sf_open.argtypes = [arg1, arg2, arg3] +_SND.sf_open.restype = sndfile_p + +arg1 = sndfile_p +_SND.sf_close.argtypes = [arg1] +_SND.sf_close.restype = c_int + +arg1 = c_int +arg2 = c_int +arg3 = sf_info_p +arg4 = c_int +_SND.sf_open_fd.argtypes = [arg1, arg2, arg3, arg4] +_SND.sf_open_fd.restype = sndfile_p + +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.float64) +arg3 = c_int64 + +# double function +_SND.sf_readf_double.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_double.restype = c_int64 + +_SND.sf_writef_double.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_double.restype = c_int64 + +# float function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.float32) +arg3 = c_int64 +_SND.sf_readf_float.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_float.restype = c_int64 + +_SND.sf_writef_float.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_float.restype = c_int64 + +# int function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.int32) +arg3 = c_int64 +_SND.sf_readf_int.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_int.restype = c_int64 + +_SND.sf_writef_int.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_int.restype = c_int64 + +# short function +arg1 = sndfile_p +arg2 = ndpointer(dtype=N.int16) +arg3 = c_int64 +_SND.sf_readf_short.argtypes = [arg1, arg2, arg3] +_SND.sf_readf_short.restype = c_int64 + +_SND.sf_writef_short.argtypes = [arg1, arg2, arg3] +_SND.sf_writef_short.restype = c_int64 + +# Error functions +arg1 = sndfile_p +_SND.sf_strerror.argtypes = [arg1] +_SND.sf_strerror.restype = c_char_p + +# Function to sync data to file +arg1 = sndfile_p +_SND.sf_write_sync.argtypes = [arg1] + +# Function to seek +arg1 = sndfile_p +arg2 = c_int64 +arg3 = c_int +_SND.sf_seek.argtypes = [arg1, arg2, arg3] +_SND.sf_seek.restype = c_int64 + +# To pass when a C function needs a NULL arg +_cNULL = POINTER(c_int)() + +class _format_from_internal: + """Class to handle audio format with sndfile. + + DO NOT USE THIS CLASS OUTSIDE pysndfile.py MODULE: YOU MAY CRASH YOUR + INTERPRETER ! + + Basically, we have 3 classes of parameters: + - the main format: (major format), like wav, aiff, etc... + - the subtype format: pcm, bits resolution + - endianness: little, big, as the cpu, default of the format + + This class encapsulates those parameters, and can build a representation of + them from the format integer of sf_info. This should *NOT* be used, use + format instead, which inherits this class to build a valid format from user + friendly arguments. """ + def __init__(self, format_integer): + # Get the internal values which corresponds to the values libsndfile + # can understand + self._int_type = format_integer & SF_FORMAT['SF_FORMAT_TYPEMASK'] + self._int_encoding = format_integer & SF_FORMAT['SF_FORMAT_SUBMASK'] + self._int_endianness = format_integer & SF_FORMAT['SF_FORMAT_ENDMASK'] + + assert format_integer == self._int_type | self._int_encoding |\ + self._int_endianness + self._format = format_integer + + # Now, we need to test if the combination of format, encoding and + # endianness is valid. sf_format_check needs also samplerate and + # channel information, so just give a fake samplerate and channel + # number. Looking at sndfile.c, it looks like samplerate is never + # actually checked, and that when channels is checked, it is only + # checked against values different than 1 or 2, so giving a value of + # 1 to channel should be ok. + self._sfinfo = _sf_info() + self._sfinfo.channels = 1 + self._sfinfo.samplerate = 8000 + self._sfinfo.format = self._format + + ret = _SND.sf_format_check(pointer(self._sfinfo)) + if ret is not SF_BOOL['SF_TRUE']: + raise InvalidFormat() + + # Get the sndfile string description of the format type + blop = _sf_format_info() + blop.format = self._int_type + st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \ + pointer(blop), sizeof(blop)) + if st is not 0: + if SND_TO_PY_FILE_FORMAT[self._int_type] == 'flac': + raise FlacUnsupported("Flac is not supported by your version"\ + " of libsndfile") + else: + raise WrappingError("Could not get format string for format "\ + "%d, " % blop.format + "please report this problem "\ + "to the maintainer") + + self.format_str = blop.name + + # Get the sndfile string description of the format subtype + blop.format = self._int_encoding + st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \ + pointer(blop), sizeof(blop)) + if st is not 0: + raise WrappingError() + + self.encoding_str = blop.name + + def get_format_raw(self): + """Do not use this function !""" + return self._format + + def get_major_str(self): + """Do not use this function !""" + return self.format_str + + def get_encoding_str(self): + """Do not use this function !""" + return self.encoding_str + + def get_file_format(self): + """return user friendly file format string""" + return SND_TO_PY_FILE_FORMAT[self._int_type] + + def get_encoding(self): + """return user friendly encoding string""" + return SND_TO_PY_ENCODING[self._int_encoding] + + def get_endianness(self): + """return user friendly file format string""" + return SND_TO_PY_ENDIANNESS[self._int_endianness] + + # Various function + def is_type(self, t): + return (self._format & SF_FORMAT['SF_FORMAT_TYPEMASK']) \ + == py_to_snd_file_format_dic[t] + + # Syntactic sugar + def __str__(self): + return "Major Format: %s, Encoding Format: %s" % \ + (self.format_str, self.encoding_str) + + def __repr__(self): + return self.__str__() + +#+++++++++++ +# Public API +#+++++++++++ + +class formatinfo(_format_from_internal): + def __init__(self, type = 'wav', encoding = 'pcm16', endianness = 'file'): + """Build a valid format usable by the sndfile class when opening an + audio file for writing. + + Blah blah + + :Parameters: + type : string + represents the major file format (wav, etc...). + encoding : string + represents the encoding (pcm16, etc..). + endianness : string + represents the endianess. + + Notes + ----- + + Valid type strings are listed by file_format_dic.keys() Valid encoding + strings are listed by encoding_dic.keys() Valid endianness strings are + listed by endianness_dic.keys() """ + # Keep the arguments + self.type = type + self.encoding = encoding + self.endianness = endianness + + # Get the internal values which corresponds to the values libsndfile + # can understand + self._int_type = py_to_snd_file_format_dic[type] + self._int_encoding = py_to_snd_encoding_dic[encoding] + self._int_endianness = py_to_snd_endianness_dic[endianness] + + # Build the internal integer from parameters, and pass it to the super + # class, which will do all the work + format = self._int_type | self._int_encoding | self._int_endianness + + _format_from_internal.__init__(self, format) + +class sndfile: + """Main class to open, read and write audio files""" + def __init__(self, filename, mode = 'read', format = None, channels = 0, \ + samplerate = 0): + """Create an instance of sndfile. + + :Parameters: + filename : string or int + name of the file to open (string), or file descriptor (integer) + mode : string + 'read' for read, 'write' for write, or 'rwrite' for read and + write. + format : formatinfo + when opening a new file for writing, give the format to write + in. + channels : int + number of channels. + samplerate : int + sampling rate. + + :Returns: + sndfile: a valid sndfile object + + Notes + ----- + + format, channels and samplerate need to be given only in the write + modes and for raw files. """ + # Check the mode is one of the expected values + if mode == 'read': + sfmode = SFM['SFM_READ'] + elif mode == 'write': + sfmode = SFM['SFM_WRITE'] + if format == None: + raise Exception("For write mode, you should provide"\ + "a format argument !") + elif mode == 'rwrite': + sfmode = SFM['SFM_RDWR'] + if format == None: + raise Exception("For write mode, you should provide"\ + "a format argument !") + else: + raise Exception("mode %s not recognized" % str(mode)) + + sfinfo = _sf_info() + sfinfo_p = pointer(sfinfo) + + # Fill the sfinfo struct + sfinfo.frames = c_int64(0) + if type(channels) is not int: + print "Warning, channels is converted to int, was %s" % \ + str(type(channels)) + sfinfo.channels = int(channels) + else: + sfinfo.channels = channels + + if type(samplerate) is not int: + print "Warning, sampling rate is converted to int, was %s" % \ + str(type(samplerate)) + sfinfo.samplerate = int(samplerate) + else: + sfinfo.samplerate = samplerate + + sfinfo.sections = 0 + sfinfo.seekable = False + if mode == 'read' and format == None: + sfinfo.format = 0 + else: + if sfinfo.channels > 256 or sfinfo.channels < 1: + msg = "number of channels is %d, expected " \ + "between 1 and 256" % sfinfo.channels + raise RuntimeError(msg) + sfinfo.format = format.get_format_raw() + if not _SND.sf_format_check(sfinfo_p): + msg = "unknown error in format specification ?" +\ + " Please report this to the author" + raise WrappingError() + + sfinfo_p = pointer(sfinfo) + self._sfmode = sfmode + self.hdl = 0 + + if type(filename) == int: + res = _SND.sf_open_fd(filename, self._sfmode, sfinfo_p, + SF_BOOL['SF_FALSE']) + self._byfd = True + self.fd = filename + self.filename = "" + else: + res = _SND.sf_open(filename, self._sfmode, sfinfo_p) + self._byfd = False + self.filename = filename + + try: + # If res is NULL, this statement will raise a ValueError exception + a = res[0] + except ValueError: + if self._byfd: + msg = "error while opening file descriptor %d\n\t->" % self.fd + else: + msg = "error while opening file %s\n\t-> " % self.filename + msg += _SND.sf_strerror(res) + if self._byfd: + msg += """ +(Check that the mode argument passed to sndfile is the same than the one used +when getting the file descriptor, eg do not pass 'read' to sndfile if you +passed 'write' to open to get the file descriptor. If you are on win32, you are +out of luck, because its implementation of POSIX open is broken)""" + raise IOError("error while opening %s\n\t->%s" % (filename, msg)) + + if mode == 'read': + tmp = _format_from_internal(sfinfo.format) + self._format = formatinfo(tmp.get_file_format(), \ + tmp.get_encoding(), tmp.get_endianness()) + else: + self._format = format + + self._sfinfo = sfinfo + self.hdl = res + + if self.get_file_format() == 'flac': + def SeekNotEnabled(self, *args): + raise FlacUnsupported("seek not supported on Flac by default,"\ + " because\n some version of FLAC libraries are buggy."\ + " Read FLAC_SUPPORT.txt") + self.seek = SeekNotEnabled + else: + self.seek = self._seek + + def __del__(self, close_func = _SND.sf_close): + # Stupid python needs the close_func, otherwise + # it may clean ctypes before calling here + if hasattr(self,'hdl'): + if not(self.hdl == 0): + close_func(self.hdl) + self.hdl = 0 + + def close(self): + """close the file.""" + self.__del__() + + def sync(self): + """call the operating system's function to force the writing of all + file cache buffers to disk the file. + + No effect if file is open as read""" + _SND.sf_write_sync(self.hdl) + + def _seek(self, offset, whence = 0, mode = 'rw'): + """similar to python seek function, taking only in account audio data. + + :Parameters: + offset : int + the number of frames (eg two samples for stereo files) to move + relatively to position set by whence. + whence : int + only 0 (beginning), 1 (current) and 2 (end of the file) are + valid. + mode : string + If set to 'rw', both read and write pointers are updated. If + 'r' is given, only read pointer is updated, if 'w', only the + write one is (this may of course make sense only if you open + the file in a certain mode). + + Notes + ----- + + - one only takes into accound audio data. + - if an invalid seek is given (beyond or before the file), a + PyaudioIOError is launched.""" + c_offset = _num2int64(offset) + if mode == 'rw': + # Update both read and write pointers + st = _SND.sf_seek(self.hdl, c_offset, whence) + elif mode == 'r': + whence = whence | SFM['SFM_READ'] + st = _SND.sf_seek(self.hdl, c_offset, whence) + elif mode == 'w': + whence = whence | SFM['SFM_WRITE'] + st = _SND.sf_seek(self.hdl, c_offset, whence) + else: + raise ValueError("mode should be one of 'r', 'w' or 'rw' only") + + if st == -1: + msg = "Error while seeking, libsndfile error is %s" \ + % (_SND.sf_strerror(self.hdl)) + raise PyaudioIOError(msg) + return st + + # Functions to get informations about the file + def get_nframes(self): + """ Return the number of frames of the file""" + if self._sfmode == SFM['SFM_READ']: + # XXX: is this reliable for any file (think pipe and co ?) + return self._sfinfo.frames + else: + # In write/rwrite mode, the only reliable way to get the number of + # frames is to use seek. + raise NotImplementedError("Sorry, getting the current number of" + "frames in write modes is not supported yet") + + def get_samplerate(self): + """ Return the samplerate in Hz of the file""" + return self._sfinfo.samplerate + + def get_channels(self): + """ Return the number of channels of the file""" + return self._sfinfo.channels + + def get_file_format(self): + """return user friendly file format string""" + return SND_TO_PY_FILE_FORMAT[self._format._int_type] + + def get_encoding(self): + """return user friendly encoding string""" + return SND_TO_PY_ENCODING[self._format._int_encoding] + + def get_endianness(self): + """return user friendly file format string""" + return SND_TO_PY_ENDIANNESS[self._format._int_endianness] + + #------------------ + # Functions to read + #------------------ + def read_frames(self, nframes, dtype = N.float64): + """Read nframes frames of the file. + + :Parameters: + nframes : int + number of frames to read. + dtype : numpy dtype + dtype of the returned array containing read data (see note). + + Notes + ----- + + - read_frames updates the read pointer. + - One column is one channel. + - if float are requested when the file contains integer data, you will + get normalized data (that is the max possible integer will be 1.0, + and the minimal possible value -1.0). + - if integers are requested when the file contains floating point data, + it may give wrong results because there is an ambiguity: if the + floating data are normalized, you can get a file with only 0 ! + Getting integer data from files encoded in normalized floating point + is not supported (yet: sndfile supports it).""" + c_nframes = _num2int64(nframes) + if c_nframes < 0: + raise ValueError("number of frames has to be >= 0") + + # XXX: inout argument + if self._sfinfo.channels > 1: + y = N.zeros((nframes, self._sfinfo.channels), dtype) + else: + y = N.zeros(nframes, dtype) + + if dtype == N.float64: + res = _SND.sf_readf_double(self.hdl, y, c_nframes) + elif dtype == N.float32: + res = _SND.sf_readf_float(self.hdl, y, c_nframes) + elif dtype == N.int32: + res = _SND.sf_readf_int(self.hdl, y, c_nframes) + elif dtype == N.int16: + res = _SND.sf_readf_short(self.hdl, y, c_nframes) + else: + RuntimeError("Sorry, only float, double, int and short read " + \ + "supported for now") + + if not(res == nframes): + msg = "Read %d frames, expected to read %d" % (res, nframes) + msg += ", libsndfile last msg is \n\t%s" \ + % _SND.sf_strerror(self.hdl) + raise IOError(msg) + + return y + + #------------------- + # Functions to write + #------------------- + # TODO: Think about overflow vs type of input, etc... + def write_frames(self, input, nframes = -1): + """write data to file. + + :Parameters: + input : ndarray + array containing data to write. + nframes : int + number of frames to write. + + Notes + ----- + + - one channel is one column + - updates the write pointer. + - if float are given when the file contains integer data, you should + put normalized data (that is the range [-1..1] will be written as the + maximum range allowed by the integer bitwidth).""" + # First, get the number of channels and frames from input + if input.ndim == 1: + nc = 1 + else: + if input.ndim > 2: + raise Exception("Expect array of rank <= 2, got %d" \ + % input.ndim) + nc = input.shape[1] + + if nframes == -1: + nframes = N.size(input) + # Number of channels should be the one expected + if not(nc == self._sfinfo.channels): + raise Exception("Expected %d channels, got %d" % \ + (self._sfinfo.channels, nc)) + + # Writing to the file + c_nframes = _num2int64(nframes) + if c_nframes < 0: + raise ValueError("number of frames has to be >= 0") + + input = N.require(input, requirements = 'C') + + if input.dtype == N.float32: + if self._check_overflow(input): + warnings.warn("Warning, overflow detected when writing.") + res = _SND.sf_writef_float(self.hdl, input, c_nframes) + elif input.dtype == N.float64: + self._check_overflow(input) + if self._check_overflow(input): + warnings.warn("Warning, overflow detected when writing.") + res = _SND.sf_writef_double(self.hdl, input, c_nframes) + elif input.dtype == N.int32: + res = _SND.sf_writef_int(self.hdl, input, c_nframes) + elif input.dtype == N.int16: + res = _SND.sf_writef_short(self.hdl, input, c_nframes) + else: + raise Exception("type of input not understood: input should" + " be float64 or float32""") + + if not(res == nframes): + raise IOError("write %d frames, expected to write %d" \ + % res, nframes) + + def _check_overflow(self, data): + if N.max(data ** 2) >= 1.: + return True + return False + + # Syntactic sugar + def __repr__(self): + return self.__str__() + + def __str__(self): + repstr = "----------------------------------------\n" + if self._byfd: + repstr += "File : %d (opened by file descriptor)\n" % self.fd + else: + repstr += "File : %s\n" % self.filename + repstr += "Channels : %d\n" % self._sfinfo.channels + repstr += "Sample rate : %d\n" % self._sfinfo.samplerate + repstr += "Frames : %d\n" % self._sfinfo.frames + repstr += "Raw Format : %#010x -> %s\n" % \ + (self._format.get_format_raw(), self._format.get_major_str()) + repstr += "File format : %s\n" % self.get_file_format() + repstr += "Encoding : %s\n" % self.get_encoding() + repstr += "Endianness : %s\n" % self.get_endianness() + repstr += "Sections : %d\n" % self._sfinfo.sections + if self._sfinfo.seekable: + seek = 'True' + else: + seek = 'False' + repstr += "Seekable : %s\n" % seek + repstr += "Duration : %s\n" % self._generate_duration_str() + return repstr + + def _generate_duration_str(self): + if self._sfinfo.samplerate < 1: + return None + tsec = self._sfinfo.frames / self._sfinfo.samplerate + hrs = tsec / 60 / 60 + tsec = tsec % (60 ** 2) + mins = tsec / 60 + tsec = tsec % 60 + secs = tsec + ms = 1000 * self._sfinfo.frames / self._sfinfo.samplerate % 1000 + + return "%02d:%02d:%02d.%3d" % (hrs, mins, secs, ms) + +def supported_format(): + # XXX: broken + return py_to_snd_file_format_dic.keys() + +def supported_endianness(): + # XXX: broken + return py_to_snd_endianness_dic.keys() + +def supported_encoding(): + # XXX: broken + return py_to_snd_encoding_dic.keys() + +def _num2int64(value): + """ Convert a python objet to a c_int64, safely.""" + if not (type(value) == int or type(value) == long): + value = long(value) + print "Warning, converting %s to long" % str(value) + c_value = c_int64(value) + if not c_value.value == value: + raise RuntimeError("Error while converting %s to a c_int64"\ + ", maybe %s is too big ?" % str(value)) + return c_value diff --git a/telemeta/util/audiolab/scikits/audiolab/pysndfile.pyc b/telemeta/util/audiolab/scikits/audiolab/pysndfile.pyc new file mode 100644 index 00000000..a162db43 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/pysndfile.pyc differ diff --git a/telemeta/util/audiolab/scikits/audiolab/soundio/SConstruct b/telemeta/util/audiolab/scikits/audiolab/soundio/SConstruct new file mode 100644 index 00000000..1d1b221e --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/soundio/SConstruct @@ -0,0 +1,2 @@ +Program('main', source = ['simple.c'], LIBS = ['asound']) +Program('main2', source = ['simple2.c'], LIBS = ['asound']) diff --git a/telemeta/util/audiolab/scikits/audiolab/soundio/_alsa.pyx b/telemeta/util/audiolab/scikits/audiolab/soundio/_alsa.pyx new file mode 100644 index 00000000..5fb6a117 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/soundio/_alsa.pyx @@ -0,0 +1,198 @@ +cdef extern from "alsa/asoundlib.h": + ctypedef enum snd_pcm_stream_t: + SND_PCM_STREAM_PLAYBACK + SND_PCM_STREAM_CAPTURE + ctypedef enum snd_pcm_access_t : + SND_PCM_ACCESS_MMAP_INTERLEAVED + SND_PCM_ACCESS_MMAP_NONINTERLEAVED + SND_PCM_ACCESS_MMAP_COMPLEX + SND_PCM_ACCESS_RW_INTERLEAVED + SND_PCM_ACCESS_RW_NONINTERLEAVED + ctypedef enum snd_pcm_format_t : + SND_PCM_FORMAT_UNKNOWN + SND_PCM_FORMAT_S8 + SND_PCM_FORMAT_U8 + SND_PCM_FORMAT_S16_LE + SND_PCM_FORMAT_S16_BE + SND_PCM_FORMAT_U16_LE + SND_PCM_FORMAT_U16_BE + SND_PCM_FORMAT_S24_LE + SND_PCM_FORMAT_S24_BE + SND_PCM_FORMAT_U24_LE + SND_PCM_FORMAT_U24_BE + SND_PCM_FORMAT_S32_LE + SND_PCM_FORMAT_S32_BE + SND_PCM_FORMAT_U32_LE + SND_PCM_FORMAT_U32_BE + SND_PCM_FORMAT_FLOAT_LE + SND_PCM_FORMAT_FLOAT_BE + SND_PCM_FORMAT_FLOAT64_LE + SND_PCM_FORMAT_FLOAT64_BE + SND_PCM_FORMAT_IEC958_SUBFRAME_LE + SND_PCM_FORMAT_IEC958_SUBFRAME_BE + SND_PCM_FORMAT_MU_LAW + SND_PCM_FORMAT_A_LAW + SND_PCM_FORMAT_IMA_ADPCM + SND_PCM_FORMAT_MPEG + SND_PCM_FORMAT_GSM + SND_PCM_FORMAT_SPECIAL + SND_PCM_FORMAT_S24_3LE + SND_PCM_FORMAT_S24_3BE + SND_PCM_FORMAT_U24_3LE + SND_PCM_FORMAT_U24_3BE + SND_PCM_FORMAT_S20_3LE + SND_PCM_FORMAT_S20_3BE + SND_PCM_FORMAT_U20_3LE + SND_PCM_FORMAT_U20_3BE + SND_PCM_FORMAT_S18_3LE + SND_PCM_FORMAT_S18_3BE + SND_PCM_FORMAT_U18_3LE + SND_PCM_FORMAT_U18_3BE + SND_PCM_FORMAT_S16 + SND_PCM_FORMAT_U16 + SND_PCM_FORMAT_S24 + SND_PCM_FORMAT_U24 + SND_PCM_FORMAT_S32 + SND_PCM_FORMAT_U32 + SND_PCM_FORMAT_FLOAT + SND_PCM_FORMAT_FLOAT64 + SND_PCM_FORMAT_IEC958_SUBFRAME + + ctypedef struct snd_pcm_t + # XXX: how to make sure the typedef is OK ? + ctypedef unsigned long snd_pcm_uframes_t + + int snd_pcm_open(snd_pcm_t **, char*, int, int) + int snd_pcm_close(snd_pcm_t *) + int snd_pcm_drain(snd_pcm_t *) + + int snd_pcm_set_params(snd_pcm_t *, snd_pcm_format_t, + snd_pcm_access_t, unsigned int, + unsigned int, int, unsigned int) + + int snd_pcm_writei(snd_pcm_t *, void*, snd_pcm_uframes_t) + + char* snd_strerror(int error) + + int snd_card_next(int *icard) + int snd_card_get_name(int icard, char** name) + char* snd_asoundlib_version() + +cdef extern from "numpy/arrayobject.h": + ctypedef int intp + ctypedef extern class numpy.ndarray [object PyArrayObject]: + cdef char *data + cdef int nd + cdef intp *dimensions + cdef intp *strides + cdef int flags + +cdef extern from "stdlib.h": + ctypedef unsigned long size_t + void free(void *ptr) + void *malloc(size_t size) + void *realloc(void *ptr, size_t size) + size_t strlen(char *s) + char *strcpy(char *dest, char *src) + +cdef extern from "stdint.h": + ctypedef unsigned short int16_t + +cdef extern from "Python.h": + object PyString_FromStringAndSize(char *v, int len) + +class AlsaException(Exception): + pass + +def asoundlib_version(): + return snd_asoundlib_version() + +def card_indexes(): + """Returns a list containing index of cards recognized by alsa.""" + cdef int icur = -1 + + cards = [] + while 1: + st = snd_card_next(&icur) + if st < 0: + raise AlsaException("Could not get next card") + if icur < 0: + break + cards.append(icur) + return tuple(cards) + +def card_name(index): + """Get the name of the card corresponding to the given index.""" + cdef char* sptr + st = snd_card_get_name(index, &sptr) + if st < 0: + raise AlsaException("Error while getting card name %d: alsa error "\ + "was %s" % (index, snd_strerror(st))) + else: + cardname = PyString_FromStringAndSize(sptr, len(sptr)) + free(sptr) + return cardname + +cdef class _PCM: + cdef snd_pcm_t* pcmhdl + cdef public char* name + + def __new__(self, device = "default", stream = SND_PCM_STREAM_PLAYBACK): + self.pcmhdl = NULL + + st = snd_pcm_open(&self.pcmhdl, device, stream, 0) + if st < 0: + raise AlsaException("Cannot open device %s: %s" % (device, snd_strerror(st))) + + def __init__(self, device = "default", stream = SND_PCM_STREAM_PLAYBACK): + self.name = device + + def __dealloc__(self): + if self.pcmhdl: + snd_pcm_close(self.pcmhdl) + +cdef class Device: + cdef _PCM pcm + cdef unsigned int samplerate + # XXX: set property instead + cdef public unsigned int channels + cdef snd_pcm_format_t format + cdef snd_pcm_access_t access + def __init__(self, samplerate = 48000, channels = 1, + format = SND_PCM_FORMAT_S16, + access = SND_PCM_ACCESS_RW_INTERLEAVED): + self.pcm = _PCM() + + self.samplerate = samplerate + self.channels = channels + + self.access = access + self.format = format + + st = snd_pcm_set_params(self.pcm.pcmhdl, format, access, channels, + samplerate, 1, 1000000) + if st < 0: + raise AlsaException() + + def play_short(self, ndarray input): + cdef int16_t* p = input.data + cdef int16_t* buff = NULL + #cdef int n = input.dimensions[0] + cdef int n = input.dimensions[0] + cdef int nb = n / 1024 / 16 + cdef snd_pcm_uframes_t bufsz = 1024 * 16 + + assert self.format == SND_PCM_FORMAT_S16 + assert self.access == SND_PCM_ACCESS_RW_INTERLEAVED + + for i from 0 <= i < nb: + st = snd_pcm_writei(self.pcm.pcmhdl, &p[i * bufsz], bufsz) + if not st == bufsz: + print "Error while writing to device", st + print snd_strerror(st) + + snd_pcm_drain(self.pcm.pcmhdl) + + def _get_name(self): + return self.pcm.name + name = property(_get_name) diff --git a/telemeta/util/audiolab/scikits/audiolab/soundio/alsa.py b/telemeta/util/audiolab/scikits/audiolab/soundio/alsa.py new file mode 100644 index 00000000..15754deb --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/soundio/alsa.py @@ -0,0 +1,35 @@ +import numpy as np + +from _alsa import card_name, card_indexes, asoundlib_version +from _alsa import Device, AlsaException + +def play(input, samplerate = 48000): + if input.ndim == 1: + n = input.size + nc = 1 + elif input.ndim == 2: + n, nc = input.shape + else: + raise ValueError("Only ndim 1 or 2 supported") + + if not input.dtype in (np.float32, np.float64): + raise ValueError("input should be array of float32 or float64 !") + + try: + dev = Device(samplerate = samplerate, channels = nc) + dev.play_short((16384 * input).astype(np.int16)) + except AlsaException, e: + raise IOError(str(e)) + +if __name__ == '__main__': + print "Asoundlib version is", asoundlib_version() + for i in card_indexes(): + print card_name(i) + + dev = Device() + print "Device name:", dev.name + + a = 0.2 * np.random.randn(4e4) + play(a, 16000) + play(a, 8000) + play(a, 22050) diff --git a/telemeta/util/audiolab/scikits/audiolab/soundio/alsa_ctypes.py b/telemeta/util/audiolab/scikits/audiolab/soundio/alsa_ctypes.py new file mode 100644 index 00000000..d1c65fc8 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/soundio/alsa_ctypes.py @@ -0,0 +1,440 @@ +#! /usr/bin/env python +# Last Change: Fri Sep 07 02:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# vim:syntax=python + +"""This is a small module to wrap basic functionalities of alsa: it uses ctypes +for the wrapping.""" + +__docformat__ = 'restructuredtext' + +__all__ = ['asoundlib_version', 'asoundlib_version_numbers', 'card_indexes', \ + 'card_name', 'card_longname'] +#================ +# Load alsa lib +#================ +import ctypes +from ctypes import cdll, Structure, c_int, pointer, POINTER, c_uint,\ + create_string_buffer, c_char_p, sizeof, byref, string_at, c_size_t,\ + c_void_p, c_ulong, c_uint16, c_long, c_uint8, c_int16, c_float +try: + from ctypes import c_int64 +except ImportError, e: + print "Cannot import c_int64 from ctypes: if you are on ubuntu/debian," +\ + " this is likely because ctypes was compiled with libffi. see" +\ + " https://launchpad.net/ubuntu/+source/python2.5/+bug/71914" + raise e + +from numpy.ctypeslib import ndpointer +CTYPES_MAJOR = int(ctypes.__version__.split('.')[0]) +CTYPES_MINOR = int(ctypes.__version__.split('.')[1]) +CTYPES_MICRO = int(ctypes.__version__.split('.')[2]) +if CTYPES_MAJOR < 1 or (CTYPES_MINOR == 0 and CTYPES_MICRO < 1): + raise ImportError("version of ctypes is %s, expected at least %s" \ + % (ctypes.__version__, '1.0.1')) +import numpy as N + +ALSALOC = 'libasound.so.2' +_ALSA = cdll.LoadLibrary(ALSALOC) +# XXX: alsa means linux, normally. How to locate the glibc ? +_C = cdll.LoadLibrary("libc.so.6") + +# Check version +_ALSA.snd_asoundlib_version.args = [] +_ALSA.snd_asoundlib_version.restype = c_char_p + +def asoundlib_version(): + """Return the version of asoundlib as a string""" + version = _ALSA.snd_asoundlib_version() + return version + +def asoundlib_version_numbers(): + """Return the version of asoundlib as a tuple(major, minor, micro).""" + version = asoundlib_version() + return tuple(int(i) for i in version.split('.')) + +major, minor, micro = asoundlib_version_numbers() +if not (major == 1 and minor == 0): + raise RuntimeError("Expected 1.0.x version, got %s instead" % asoundlib_version()) + +# Define pointers to opaque structures +class snd_pcm_t(Structure): + pass + +class snd_pcm_hw_params_t(Structure): + pass + +class snd_pcm_sw_params_t(Structure): + pass + +class snd_output_t(Structure): + pass + +snd_pcm_t_p = POINTER(snd_pcm_t) +snd_pcm_hw_params_t_p = POINTER(snd_pcm_hw_params_t) +snd_pcm_sw_params_t_p = POINTER(snd_pcm_sw_params_t) +snd_output_t_p = POINTER(snd_output_t) + +#------------ +# Define enum +#------------ +SND_PCM_STREAM = {'SND_PCM_STREAM_PLAYBACK' : 0, + 'SND_PCM_STREAM_CAPTURE' : 1} + +#----------------------------------------------- +# Define all the function we need from asoundlib +#----------------------------------------------- +arg1 = c_int +arg2 = POINTER(c_char_p) +_ALSA.snd_card_get_name.argtypes = [arg1, arg2] +_ALSA.snd_card_get_name.restype = c_int + +arg1 = c_int +arg2 = POINTER(c_char_p) +_ALSA.snd_card_get_longname.argtypes = [arg1, arg2] +_ALSA.snd_card_get_longname.restype = c_int + +arg1 = POINTER(c_int) +_ALSA.snd_card_next.argtypes = [arg1] +_ALSA.snd_card_next.restype = c_int + +arg1 = c_int +_ALSA.snd_strerror.argtypes = [arg1] +_ALSA.snd_strerror.restype = c_char_p + +# output related functions +arg1 = POINTER(snd_output_t_p) +_ALSA.snd_output_buffer_open.argtypes = [arg1] +_ALSA.snd_output_buffer_open.restype = c_int + +arg1 = snd_output_t_p +_ALSA.snd_output_close.argtypes = [arg1] +_ALSA.snd_output_close.restype = c_int + +arg1 = snd_output_t_p +arg2 = POINTER(c_char_p) +_ALSA.snd_output_buffer_string.argtypes = [arg1, arg2] +_ALSA.snd_output_buffer_string.restype = c_size_t + +arg1 = snd_pcm_t_p +arg2 = snd_output_t_p +_ALSA.snd_pcm_dump.argtypes = [arg1, arg2] +_ALSA.snd_pcm_dump.restype = c_int + +# pcm related functions +arg1 = POINTER(POINTER(snd_pcm_t)) +arg2 = c_char_p +arg3 = c_int +arg4 = c_int +_ALSA.snd_pcm_open.argtypes = [arg1, arg2, arg3, arg4] +_ALSA.snd_pcm_open.restype = c_int + +arg1 = POINTER(snd_pcm_t) +_ALSA.snd_pcm_close.argtypes = [arg1] +_ALSA.snd_pcm_close.restype = c_int + +arg1 = POINTER(snd_pcm_t) +_ALSA.snd_pcm_reset.argtypes = [arg1] +_ALSA.snd_pcm_reset.restype = c_int + +arg1 = POINTER(snd_pcm_t) +_ALSA.snd_pcm_drain.argtypes = [arg1] +_ALSA.snd_pcm_drain.restype = c_int + +arg1 = POINTER(snd_pcm_hw_params_t_p) +_ALSA.snd_pcm_hw_params_malloc.argtypes = [arg1] +_ALSA.snd_pcm_hw_params_malloc.restype = c_int + +arg1 = snd_pcm_hw_params_t_p +_ALSA.snd_pcm_hw_params_free.argtypes = [arg1] +_ALSA.snd_pcm_hw_params_free.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +_ALSA.snd_pcm_hw_params_any.argtypes = [arg1, arg2] +_ALSA.snd_pcm_hw_params_any.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +_ALSA.snd_pcm_hw_params.argtypes = [arg1, arg2] +_ALSA.snd_pcm_hw_params.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +arg3 = c_int +_ALSA.snd_pcm_hw_params_set_access.argtypes = [arg1, arg2, arg3] +_ALSA.snd_pcm_hw_params_set_access.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +arg3 = POINTER(c_uint) +arg4 = c_int +_ALSA.snd_pcm_hw_params_set_rate_near.argtypes = [arg1, arg2, arg3, arg4] +_ALSA.snd_pcm_hw_params_set_rate_near.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +arg3 = c_uint +_ALSA.snd_pcm_hw_params_set_channels.argtypes = [arg1, arg2, arg3] +_ALSA.snd_pcm_hw_params_set_channels.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +arg3 = c_int +_ALSA.snd_pcm_hw_params_set_format.argtypes = [arg1, arg2, arg3] +_ALSA.snd_pcm_hw_params_set_format.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +arg3 = POINTER(c_ulong) +_ALSA.snd_pcm_hw_params_set_buffer_size_near.argtypes = [arg1, arg2, arg3] +_ALSA.snd_pcm_hw_params_set_buffer_size_near.restype = c_int + +arg1 = snd_pcm_t_p +arg2 = snd_pcm_hw_params_t_p +arg3 = POINTER(c_ulong) +arg4 = c_int +_ALSA.snd_pcm_hw_params_set_period_size_near.argtypes = [arg1, arg2, arg3, arg4] +_ALSA.snd_pcm_hw_params_set_period_size_near.restype = c_int + +# write +arg1 = snd_pcm_t_p +arg2 = c_void_p +arg3 = c_ulong +_ALSA.snd_pcm_writei.argtypes = [arg1, arg2, arg3] +_ALSA.snd_pcm_writei.restype = c_ulong + +#================== +# General functions +#================== +class AlsaException(Exception): + pass + +class OutputBuffer(): + def __init__(self): + ohdl = snd_output_t_p() + st = _ALSA.snd_output_buffer_open(byref(ohdl)) + if st: + raise AlsaException("Error creating output buffer") + self._hdl = ohdl + + def __del__(self, close_func = _ALSA.snd_output_close): + if hasattr(self, '_hdl'): + if not(self._hdl == 0): + close_func(self._hdl) + self._hdl = 0 + + +OHDL = OutputBuffer() + +#======================== +# Cards related functions +#======================== +def card_indexes(): + """Returns a list containing index of cards recognized by alsa.""" + cards = [] + cur = c_int(-1) + while 1: + st = _ALSA.snd_card_next(byref(cur)) + if st < 0: + raise AlsaException("Could not get next card") + if cur.value < 0: + break + cards.append(cur.value) + return tuple(cards) + +def card_name(index): + """Get the name of the card corresponding to the given index.""" + sptr = c_char_p(0) + st = _ALSA.snd_card_get_name(index, byref(sptr)) + if st < 0: + raise AlsaException("Error while getting card name %d: alsa error "\ + "was %s" % (index, _ALSA.snd_strerror(st))) + cardname = string_at(sptr) + _C.free(sptr) + return cardname + +def card_longname(index): + """Get the long name of the card corresponding to the given index.""" + sptr = c_char_p(0) + st = _ALSA.snd_card_get_longname(index, byref(sptr)) + if st < 0: + raise AlsaException("Error while getting card longname %d: alsa error "\ + "was %s" % (index, _ALSA.snd_strerror(st))) + cardname = string_at(sptr) + _C.free(sptr) + return cardname + +#======================= +# Pcm related functions +#======================= +class _HwParams: + """Small class to assure that the hw parmas are always freed.""" + def __init__(self): + self._hdl = snd_pcm_hw_params_t_p() + st = _ALSA.snd_pcm_hw_params_malloc(byref(self._hdl)) + if st: + raise AlsaException("error while creating hw params %s" \ + % _ALSA.snd_strerror(st)) + + def __del__(self, close_func = _ALSA.snd_pcm_hw_params_free): + if hasattr(self, '_hdl'): + if not(self._hdl == 0): + close_func(self._hdl) + self._hdl = 0 + +class Pcm: + def __init__(self, device = 'default', samplerate = 48000, channels = 1): + self._pcmhdl = POINTER(snd_pcm_t)() + + # Open the pcm device + st = _ALSA.snd_pcm_open(byref(self._pcmhdl), device, + SND_PCM_STREAM['SND_PCM_STREAM_PLAYBACK'], 0) + if st: + raise AlsaException("error while opening pcm device %s; alsa error is %s"\ + % (device, _ALSA.snd_strerror(st))) + + # Set hw params + self._set_hw_params(samplerate, channels) + + # Reset the pcm device + st = _ALSA.snd_pcm_reset(self._pcmhdl) + if st: + raise AlsaException("error while resetting pcm : %s" \ + % _ALSA.snd_strerror(st)) + + self.nc = channels + self.samplerate = samplerate + + def _set_hw_params(self, samplerate, channels): + # XXX: Parameters copied from sndfile-play.c from libsndfile. Check the + # meaning + alsa_period_size = c_ulong(1024) + alsa_buffer_frames = c_ulong(alsa_period_size.value * 4) + + hw = _HwParams() + hwhdl = hw._hdl + st = _ALSA.snd_pcm_hw_params_any(self._pcmhdl, hwhdl) + if st < 0: + raise AlsaException("cannot initialize hw st: %s" \ + % _ALSA.snd_strerror(st)) + + st = _ALSA.snd_pcm_hw_params_set_access(self._pcmhdl, hwhdl, 3) + if st < 0: + raise AlsaException("cannot initialize hw st: %s" \ + % _ALSA.snd_strerror(st)) + + rrate = c_uint(samplerate) + st = _ALSA.snd_pcm_hw_params_set_rate_near(self._pcmhdl, hwhdl, byref(rrate), 0) + if st < 0: + raise AlsaException("cannot set samplerate : %s" \ + % _ALSA.snd_strerror(st)) + + st = _ALSA.snd_pcm_hw_params_set_format(self._pcmhdl, hwhdl, 14) + if st < 0: + raise AlsaException("cannot set format : %s" \ + % _ALSA.snd_strerror(st)) + + st = _ALSA.snd_pcm_hw_params_set_channels(self._pcmhdl, hwhdl, channels) + if st < 0: + raise AlsaException("cannot set number of channels : %s" \ + % _ALSA.snd_strerror(st)) + + st = _ALSA.snd_pcm_hw_params_set_buffer_size_near(self._pcmhdl, hwhdl, + byref(alsa_buffer_frames)) + if st < 0: + raise AlsaException("cannot set buffer size: %s" \ + % _ALSA.snd_strerror(st)) + + st = _ALSA.snd_pcm_hw_params_set_period_size_near(self._pcmhdl, hwhdl, + byref(alsa_period_size), 0) + if st < 0: + raise AlsaException("cannot set period size: %s" \ + % _ALSA.snd_strerror(st)) + + st = _ALSA.snd_pcm_hw_params(self._pcmhdl, hwhdl) + if st < 0: + raise AlsaException("cannot set params : %s" \ + % _ALSA.snd_strerror(st)) + + def __del__(self, close_func = _ALSA.snd_pcm_close): + if hasattr(self, '_pcmhdl'): + try: + self._pcmhdl[0] + close_func(self._pcmhdl) + self._pcmhdl = POINTER(snd_pcm_t)() + except ValueError: + pass + + def __str__(self): + buf = c_char_p() + msg = "Pcm device: " + if self._pcmhdl > 0: + msg += " Opened\n" + # XXX error checking + st = _ALSA.snd_pcm_dump(self._pcmhdl, OHDL._hdl) + st = _ALSA.snd_output_buffer_string(OHDL._hdl, byref(buf)) + if st > 0: + msg += string_at(buf) + return msg + + def __repr__(self): + return self.__str__() + + def write_float(self, data): + bufsz = 1024 * 2 + nb = data.size / bufsz / self.nc + for i in range(nb): + b = a[i * bufsz: (i+1) * bufsz] + st = _ALSA.snd_pcm_writei(self._pcmhdl, + b.ctypes.data_as(POINTER(c_float)), bufsz) + if not st == bufsz: + print "Error while writing to device" + b = a[nb * bufsz: -1] + reframes = b.size / self.nc + st = _ALSA.snd_pcm_writei(self._pcmhdl, + b.ctypes.data_as(POINTER(c_float)), reframes) + if not st == reframes: + print "Error while writing to device" + + _ALSA.snd_pcm_drain(self._pcmhdl) +#class AlsaPlayer: +# def __init__(self, device = "default"): +# pcm = _Pcm(device) +# hw = _HwParams() +# err = _ALSA.snd_pcm_hw_params_any(pcm.pcmhdl, hw.hwparamshdl) +# if err: +# raise AlsaException(" + +if __name__ == '__main__': + print card_indexes() + print [card_name(i) for i in card_indexes()] + print [card_longname(i) for i in card_indexes()] + print asoundlib_version() + print asoundlib_version_numbers() + from scikits.audiolab import wavread + a, fs = wavread('test.wav')[:2] + if a.ndim > 1: + nc = a.shape[1] + else: + nc = 1 + a = a.astype(N.float32) + pcm = Pcm("default:1", 22050, channels = nc) + print pcm + pcm.write_float(a) diff --git a/telemeta/util/audiolab/scikits/audiolab/soundio/setup.py b/telemeta/util/audiolab/scikits/audiolab/soundio/setup.py new file mode 100644 index 00000000..a2234513 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/soundio/setup.py @@ -0,0 +1,12 @@ +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext +from numpy.distutils.misc_util import get_numpy_include_dirs + +setup(name = "PyrexGuide", + ext_modules=[ + Extension("_alsa", ["_alsa.pyx"], + libraries = ['asound'], + include_dirs = get_numpy_include_dirs())], + cmdclass = {'build_ext': build_ext}) + diff --git a/telemeta/util/audiolab/scikits/audiolab/soundio/simple.c b/telemeta/util/audiolab/scikits/audiolab/soundio/simple.c new file mode 100644 index 00000000..96e1c37f --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/soundio/simple.c @@ -0,0 +1,74 @@ +#include + +#include + +int main() +{ + snd_pcm_t *pcm; + snd_pcm_hw_params_t *hw; + + int st; + unsigned int rrate = 44100; + snd_output_t *output; + + st = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (st) { + fprintf(stderr, "Error opening pcm\n"); + return -1; + } + + st = snd_pcm_hw_params_malloc(&hw); + if (st) { + fprintf(stderr, "Error while allocating hw\n"); + return -1; + } + + st = snd_pcm_hw_params_any(pcm, hw); + if (st) { + fprintf(stderr, "Error while allocating hw\n"); + return -1; + } + + st = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); + if (st) { + fprintf(stderr, "Error while allocating hw\n"); + return -1; + } + + st = snd_pcm_hw_params_set_rate_near(pcm, hw, &rrate, 0); + if (st) { + fprintf(stderr, "Error while allocating hw\n"); + return -1; + } + fprintf(stdout, "rate is %d\n", rrate); + + st = snd_pcm_hw_params(pcm, hw); + if (st) { + fprintf(stderr, "Error while allocating hw\n"); + return -1; + } + + //st = snd_output_stdio_attach(&output, stdout, 0); + //if (st < 0) { + // printf("Output failed: %s\n", snd_strerror(st)); + // return 0; + //} + st = snd_output_buffer_open(&output); + if (st < 0) { + printf("Output buffer open failed: %s\n", snd_strerror(st)); + return 0; + } + char* buf; + snd_pcm_dump(pcm, output); + st = snd_output_buffer_string(output, &buf); + fprintf(stdout, "%d chars\n", st); + fprintf(stdout, "%s", buf); + + //snd_output_close(output); + //snd_pcm_hw_params_free(hw); + snd_pcm_close(pcm); + + snd_config_update_free_global(); + + return 0; +} diff --git a/telemeta/util/audiolab/scikits/audiolab/soundio/simple2.c b/telemeta/util/audiolab/scikits/audiolab/soundio/simple2.c new file mode 100644 index 00000000..401d78dd --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/soundio/simple2.c @@ -0,0 +1,80 @@ +#include +#include + +#include + +int play(snd_pcm_t *pcm) +{ + int16_t buff[1024 * 16]; + int i, j; + snd_pcm_uframes_t frames; + + for (i = 0; i < 4; ++i) { + for (j = 0; j < 1024 * 16; ++j) { + buff[j] = random() & 0xff; + } + frames = snd_pcm_writei(pcm, buff, 1024 * 16); + fprintf(stderr, "%lu\n", frames); + } + + return 0; +} + +int set(snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, + unsigned int channels, unsigned int samplerate) +{ + int st; + + st = snd_pcm_set_params(pcm, format, access, channels, samplerate, + 1, 1000000); + if (st < 0) { + printf("Output failed: %s\n", snd_strerror(st)); + return st; + } + + return 0; +} + +int main() +{ + snd_pcm_t *pcm; + snd_pcm_hw_params_t *hw; + + int st; + unsigned int rrate = 48000; + unsigned int nc = 1; + snd_output_t *output; + + int i, j; + snd_pcm_sframes_t frames; + + st = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (st) { + fprintf(stderr, "Error opening pcm\n"); + return -1; + } + + st = snd_output_stdio_attach(&output, stdout, 0); + if (st < 0) { + printf("Output failed: %s\n", snd_strerror(st)); + return -1; + } + + set(pcm, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 44100); + + play(pcm); + snd_pcm_drain(pcm); + + set(pcm, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 8000); + + play(pcm); + + //snd_pcm_dump(pcm, output); + + snd_output_close(output); + snd_pcm_close(pcm); + + snd_config_update_free_global(); + + return 0; +} diff --git a/telemeta/util/audiolab/scikits/audiolab/test_data/original.aif b/telemeta/util/audiolab/scikits/audiolab/test_data/original.aif new file mode 100644 index 00000000..457a12c8 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/test_data/original.aif differ diff --git a/telemeta/util/audiolab/scikits/audiolab/test_data/test.aiff b/telemeta/util/audiolab/scikits/audiolab/test_data/test.aiff new file mode 100644 index 00000000..93bc793c Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/test_data/test.aiff differ diff --git a/telemeta/util/audiolab/scikits/audiolab/test_data/test.au b/telemeta/util/audiolab/scikits/audiolab/test_data/test.au new file mode 100644 index 00000000..8311e387 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/test_data/test.au differ diff --git a/telemeta/util/audiolab/scikits/audiolab/test_data/test.flac b/telemeta/util/audiolab/scikits/audiolab/test_data/test.flac new file mode 100644 index 00000000..64074f9e Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/test_data/test.flac differ diff --git a/telemeta/util/audiolab/scikits/audiolab/test_data/test.raw b/telemeta/util/audiolab/scikits/audiolab/test_data/test.raw new file mode 100644 index 00000000..fb2a04cb Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/test_data/test.raw differ diff --git a/telemeta/util/audiolab/scikits/audiolab/test_data/test.sdif b/telemeta/util/audiolab/scikits/audiolab/test_data/test.sdif new file mode 100644 index 00000000..0125b89c Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/test_data/test.sdif differ diff --git a/telemeta/util/audiolab/scikits/audiolab/test_data/test.wav b/telemeta/util/audiolab/scikits/audiolab/test_data/test.wav new file mode 100644 index 00000000..d6c80126 Binary files /dev/null and b/telemeta/util/audiolab/scikits/audiolab/test_data/test.wav differ diff --git a/telemeta/util/audiolab/scikits/audiolab/tests/__init__.py b/telemeta/util/audiolab/scikits/audiolab/tests/__init__.py new file mode 100644 index 00000000..7f2fbc0c --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/tests/__init__.py @@ -0,0 +1,2 @@ +#! /usr/bin/env python +# Last Change: Mon May 21 12:00 PM 2007 J diff --git a/telemeta/util/audiolab/scikits/audiolab/tests/test_matapi.py b/telemeta/util/audiolab/scikits/audiolab/tests/test_matapi.py new file mode 100644 index 00000000..fe17b8e9 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/tests/test_matapi.py @@ -0,0 +1,164 @@ +#! /usr/bin/env python +# Last Change: Tue Jul 17 11:00 AM 2007 J +from os.path import join, dirname +from os import remove +from tempfile import mkstemp + +from numpy.testing import * +import numpy as N + +set_package_path() +from audiolab.matapi import wavread, auread, aiffread, sdifread, flacread +from audiolab.matapi import wavwrite, auwrite, aiffwrite, sdifwrite, flacwrite +from audiolab.pysndfile import PyaudioException, sndfile, formatinfo as audio_format +from audiolab.pysndfile import FlacUnsupported +restore_path() + +#Optional: +set_local_path() +# import modules that are located in the same directory as this file. +from testcommon import open_tmp_file, close_tmp_file +restore_path() + +class test_audiolab(NumpyTestCase): + def _test_read(self, func, format, filext): + # Create a tmp audio file, write some random data into it, and check it + # is the expected data when read from a function from the matapi. + rfd, fd, cfilename = open_tmp_file('pysndfiletest.' + filext) + try: + nbuff = 22050 + noise = 0.1 * N.random.randn(nbuff) + + # Open the copy file for writing + b = sndfile(cfilename, 'write', format, 1, nbuff) + b.write_frames(noise, nbuff) + b.close() + + # Reread the data + b = sndfile(cfilename, 'read') + rcnoise = b.read_frames(nbuff) + b.close() + + rnoise = func(cfilename)[0] + + assert_array_equal(rnoise, rcnoise) + finally: + close_tmp_file(rfd, cfilename) + + def test_wavread(self): + """ Check wavread """ + self._test_read(wavread, audio_format('wav', 'pcm16', 'file'), 'wav') + + def test_flacread(self): + """ Check flacread """ + try: + self._test_read(flacread, audio_format('flac', 'pcm16', 'file'), 'flac') + except FlacUnsupported: + print "Flac unsupported, flacread not tested" + + def test_auread(self): + """ Check auread """ + self._test_read(auread, audio_format('au', 'ulaw', 'file'), 'au') + + def test_aiffread(self): + """ Check aiffread """ + self._test_read(aiffread, audio_format('aiff', 'pcm16', 'file'), 'aiff') + + def test_sdifread(self): + """ Check sdifread (ircam format) """ + self._test_read(sdifread, audio_format('ircam', 'pcm16', 'file'), 'sdif') + + def test_bad_wavread(self): + """ Check wavread on bad file""" + # Create a tmp audio file with non wav format, write some random data into it, + # and check it can not be opened by wavread + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + noise = 0.1 * N.random.randn(nbuff) + + # Open the copy file for writing + format = audio_format('aiff', 'pcm16') + b = sndfile(cfilename, 'write', format, 1, nbuff) + + b.write_frames(noise, nbuff) + + b.close() + + b = sndfile(cfilename, 'read') + rcnoise = b.read_frames(nbuff) + b.close() + + try: + rnoise = wavread(cfilename)[0] + raise Exception("wavread on non wav file succeded, expected to fail") + except PyaudioException, e: + pass + #print str(e) + ", as expected" + + finally: + close_tmp_file(rfd, cfilename) + + def _test_write(self, func, format, filext): + """ Check *write functions from matpi """ + rfd1, fd1, cfilename1 = open_tmp_file('pysndfiletest.' + filext) + rfd2, fd2, cfilename2 = open_tmp_file('pysndfiletest.' + filext) + try: + nbuff = 22050 + fs = nbuff + noise = 0.1 * N.random.randn(nbuff) + + # Open the first file for writing with sndfile + b = sndfile(cfilename1, 'write', format, 1, fs) + + b.write_frames(noise, nbuff) + + b.close() + + # Write same data with wavwrite + func(noise, cfilename2, fs) + + # Compare if both files have same hash + f1 = open(cfilename1) + f2 = open(cfilename2) + + import md5 + + m1 = md5.new() + m2 = md5.new() + + m1.update(f1.read()) + m2.update(f2.read()) + + f1.close() + f2.close() + assert m1.hexdigest() == m2.hexdigest() + finally: + close_tmp_file(rfd1, cfilename1) + close_tmp_file(rfd2, cfilename2) + + def test_wavwrite(self): + """ Check wavwrite """ + self._test_write(wavwrite, audio_format('wav', 'pcm16', 'file'), 'wav') + + def test_aiffwrite(self): + """ Check aiffwrite """ + self._test_write(aiffwrite, audio_format('aiff', 'pcm16', 'file'), 'aiff') + + def test_auwrite(self): + """ Check wavwrite """ + self._test_write(auwrite, audio_format('au', 'ulaw', 'file'), 'au') + + def test_sdifwrite(self): + """ Check wavwrite """ + self._test_write(sdifwrite, audio_format('ircam', 'pcm16', 'file'), 'sdif') + + def test_flacwrite(self): + """ Check flacwrite """ + try: + self._test_write(flacwrite, audio_format('flac', 'pcm16', 'file'), 'flac') + except FlacUnsupported: + print "Flac unsupported, flacwrite not tested" + +if __name__ == "__main__": + NumpyTest().run() diff --git a/telemeta/util/audiolab/scikits/audiolab/tests/test_pysndfile.py b/telemeta/util/audiolab/scikits/audiolab/tests/test_pysndfile.py new file mode 100644 index 00000000..632a4bc8 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/tests/test_pysndfile.py @@ -0,0 +1,396 @@ +#! /usr/bin/env python +# Last Change: Tue Jul 17 11:00 AM 2007 J +"""Test for the sndfile class.""" +from os.path import join, dirname +import os +import sys + +from numpy.testing import NumpyTestCase, assert_array_equal, NumpyTest, \ + assert_array_almost_equal, set_package_path, restore_path, set_local_path +import numpy as N + +set_package_path() +from audiolab import pysndfile +from audiolab.pysndfile import sndfile, formatinfo as audio_format +restore_path() + +set_local_path() +from testcommon import open_tmp_file, close_tmp_file +restore_path() + +# XXX: there is a lot to refactor here +class test_pysndfile(NumpyTestCase): + def test_basic_io(self): + """ Check open, close and basic read/write""" + # dirty ! + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + # Open the copy file for writing + format = audio_format('wav', 'pcm16') + b = sndfile(fd, 'write', format, a.get_channels(), + a.get_samplerate()) + + # Copy the data + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff) + assert tmpa.dtype == N.float + b.write_frames(tmpa, nbuff) + nrem = nframes % nbuff + tmpa = a.read_frames(nrem) + assert tmpa.dtype == N.float + b.write_frames(tmpa, nrem) + + a.close() + b.close() + finally: + close_tmp_file(rfd, cfilename) + + + def test_basic_io_fd(self): + """ Check open from fd works""" + # dirty ! + if sys.platform == 'win32': + print "Not testing opening by fd because does not work on win32" + else: + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + fd = os.open(ofilename, os.O_RDONLY) + hdl = sndfile(fd, 'read') + hdl.close() + + def test_raw(self): + rawname = join(dirname(pysndfile.__file__), 'test_data', 'test.raw') + format = audio_format('raw', 'pcm16', 'little') + a = sndfile(rawname, 'read', format, 1, 11025) + assert a.get_nframes() == 11290 + a.close() + + def test_float64(self): + """Check float64 write/read works""" + # dirty ! + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + # Open the copy file for writing + format = audio_format('wav', 'float64') + b = sndfile(fd, 'write', format, a.get_channels(), + a.get_samplerate()) + + # Copy the data in the wav file + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float64) + assert tmpa.dtype == N.float64 + b.write_frames(tmpa, nbuff) + nrem = nframes % nbuff + tmpa = a.read_frames(nrem) + b.write_frames(tmpa, nrem) + + a.close() + b.close() + + # Now, reopen both files in for reading, and check data are + # the same + a = sndfile(ofilename, 'read') + b = sndfile(cfilename, 'read') + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float64) + tmpb = b.read_frames(nbuff, dtype = N.float64) + assert_array_equal(tmpa, tmpb) + + a.close() + b.close() + + finally: + close_tmp_file(rfd, cfilename) + + def test_float32(self): + """Check float write/read works""" + # dirty ! + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nbuff = 22050 + + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + # Open the copy file for writing + format = audio_format('wav', 'float32') + b = sndfile(fd, 'write', format, a.get_channels(), + a.get_samplerate()) + + # Copy the data in the wav file + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float32) + assert tmpa.dtype == N.float32 + b.write_frames(tmpa, nbuff) + nrem = nframes % nbuff + tmpa = a.read_frames(nrem) + b.write_frames(tmpa, nrem) + + a.close() + b.close() + + # Now, reopen both files in for reading, and check data are + # the same + a = sndfile(ofilename, 'read') + b = sndfile(cfilename, 'read') + for i in range(nframes / nbuff): + tmpa = a.read_frames(nbuff, dtype = N.float32) + tmpb = b.read_frames(nbuff, dtype = N.float32) + assert_array_equal(tmpa, tmpb) + + a.close() + b.close() + + finally: + close_tmp_file(rfd, cfilename) + + def test_supported_features(self): + msg = "\nsupported file format are : this test is broken FIXME" + #for i in pysndfile.supported_format(): + # msg += str(i) + ', ' + #print msg + #msg = "supported encoding format are : " + #for i in pysndfile.supported_encoding(): + # msg += str(i) + ', ' + #print msg + #msg = "supported endianness are : " + #for i in pysndfile.supported_endianness(): + # msg += str(i) + ', ' + print msg + + def test_short_io(self): + # TODO: check if neg or pos value is the highest in abs + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nb = 2 ** 14 + nbuff = 22050 + fs = 22050 + a = N.random.random_integers(-nb, nb, nbuff) + a = a.astype(N.short) + + # Open the file for writing + format = audio_format('wav', 'pcm16') + b = sndfile(fd, 'write', format, 1, fs) + + b.write_frames(a, nbuff) + b.close() + + b = sndfile(cfilename, 'read') + + read_a = b.read_frames(nbuff, dtype = N.short) + b.close() + + assert_array_equal(a, read_a) + + finally: + close_tmp_file(rfd, cfilename) + + def test_int_io(self): + # TODO: check if neg or pos value is the highest in abs + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + nb = 2 ** 25 + nbuff = 22050 + fs = 22050 + a = N.random.random_integers(-nb, nb, nbuff) + a = a.astype(N.int32) + + # Open the file for writing + format = audio_format('wav', 'pcm32') + b = sndfile(fd, 'write', format, 1, fs) + + b.write_frames(a, nbuff) + b.close() + + b = sndfile(cfilename, 'read') + + read_a = b.read_frames(nbuff, dtype = N.int32) + b.close() + + assert_array_equal(a, read_a) + + finally: + close_tmp_file(rfd, cfilename) + + def test_mismatch(self): + # This test open a file for writing, but with bad args (channels and + # nframes inverted) + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + # Open the file for writing + format = audio_format('wav', 'pcm16') + try: + b = sndfile(fd, 'write', \ + format, channels = 22000, samplerate = 1) + raise Exception("Try to open a file with more than 256 "\ + "channels, this should not succeed !") + except RuntimeError, e: + #print "Gave %d channels, error detected is \"%s\"" % (22000, e) + pass + + finally: + close_tmp_file(rfd, cfilename) + + def test_bigframes(self): + """ Try to seek really far""" + rawname = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + a = sndfile(rawname, 'read') + try: + try: + a.seek(2 ** 60) + raise Exception("Seek really succeded ! This should not happen") + except pysndfile.PyaudioIOError, e: + pass + finally: + a.close() + + def test_float_frames(self): + """ Check nframes can be a float""" + rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav') + try: + # Open the file for writing + format = audio_format('wav', 'pcm16') + a = sndfile(fd, 'rwrite', format, channels = 1, + samplerate = 22050) + tmp = N.random.random_integers(-100, 100, 1000) + tmp = tmp.astype(N.short) + a.write_frames(tmp, tmp.size) + a.seek(0) + a.sync() + ctmp = a.read_frames(1e2, dtype = N.short) + a.close() + + finally: + close_tmp_file(rfd, cfilename) + + def test_nofile(self): + """ Check the failure when opening a non existing file.""" + try: + f = sndfile("floupi.wav", "read") + raise AssertionError("call to non existing file should not succeed") + except IOError: + pass + except Exception, e: + raise AssertionError("opening non existing file should raise a IOError exception, got %s instead" % e.__class__) + +class test_seek(NumpyTestCase): + def test_simple(self): + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + # Open the test file for reading + a = sndfile(ofilename, 'read') + nframes = a.get_nframes() + + buffsize = 1024 + buffsize = min(nframes, buffsize) + + # First, read some frames, go back, and compare buffers + buff = a.read_frames(buffsize) + a.seek(0) + buff2 = a.read_frames(buffsize) + assert_array_equal(buff, buff2) + + a.close() + + # Now, read some frames, go back, and compare buffers + # (check whence == 1 == SEEK_CUR) + a = sndfile(ofilename, 'read') + a.read_frames(buffsize) + buff = a.read_frames(buffsize) + a.seek(-buffsize, 1) + buff2 = a.read_frames(buffsize) + assert_array_equal(buff, buff2) + + a.close() + + # Now, read some frames, go back, and compare buffers + # (check whence == 2 == SEEK_END) + a = sndfile(ofilename, 'read') + buff = a.read_frames(nframes) + a.seek(-buffsize, 2) + buff2 = a.read_frames(buffsize) + assert_array_equal(buff[-buffsize:], buff2) + + def test_rw(self): + """Test read/write pointers for seek.""" + ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav') + rfd, fd, cfilename = open_tmp_file('rwseektest.wav') + try: + ref = sndfile(ofilename, 'read') + test = sndfile(fd, 'rwrite', format = ref._format, channels = + ref.get_channels(), samplerate = ref.get_samplerate()) + n = 1024 + + rbuff = ref.read_frames(n, dtype = N.int16) + test.write_frames(rbuff) + tbuff = test.read_frames(n, dtype = N.int16) + + assert_array_equal(rbuff, tbuff) + + # Test seeking both read and write pointers + test.seek(0, 0) + test.write_frames(rbuff) + tbuff = test.read_frames(n, dtype = N.int16) + assert_array_equal(rbuff, tbuff) + + # Test seeking only read pointer + rbuff1 = rbuff.copy() + rbuff2 = rbuff1 * 2 + 1 + rbuff2.clip(-30000, 30000) + test.seek(0, 0, 'r') + test.write_frames(rbuff2) + tbuff1 = test.read_frames(n, dtype = N.int16) + try: + tbuff2 = test.read_frames(n, dtype = N.int16) + except IOError, e: + msg = "write pointer was updated in read seek !" + msg += "\n(msg is %s)" % e + raise AssertionError(msg) + + assert_array_equal(rbuff1, tbuff1) + assert_array_equal(rbuff2, tbuff2) + if N.all(rbuff2 == tbuff1): + raise AssertionError("write pointer was updated"\ + " in read seek !") + + # Test seeking only write pointer + rbuff3 = rbuff1 * 2 - 1 + rbuff3.clip(-30000, 30000) + test.seek(0, 0, 'rw') + test.seek(n, 0, 'w') + test.write_frames(rbuff3) + tbuff1 = test.read_frames(n, N.int16) + try: + assert_array_equal(tbuff1, rbuff1) + except AssertionError: + raise AssertionError("read pointer was updated in write seek !") + + try: + tbuff3 = test.read_frames(n, N.int16) + except IOError, e: + msg = "read pointer was updated in write seek !" + msg += "\n(msg is %s)" % e + raise AssertionError(msg) + + assert_array_equal(tbuff3, rbuff3) + test.close() + + finally: + close_tmp_file(rfd, cfilename) + +if __name__ == "__main__": + NumpyTest().run() diff --git a/telemeta/util/audiolab/scikits/audiolab/tests/testcommon.py b/telemeta/util/audiolab/scikits/audiolab/tests/testcommon.py new file mode 100644 index 00000000..dd8c4a42 --- /dev/null +++ b/telemeta/util/audiolab/scikits/audiolab/tests/testcommon.py @@ -0,0 +1,20 @@ +import os +from tempfile import mkstemp +import sys + +def open_tmp_file(name): + """On any sane platforms, return a fd on a tmp file. On windows, returns + the filename, and as such, is not secure (someone else can reopen the file + in between).""" + fd, cfilename = mkstemp('pysndfiletest.wav') + if sys.platform == 'win32': + return fd, cfilename, cfilename + else: + return fd, fd, cfilename + +def close_tmp_file(fd, filename): + """On any sane platforms, remove the file . On windows, only close the + file.""" + os.close(fd) + os.remove(filename) + diff --git a/telemeta/util/audiolab/setup.cfg b/telemeta/util/audiolab/setup.cfg new file mode 100644 index 00000000..7b3b6f1b --- /dev/null +++ b/telemeta/util/audiolab/setup.cfg @@ -0,0 +1,4 @@ +[egg_info] +#tag_svn_revision = 1 +tag_build = .dev + diff --git a/telemeta/util/audiolab/setup.py b/telemeta/util/audiolab/setup.py new file mode 100644 index 00000000..0963848b --- /dev/null +++ b/telemeta/util/audiolab/setup.py @@ -0,0 +1,222 @@ +#! /usr/bin/env python +# Last Change: Tue Jul 29 12:00 PM 2008 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) any +# later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this library; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# TODO: +# - check how to handle cmd line build options with distutils and use +# it in the building process + +descr = """ audiolab is a small python package to import data from audio +files to numpy arrays and export data from numpy arrays to audio files. It uses +libsndfile from Erik Castro de Lopo for the underlying IO, which supports many +different audio formats: http://www.mega-nerd.com/libsndfile/ + +For now, the python api for audio IO should be stable; a matlab-like API is +also available for quick read/write (ala wavread, wavwrite, etc...). For 1.0 +release, I hope to add support for simple read/write to soundcard, to be able +to record and listen to data in numpy arrays. + +2006-2007, David Cournapeau + +LICENSE: audiolab is licensed under the LGPL, as is libsndfile itself. See +COPYING.txt for details. """ + +from os.path import join +import os +import sys + +DISTNAME = 'scikits.audiolab' +DESCRIPTION = 'A python module to make noise from numpy arrays' +LONG_DESCRIPTION = descr +MAINTAINER = 'David Cournapeau', +MAINTAINER_EMAIL = 'david@ar.media.kyoto-u.ac.jp', +URL = 'http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/audiolab', +LICENSE = 'LGPL' +DOWNLOAD_URL = URL + +SNDFILE_MAJ_VERSION = 1 + +# The following is more or less random copy/paste from numpy.distutils ... +import setuptools +from distutils.errors import DistutilsError +from numpy.distutils.system_info import system_info, NotFoundError, dict_append, so_ext +from numpy.distutils.core import setup, Extension + +class SndfileNotFoundError(NotFoundError): + """ sndfile (http://www.mega-nerd.com/libsndfile/) library not found. + Directories to search for the libraries can be specified in the + site.cfg file (section [sndfile]).""" + +class sndfile_info(system_info): + #variables to override + section = 'sndfile' + notfounderror = SndfileNotFoundError + libname = 'sndfile' + header = 'sndfile.h' + + def __init__(self): + system_info.__init__(self) + + def library_extensions(self): + # We rewrite library_extension + exts = system_info.library_extensions(self) + if sys.platform == 'win32': + exts.insert(0, '.dll') + return exts + + def calc_info(self): + """ Compute the informations of the library """ + prefix = 'lib' + + # Look for the shared library + sndfile_libs = self.get_libs('sndfile_libs', self.libname) + lib_dirs = self.get_lib_dirs() + tmp = None + for i in lib_dirs: + tmp = self.check_libs(i, sndfile_libs) + if tmp is not None: + info = tmp + break + if tmp is None: + raise SndfileNotFoundError("sndfile library not found") + + # Look for the header file + include_dirs = self.get_include_dirs() + inc_dir = None + for d in include_dirs: + p = self.combine_paths(d,self.header) + if p: + inc_dir = os.path.dirname(p[0]) + headername = os.path.abspath(p[0]) + break + + if inc_dir is None: + raise SndfileNotFoundError("header not found") + + if inc_dir is not None and tmp is not None: + if sys.platform == 'win32': + # win32 case + fullname = prefix + tmp['libraries'][0] + \ + '.dll' + elif sys.platform == 'darwin': + # Mac Os X case + fullname = prefix + tmp['libraries'][0] + '.' + \ + str(SNDFILE_MAJ_VERSION) + '.dylib' + else: + # All others (Linux for sure; what about solaris) ? + fullname = prefix + tmp['libraries'][0] + '.so' + \ + '.' + str(SNDFILE_MAJ_VERSION) + fullname = os.path.join(info['library_dirs'][0], fullname) + dict_append(info, include_dirs=[inc_dir], + fullheadloc = headername, + fulllibloc = fullname) + else: + raise RuntimeError("This is a bug") + + #print self + self.set_info(**info) + return + +from header_parser import do_subst_in_file +def configuration(parent_package='',top_path=None, package_name=DISTNAME): + if os.path.exists('MANIFEST'): os.remove('MANIFEST') + if os.path.exists('scikits/audiolab/pysndfile.py'): os.remove('scikits/audiolab/pysndfile.py') + + pkg_prefix_dir = os.path.join('scikits', 'audiolab') + # Check that sndfile can be found and get necessary informations + # (assume only one header and one library file) + sf_info = sndfile_info() + sf_config = sf_info.get_info(2) + headername = sf_config['fullheadloc'] + libname = sf_config['fulllibloc'] + + # Now, generate pysndfile.py.in + from generate_const import generate_enum_dicts + repdict = generate_enum_dicts(headername) + repdict['%SHARED_LOCATION%'] = libname + #do_subst_in_file('pysndfile.py.in', 'pysndfile.py', repdict) + do_subst_in_file(os.path.join(pkg_prefix_dir, 'pysndfile.py.in'), + os.path.join(pkg_prefix_dir, 'pysndfile.py'), + repdict) + + # Get the version + from scikits.audiolab.info import VERSION as audiolab_version + + from numpy.distutils.misc_util import Configuration + config = Configuration(package_name,parent_package,top_path, + version = audiolab_version, + maintainer = MAINTAINER, + maintainer_email = MAINTAINER_EMAIL, + description = DESCRIPTION, + license = LICENSE, + url = URL, + download_url = DOWNLOAD_URL, + long_description = LONG_DESCRIPTION) + + # XXX: once in SVN, should add svn version... + #print config.make_svn_version_py() + + # package_data does not work with sdist for setuptools 0.5 (setuptools bug), + # so we need to add them here while the bug is not solved... + config.add_data_files(('docs', \ + ['scikits/audiolab/docs/' + i for i in DOC_FILES])) + + config.add_data_files(('test_data', \ + ['scikits/audiolab/test_data/' + i + for i in TEST_DATA_FILES])) + + config.add_data_files(('misc', \ + ['scikits/audiolab/misc/' + i + for i in BAD_FLAC_FILES])) + + config.add_data_dir(('examples', 'scikits/audiolab/docs/examples')) + + return config + +TEST_DATA_FILES = ['test.raw', 'test.flac', 'test.wav', 'test.au', + 'test.sdif'] +DOC_FILES = ['audiolab.pdf', 'index.txt'] +BAD_FLAC_FILES = ['Makefile', 'badflac.flac', 'badflac.c'] + +if __name__ == "__main__": + # setuptools version of config script + + # package_data does not work with sdist for setuptools 0.5 (setuptools bug) + # So we cannot add data files via setuptools yet. + + #data_files = ['test_data/' + i for i in TEST_DATA_FILES] + #data_files.extend(['docs/' + i for i in doc_files]) + + setup(configuration = configuration, + install_requires='numpy', # can also add version specifiers + namespace_packages=['scikits'], + packages=setuptools.find_packages(), + include_package_data = True, + #package_data = {'scikits.audiolab': data_files}, + test_suite="tester", # for python setup.py test + zip_safe=True, # the package can run out of an .egg file + #FIXME url, download_url, ext_modules + classifiers = + [ 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: LGPL License', + 'Topic :: Multimedia :: Sound/Audio', + 'Topic :: Scientific/Engineering'] + ) diff --git a/telemeta/util/audiolab/site.cfg.win32 b/telemeta/util/audiolab/site.cfg.win32 new file mode 100644 index 00000000..f9bdd910 --- /dev/null +++ b/telemeta/util/audiolab/site.cfg.win32 @@ -0,0 +1,5 @@ +# Check Win32 info for using audiolab on windows +[sndfile] +include_dirs = c:\local\include +library_dirs = c:\local\lib +sndfile_libs = sndfile-1 diff --git a/telemeta/util/audiolab/site.cfg_noflac b/telemeta/util/audiolab/site.cfg_noflac new file mode 100644 index 00000000..66adc498 --- /dev/null +++ b/telemeta/util/audiolab/site.cfg_noflac @@ -0,0 +1,8 @@ +[sndfile] +include_dirs = /usr/media/boulot/src/sigtools/pyaudio/local/include +library_dirs = /usr/media/boulot/src/sigtools/pyaudio/local/lib +sndfile_libs = sndfile +#include_dirs = /home/david/local/include +#library_dirs = /home/david/local/lib +#libraries = sndfile + diff --git a/telemeta/util/audiolab/tester.py b/telemeta/util/audiolab/tester.py new file mode 100644 index 00000000..e0f2262c --- /dev/null +++ b/telemeta/util/audiolab/tester.py @@ -0,0 +1,13 @@ +#! /usr/bin/env python +# Last Change: Sat Jul 21 03:00 PM 2007 J + +"""Mini script useful for top level testing of the learn package.""" + +from numpy.testing import NumpyTest + +def additional_tests(): + # XXX: does this guarantee that the package is the one in the dev trunk, and + # not scikits.foo installed somewhere else ? + import scikits.audiolab + np = NumpyTest(scikits.audiolab) + return np._test_suite_from_all_tests(np.package, level = 10, verbosity = 1) diff --git a/telemeta/visualization/octave_core.py b/telemeta/visualization/octave_core.py index 0664dc47..cb8f4475 100644 --- a/telemeta/visualization/octave_core.py +++ b/telemeta/visualization/octave_core.py @@ -47,8 +47,6 @@ class OctaveCoreVisualizer(Component): mFile_tmp = open(mFile_name,'w') self.pngFile = NamedTemporaryFile(suffix='.png') command = ['octave', mFile_name] - print command - print self.pngFile.name for line in self.get_mFile_line(): mFile_tmp.write(line) @@ -71,6 +69,6 @@ class OctaveCoreVisualizer(Component): self.ppmFile.close() self.pngFile.close() - #os.remove(mFile_name) + os.remove(mFile_name) diff --git a/telemeta/visualization/spectrogram3.py b/telemeta/visualization/spectrogram3.py index fa990c71..0930f502 100644 --- a/telemeta/visualization/spectrogram3.py +++ b/telemeta/visualization/spectrogram3.py @@ -18,9 +18,6 @@ class SpectrogramVisualizer3(Component): implements(IMediaItemVisualizer) - def __init__(self): - pass - def get_id(self): return "spectrogram3" diff --git a/telemeta/visualization/waveform3.py b/telemeta/visualization/waveform3.py index bc6eaf56..4d2e806c 100644 --- a/telemeta/visualization/waveform3.py +++ b/telemeta/visualization/waveform3.py @@ -18,9 +18,6 @@ class WaveFormVisualizer(Component): implements(IMediaItemVisualizer) - def __init__(self): - pass - def get_id(self): return "waveform3" @@ -47,5 +44,3 @@ class WaveFormVisualizer(Component): pngFile_w.close() pngFile_s.close() - - diff --git a/telemeta/web/base.py b/telemeta/web/base.py index af14cd8f..9f332ef1 100644 --- a/telemeta/web/base.py +++ b/telemeta/web/base.py @@ -47,12 +47,12 @@ class WebView(Component): formats.append({'name': exporter.get_format(), 'extension': exporter.get_file_extension()}) visualizers = [] for visualizer in self.visualizers: - visualizers.append({'name':visualizer.get_name(), 'id': + visualizers.append({'name':visualizer.get_name(), 'id': visualizer.get_id()}) if request.REQUEST.has_key('visualizer_id'): visualizer_id = request.REQUEST['visualizer_id'] else: - visualizer_id = 'waveform' + visualizer_id = 'waveform3' return render_to_response(template, {'item': item, 'export_formats': formats, @@ -302,10 +302,3 @@ class WebView(Component): template_name='geo_country_collections.html', paginate_by=20, extra_context={'country': country, 'continent': continent}) - - - - - - -