From 2ab3d99967d68c79351fe2a3df22c445447e3010 Mon Sep 17 00:00:00 2001 From: Michał Cichoń Date: Tue, 15 Nov 2011 22:28:48 +0100 Subject: Initial import. --- libao/libao.vcproj | 205 +++++ libao/src/AUTHORS | 4 + libao/src/CHANGES | 103 +++ libao/src/COPYING | 340 +++++++++ libao/src/README | 70 ++ libao/src/include/ao/ao.h | 139 ++++ libao/src/include/ao/ao_private.h | 211 ++++++ libao/src/include/ao/os_types.h | 39 + libao/src/include/ao/plugin.h | 51 ++ libao/src/src/ao_aixs.c | 256 +++++++ libao/src/src/ao_au.c | 250 ++++++ libao/src/src/ao_null.c | 153 ++++ libao/src/src/ao_raw.c | 160 ++++ libao/src/src/ao_wav.c | 291 +++++++ libao/src/src/ao_wmm.c | 645 ++++++++++++++++ libao/src/src/audio_out.c | 1514 +++++++++++++++++++++++++++++++++++++ libao/src/src/config.c | 111 +++ 17 files changed, 4542 insertions(+) create mode 100644 libao/libao.vcproj create mode 100644 libao/src/AUTHORS create mode 100644 libao/src/CHANGES create mode 100644 libao/src/COPYING create mode 100644 libao/src/README create mode 100644 libao/src/include/ao/ao.h create mode 100644 libao/src/include/ao/ao_private.h create mode 100644 libao/src/include/ao/os_types.h create mode 100644 libao/src/include/ao/plugin.h create mode 100644 libao/src/src/ao_aixs.c create mode 100644 libao/src/src/ao_au.c create mode 100644 libao/src/src/ao_null.c create mode 100644 libao/src/src/ao_raw.c create mode 100644 libao/src/src/ao_wav.c create mode 100644 libao/src/src/ao_wmm.c create mode 100644 libao/src/src/audio_out.c create mode 100644 libao/src/src/config.c (limited to 'libao') diff --git a/libao/libao.vcproj b/libao/libao.vcproj new file mode 100644 index 0000000..d1fc210 --- /dev/null +++ b/libao/libao.vcproj @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libao/src/AUTHORS b/libao/src/AUTHORS new file mode 100644 index 0000000..30f7267 --- /dev/null +++ b/libao/src/AUTHORS @@ -0,0 +1,4 @@ +Aaron Holtzman +Stan Seibert +Jack Moffitt +Ralph Giles diff --git a/libao/src/CHANGES b/libao/src/CHANGES new file mode 100644 index 0000000..99787b7 --- /dev/null +++ b/libao/src/CHANGES @@ -0,0 +1,103 @@ +1.1.0 - February 21, 2011 + - Add autofoo ld symbol versioning to build system + - Update Roar driver to latest API + - Fix Roar driver to not block on SLP lookup during probe + - Improve/correct latency setup in ALSA (see Trac #1762) + - Add missing ctype.h header in build (see Trac #1760) + - Move toward more consistent option naming across drivers + - Fix Mac OS X AUHAL support to properly handle suspend/wakeup, + headphone plug/unplug, other hardware events + - Correct ao_example.c source to not pass dangling pointer for + the matrix argument. + - Add 24 bit playback to Pulse plugin + - Fix 24 bit playback in ALSA plugin + - Fix segfaults when closing a driver that did not successfully open. + - Fix compilation of sndio plugin + - Fix building Mac OS X driver AUHAL compilation for 10.5, + restore Mac OS X 10.4 support + +1.0.0 - March 25, 2010 + - AO returns to active development + - Added surround channel mapping API and capability + - Update and test all drivers on modern installs + - New config file options + - Driver options may be specifid in config file + - Support for MacosX < 10.5 dropped, driver modded to AUHAL + - Build in WMM driver rather than using dlopen() + - Added Roar Audio driver + - Added OpenBSD SNDIO driver + - Work around ESD non-4096 byte write bug + - Work around aRts server crash bug + - Workaround for VIA82xx click/crackle bugs under ALSA + - Remove dead/unused drivers (solaris, alasa05, mmsound) + - Numerous patches from multiple downstreams + +0.8.8 - May 24, 2007 + - New win32 driver + - Few fixes and changes in autotools and configuration files. + +0.8.7 - unreleased snapshot +- obsolete alsa device renamed to alsa05 +- the 'alsa' device now uses the 0.9/1.0 API +- ALSA driver fixes + +0.8.6 - January 11, 2005 +- Added Polypaudio driver(libao-polyp 0.4) + from Lennart Poettering +- Use esd_close() in the esd driver +- Broaden sample rate tolerance in the OSS driver for nForce + compatibility + +0.8.5 - March 19, 2004 +- Support now for the ALSA 1.0 API +- Can build with --disable-esd option again +- Minor build fixes +- ALSA mmap() mode can be enabled/disabled at runtime using "use_mmap" + option. +- Patch to OSS plugin to fix skipping issues with some drivers + (like emu10k1). + +0.8.4 - October 4, 2003 +- Added AIX sound driver from Stefan Tibus +- Committed some fixes from the Fink project to allow compiling on + OS X (still with all the dlcompat stuff) +- Improvements in plugin detection code by David Walser, especially + when ESD or aRts are present. +- NAS plugin from Antoine Mathys. +- Portability fixes for various platforms (including dlopen() bugs) + including HP-UX and *BSD. +- Memory/resource leak fixes +- Significant fixes to ALSA 0.9.x plugin by Kevin Cody, Jr. This + should fix stuttering audio for mpg321 users. The + --enable-alsa09-mmap option to ./configure turns on memory-mapped + I/O. Note this is an experimental feature and causes some problems + with some sound card drivers and also users of the dmix software mixer. + +0.8.3 - July 2002 +- fix to ao.m4 macro +- minor alsa09 plugin updates +- fixes to irix plugin + +0.8.2 - December 2001 +- alsa09 plugin updated + +0.8.0 - August 2000 +- Major API rewrite. Old apps and plugins will not work with this library! +- Default driver detection now works. +- /etc/libao.conf and ~/.libao config files now supported, see libao.conf man + page for details. +- WAV files can now be piped to non-picky applications. (Please don't + complain if this fails. You shouldn't be using be using WAV for this + anyway.) +- Various portability fixes for Solaris and *BSD. + +0.7.0 - June 2000 +- minor build fixes for different platforms + +0.6.0 - December 2000 +- slight api modification with ao_append_option() +- fixed an option leak + +0.5.0 - November 2000 +- first official release under the Xiphophorus projects + diff --git a/libao/src/COPYING b/libao/src/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/libao/src/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/libao/src/README b/libao/src/README new file mode 100644 index 0000000..6a08b56 --- /dev/null +++ b/libao/src/README @@ -0,0 +1,70 @@ +libao - A Cross-platform Audio Library, Version 0.8.6 + +Originally Copyright (C) Aaron Holtzman - May 1999 +Changes Copyright (C) Jack Moffitt - October 2000 +Changes Copyright (C) Stan Seibert - July 2000-March 2004 +libao-pulse Copyright (C) Lennart Poettering 2004-2006 +Changes Copyright (C) 2004-2005 Xiph.org Foundation +Changes Maintainer Benjamin Gerard + +libao is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +libao is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Make; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +--------------------------------------------------------------------- + +OVERVIEW + +Libao is a cross-platform audio library that allows programs to output +audio using a simple API on a wide variety of platforms. It currently +supports: + * Null output + * WAV files + * OSS (Open Sound System) + * ESD (ESounD or Enlightened Sound Daemon) + * ALSA (Advanced Linux Sound Architecture) + * PulseAudio (next generation GNOME sound server) + * AIX + * Solaris (untested) + * IRIX (untested) + +HISTORY + +Libao began life as cross-platform audio library inside of ac3dec, an +AC3 decoder by Aaron Holtzman that is part of the LiViD project. When +ogg123 (part of the command line vorbis tools) needed a way to play +audio on multiple operating systems, someone on the vorbis-dev mailing +list suggested the libao library as a possible way to add cross-platform +support to ogg123. Stan Seibert downloaded the libao library, severely +hacked it up in order to make the build process simpler and support +multiple live-playback devices. (The original code allowed one live +playback driver, the wav driver, and a null driver to be compiled into +the library.) Jack Moffitt got it supporting dynamically loaded plugins +so that binary versions of libao could be provided. The API was revised +for version 0.8.0. + +This code is being maintained by Stan Seibert (volsung@xiph.org) +and various other individuals. Please DO NOT annoy Aaron Holtzman about +bugs, features, comments, etc. regarding this code. + +WORKAROUNDS + +The OSS emulation in ALSA deviates from the OSS spec by not returning +immediately from an open() call if the OSS device is already in use. +Instead, it makes the application wait until the device is available. +This is not desirable during the autodetection phase of libao, so a +workaround has been included in the source. Since the workaround +itself violates the OSS spec and causes other problems on some +platforms, it is only enabled when ALSA is detected. The workaround +can be turned on or off by passing the --enable-broken-oss or +--disable-broken-oss flag to the configure script. diff --git a/libao/src/include/ao/ao.h b/libao/src/include/ao/ao.h new file mode 100644 index 0000000..cb8c035 --- /dev/null +++ b/libao/src/include/ao/ao.h @@ -0,0 +1,139 @@ +/* + * + * ao.h + * + * Original Copyright (C) Aaron Holtzman - May 1999 + * Modifications Copyright (C) Stan Seibert - July 2000, July 2001 + * More Modifications Copyright (C) Jack Moffitt - October 2000 + * + * This file is part of libao, a cross-platform audio outputlibrary. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __AO_H__ +#define __AO_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include +#include +#include +#include "os_types.h" + +/* --- Constants ---*/ + +#define AO_TYPE_LIVE 1 +#define AO_TYPE_FILE 2 + + +#define AO_ENODRIVER 1 +#define AO_ENOTFILE 2 +#define AO_ENOTLIVE 3 +#define AO_EBADOPTION 4 +#define AO_EOPENDEVICE 5 +#define AO_EOPENFILE 6 +#define AO_EFILEEXISTS 7 +#define AO_EBADFORMAT 8 + +#define AO_EFAIL 100 + + +#define AO_FMT_LITTLE 1 +#define AO_FMT_BIG 2 +#define AO_FMT_NATIVE 4 + +/* --- Structures --- */ + +typedef struct ao_info { + int type; /* live output or file output? */ + char *name; /* full name of driver */ + char *short_name; /* short name of driver */ + char *author; /* driver author */ + char *comment; /* driver comment */ + int preferred_byte_format; + int priority; + char **options; + int option_count; +} ao_info; + +typedef struct ao_functions ao_functions; +typedef struct ao_device ao_device; + +typedef struct ao_sample_format { + int bits; /* bits per sample */ + int rate; /* samples per second (in a single channel) */ + int channels; /* number of audio channels */ + int byte_format; /* Byte ordering in sample, see constants below */ + char *matrix; /* input channel location/ordering */ +} ao_sample_format; + +typedef struct ao_option { + char *key; + char *value; + struct ao_option *next; +} ao_option; + +#if defined(AO_BUILDING_LIBAO) +#include "ao_private.h" +#endif + +/* --- Functions --- */ + +/* library setup/teardown */ +void ao_initialize(void); +void ao_shutdown(void); + +/* device setup/playback/teardown */ +int ao_append_global_option(const char *key, + const char *value); +int ao_append_option(ao_option **options, + const char *key, + const char *value); +void ao_free_options(ao_option *options); +ao_device* ao_open_live(int driver_id, + ao_sample_format *format, + ao_option *option); +ao_device* ao_open_file(int driver_id, + const char *filename, + int overwrite, + ao_sample_format *format, + ao_option *option); + +int ao_play(ao_device *device, + char *output_samples, + uint_32 num_bytes); +int ao_close(ao_device *device); + +/* driver information */ +int ao_driver_id(const char *short_name); +int ao_default_driver_id(void); +ao_info *ao_driver_info(int driver_id); +ao_info **ao_driver_info_list(int *driver_count); +char *ao_file_extension(int driver_id); + +/* miscellaneous */ +int ao_is_big_endian(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __AO_H__ */ diff --git a/libao/src/include/ao/ao_private.h b/libao/src/include/ao/ao_private.h new file mode 100644 index 0000000..5d4dfd6 --- /dev/null +++ b/libao/src/include/ao/ao_private.h @@ -0,0 +1,211 @@ +/* + * + * ao_private.c + * + * Copyright (C) Stan Seibert - July 2001 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __AO_PRIVATE_H__ +#define __AO_PRIVATE_H__ + +/* --- Operating System Compatibility --- */ + +/* + OpenBSD systems with a.out binaries require dlsym()ed symbols to be + prepended with an underscore, so we need the following nasty #ifdef + hack. +*/ +#if defined(__OpenBSD__) && !defined(__ELF__) +#define dlsym(h,s) dlsym(h, "_" s) +#endif + +/* RTLD_NOW is the preferred symbol resolution behavior, but + * some platforms do not support it. The autoconf script will have + * already defined DLOPEN_FLAG if the default is unacceptable on the + * current platform. + * + * ALSA requires RTLD_GLOBAL. + */ +#if !defined(DLOPEN_FLAG) +#define DLOPEN_FLAG (RTLD_NOW | RTLD_GLOBAL) +#endif + +/* --- Constants --- */ + +#ifndef AO_SYSTEM_CONFIG +#define AO_SYSTEM_CONFIG "/etc/libao.conf" +#endif +#ifndef AO_USER_CONFIG +#define AO_USER_CONFIG "/.libao" +#endif + +/* --- Structures --- */ + +typedef struct ao_config { + char *default_driver; +} ao_config; + +typedef enum { + AO_OUTPUT_MATRIX_UNDEFINED=0, /* matrix unset */ + AO_OUTPUT_MATRIX_FIXED=1, /* fixed, immutable channel order, eg, ALSA */ + AO_OUTPUT_MATRIX_COLLAPSIBLE=2, /* fixed order but only used channels sent, eg MACOS */ + AO_OUTPUT_MATRIX_PERMUTABLE=3, /* channel map is fully permutable. eg Pulse */ +} ao_outorder; + +struct ao_device { + int type; /* live output or file output? */ + int driver_id; + ao_functions *funcs; + FILE *file; /* File for output if this is a file driver */ + + /* input not necessarily == output. Right now, byte order, channel + count, and channel mappings may be altered. */ + + int client_byte_format; + int machine_byte_format; + int driver_byte_format; + char *swap_buffer; + int swap_buffer_size; /* Bytes allocated to swap_buffer */ + + int input_channels; + int output_channels; + int bytewidth; + int rate; + + ao_outorder output_matrix_order; + char *output_matrix; /* physical output channel + ordering/numbering matrix set by + driver if there's a channel + name->number mapping useful to the + backend driver in some way. Eg, + Pulse has fully permutable input + channel masks, but specific channels + locations (eg, 'Center') still have + assigned numbers even if not a + specific slot int he input + interleave. */ + int output_mask; + int *input_map; /* input permutation mapping from each + input channel to a location in the + output_matrix. Made by ao_open, + intended for convenience use by + driver in device open. */ + + char *inter_matrix; /* channel matrix as presented to the + backend API */ + int *inter_permute; /* maps from each channel in the + inter_matrix back to an input channel + (if any) */ + + void *internal; /* Pointer to driver-specific data */ + + int verbose; +}; + +struct ao_functions { + int (*test)(void); + ao_info* (*driver_info)(void); + int (*device_init)(ao_device *device); + int (*set_option)(ao_device *device, const char *key, + const char *value); + int (*open)(ao_device *device, ao_sample_format *format); + int (*play)(ao_device *device, const char *output_samples, + uint_32 num_bytes); + int (*close)(ao_device *device); + void (*device_clear)(ao_device *device); + char* (*file_extension)(void); +}; + +/* --- Functions --- */ + +void ao_read_config_files (ao_config *config); + +#define adebug(format, ...) {\ + if(device->verbose==2){ \ + if(strcmp(format,"\n")){ \ + if(device->funcs->driver_info()->short_name){ \ + fprintf(stderr,"ao_%s debug: " format,device->funcs->driver_info()->short_name,##__VA_ARGS__); \ + }else{ \ + fprintf(stderr,"debug: " format,##__VA_ARGS__); \ + } \ + }else{ \ + fprintf(stderr,"\n"); \ + } \ + } \ + } + +#define averbose(format, ...) {\ + if(device->verbose>0){ \ + if(strcmp(format,"\n")){ \ + if(device->funcs->driver_info()->short_name){ \ + fprintf(stderr,"ao_%s info: " format,device->funcs->driver_info()->short_name,##__VA_ARGS__); \ + }else{ \ + fprintf(stderr,"info: " format,##__VA_ARGS__); \ + } \ + }else{ \ + fprintf(stderr,"\n"); \ + } \ + } \ + } + +#define ainfo(format, ...) {\ + if(device->verbose>=0){ \ + if(strcmp(format,"\n")){ \ + if(device->funcs->driver_info()->short_name){ \ + fprintf(stderr,"ao_%s info: " format,device->funcs->driver_info()->short_name,##__VA_ARGS__); \ + }else{ \ + fprintf(stderr,"info: " format,##__VA_ARGS__); \ + } \ + }else{ \ + fprintf(stderr,"\n"); \ + } \ + } \ + } + +#define awarn(format, ...) {\ + if(device->verbose>=0){ \ + if(strcmp(format,"\n")){ \ + if(device->funcs->driver_info()->short_name){ \ + fprintf(stderr,"ao_%s WARNING: " format,device->funcs->driver_info()->short_name,##__VA_ARGS__); \ + }else{ \ + fprintf(stderr,"WARNING: " format,##__VA_ARGS__); \ + } \ + }else{ \ + fprintf(stderr,"\n"); \ + } \ + } \ + } + +#define aerror(format, ...) { \ + if(device->verbose>=0){ \ + if(strcmp(format,"\n")){ \ + if(device->funcs->driver_info()->short_name){ \ + fprintf(stderr,"ao_%s ERROR: " format,device->funcs->driver_info()->short_name,##__VA_ARGS__); \ + }else{ \ + fprintf(stderr,"ERROR: " format,##__VA_ARGS__); \ + } \ + }else{ \ + fprintf(stderr,"\n"); \ + } \ + } \ + } + +#endif /* __AO_PRIVATE_H__ */ diff --git a/libao/src/include/ao/os_types.h b/libao/src/include/ao/os_types.h new file mode 100644 index 0000000..8a56553 --- /dev/null +++ b/libao/src/include/ao/os_types.h @@ -0,0 +1,39 @@ +/* + * + * os_types.h + * + * Original Copyright (C) Aaron Holtzman - May 1999 + * Modifications Copyright (C) Stan Seibert - July 2000 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* Set type sizes for this platform (Requires Autoconf) */ + +#ifndef __OS_TYPES_H__ +#define __OS_TYPES_H__ + +typedef unsigned char uint_8; +typedef unsigned short uint_16; +typedef unsigned int uint_32; +typedef signed char sint_8; +typedef signed short sint_16; +typedef signed int sint_32; + +#endif /* __OS_TYPES_H__ */ diff --git a/libao/src/include/ao/plugin.h b/libao/src/include/ao/plugin.h new file mode 100644 index 0000000..29a3d96 --- /dev/null +++ b/libao/src/include/ao/plugin.h @@ -0,0 +1,51 @@ +/* + * + * plugin.h - function declarations for libao plugins + * + * Copyright (C) Stan Seibert - June 2001 + * + * This file is part of libao, a cross-platform audio outputlibrary. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __PLUGIN_H__ +#define __PLUGIN_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include +#include "os_types.h" + +int ao_plugin_test(); +ao_info *ao_plugin_driver_info(); +int ao_plugin_device_init(ao_device *device); +int ao_plugin_set_option(ao_device *device, const char *key, const char *value); +int ao_plugin_open(ao_device *device, ao_sample_format *format); +int ao_plugin_play(ao_device *device, const char *output_samples, + uint_32 num_bytes); +int ao_plugin_close(ao_device *device); +void ao_plugin_device_clear(ao_device *device); +char *ao_plugin_file_extension(); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __PLUGIN_H__ */ diff --git a/libao/src/src/ao_aixs.c b/libao/src/src/ao_aixs.c new file mode 100644 index 0000000..d8ba6ed --- /dev/null +++ b/libao/src/src/ao_aixs.c @@ -0,0 +1,256 @@ +/* + * + * ao_aixs.c AIX (5.1) + * + * Original Copyright (C) Stefan Tibus - August 2002 + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: ao_aixs.c 17718 2010-12-06 20:09:29Z xiphmont $ + + ********************************************************************/ + +#ifdef HAVE_SYS_AUDIO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* + * default audio device to be used, + * possible options: + * /dev/paud0/1 on PCI machines with the Crystal chipset + * /dev/baud0/1 on MCA machines with the Crystal chipset + * /dev/acpa0/1 on MCA machines with the ACPA + */ +#ifndef AO_AIX_DEFAULT_DEV +#define AO_AIX_DEFAULT_DEV "/dev/baud0/1" +#define AO_AIX_DEFAULT_DEV2 "/dev/paud0/1" +#define AO_AIX_DEFAULT_DEV3 "/dev/acpa0/1" +#endif + + +static char *ao_aixs_options[] = {"dev","id","matrix","verbose","quiet","debug"}; +ao_info ao_aixs_info = { + AO_TYPE_LIVE, + "AIX audio driver output", + "aixs", + "Stefan Tibus ", + "Outputs to the AIX audio system.", + AO_FMT_NATIVE, + 20, + ao_aixs_options, + sizeof(ao_aixs_options)/sizeof(*ao_aixs_options) +}; + + +typedef struct ao_aixs_internal { + char *dev; + int id; + int fd; +} ao_aixs_internal; + + +int ao_aixs_test() +{ + int fd; + + fd = open(AO_AIX_DEFAULT_DEV, O_WRONLY); + if(fd<0) + fd = open(AO_AIX_DEFAULT_DEV2, O_WRONLY); + if(fd<0) + fd = open(AO_AIX_DEFAULT_DEV3, O_WRONLY); + if(fd<0) + return 0; /* Cannot use this plugin with default parameters */ + + close(fd); + return 1; /* This plugin works in default mode */ +} + + +ao_info *ao_aixs_driver_info(void) +{ + return &ao_aixs_info; +} + + +int ao_aixs_device_init(ao_device *device) +{ + ao_aixs_internal *internal; + + internal = (ao_aixs_internal *) calloc(1,sizeof(ao_aixs_internal)); + + if (internal == NULL) + return 0; /* Could not initialize device memory */ + + device->internal = internal; + device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED; + + return 1; /* Memory alloc successful */ +} + +int ao_aixs_set_option(ao_device *device, const char *key, const char *value) +{ + ao_aixs_internal *internal = (ao_aixs_internal *) device->internal; + + if (!strcmp(key, "dev")) { + if(internal->dev) + free(internal->dev); + internal->dev = strdup(value); + } + if (!strcmp(key, "id")) { + internal->id = atoi(value); + if(internal->dev) + free(internal->dev); + internal->dev = NULL; + } + + return 1; +} + + +int ao_aixs_open(ao_device *device, ao_sample_format *format) +{ + ao_aixs_internal *internal = (ao_aixs_internal *) device->internal; + + audio_init init; + audio_control control; + audio_change change; + + if(!internal->dev){ + char buffer[80]; + int fd; + + sprintf(buffer,"/dev/baud%d/1",id); + fd = open(buffer, O_WRONLY); + if(fd<0){ + sprintf(buffer,"/dev/paud%d/1",id); + fd = open(buffer, O_WRONLY); + } + if(fd<0){ + sprintf(buffer,"/dev/acpa%d/1",id); + fd = open(buffer, O_WRONLY); + } + if(fd<0) return 0; + internal->fd = fd; + internal->dev = strdup(buffer); + }else + if ( (internal->fd = open(internal->dev, O_WRONLY)) < 0 ) + return 0; + + init.srate = format->rate; + init.bits_per_sample = format->bits; + init.channels = device->output_channels; + init.mode = AUDIO_PCM; + init.flags = AUDIO_BIG_ENDIAN | AUDIO_TWOS_COMPLEMENT; + init.operation = AUDIO_PLAY; + + if (ioctl(internal->fd, AUDIO_INIT, &init) < 0) { + close(internal->fd); + return 0; /* Unsupported audio format */ + } + + change.balance = 0x3fff0000; + change.volume = 0x3fff0000; + change.monitor = AUDIO_IGNORE; + change.input = AUDIO_IGNORE; + change.output = AUDIO_OUTPUT_1; + + control.ioctl_request = AUDIO_CHANGE; + control.position = 0; + control.request_info = &change; + + if (ioctl(internal->fd, AUDIO_CONTROL, &control) < 0) { + close(internal->fd); + return 0; + } + + control.ioctl_request = AUDIO_START; + control.request_info = NULL; + + if (ioctl(internal->fd, AUDIO_CONTROL, &control) < 0) { + close(internal->fd); + return 0; + } + + device->driver_byte_format = AO_FMT_NATIVE; + if(!device->inter_matrix){ + /* set up matrix such that users are warned about > stereo playback */ + if(device->output_channels<=2) + device->inter_matrix=strdup("L,R"); + //else no matrix, which results in a warning + } + + return 1; +} + + +int ao_aixs_play(ao_device *device, const char *output_samples, + uint_32 num_bytes) +{ + ao_aixs_internal *internal = (ao_aixs_internal *) device->internal; + + if (write(internal->fd, output_samples, num_bytes) < 0) + return 0; + else + return 1; +} + + +int ao_aixs_close(ao_device *device) +{ + ao_aixs_internal *internal = (ao_aixs_internal *) device->internal; + + if(internal->fd) + close(internal->fd); + internal->fd=-1; + return 1; +} + + +void ao_aixs_device_clear(ao_device *device) +{ + ao_aixs_internal *internal = (ao_aixs_internal *) device->internal; + + if(internal->dev) + free(internal->dev); + free(internal); + device->internal = NULL; +} + +ao_functions ao_aixs = { + ao_aixs_test, + ao_aixs_driver_info, + ao_aixs_device_init, + ao_aixs_set_option, + ao_aixs_open, + ao_aixs_play, + ao_aixs_close, + ao_aixs_device_clear +}; + +#endif diff --git a/libao/src/src/ao_au.c b/libao/src/src/ao_au.c new file mode 100644 index 0000000..f6aad2d --- /dev/null +++ b/libao/src/src/ao_au.c @@ -0,0 +1,250 @@ +/* + * + * ao_au.c + * + * Copyright (C) Wil Mahan - May 2001 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: ao_au.c 17718 2010-12-06 20:09:29Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#ifndef _MSC_VER +# include +#endif +#include +#include + +#define AUDIO_FILE_MAGIC ((uint_32)0x2e736e64) /* ".snd" */ + +#define AUDIO_UNKNOWN_SIZE (~0) /* (unsigned) -1 */ + +/* Format codes (not comprehensive) */ +#define AUDIO_FILE_ENCODING_LINEAR_8 (2) /* 8-bit linear PCM */ +#define AUDIO_FILE_ENCODING_LINEAR_16 (3) /* 16-bit linear PCM */ + +#define AU_HEADER_LEN (28) + +#define DEFAULT_SWAP_BUFFER_SIZE 2048 + +/* Write a uint_32 in big-endian order. */ +#define WRITE_U32(buf, x) \ + *(buf) = (unsigned char)(((x)>>24)&0xff);\ + *((buf)+1) = (unsigned char)(((x)>>16)&0xff);\ + *((buf)+2) = (unsigned char)(((x)>>8)&0xff);\ + *((buf)+3) = (unsigned char)((x)&0xff); + +typedef struct Audio_filehdr { + uint_32 magic; /* magic number */ + uint_32 hdr_size; /* offset of the data */ + uint_32 data_size; /* length of data (optional) */ + uint_32 encoding; /* data format code */ + uint_32 sample_rate; /* samples per second */ + uint_32 channels; /* number of interleaved channels */ + char info[4]; /* optional text information */ +} Audio_filehdr; + +static char *ao_au_options[] = {"matrix","verbose","quiet","debug"}; +static ao_info ao_au_info = +{ + AO_TYPE_FILE, + "AU file output", + "au", + "Wil Mahan ", + "Sends output to a .au file", + AO_FMT_BIG, + 0, + ao_au_options, + sizeof(ao_au_options)/sizeof(*ao_au_options) +}; + +typedef struct ao_au_internal +{ + Audio_filehdr au; +} ao_au_internal; + + +static int ao_au_test(void) +{ + return 1; /* File driver always works */ +} + + +static ao_info *ao_au_driver_info(void) +{ + return &ao_au_info; +} + + +static int ao_au_device_init(ao_device *device) +{ + ao_au_internal *internal; + + internal = (ao_au_internal *) malloc(sizeof(ao_au_internal)); + + if (internal == NULL) + return 0; /* Could not initialize device memory */ + + memset(&(internal->au), 0, sizeof(internal->au)); + + device->internal = internal; + device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED; + + return 1; /* Memory alloc successful */ +} + + +static int ao_au_set_option(ao_device *device, const char *key, + const char *value) +{ + return 1; /* No options! */ +} + + +static int ao_au_open(ao_device *device, ao_sample_format *format) +{ + ao_au_internal *internal = (ao_au_internal *) device->internal; + unsigned char buf[AU_HEADER_LEN]; + + /* The AU format is big-endian */ + device->driver_byte_format = AO_FMT_BIG; + + /* Fill out the header */ + internal->au.magic = AUDIO_FILE_MAGIC; + internal->au.channels = device->output_channels; + if (format->bits == 8) + internal->au.encoding = AUDIO_FILE_ENCODING_LINEAR_8; + else if (format->bits == 16) + internal->au.encoding = AUDIO_FILE_ENCODING_LINEAR_16; + else { + /* Only 8 and 16 bits are supported at present. */ + return 0; + } + internal->au.sample_rate = format->rate; + internal->au.hdr_size = AU_HEADER_LEN; + + /* From the AU specification: "When audio files are passed + * through pipes, the 'data_size' field may not be known in + * advance. In such cases, the 'data_size' should be set + * to AUDIO_UNKNOWN_SIZE." + */ + internal->au.data_size = AUDIO_UNKNOWN_SIZE; + /* strncpy(state->au.info, "OGG ", 4); */ + + /* Write the header in big-endian order */ + WRITE_U32(buf, internal->au.magic); + WRITE_U32(buf + 4, internal->au.hdr_size); + WRITE_U32(buf + 8, internal->au.data_size); + WRITE_U32(buf + 12, internal->au.encoding); + WRITE_U32(buf + 16, internal->au.sample_rate); + WRITE_U32(buf + 20, internal->au.channels); + strncpy (buf + 24, internal->au.info, 4); + + if (fwrite(buf, sizeof(char), AU_HEADER_LEN, device->file) + != AU_HEADER_LEN) { + return 0; /* Error writing header */ + } + + if(!device->inter_matrix){ + /* set up matrix such that users are warned about > stereo playback */ + if(device->output_channels<=2) + device->inter_matrix=strdup("L,R"); + //else no matrix, which results in a warning + } + + + return 1; +} + + +/* + * play the sample to the already opened file descriptor + */ +static int ao_au_play(ao_device *device, const char *output_samples, + uint_32 num_bytes) +{ + if (fwrite(output_samples, sizeof(char), num_bytes, + device->file) < num_bytes) + return 0; + else + return 1; +} + +static int ao_au_close(ao_device *device) +{ + ao_au_internal *internal = (ao_au_internal *) device->internal; + + off_t size; + unsigned char buf[4]; + + /* Try to find the total file length, including header */ + size = ftell(device->file); + + /* It's not a problem if the lseek() fails; the AU + * format does not require a file length. This is + * useful for writing to non-seekable files (e.g. + * pipes). + */ + if (size > 0) { + internal->au.data_size = size - AU_HEADER_LEN; + + /* Rewind the file */ + if (fseek(device->file, 8 /* offset of data_size */, + SEEK_SET) < 0) + { + return 1; /* Seek failed; that's okay */ + } + + /* Fill in the file length */ + WRITE_U32 (buf, internal->au.data_size); + if (fwrite(buf, sizeof(char), 4, device->file) < 4) { + return 1; /* Header write failed; that's okay */ + } + } + + return 1; +} + + +static void ao_au_device_clear(ao_device *device) +{ + ao_au_internal *internal = (ao_au_internal *) device->internal; + + free(internal); + device->internal=NULL; +} + +ao_functions ao_au = { + ao_au_test, + ao_au_driver_info, + ao_au_device_init, + ao_au_set_option, + ao_au_open, + ao_au_play, + ao_au_close, + ao_au_device_clear +}; diff --git a/libao/src/src/ao_null.c b/libao/src/src/ao_null.c new file mode 100644 index 0000000..cd7271d --- /dev/null +++ b/libao/src/src/ao_null.c @@ -0,0 +1,153 @@ +/* + * + * ao_null.c + * + * Original Copyright (C) Aaron Holtzman - May 1999 + * Modifications Copyright (C) Stan Seibert - July 2000, July 2001 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: ao_null.c 17718 2010-12-06 20:09:29Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include + +static char *ao_null_options[] = { + "debug","verbose","matrix","quiet" +}; +static ao_info ao_null_info = { + AO_TYPE_LIVE, + "Null output", + "null", + "Stan Seibert ", + "This driver does nothing.", + AO_FMT_NATIVE, + 0, + ao_null_options, + sizeof(ao_null_options)/sizeof(*ao_null_options) +}; + + +typedef struct ao_null_internal { + unsigned long byte_counter; +} ao_null_internal; + + +static int ao_null_test(void) +{ + return 1; /* Null always works */ +} + + +static ao_info *ao_null_driver_info(void) +{ + return &ao_null_info; +} + + +static int ao_null_device_init(ao_device *device) +{ + ao_null_internal *internal; + + internal = (ao_null_internal *) malloc(sizeof(ao_null_internal)); + + if (internal == NULL) + return 0; /* Could not initialize device memory */ + + internal->byte_counter = 0; + + device->internal = internal; + device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED; + + return 1; /* Memory alloc successful */ +} + + +static int ao_null_set_option(ao_device *device, const char *key, + const char *value) +{ + /*ao_null_internal *internal = (ao_null_internal *) device->internal;*/ + (void)value; + (void)key; + (void)device; + + return 1; +} + + + +static int ao_null_open(ao_device *device, ao_sample_format *format) +{ + /* Use whatever format the client requested */ + device->driver_byte_format = device->client_byte_format; + + if(!device->inter_matrix){ + /* by default, we want inter == in */ + if(format->matrix) + device->inter_matrix = _strdup(format->matrix); + } + + return 1; +} + + +static int ao_null_play(ao_device *device, const char *output_samples, + uint_32 num_bytes) +{ + ao_null_internal *internal = (ao_null_internal *)device->internal; + + internal->byte_counter += num_bytes; + + return 1; +} + + +static int ao_null_close(ao_device *device) +{ + ao_null_internal *internal = (ao_null_internal *) device->internal; + + adebug("%ld bytes sent to null device.\n", internal->byte_counter); + + return 1; +} + + +static void ao_null_device_clear(ao_device *device) +{ + ao_null_internal *internal = (ao_null_internal *) device->internal; + + free(internal); + device->internal=NULL; +} + + +ao_functions ao_null = { + ao_null_test, + ao_null_driver_info, + ao_null_device_init, + ao_null_set_option, + ao_null_open, + ao_null_play, + ao_null_close, + ao_null_device_clear +}; diff --git a/libao/src/src/ao_raw.c b/libao/src/src/ao_raw.c new file mode 100644 index 0000000..50b21be --- /dev/null +++ b/libao/src/src/ao_raw.c @@ -0,0 +1,160 @@ +/* + * + * ao_raw.c + * + * Copyright (C) Stan Seibert - January 2001, July 2001 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: ao_raw.c 17718 2010-12-06 20:09:29Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include + +static char *ao_raw_options[] = {"byteorder","matrix","verbose","quiet","debug"}; +static ao_info ao_raw_info = +{ + AO_TYPE_FILE, + "RAW sample output", + "raw", + "Stan Seibert ", + "Writes raw audio samples to a file", + AO_FMT_NATIVE, + 0, + ao_raw_options, + sizeof(ao_raw_options)/sizeof(*ao_raw_options) +}; + +typedef struct ao_raw_internal +{ + int byte_order; +} ao_raw_internal; + + +static int ao_raw_test(void) +{ + return 1; /* Always works */ +} + + +static ao_info *ao_raw_driver_info(void) +{ + return &ao_raw_info; +} + + +static int ao_raw_device_init(ao_device *device) +{ + ao_raw_internal *internal; + + internal = (ao_raw_internal *) malloc(sizeof(ao_raw_internal)); + + if (internal == NULL) + return 0; /* Could not initialize device memory */ + + internal->byte_order = AO_FMT_NATIVE; + + device->internal = internal; + device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED; + + return 1; /* Memory alloc successful */ +} + +static int ao_raw_set_option(ao_device *device, const char *key, + const char *value) +{ + ao_raw_internal *internal = (ao_raw_internal *)device->internal; + + if (!strcmp(key, "byteorder")) { + if (!strcmp(value, "native")) + internal->byte_order = AO_FMT_NATIVE; + else if (!strcmp(value, "big")) + internal->byte_order = AO_FMT_BIG; + else if (!strcmp(value, "little")) + internal->byte_order = AO_FMT_LITTLE; + else + return 0; /* Bad option value */ + } + + return 1; +} + + +static int ao_raw_open(ao_device *device, ao_sample_format *format) +{ + ao_raw_internal *internal = (ao_raw_internal *)device->internal; + + device->driver_byte_format = internal->byte_order; + + //if(!device->inter_matrix){ + ///* by default, inter == in */ + //if(format->matrix) + // device->inter_matrix = strdup(format->matrix); + //} + + return 1; +} + + +/* + * play the sample to the already opened file descriptor + */ +static int ao_raw_play(ao_device *device, const char *output_samples, + uint_32 num_bytes) +{ + if (fwrite(output_samples, sizeof(char), num_bytes, + device->file) < num_bytes) + return 0; + else + return 1; +} + + +static int ao_raw_close(ao_device *device) +{ + /* No closeout needed */ + return 1; +} + + +static void ao_raw_device_clear(ao_device *device) +{ + ao_raw_internal *internal = (ao_raw_internal *) device->internal; + + free(internal); + device->internal=NULL; +} + + +ao_functions ao_raw = { + ao_raw_test, + ao_raw_driver_info, + ao_raw_device_init, + ao_raw_set_option, + ao_raw_open, + ao_raw_play, + ao_raw_close, + ao_raw_device_clear +}; diff --git a/libao/src/src/ao_wav.c b/libao/src/src/ao_wav.c new file mode 100644 index 0000000..297a8ac --- /dev/null +++ b/libao/src/src/ao_wav.c @@ -0,0 +1,291 @@ +/* + * + * ao_wav.c + * + * Original Copyright (C) Aaron Holtzman - May 1999 + * Modifications Copyright (C) Stan Seibert - July 2000, July 2001 + * Copyright (C) Monty - January 2010 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: ao_wav.c 17718 2010-12-06 20:09:29Z xiphmont $ + + ********************************************************************/ + + +#include +#include +#include +#include +#include + +#define WAVE_FORMAT_PCM 0x0001 +#define FORMAT_MULAW 0x0101 +#define IBM_FORMAT_ALAW 0x0102 +#define IBM_FORMAT_ADPCM 0x0103 +#define WAVE_FORMAT_EXTENSIBLE 0xfffe + +#define WAV_HEADER_LEN 68 + +#define WRITE_U32(buf, x) *(buf) = (unsigned char)(x&0xff);\ + *((buf)+1) = (unsigned char)((x>>8)&0xff);\ + *((buf)+2) = (unsigned char)((x>>16)&0xff);\ + *((buf)+3) = (unsigned char)((x>>24)&0xff); + +#define WRITE_U16(buf, x) *(buf) = (unsigned char)(x&0xff);\ + *((buf)+1) = (unsigned char)((x>>8)&0xff); + +#define DEFAULT_SWAP_BUFFER_SIZE 2048 + +struct riff_struct { + unsigned char id[4]; /* RIFF */ + unsigned int len; + unsigned char wave_id[4]; /* WAVE */ +}; + + +struct chunk_struct +{ + unsigned char id[4]; + unsigned int len; +}; + +struct common_struct +{ + unsigned short wFormatTag; + unsigned short wChannels; + unsigned int dwSamplesPerSec; + unsigned int dwAvgBytesPerSec; + unsigned short wBlockAlign; + unsigned short wBitsPerSample; + unsigned short cbSize; + unsigned short wValidBitsPerSample; + unsigned int dwChannelMask; + unsigned short subFormat; +}; + +struct wave_header +{ + struct riff_struct riff; + struct chunk_struct format; + struct common_struct common; + struct chunk_struct data; +}; + + +static char *ao_wav_options[] = {"matrix","verbose","quiet","debug"}; +static ao_info ao_wav_info = +{ + AO_TYPE_FILE, + "WAV file output", + "wav", + "Aaron Holtzman ", + "Sends output to a .wav file", + AO_FMT_LITTLE, + 0, + ao_wav_options, + sizeof(ao_wav_options)/sizeof(*ao_wav_options) +}; + +typedef struct ao_wav_internal +{ + struct wave_header wave; +} ao_wav_internal; + + +static int ao_wav_test(void) +{ + return 1; /* File driver always works */ +} + + +static ao_info *ao_wav_driver_info(void) +{ + return &ao_wav_info; +} + + +static int ao_wav_device_init(ao_device *device) +{ + ao_wav_internal *internal; + + internal = (ao_wav_internal *) malloc(sizeof(ao_wav_internal)); + + if (internal == NULL) + return 0; /* Could not initialize device memory */ + + memset(&(internal->wave), 0, sizeof(internal->wave)); + + device->internal = internal; + device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR"); + device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE; + + return 1; /* Memory alloc successful */ +} + + +static int ao_wav_set_option(ao_device *device, const char *key, + const char *value) +{ + return 1; /* No options! */ +} + +static int ao_wav_open(ao_device *device, ao_sample_format *format) +{ + ao_wav_internal *internal = (ao_wav_internal *) device->internal; + unsigned char buf[WAV_HEADER_LEN]; + int size = 0x7fffffff; /* Use a bogus size initially */ + + /* Store information */ + internal->wave.common.wChannels = device->output_channels; + internal->wave.common.wBitsPerSample = ((format->bits+7)>>3)<<3; + internal->wave.common.wValidBitsPerSample = format->bits; + internal->wave.common.dwSamplesPerSec = format->rate; + + memset(buf, 0, WAV_HEADER_LEN); + + /* Fill out our wav-header with some information. */ + strncpy(internal->wave.riff.id, "RIFF",4); + internal->wave.riff.len = size - 8; + strncpy(internal->wave.riff.wave_id, "WAVE",4); + + strncpy(internal->wave.format.id, "fmt ",4); + internal->wave.format.len = 40; + + internal->wave.common.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + internal->wave.common.dwAvgBytesPerSec = + internal->wave.common.wChannels * + internal->wave.common.dwSamplesPerSec * + (internal->wave.common.wBitsPerSample >> 3); + + internal->wave.common.wBlockAlign = + internal->wave.common.wChannels * + (internal->wave.common.wBitsPerSample >> 3); + internal->wave.common.cbSize = 22; + internal->wave.common.subFormat = WAVE_FORMAT_PCM; + internal->wave.common.dwChannelMask=device->output_mask; + + strncpy(internal->wave.data.id, "data",4); + + internal->wave.data.len = size - WAV_HEADER_LEN; + + strncpy(buf, internal->wave.riff.id, 4); + WRITE_U32(buf+4, internal->wave.riff.len); + strncpy(buf+8, internal->wave.riff.wave_id, 4); + strncpy(buf+12, internal->wave.format.id,4); + WRITE_U32(buf+16, internal->wave.format.len); + WRITE_U16(buf+20, internal->wave.common.wFormatTag); + WRITE_U16(buf+22, internal->wave.common.wChannels); + WRITE_U32(buf+24, internal->wave.common.dwSamplesPerSec); + WRITE_U32(buf+28, internal->wave.common.dwAvgBytesPerSec); + WRITE_U16(buf+32, internal->wave.common.wBlockAlign); + WRITE_U16(buf+34, internal->wave.common.wBitsPerSample); + WRITE_U16(buf+36, internal->wave.common.cbSize); + WRITE_U16(buf+38, internal->wave.common.wValidBitsPerSample); + WRITE_U32(buf+40, internal->wave.common.dwChannelMask); + WRITE_U16(buf+44, internal->wave.common.subFormat); + memcpy(buf+46,"\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71",14); + strncpy(buf+60, internal->wave.data.id, 4); + WRITE_U32(buf+64, internal->wave.data.len); + + if (fwrite(buf, sizeof(char), WAV_HEADER_LEN, device->file) + != WAV_HEADER_LEN) { + return 0; /* Could not write wav header */ + } + + device->driver_byte_format = AO_FMT_LITTLE; + + return 1; +} + + +/* + * play the sample to the already opened file descriptor + */ +static int ao_wav_play(ao_device *device, const char *output_samples, + uint_32 num_bytes) +{ + if (fwrite(output_samples, sizeof(char), num_bytes, + device->file) < num_bytes) + return 0; + else + return 1; + +} + +static int ao_wav_close(ao_device *device) +{ + ao_wav_internal *internal = (ao_wav_internal *) device->internal; + unsigned char buf[4]; /* For holding length values */ + + long size; + + /* Find how long our file is in total, including header */ + size = ftell(device->file); + + if (size < 0) { + return 0; /* Wav header corrupt */ + } + + /* Go back and set correct length info */ + + internal->wave.riff.len = size - 8; + internal->wave.data.len = size - WAV_HEADER_LEN; + + /* Rewind to riff len and write it */ + if (fseek(device->file, 4, SEEK_SET) < 0) + return 0; /* Wav header corrupt */ + + WRITE_U32(buf, internal->wave.riff.len); + if (fwrite(buf, sizeof(char), 4, device->file) < 4) + return 0; /* Wav header corrupt */ + + + /* Rewind to data len and write it */ + if (fseek(device->file, 64, SEEK_SET) < 0) + return 0; /* Wav header corrupt */ + + WRITE_U32(buf, internal->wave.data.len); + if (fwrite(buf, sizeof(char), 4, device->file) < 4) + return 0; /* Wav header corrupt */ + + + return 1; /* Wav header correct */ +} + +static void ao_wav_device_clear(ao_device *device) +{ + ao_wav_internal *internal = (ao_wav_internal *) device->internal; + + free(internal); + device->internal=NULL; +} + + +ao_functions ao_wav = { + ao_wav_test, + ao_wav_driver_info, + ao_wav_device_init, + ao_wav_set_option, + ao_wav_open, + ao_wav_play, + ao_wav_close, + ao_wav_device_clear +}; diff --git a/libao/src/src/ao_wmm.c b/libao/src/src/ao_wmm.c new file mode 100644 index 0000000..7cd05f1 --- /dev/null +++ b/libao/src/src/ao_wmm.c @@ -0,0 +1,645 @@ +/* + * + * ao_wmm.c + * + * Copyright (C) Benjamin Gerard - March 2007 + * + * This file is part of libao, a cross-platform library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: ao_wmm.c 17629 2010-11-18 12:04:46Z xiphmont $ + + ********************************************************************/ + +//#define PREPARE_EACH +//#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +//#include +//#include + +#include +#include + +#include +#include + +//#ifndef KSDATAFORMAT_SUBTYPE_PCM +//#define KSDATAFORMAT_SUBTYPE_PCM (GUID) {0x00000001,0x0000,0x0010,{0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}} +//#endif + +GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010,{0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}}; + + +#include "ao/ao.h" +/* #include "ao/plugin.h" */ + +#define GALLOC_WVHD_TYPE (GHND) +#define GALLOC_DATA_TYPE (GHND) + +static const char * mmerror(MMRESULT mmrError) +{ + static char mmbuffer[1024]; + int len; + sprintf(mmbuffer,"mm:%d ",(int)mmrError); + len = (int)strlen(mmbuffer); + waveOutGetErrorTextA(mmrError, mmbuffer+len, sizeof(mmbuffer)-len); + mmbuffer[sizeof(mmbuffer)-1] = 0; + return mmbuffer; +} + +static char * ao_wmm_options[] = {"dev", "id", "matrix","verbose","quiet","debug"}; +static ao_info ao_wmm_info = + { + /* type */ AO_TYPE_LIVE, + /* name */ "WMM audio driver output ", + /* short-name */ "wmm", + /* author */ "Benjamin Gerard ", + /* comment */ "Outputs audio to the Windows MultiMedia driver.", + /* prefered format */ AO_FMT_LITTLE, + /* priority */ 20, + /* options */ ao_wmm_options, + /* # of options */ sizeof(ao_wmm_options)/sizeof(*ao_wmm_options) + }; + +typedef struct { + WAVEHDR wh; /* waveheader */ + char * data; /* sample data ptr */ + int idx; /* index of this header */ + int count; /* current byte count */ + int length; /* size of data */ + int sent; /* set when header is sent to device */ +} myWH_t; + +typedef struct ao_wmm_internal { + UINT id; /* device id */ + HWAVEOUT hwo; /* waveout handler */ + WAVEOUTCAPSA caps; /* device caps */ + WAVEFORMATEXTENSIBLE wavefmt; /* sample format */ + + int opened; /* device has been opened */ + int prepared; /* waveheaders have been prepared */ + int blocks; /* number of blocks (wave headers) */ + int splPerBlock; /* sample per blocks. */ + int msPerBlock; /* millisecond per block (approx.) */ + + void * bigbuffer; /* Allocated buffer for waveheaders and sound data */ + myWH_t * wh; /* Pointer to waveheaders in bigbuffer */ + BYTE * spl; /* Pointer to sound data in bigbuffer */ + + int sent_blocks; /* Number of waveheader sent (not ack). */ + int full_blocks; /* Number of waveheader full (ready to send). */ + int widx; /* Index to the block being currently filled. */ + int ridx; /* Index to the block being sent. */ + +} ao_wmm_internal; + +int ao_wmm_test(void) +{ + return 1; /* This plugin works in default mode */ +} + +ao_info *ao_wmm_driver_info(void) +{ + return &ao_wmm_info; +} + +int ao_wmm_set_option(ao_device *device, + const char *key, const char *value) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + int res = 0; + + if (!strcmp(key, "dev")) { + if (!strcmp(value,"default")) { + key = "id"; + value = "0"; + } else { + WAVEOUTCAPSA caps; + int i, max = waveOutGetNumDevs(); + + adebug("searching for device %s among %d\n", value, max); + for (i=0; i [%s]\n", + i,caps.szPname,caps.vDriverVersion>>8,caps.vDriverVersion&255,res?"YES":"no"); + if (res) { + internal->id = i; + internal->caps = caps; + break; + } + } else { + aerror("waveOutGetDevCaps(%d) => %s",i,mmerror(mmres)); + } + } + goto finish; + } + } + + if (!strcmp(key,"id")) { + MMRESULT mmres; + WAVEOUTCAPSA caps; + + int id = strtol(value,0,0); + int max = waveOutGetNumDevs(); + + if (id >= 0 && id <= max) { + if (id-- == 0) { + adebug("set default wavemapper\n"); + id = WAVE_MAPPER; + } + mmres = waveOutGetDevCapsA(id, &caps, sizeof(caps)); + + if (mmres == MMSYSERR_NOERROR) { + res = 1; + adebug("checking id=%d, name='%s', ver=%d.%d => [YES]\n", + id,caps.szPname,caps.vDriverVersion>>8,caps.vDriverVersion&255); + internal->id = id; + internal->caps = caps; + } else { + aerror("waveOutGetDevCaps(%d) => %s",id,mmerror(mmres)); + } + } + } + + finish: + return res; +} + + +int ao_wmm_device_init(ao_device *device) +{ + ao_wmm_internal *internal; + int res; + + internal = (ao_wmm_internal *) malloc(sizeof(ao_wmm_internal)); + device->internal = internal; + if (internal != NULL) { + memset(internal,0,sizeof(ao_wmm_internal)); + internal->id = WAVE_MAPPER; + internal->blocks = 32; + internal->splPerBlock = 512; + /* set default device */ + ao_wmm_set_option(device,"id","0"); + } + + res = internal != NULL; + + device->output_matrix = _strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR"); + device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE; + + return res; +} + +static int _ao_open_device(ao_device *device) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + int res; + MMRESULT mmres; + + mmres = + waveOutOpen(&internal->hwo, + internal->id, + &internal->wavefmt.Format, + (DWORD_PTR)0/* waveOutProc */, + (DWORD_PTR)device, + CALLBACK_NULL/* |WAVE_FORMAT_DIRECT */|WAVE_ALLOWSYNC); + + if(mmres == MMSYSERR_NOERROR){ + adebug("waveOutOpen id=%d, channels=%d, bits=%d, rate %d => SUCCESS\n", + internal->id, + internal->wavefmt.Format.nChannels, + (int)internal->wavefmt.Format.wBitsPerSample, + (int)internal->wavefmt.Format.nSamplesPerSec); + }else{ + aerror("waveOutOpen id=%d, channels=%d, bits=%d, rate %d => FAILED\n", + internal->id, + internal->wavefmt.Format.nChannels, + (int)internal->wavefmt.Format.wBitsPerSample, + (int)internal->wavefmt.Format.nSamplesPerSec); + } + + if (mmres == MMSYSERR_NOERROR) { + UINT id; + if (MMSYSERR_NOERROR == waveOutGetID(internal->hwo,&id)) { + internal->id = id; + } + } + + res = (mmres == MMSYSERR_NOERROR); + return res; +} + +static int _ao_close_device(ao_device *device) +{ + ao_wmm_internal * internal = (ao_wmm_internal *) device->internal; + int res; + MMRESULT mmres; + + mmres = waveOutClose(internal->hwo); + if(mmres == MMSYSERR_NOERROR) { + adebug("waveOutClose(%d)\n => %s\n", internal->id, mmerror(mmres)); + }else{ + aerror("waveOutClose(%d)\n => %s\n", internal->id, mmerror(mmres)); + } + res = (mmres == MMSYSERR_NOERROR); + + return res; +} + +static int _ao_alloc_wave_headers(ao_device *device) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + int bytesPerBlock = internal->wavefmt.Format.nBlockAlign * internal->splPerBlock; + /* int bytes = internal->blocks * (sizeof(WAVEHDR) + bytesPerBlock); */ + int bytes = internal->blocks * (sizeof(*internal->wh) + bytesPerBlock); + int res; + MMRESULT mmres; + + adebug("_ao_alloc_wave_headers blocks=%d, bytes/blocks=%d, total=%d\n", + internal->blocks,bytesPerBlock,bytes); + + internal->bigbuffer = malloc(bytes); + if (internal->bigbuffer != NULL) { + int i; + BYTE * b; + + memset(internal->bigbuffer,0,bytes); + internal->wh = internal->bigbuffer; + internal->spl = (LPBYTE) (internal->wh+internal->blocks); + for (i=0, b=internal->spl; iblocks; ++i, b+=bytesPerBlock) { + internal->wh[i].data = (char*)b; + internal->wh[i].wh.lpData = internal->wh[i].data; + internal->wh[i].length = bytesPerBlock; + internal->wh[i].wh.dwBufferLength = internal->wh[i].length; + internal->wh[i].wh.dwUser = (DWORD_PTR)device; + mmres = waveOutPrepareHeader(internal->hwo, + &internal->wh[i].wh,sizeof(WAVEHDR)); + if (MMSYSERR_NOERROR != mmres) { + aerror("waveOutPrepareHeader(%d) => %s\n",i, mmerror(mmres)); + break; + } + } + if (iblocks) { + while (--i >= 0) { + waveOutUnprepareHeader(internal->hwo, + &internal->wh[i].wh,sizeof(WAVEHDR)); + } + free(internal->bigbuffer); + internal->wh = 0; + internal->spl = 0; + internal->bigbuffer = 0; + } else { + /* all ok ! */ + } + } else { + adebug("malloc() => FAILED\n"); + } + + res = (internal->bigbuffer != NULL); + if(!res){ + aerror("_ao_alloc_wave_headers() => FAILED\n"); + }else{ + adebug("_ao_alloc_wave_headers() => success\n"); + } + return res; +} + +static int _ao_get_free_block(ao_device * device); +static int _ao_wait_wave_headers(ao_device *device, int wait_all) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + int res = 1; + + adebug("wait for %d blocks (%swait all)\n", + internal->sent_blocks,wait_all?"":"not "); + + while (internal->sent_blocks > 0) { + int n; + _ao_get_free_block(device); + n = internal->sent_blocks; + if (n > 0) { + unsigned int ms = (internal->msPerBlock>>1)+1; + if (wait_all) ms *= n; + adebug("sleep for %ums wait on %d blocks\n",ms, internal->sent_blocks); + Sleep(ms); + } + } + + res &= !internal->sent_blocks; + if(!res){ + aerror("_ao_wait_wave_headers => FAILED\n"); + }else{ + adebug("_ao_wait_wave_headers => success\n"); + } + return res; +} + +static int _ao_free_wave_headers(ao_device *device) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + MMRESULT mmres; + int res = 1; + + if (internal->wh) { + int i; + + /* Reset so we dont need to wait ... Just a satefy net + * since _ao_wait_wave_headers() has been called once before. + */ + mmres = waveOutReset(internal->hwo); + adebug("waveOutReset(%d) => %s\n", internal->id, mmerror(mmres)); + /* Wait again to be sure reseted waveheaders has been released. */ + _ao_wait_wave_headers(device,0); + + for (i=internal->blocks; --i>=0; ) { + mmres = waveOutUnprepareHeader(internal->hwo, + &internal->wh[i].wh,sizeof(WAVEHDR)); + if (mmres != MMSYSERR_NOERROR) + aerror("waveOutUnprepareHeader(%d) => %s\n", i, mmerror(mmres)); + + res &= mmres == MMSYSERR_NOERROR; + } + internal->wh = 0; + internal->spl = 0; + } + + if(!res){ + aerror("_ao_alloc_wave_headers() => FAILED\n"); + }else{ + adebug("_ao_alloc_wave_headers() => success\n"); + } + return res; +} + + +/* + * open the audio device for writing to + */ +int ao_wmm_open(ao_device * device, ao_sample_format * format) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + int res = 0; + WAVEFORMATEXTENSIBLE wavefmt; + + adebug("open() channels=%d, bits=%d, rate=%d, format %d(%s)\n", + device->output_channels,format->bits,format->rate,format->byte_format, + format->byte_format==AO_FMT_LITTLE + ?"little" + :(format->byte_format==AO_FMT_NATIVE + ?"native" + :(format->byte_format==AO_FMT_BIG?"big":"unknown"))); + + if(internal->opened) { + aerror("open() => already opened\n"); + goto error_no_close; + } + + /* Force LITTLE as specified by WIN32 API */ + format->byte_format = AO_FMT_LITTLE; + device->driver_byte_format = AO_FMT_LITTLE; + + /* $$$ WMM 8 bit samples are unsigned... Not sure for ao ... */ + /* Yes, ao 8 bit PCM is unsigned -- Monty */ + + /* Make sample format */ + memset(&wavefmt,0,sizeof(wavefmt)); + wavefmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wavefmt.Format.nChannels = device->output_channels; + wavefmt.Format.wBitsPerSample = (((format->bits+7)>>3)<<3); + wavefmt.Format.nSamplesPerSec = format->rate; + wavefmt.Format.nBlockAlign = (wavefmt.Format.wBitsPerSample>>3)*wavefmt.Format.nChannels; + wavefmt.Format.nAvgBytesPerSec = wavefmt.Format.nSamplesPerSec*wavefmt.Format.nBlockAlign; + wavefmt.Format.cbSize = 22; + wavefmt.Samples.wValidBitsPerSample = format->bits; + wavefmt.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + wavefmt.dwChannelMask = device->output_mask; + + internal->wavefmt = wavefmt; + + /* $$$ later this should be optionnal parms */ + internal->blocks = 64; + internal->splPerBlock = 512; + internal->msPerBlock = + (internal->splPerBlock * 1000 + format->rate - 1) / format->rate; + + /* Open device */ + if(!_ao_open_device(device)) + goto error; + internal->opened = 1; + + /* Allocate buffers */ + if (!_ao_alloc_wave_headers(device)) + goto error; + internal->prepared = 1; + + res = 1; + error: + if (!res) { + if (internal->prepared) { + _ao_free_wave_headers(device); + internal->prepared = 0; + } + if (internal->opened) { + _ao_close_device(device); + internal->opened = 0; + } + } + + error_no_close: + if(res){ + adebug("open() => success\n"); + }else{ + aerror("open() => FAILED\n"); + } + return res; +} + + + +/* Send a block to audio hardware */ +static int _ao_send_block(ao_device *device, const int idx) +{ + ao_wmm_internal * internal = (ao_wmm_internal *) device->internal; + MMRESULT mmres; + + /* Satanity checks */ + if (internal->wh[idx].sent) { + adebug("block %d marked SENT\n",idx); + return 0; + } + if (!!(internal->wh[idx].wh.dwFlags & WHDR_DONE)) { + adebug("block %d marked DONE\n",idx); + return 0; + } + + /* count <= 0, just pretend it's been sent */ + if (internal->wh[idx].count <= 0) { + internal->wh[idx].sent = 2; /* set with 2 so we can track these special cases */ + internal->wh[idx].wh.dwFlags |= WHDR_DONE; + ++internal->sent_blocks; + return 1; + } + + internal->wh[idx].wh.dwBufferLength = internal->wh[idx].count; + internal->wh[idx].count = 0; + mmres = waveOutWrite(internal->hwo, + &internal->wh[idx].wh, sizeof(WAVEHDR)); + internal->wh[idx].sent = (mmres == MMSYSERR_NOERROR); + /*&& !(internal->wh[idx].wh.dwFlags & WHDR_DONE);*/ + internal->sent_blocks += internal->wh[idx].sent; + if (mmres != MMSYSERR_NOERROR) { + adebug("waveOutWrite(%d) => %s\n",idx,mmerror(mmres)); + } + return mmres == MMSYSERR_NOERROR; +} + +/* Get idx of next free block. */ +static int _ao_get_free_block(ao_device * device) +{ + ao_wmm_internal * internal = (ao_wmm_internal *) device->internal; + const int idx = internal->widx; + int ridx = internal->ridx; + + while (internal->wh[ridx].sent && !!(internal->wh[ridx].wh.dwFlags & WHDR_DONE)) { + /* block successfully sent to hardware, release it */ + /*debug("_ao_get_free_block: release block %d\n",ridx);*/ + internal->wh[ridx].sent = 0; + internal->wh[ridx].wh.dwFlags &= ~WHDR_DONE; + + --internal->full_blocks; + if (internal->full_blocks<0) { + adebug("internal error with full block counter\n"); + internal->full_blocks = 0; + } + + --internal->sent_blocks; + if (internal->sent_blocks<0) { + adebug("internal error with sent block counter\n"); + internal->sent_blocks = 0; + } + if (++ridx >= internal->blocks) ridx = 0; + } + internal->ridx = ridx; + + return internal->wh[idx].sent + ? -1 + : idx; +} + +/* + * play the sample to the already opened file descriptor + */ +int ao_wmm_play(ao_device *device, + const char *output_samples, uint_32 num_bytes) +{ + int ret = 1; + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + + while(ret && num_bytes > 0) { + int n; + const int idx = _ao_get_free_block(device); + + if (idx == -1) { + Sleep(internal->msPerBlock); + continue; + } + + /* Get free bytes in the block */ + n = internal->wh[idx].wh.dwBufferLength + - internal->wh[idx].count; + + /* Get amount to copy */ + if (n > (int)num_bytes) { + n = num_bytes; + } + + /* Do copy */ + CopyMemory((char*)internal->wh[idx].wh.lpData + + internal->wh[idx].count, + output_samples, n); + + /* Updates pointers and counters */ + output_samples += n; + num_bytes -= n; + internal->wh[idx].count += n; + + /* Is this block full ? */ + if (internal->wh[idx].count + == internal->wh[idx].wh.dwBufferLength) { + ++internal->full_blocks; + if (++internal->widx == internal->blocks) { + internal->widx = 0; + } + ret = _ao_send_block(device,idx); + } + } + + adebug("ao_wmm_play => %d rem => [%s]\n",num_bytes,ret?"success":"error"); + return ret; + +} + +int ao_wmm_close(ao_device *device) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + int ret = 0; + + if (internal->opened && internal->prepared) { + _ao_wait_wave_headers(device, 1); + } + + if (internal->prepared) { + ret = _ao_free_wave_headers(device); + internal->prepared = 0; + } + + if (internal->opened) { + ret = _ao_close_device(device); + internal->opened = 0; + } + + return ret; +} + +void ao_wmm_device_clear(ao_device *device) +{ + ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; + + if (internal->bigbuffer) { + free(internal->bigbuffer); internal->bigbuffer = NULL; + } + free(internal); + device->internal=NULL; +} + +ao_functions ao_wmm = { + ao_wmm_test, + ao_wmm_driver_info, + ao_wmm_device_init, + ao_wmm_set_option, + ao_wmm_open, + ao_wmm_play, + ao_wmm_close, + ao_wmm_device_clear +}; diff --git a/libao/src/src/audio_out.c b/libao/src/src/audio_out.c new file mode 100644 index 0000000..f83199d --- /dev/null +++ b/libao/src/src/audio_out.c @@ -0,0 +1,1514 @@ +/* + * + * audio_out.c + * + * Original Copyright (C) Aaron Holtzman - May 1999 + * Modifications Copyright (C) Stan Seibert - July 2000 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: audio_out.c 17008 2010-03-24 03:12:16Z xiphmont $ + + ********************************************************************/ + +#define STRIP_PLUGINS + +#define HAVE_WMM + +#include +#include +#include +#include +#include +#ifndef STRIP_PLUGINS +#if defined HAVE_DLFCN_H && defined HAVE_DLOPEN +# include +#else +static void *dlopen(const char *filename, int flag) {return 0;} +static char *dlerror(void) { return "dlopen: unsupported"; } +static void *dlsym(void *handle, const char *symbol) { return 0; } +static int dlclose(void *handle) { return 0; } +#undef DLOPEN_FLAG +#define DLOPEN_FLAG 0 +#endif +#endif +#include +#include +#ifndef _MSC_VER +# include +#endif +//#include + +#include "ao/ao.h" +#include "ao/ao_private.h" + +/* These should have been set by the Makefile */ +#ifndef AO_PLUGIN_PATH +#define AO_PLUGIN_PATH "/usr/local/lib/ao" +#endif +#ifndef SHARED_LIB_EXT +#define SHARED_LIB_EXT ".so" +#endif + +/* --- Other constants --- */ +#define DEF_SWAP_BUF_SIZE 1024 + +/* --- Driver Table --- */ + +typedef struct driver_list { + ao_functions *functions; + void *handle; + struct driver_list *next; +} driver_list; + + +extern ao_functions ao_null; +//extern ao_functions ao_wav; +//extern ao_functions ao_raw; +//extern ao_functions ao_au; +#ifdef HAVE_SYS_AUDIO_H +extern ao_functions ao_aixs; +#endif +#ifdef HAVE_WMM +extern ao_functions ao_wmm; +#endif +static ao_functions *static_drivers[] = { + &ao_null, /* Must have at least one static driver! */ + //&ao_wav, + //&ao_raw, + //&ao_au, +#ifdef HAVE_SYS_AUDIO_H + &ao_aixs, +#endif +#ifdef HAVE_WMM + &ao_wmm, +#endif + + NULL /* End of list */ +}; + +static driver_list *driver_head = NULL; +static ao_config config = { + NULL /* default_driver */ +}; + +static ao_info **info_table = NULL; +static int driver_count = 0; + +/* uses the device messaging and option infrastructure */ +static ao_device *ao_global_dummy; +static ao_device ao_global_dummy_storage; +static ao_option *ao_global_options=NULL; + +/* ---------- Helper functions ---------- */ + +/* Clear out all of the library configuration options and set them to + defaults. The defaults should match the initializer above. */ +static void _clear_config() +{ + memset(ao_global_dummy,0,sizeof(*ao_global_dummy)); + ao_global_dummy = NULL; + ao_free_options(ao_global_options); + ao_global_options = NULL; + + free(config.default_driver); + config.default_driver = NULL; +} + + +#ifndef STRIP_PLUGINS +/* Load a plugin from disk and put the function table into a driver_list + struct. */ +static driver_list *_get_plugin(char *plugin_file) +{ + ao_device *device = ao_global_dummy; + driver_list *dt; + void *handle; + char *prompt=""; + + handle = dlopen(plugin_file, DLOPEN_FLAG /* See ao_private.h */); + + if (handle) { + prompt="calloc() failed"; + dt = (driver_list *)calloc(1,sizeof(driver_list)); + if (!dt) return NULL; + + dt->handle = handle; + + dt->functions = (ao_functions *)calloc(1,sizeof(ao_functions)); + if (!(dt->functions)) { + free(dt); + return NULL; + } + + prompt="ao_plugin_test() missing"; + dt->functions->test = dlsym(dt->handle, "ao_plugin_test"); + if (!(dt->functions->test)) goto failed; + + prompt="ao_plugin_driver_info() missing"; + dt->functions->driver_info = + dlsym(dt->handle, "ao_plugin_driver_info"); + if (!(dt->functions->driver_info)) goto failed; + + prompt="ao_plugin_device_list() missing"; + dt->functions->device_init = + dlsym(dt->handle, "ao_plugin_device_init"); + if (!(dt->functions->device_init )) goto failed; + + prompt="ao_plugin_set_option() missing"; + dt->functions->set_option = + dlsym(dt->handle, "ao_plugin_set_option"); + if (!(dt->functions->set_option)) goto failed; + + prompt="ao_plugin_open() missing"; + dt->functions->open = dlsym(dt->handle, "ao_plugin_open"); + if (!(dt->functions->open)) goto failed; + + prompt="ao_plugin_play() missing"; + dt->functions->play = dlsym(dt->handle, "ao_plugin_play"); + if (!(dt->functions->play)) goto failed; + + prompt="ao_plugin_close() missing"; + dt->functions->close = dlsym(dt->handle, "ao_plugin_close"); + if (!(dt->functions->close)) goto failed; + + prompt="ao_plugin_clear() missing"; + dt->functions->device_clear = + dlsym(dt->handle, "ao_plugin_device_clear"); + if (!(dt->functions->device_clear)) goto failed; + + + } else { + aerror("Failed to load plugin %s => dlopen() failed\n",plugin_file); + return NULL; + } + + adebug("Loaded driver %s\n",dt->functions->driver_info()->short_name); + return dt; + + failed: + aerror("Failed to load plugin %s => %s\n",plugin_file,prompt); + free(dt->functions); + free(dt); + return NULL; +} +#endif + + +/* If *name is a valid driver name, return its driver number. + Otherwise, test all of available live drivers until one works. */ +static int _find_default_driver_id (const char *name) +{ + int def_id; + int id; + ao_info *info; + driver_list *dl = driver_head; + ao_device *device = ao_global_dummy; + + adebug("Testing drivers to find playback default...\n"); + if ( name == NULL || (def_id = ao_driver_id(name)) < 0 ) { + /* No default specified. Find one among available drivers. */ + def_id = -1; + + id = 0; + while (dl != NULL) { + + info = dl->functions->driver_info(); + adebug("...testing %s\n",info->short_name); + if ( info->type == AO_TYPE_LIVE && + info->priority > 0 && /* Skip static drivers */ + dl->functions->test() ) { + def_id = id; /* Found a usable driver */ + adebug("OK, using driver %s\n",info->short_name); + break; + } + + dl = dl->next; + id++; + } + } + + return def_id; +} + + +/* Convert the static drivers table into a linked list of drivers. */ +static driver_list* _load_static_drivers(driver_list **end) +{ + ao_device *device = ao_global_dummy; + driver_list *head; + driver_list *driver; + int i; + + /* insert first driver */ + head = driver = calloc(1,sizeof(driver_list)); + if (driver != NULL) { + driver->functions = static_drivers[0]; + driver->handle = NULL; + driver->next = NULL; + adebug("Loaded driver %s (built-in)\n",driver->functions->driver_info()->short_name); + + i = 1; + while (static_drivers[i] != NULL) { + driver->next = calloc(1,sizeof(driver_list)); + if (driver->next == NULL) + break; + + driver->next->functions = static_drivers[i]; + driver->next->handle = NULL; + driver->next->next = NULL; + + driver = driver->next; + adebug("Loaded driver %s (built-in)\n",driver->functions->driver_info()->short_name); + i++; + } + } + + if (end != NULL) + *end = driver; + + return head; +} + + +/* Load the dynamic drivers from disk and append them to end of the + driver list. end points the driver_list node to append to. */ +static void _append_dynamic_drivers(driver_list *end) +{ +#ifdef HAVE_DLOPEN + struct dirent *plugin_dirent; + char *ext; + struct stat statbuf; + DIR *plugindir; + driver_list *plugin; + driver_list *driver = end; + ao_device *device = ao_global_dummy; + + /* now insert any plugins we find */ + plugindir = opendir(AO_PLUGIN_PATH); + adebug("Loading driver plugins from %s...\n",AO_PLUGIN_PATH); + if (plugindir != NULL) { + while ((plugin_dirent = readdir(plugindir)) != NULL) { + char fullpath[strlen(AO_PLUGIN_PATH) + 1 + strlen(plugin_dirent->d_name) + 1]; + snprintf(fullpath, sizeof(fullpath), "%s/%s", + AO_PLUGIN_PATH, plugin_dirent->d_name); + if (!stat(fullpath, &statbuf) && + S_ISREG(statbuf.st_mode) && + (ext = strrchr(plugin_dirent->d_name, '.')) != NULL) { + if (strcmp(ext, SHARED_LIB_EXT) == 0) { + plugin = _get_plugin(fullpath); + if (plugin) { + driver->next = plugin; + plugin->next = NULL; + driver = driver->next; + } + } + } + } + + closedir(plugindir); + } +#endif +} + + +/* Compare two drivers based on priority + Used as compar function for qsort() in _make_info_table() */ +static int _compar_driver_priority (const driver_list **a, + const driver_list **b) +{ + return memcmp(&((*b)->functions->driver_info()->priority), + &((*a)->functions->driver_info()->priority), + sizeof(int)); +} + + +/* Make a table of driver info structures for ao_driver_info_list(). */ +static ao_info ** _make_info_table (driver_list **head, int *driver_count) +{ + driver_list *list; + int i; + ao_info **table; + driver_list **drivers_table; + + *driver_count = 0; + + /* Count drivers */ + list = *head; + i = 0; + while (list != NULL) { + i++; + list = list->next; + } + + + /* Sort driver_list */ + drivers_table = (driver_list **) calloc(i, sizeof(driver_list *)); + if (drivers_table == NULL) + return (ao_info **) NULL; + list = *head; + *driver_count = i; + for (i = 0; i < *driver_count; i++, list = list->next) + drivers_table[i] = list; + qsort(drivers_table, i, sizeof(driver_list *), + (int(*)(const void *, const void *)) + _compar_driver_priority); + *head = drivers_table[0]; + for (i = 1; i < *driver_count; i++) + drivers_table[i-1]->next = drivers_table[i]; + drivers_table[i-1]->next = NULL; + + + /* Alloc table */ + table = (ao_info **) calloc(i, sizeof(ao_info *)); + if (table != NULL) { + for (i = 0; i < *driver_count; i++) + table[i] = drivers_table[i]->functions->driver_info(); + } + + free(drivers_table); + + return table; +} + + +/* Return the driver struct corresponding to particular driver id + number. */ +static driver_list *_get_driver(int driver_id) { + int i = 0; + driver_list *driver = driver_head; + + if (driver_id < 0) return NULL; + + while (driver && (i < driver_id)) { + i++; + driver = driver->next; + } + + if (i == driver_id) + return driver; + + return NULL; +} + + +#ifndef STRIP_PLUGINS +/* Check if driver_id is a valid id number */ +static int _check_driver_id(int driver_id) +{ + int i = 0; + driver_list *driver = driver_head; + + if (driver_id < 0) return 0; + + while (driver && (i <= driver_id)) { + driver = driver->next; + i++; + } + + if (i == (driver_id + 1)) + return 1; + + return 0; +} +#endif + + +/* helper function to convert a byte_format of AO_FMT_NATIVE to the + actual byte format of the machine, otherwise just return + byte_format */ +static int _real_byte_format(int byte_format) +{ + if (byte_format == AO_FMT_NATIVE) { + if (ao_is_big_endian()) + return AO_FMT_BIG; + else + return AO_FMT_LITTLE; + } else + return byte_format; +} + + +/* Create a new ao_device structure and populate it with data */ +static ao_device* _create_device(int driver_id, driver_list *driver, + ao_sample_format *format, FILE *file) +{ + ao_device *device; + + device = calloc(1,sizeof(ao_device)); + + if (device != NULL) { + device->type = driver->functions->driver_info()->type; + device->driver_id = driver_id; + device->funcs = driver->functions; + device->file = file; + device->machine_byte_format = + ao_is_big_endian() ? AO_FMT_BIG : AO_FMT_LITTLE; + device->client_byte_format = + _real_byte_format(format->byte_format); + device->swap_buffer = NULL; + device->swap_buffer_size = 0; + device->internal = NULL; + device->output_channels = format->channels; + device->inter_permute = NULL; + device->output_matrix = NULL; + } + + return device; +} + + +/* Expand the swap buffer in this device if it is smaller than + min_size. */ +static int _realloc_swap_buffer(ao_device *device, int min_size) +{ + void *temp; + + if (min_size > device->swap_buffer_size) { + temp = realloc(device->swap_buffer, min_size); + if (temp != NULL) { + device->swap_buffer = temp; + device->swap_buffer_size = min_size; + return 1; /* Success, realloc worked */ + } else + return 0; /* Fail to realloc */ + } else + return 1; /* Success, no need to realloc */ +} + + +static void _buffer_zero(char *target,int och,int bytewidth,int ochannels,int bytes){ + int i = och*bytewidth; + int stride = bytewidth*ochannels; + switch(bytewidth){ + case 1: + while(ip && isspace(*(t-1)))t--; + + while(mnemonics[m]){ + if(t-p && !strncmp(mnemonics[m],p,t-p) && + strlen(mnemonics[m])==t-p){ + if(count)strcat(ret,","); + strcat(ret,mnemonics[m]); + break; + } + m++; + } + if(!mnemonics[m]){ + /* unrecognized channel mnemonic */ + { + int i; + aerror("Unrecognized channel name \""); + for(i=0;ip && isspace(*(t-1)))t--; + + count++; + if(!*h)break; + p=h+1; + } + + ret = calloc(count+1,sizeof(*ret)); + + p=matrix; + count=0; + while(1){ + char *h,*t; + + /* trim leading space */ + while(*p && isspace(*p))p++; + + /* search for seperator */ + h=p; + while(*h && *h!=',')h++; + + /* trim trailing space */ + t=h; + while(t>p && isspace(*(t-1)))t--; + + ret[count] = calloc(t-p+1,1); + memcpy(ret[count],p,t-p); + count++; + if(!*h)break; + p=h+1; + } + + return ret; + +} + +static void _free_map(char **m){ + char **in=m; + while(m && *m){ + free(*m); + m++; + } + if(in)free(in); +} + +static unsigned int _matrix_to_channelmask(int ch, char *matrix, char *premap, int **mout){ + unsigned int ret=0; + char *p=matrix; + int *perm=(*mout=malloc(ch*sizeof(*mout))); + int i; + char **map = _tokenize_matrix(premap); + + for(i=0;ikey,"debug")){ + ao_global_dummy->verbose=2; + }else if(!strcmp(options->key,"verbose")){ + if(ao_global_dummy->verbose<1)ao_global_dummy->verbose=1; + }else if(!strcmp(options->key,"quiet")){ + ao_global_dummy->verbose=-1; + } + + options = options->next; + } + + return 0; + +} + +static int ao_device_load_options(ao_device *device, ao_option *options){ + + while (options != NULL) { + if(!strcmp(options->key,"matrix")){ + /* If a driver has a static channel mapping mechanism + (physically constant channel mapping, or at least an + unvarying set of constants for mapping channels), the + output_matrix is already set. An app/user specified + output mapping trumps. */ + if(device->output_matrix) + free(device->output_matrix); + /* explicitly set the output matrix to the requested + string; devices must not override. */ + device->output_matrix = _sanitize_matrix(32, options->value, device); + if(!device->output_matrix){ + aerror("Empty or inavlid output matrix\n"); + return AO_EBADOPTION; + } + adebug("Sanitized device output matrix: %s\n",device->output_matrix); + }else if(!strcmp(options->key,"debug")){ + device->verbose=2; + }else if(!strcmp(options->key,"verbose")){ + if(device->verbose<1)device->verbose=1; + }else if(!strcmp(options->key,"quiet")){ + device->verbose=-1; + }else{ + if (!device->funcs->set_option(device, options->key, options->value)) { + /* Problem setting options */ + return AO_EOPENDEVICE; + } + } + + options = options->next; + } + + return 0; +} + +/* Open a device. If this is a live device, file == NULL. */ +static ao_device* _open_device(int driver_id, ao_sample_format *format, + ao_option *options, FILE *file) +{ + ao_functions *funcs; + driver_list *driver; + ao_device *device=NULL; + int result; + ao_sample_format sformat=*format; + sformat.matrix=NULL; + + /* Get driver id */ + if ( (driver = _get_driver(driver_id)) == NULL ) { + errno = AO_ENODRIVER; + goto error; + } + + funcs = driver->functions; + + /* Check the driver type */ + if (file == NULL && + funcs->driver_info()->type != AO_TYPE_LIVE) { + + errno = AO_ENOTLIVE; + goto error; + } else if (file != NULL && + funcs->driver_info()->type != AO_TYPE_FILE) { + + errno = AO_ENOTFILE; + goto error; + } + + /* Make a new device structure */ + if ( (device = _create_device(driver_id, driver, + format, file)) == NULL ) { + errno = AO_EFAIL; + goto error; + } + + /* Initialize the device memory; get static channel mapping */ + if (!funcs->device_init(device)) { + errno = AO_EFAIL; + goto error; + } + + /* Load options */ + errno = ao_device_load_options(device,ao_global_options); + if(errno) goto error; + errno = ao_device_load_options(device,options); + if(errno) goto error; + + /* also sanitize the format input channel matrix */ + if(format->matrix){ + sformat.matrix = _sanitize_matrix(format->channels, format->matrix, device); + if(!sformat.matrix) + awarn("Input channel matrix invalid; ignoring.\n"); + + /* special-case handling of 'M'. */ + if(sformat.channels==1 && sformat.matrix && !strcmp(sformat.matrix,"M")){ + free(sformat.matrix); + sformat.matrix=NULL; + } + } + + /* If device init was able to declare a static channel mapping + mechanism, reconcile it to the input now. Odd drivers that + need to communicate with a backend device to determine + channel mapping strategy can still bypass this mechanism + entirely. Also, drivers like ALSA may need to adjust + strategy depending on what device is successfully opened, + etc, but this still saves work later. */ + + if(device->output_matrix && sformat.matrix){ + switch(device->output_matrix_order){ + case AO_OUTPUT_MATRIX_FIXED: + /* Backend channel ordering is immutable and unused + channels must still be sent. Look for the highest + channel number we are able to map from the input + matrix. */ + { + unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix, + device->output_matrix, + &device->input_map); + int max = _channelmask_maxbit(mask); + if(max<0){ + aerror("Unable to map any channels from input matrix to output"); + errno = AO_EBADFORMAT; + goto error; + } + device->output_channels = max+1; + device->output_mask = mask; + device->inter_matrix = _strdup(device->output_matrix); + } + break; + + case AO_OUTPUT_MATRIX_COLLAPSIBLE: + /* the ordering of channels submitted to the backend is + fixed, but only the channels in use should be present + in the audio stream */ + { + unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix, + device->output_matrix, + &device->input_map); + int channels = _channelmask_bits(mask); + if(channels<0){ + aerror("Unable to map any channels from input matrix to output"); + errno = AO_EBADFORMAT; + goto error; + } + device->output_channels = channels; + device->output_mask = mask; + device->inter_matrix = _channelmask_to_matrix(mask,device->output_matrix); + } + break; + + case AO_OUTPUT_MATRIX_PERMUTABLE: + /* The ordering of channels is freeform. Only the + channels in use should be sent, and they may be sent in + any order. If possible, leave the input ordering + unchanged */ + { + unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix, + device->output_matrix, + &device->input_map); + int channels = _channelmask_bits(mask); + if(channels<0){ + aerror("Unable to map any channels from input matrix to output"); + errno = AO_EBADFORMAT; + goto error; + } + device->output_channels = channels; + device->output_mask = mask; + device->inter_matrix = _matrix_intersect(sformat.matrix,device->output_matrix); + } + break; + + default: + aerror("Driver backend failed to set output ordering.\n"); + errno = AO_EFAIL; + goto error; + } + + }else{ + device->output_channels = sformat.channels; + } + + /* other housekeeping */ + device->input_channels = sformat.channels; + device->bytewidth = (sformat.bits+7)>>3; + device->rate = sformat.rate; + + /* Open the device */ + result = funcs->open(device, &sformat); + if (!result) { + errno = AO_EOPENDEVICE; + goto error; + } + + /* set up permutation based on finalized inter_matrix mapping */ + /* The only way device->output_channels could be zero here is + if the driver has opted to ouput no channels (eg, the null + driver). */ + if(sformat.matrix){ + if(!device->inter_matrix){ + awarn("Driver %s does not support automatic channel mapping;\n" + "\tRouting only L/R channels to output.\n\n", + info_table[device->driver_id]->short_name); + device->inter_matrix=_strdup("L,R"); + } + { + + /* walk thorugh the inter matrix, match channels */ + char *op=device->inter_matrix; + int count=0; + device->inter_permute = calloc(device->output_channels,sizeof(int)); + + adebug("\n"); + + while(countoutput_channels){ + int m=0,mm; + char *h=op; + + if(*op){ + /* find mnemonic offset of inter channel */ + while(*h && *h!=',')h++; + while(mnemonics[m]){ + if(!strncmp(mnemonics[m],op,h-op)) + break; + m++; + } + mm=m; + + /* find match in input if any */ + device->inter_permute[count] = _find_channel(m,sformat.matrix); + if(device->inter_permute[count] == -1 && sformat.channels == 1){ + device->inter_permute[count] = _find_channel(1,sformat.matrix); + mm=1; + } + }else + device->inter_permute[count] = -1; + + /* display resulting mapping for now */ + if(device->inter_permute[count]>=0){ + adebug("input %d (%s)\t -> backend %d (%s)\n", + device->inter_permute[count], mnemonics[mm], + count,mnemonics[m]); + }else{ + adebug(" \t backend %d (%s)\n", + count,mnemonics[m]); + } + count++; + op=h; + if(*h)op++; + } + { + char **inch = _tokenize_matrix(sformat.matrix); + int i,j; + int unflag=0; + for(j=0;joutput_channels;i++) + if(device->inter_permute[i]==j)break; + if(i==device->output_channels){ + adebug("input %d (%s)\t -> none\n", + j,inch[j]); + unflag=1; + } + } + _free_map(inch); + if(unflag) + awarn("Some input channels are unmapped and will not be used.\n"); + } + averbose("\n"); + + } + } + + /* if there's no actual permutation to do, release the permutation vector */ + if(device->inter_permute && device->output_channels == device->input_channels){ + int i; + for(i=0;ioutput_channels;i++) + if(device->inter_permute[i]!=i)break; + if(i==device->output_channels){ + free(device->inter_permute); + device->inter_permute=NULL; + } + } + + /* Resolve actual driver byte format */ + device->driver_byte_format = + _real_byte_format(device->driver_byte_format); + + /* Only create swap buffer if needed */ + if (device->bytewidth>1 && + device->client_byte_format != device->driver_byte_format){ + adebug("swap buffer required:\n"); + adebug(" machine endianness: %d\n",ao_is_big_endian()); + adebug(" device->client_byte_format:%d\n",device->client_byte_format); + adebug(" device->driver_byte_format:%d\n",device->driver_byte_format); + } + + if ( (device->bytewidth>1 && + device->client_byte_format != device->driver_byte_format) || + device->inter_permute){ + + result = _realloc_swap_buffer(device, DEF_SWAP_BUF_SIZE); + + if (!result) { + + if(sformat.matrix)free(sformat.matrix); + device->funcs->close(device); + device->funcs->device_clear(device); + free(device); + errno = AO_EFAIL; + return NULL; /* Couldn't alloc swap buffer */ + } + } + + /* If we made it this far, everything is OK. */ + if(sformat.matrix)free(sformat.matrix); + return device; + + error: + { + int errtemp = errno; + if(sformat.matrix) + free(sformat.matrix); + ao_close(device); + errno=errtemp; + } + return NULL; +} + + +/* ---------- Public Functions ---------- */ + +/* -- Library Setup/Teardown -- */ + +static ao_info ao_dummy_info= + { 0,0,0,0,0,0,0,0,0 }; +static ao_info *ao_dummy_driver_info(void){ + return &ao_dummy_info; +} +static ao_functions ao_dummy_funcs= + { 0, &ao_dummy_driver_info, 0,0,0,0,0,0,0}; + +void ao_initialize(void) +{ + driver_list *end; + ao_global_dummy = &ao_global_dummy_storage; + ao_global_dummy->funcs = &ao_dummy_funcs; + + /* Read config files */ + ao_read_config_files(&config); + ao_global_load_options(ao_global_options); + + if (driver_head == NULL) { + driver_head = _load_static_drivers(&end); + _append_dynamic_drivers(end); + } + + /* Create the table of driver info structs */ + info_table = _make_info_table(&driver_head, &driver_count); +} + +void ao_shutdown(void) +{ + driver_list *driver = driver_head; + driver_list *next_driver; + + if (!driver_head) return; + + /* unload and free all the drivers */ + while (driver) { + if (driver->handle) { +#ifndef STRIP_PLUGINS + dlclose(driver->handle); +#endif + free(driver->functions); /* DON'T FREE STATIC FUNC TABLES */ + } + next_driver = driver->next; + free(driver); + driver = next_driver; + } + + free(info_table); + + _clear_config(); + /* NULL out driver_head or ao_initialize() won't work */ + driver_head = NULL; +} + + +/* -- Device Setup/Playback/Teardown -- */ +int ao_append_option(ao_option **options, const char *key, const char *value) +{ + ao_option *op, *list; + + op = calloc(1,sizeof(ao_option)); + if (op == NULL) return 0; + + op->key = _strdup(key); + op->value = _strdup(value?value:""); + op->next = NULL; + + if ((list = *options) != NULL) { + list = *options; + while (list->next != NULL) list = list->next; + list->next = op; + } else { + *options = op; + } + + + return 1; +} + +int ao_append_global_option(const char *key, const char *value) +{ + return ao_append_option(&ao_global_options,key,value); +} + +void ao_free_options(ao_option *options) +{ + ao_option *rest; + + while (options != NULL) { + rest = options->next; + free(options->key); + free(options->value); + free(options); + options = rest; + } +} + + +ao_device *ao_open_live (int driver_id, ao_sample_format *format, + ao_option *options) +{ + return _open_device(driver_id, format, options, NULL); +} + + +ao_device *ao_open_file (int driver_id, const char *filename, int overwrite, + ao_sample_format *format, ao_option *options) +{ + FILE *file; + ao_device *device; + + if (strcmp("-", filename) == 0) + file = stdout; + else { + + if (!overwrite) { + /* Test for file existence */ + file = fopen(filename, "r"); + if (file != NULL) { + fclose(file); + errno = AO_EFILEEXISTS; + return NULL; + } + } + + + file = fopen(filename, "w"); + } + + + if (file == NULL) { + errno = AO_EOPENFILE; + return NULL; + } + + device = _open_device(driver_id, format, options, file); + + if (device == NULL) { + fclose(file); + /* errno already set by _open_device() */ + return NULL; + } + + return device; +} + + +int ao_play(ao_device *device, char* output_samples, uint_32 num_bytes) +{ + char *playback_buffer; + + if (device == NULL) + return 0; + + if (device->swap_buffer != NULL) { + int out_bytes = num_bytes*device->output_channels/device->input_channels; + if (_realloc_swap_buffer(device, out_bytes)) { + int i; + int swap = (device->bytewidth>1 && + device->client_byte_format != device->driver_byte_format); + for(i=0;ioutput_channels;i++){ + int ic = device->inter_permute ? device->inter_permute[i] : i; + if(ic==-1){ + _buffer_zero(device->swap_buffer,i,device->bytewidth,device->output_channels, + out_bytes); + }else if(swap){ + _buffer_permute_swap(device->swap_buffer,i,device->bytewidth,device->output_channels, + out_bytes, output_samples, ic, device->input_channels); + }else{ + _buffer_permute(device->swap_buffer,i,device->bytewidth,device->output_channels, + out_bytes, output_samples, ic, device->input_channels); + } + } + playback_buffer = device->swap_buffer; + num_bytes = out_bytes; + } else + return 0; /* Could not expand swap buffer */ + } else + playback_buffer = output_samples; + + return device->funcs->play(device, playback_buffer, num_bytes); +} + + +int ao_close(ao_device *device) +{ + int result; + + if (device == NULL) + result = 0; + else { + result = device->funcs->close(device); + device->funcs->device_clear(device); + + if (device->file) { + fclose(device->file); + device->file = NULL; + } + + if (device->swap_buffer != NULL) + free(device->swap_buffer); + if (device->output_matrix != NULL) + free(device->output_matrix); + if (device->input_map != NULL) + free(device->input_map); + if (device->inter_matrix != NULL) + free(device->inter_matrix); + if (device->inter_permute != NULL) + free(device->inter_permute); + free(device); + } + + return result; +} + + +/* -- Driver Information -- */ + +int ao_driver_id(const char *short_name) +{ + int i; + driver_list *driver = driver_head; + + i = 0; + while (driver) { + if (strcmp(short_name, + driver->functions->driver_info()->short_name) == 0) + return i; + driver = driver->next; + i++; + } + + return -1; /* No driver by that name */ +} + + +int ao_default_driver_id (void) +{ + /* Find the default driver in the list of loaded drivers */ + + return _find_default_driver_id(config.default_driver); +} + + +ao_info *ao_driver_info(int driver_id) +{ + driver_list *driver; + + if ( (driver = _get_driver(driver_id)) ) + return driver->functions->driver_info(); + else + return NULL; +} + + +ao_info **ao_driver_info_list(int *count) +{ + *count = driver_count; + return info_table; +} + + +/* -- Miscellaneous -- */ + +/* Stolen from Vorbis' lib/vorbisfile.c */ +int ao_is_big_endian(void) +{ + static uint_16 pattern = 0xbabe; + return 0[(volatile unsigned char *)&pattern] == 0xba; +} diff --git a/libao/src/src/config.c b/libao/src/src/config.c new file mode 100644 index 0000000..9693730 --- /dev/null +++ b/libao/src/src/config.c @@ -0,0 +1,111 @@ +/* + * + * config.c + * + * Copyright (C) Stan Seibert - July 2000 + * Copyright (C) Monty - Mar 2010 + * + * This file is part of libao, a cross-platform audio output library. See + * README for a history of this source code. + * + * libao is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * libao is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************** + + last mod: $Id: config.c 17763 2010-12-17 11:28:29Z xiphmont $ + + ********************************************************************/ + +#include "ao/ao.h" +#include "ao/ao_private.h" +#include +#include +#include +#include +#include + +#define LINE_LEN 100 + +static char *trim(char *p){ + char *t; + while(*p && isspace(*p))p++; + if(*p){ + t=p+strlen(p); + while(t>p && isspace(*(t-1))){ + t--; + *t='\0'; + } + } + return p; +} + +static int ao_read_config_file(ao_config *config, const char *config_file) +{ + FILE *fp; + char line[LINE_LEN]; + int end; + + if ( !(fp = fopen(config_file, "r")) ) + return 0; /* Can't open file */ + + while (fgets(line, LINE_LEN, fp)) { + /* All options are key=value */ + + if (strncmp(line, "default_driver=", 15) == 0) { + free(config->default_driver); + end = strlen(line); + if (line[end-1] == '\n') + line[end-1] = 0; /* Remove trailing newline */ + + config->default_driver = _strdup(line+15); + }else{ + /* entries in the config file that don't parse as + directives to AO at large are treated as driver + options */ + char *key=trim(line); + if(key && *key){ + char *val=strchr(key,'='); + if(val){ + *val='\0'; + val++; + } + ao_append_global_option(key,val); + } + } + } + + fclose(fp); + + return 1; +} + +void ao_read_config_files (ao_config *config) +{ + char userfile[FILENAME_MAX+1]; + char *homedir = getenv("HOME"); + + /* Read the system-wide config file */ + ao_read_config_file(config, AO_SYSTEM_CONFIG); + + /* Read the user config file */ + if ( homedir!=NULL && + strlen(homedir) <= FILENAME_MAX - strlen(AO_USER_CONFIG) ) + { + strncpy(userfile, homedir, FILENAME_MAX); + strcat(userfile, AO_USER_CONFIG); + ao_read_config_file(config, userfile); + } +} + -- cgit v1.2.3